By Arnon Puitrakul - 25 พฤศจิกายน 2021
เวลาเราใช้งานพวก Library ต่าง ๆ เรายกตัวอย่างเช่น Pandas ละกันหลาย ๆ คนน่าจะต้องผ่านมือกันมาบ้างแล้ว ตัว Pandas เราต้องเข้าใจเขาว่า เขาออกแบบมาให้ทำงานได้กว้างมาก ๆ เลยทีเดียว ตั้งแต่ข้อมูลขนาดเล็ก ๆ ไม่ซับซ้อน ยันข้อมูลที่มีขนาดใหญ่พอสมควร และ มีความซับซ้อนสูง ๆ การทำงานกับข้อมูลที่มีความหลากหลายมาก ๆ โดยเฉพาะตัวที่มันซับซ้อนมาก ๆ ถ้าเราเขียนโปรแกรมด้วยวิธีทั่ว ๆ ไป มันก็จะทำงานได้ช้า ซึ่งแน่ละ เราก็เขียนให้มันเร็วขึ้นได้ แต่มันก็เร็วขึ้นได้อีกประมาณนึงเท่านั้น แล้วเขาทำยังไงละ ถึงทำให้ Pandas มันเร็วได้อีก เร็วอี๊กกกก
นั่นคือการใช้ Concept เรื่องของ Multiprogramming เข้ามาช่วย เช่นการกระจายงานไปตาม Thread ต่าง ๆ เพื่อให้มันทำงานได้เร็วขึ้นบน CPU รุ่นใหม่ ๆ ที่มีจำนวน Thread ในการประมวลผลมากขึ้นเรื่อย ๆ นั่นเอง แต่เราจะบอกว่า การทำแบบนี้ตรง ๆ กับทุก ๆ เคส การเพิ่ม Thread ไม่ได้ทำให้เราได้ Performance ที่ดีขึ้นเสมอไป วันนี้เราไปหาคำตอบกันว่าทำไม และ เราจะแก้ปัญหานี้ได้อย่างไรบ้าง
เราเริ่มจากเคสที่ง่ายที่สุดก่อนละกัน ถ้าเราเขียนโปรแกรมแบบที่เราไม่ได้มีการเรียกใช้พวก Multiprogramming Pattern อะไรเลย เราเขียนเป็นแบบ Single Thread เราก็จะใช้งาน CPU เราได้แค่ Thread เดียว หรือถ้าเราเทียบเป็น UNIX Utilisation ก็จะได้ 100% ไป
เมื่อเราลองบอกว่า โอเค เราจะเรียกพวก Multiprogramming เข้ามา เช่นเราเรียกการใช้งาน Threading เข้ามา เราบอกว่า งั้นเราแบ่งงานไปเลย 2 Thread เอาไปกันคนละครึ่ง มันก็จะต้องมีการ แบ่งงานออกจากกัน การกระจายงาน การส่ง Signal ต่าง ๆ ระหว่าง Thread นั่นนี่ ง่าย ๆ คือ มันก็จะเริ่มมีส่วนของ Overhead เกิดขึ้นแล้ว แต่แน่นอนว่าเมื่อเราแบ่งงานกันแบบนี้ทำให้เราสามารถใช้ Utilisation ได้มากขึ้นอาจจะเป็น 200% ไปก็เป็นได้ และเราเพิ่มแบบนี้ไปเรื่อย ๆ ก็ดูเหมือนเราจะใช้ CPU ได้มากขึ้นเรื่อย ๆ แต่จริง ๆ แล้ว มันไม่ได้เป็นแบบนั้นเลย (ไม่ได้เกี่ยวว่า เครื่องเรามีกี่ Thread ด้วยนะ)
เมื่อเราเพิ่ม Thread ไปเรื่อย ๆ (แต่งานเราเท่าเดิม) ถึงจุดนึง CPU Utilisation เราจะทำได้น้อยลง เราเรียกอาการแบบนี้ว่า Thrashing อย่างที่เราบอกว่า การเพิ่ม Thread ไปเรื่อย ๆ มันไม่ได้เอาแค่งานไป แต่มันมี Overhead และการ Copy ข้อมูลไปมาอีกหลายเรื่องมาก ๆ ประกอบกับ Resource ของเรามันมีจำกัด ทำให้ OS มันจำเป็นที่จะต้อง เลือก หรือ สลับงาน กันไปมา แทนที่มันจะทำงานเรียง ๆ เอาให้เสร็จ แต่การสลับงานมันก็มีราคาของมันที่ต้องจ่ายในอีกหลาย ๆ ส่วนอีก ที่เราขอไม่กล่าวถึงละกันมันยาววววววววว นั่นแปลว่าเวลาในการทำงานของเรามันก็จะเพิ่มขึ้นด้วยเช่นกัน มันก็จะกลายเป็นว่า อ้าว.... ทำไมเราเพิ่ม Thread ไปแล้วมันช้าลงละ นั่นก็เป็นเพราะปรากฏการณ์แบบนี้แหละ
ถามคำถามนี้ การจะหาคำตอบได้ยากอยู่ เราไม่สามารถบอก Magic Number ได้ เพราะมันไม่มีอยู่ มันขึ้นกับหลาย ๆ ปัจจัย เช่น ลักษณะการทำงานของโปรแกรม และข้อมูลต่าง ๆ ที่เราเอาเข้า และ เอาออก มันมีผลกันหมด
แต่เราจะมาคลายความเข้าใจผิดอะไรบางอย่างละกัน โดยเฉพาะ การที่บอกว่า เราไม่ควรที่จะตั้งจำนวน Thread มากกว่าจำนวน Thread ใน CPU ของเรา จริง ๆ อื้ม.... ต้องบอกว่าถูกครึ่ง ผิดครึ่งละกัน เพราะจริง ๆ แล้ว มันจะมีเคสที่เราแนะนำให้ลองตั้ง Thread จำนวนเยอะกว่า Thread บน CPU จริง ๆ เช่น Thread ที่มันต้องมีจังหวะการรอ เช่นรอข้อมูลจาก Disk หรือ Network พวกนี้พอมันทำงานไปถึงจุดนึง เราลองสังเกต CPU Utilisation มันจะแกว่ง ๆ ไม่ก็รันไปแปบ ๆ อ้าว.... เหลือไม่ถึง 100% ซะงั้น เป็นเพราะมันถึงจุดที่มันต้องรอโหลดข้อมูล หรือ รับส่งข้อมูลต่าง ๆ ก่อนที่มันจะรันต่อได้ ทำให้พวกนี้แหละ เราสามารถอัด Thread ไปได้เยอะ ๆ เลย เพื่อ Fed ให้ CPU เราทำงานได้ตลอด ๆ ได้มากที่สุด มันก็จะลดเวลาในการคำนวณไปได้ แต่แน่นอนว่า การเพิ่มไปเยอะ ๆๆๆๆๆๆ เลยก็ไม่ใช่คำตอบอีก เพราะ มันก็จะไปคอขวดตรงส่วนที่รอนี่แหละ เคยเจอเคสที่ทำให้ HDD แตกมาแล้ว เพราะ HDD มันมีธรรมชาติในการอ่านเขียนไม่เหมือนกับ SSD ตอนนั้นไม่รู้ HDD พังไป 2 ลูก อ่านข้อมูลไม่ขึ้นเลย เศร้าไปอีก ดังนั้นสุดท้ายมันก็จะต้องมี Limit อยู่ดี
ว่าแต่เคสไหนละที่เราควรจะตั้งไม่เกินจำนวน Thread ที่ CPU ของเรามี ก็คือเคสที่ Thread เราเน้นการคำนวณเป็นหลักเลย เช่นเราบอกให้มันบวกเลขเยอะ ๆ ทั้งงานคือคำนวณแล้วยัดใส่ Memory ล้วน ๆ แบบนั้นแหละ เราควรที่จะไม่เริ่มต้นเลือกจำนวน Thread ที่สูงกว่าที่ CPU เราทำงานได้พร้อม ๆ กัน ไม่งั้นมันจะไปถึงจุดที่ Thrashing ได้เร็วมาก ๆ เพราะ OS มันก็รับบทแม่พระ สลับงานให้ เห็นมาเยอะ เอาไปกันคนละนิดคนละหน่อย เจอค่าสลับเข้าไปอ้วกเลย ช้ากว่าเดิมอีก
ทำให้นำไปสู่คำถามที่ว่า แล้วเราจะหายังไงละว่าเท่าไหร่ สั้น ๆ สำหรับเราเลยนะคือ ลอง เท่านั้นเลย เราอาจจะลองกับ Input ที่ใหญ่ประมาณนึง ค่อย ๆ ลองเพิ่ม ๆ ลด ๆ ไปเรื่อย ๆ สุดท้าย เราน่าจะเจอจุดที่เป็น Optimal ของมันก็ได้
ในการตั้งค่าจำนวน Thread ที่เราจะให้พวกหลาย ๆ Library อย่าง Pandas และ Numpy มันทำงาน เราสามารถทำได้ผ่านการตั้ง Environment Variable ที่ชื่อว่า OMP_NUM_THREADS ถ้าใครที่เคยใช้ OpenMP น่าจะคุ้นชื่อกัน มันคือตัวเดียวกันเลย
OMP_NUM_THREADS=1 python run_benchmark.py
โดยเราสามารถกำหนดได้ตรง ๆ เลย ถ้าเป็นฝั่งของ UNIX อย่าง macOS และ Linux เราสามารถกำหนดตอนที่เรารันได้เลย เช่น Command ด้านบน หรือถ้าเป็น Windows อาจจะต้องไปหาว่าการตั้งค่า Environment Variable ทำอย่างไร เท่านี้โปรแกรมเราก็จะรันได้ตามจำนวน Thread ที่เราต้องการได้แล้ว ทั่ว ๆ ไปเริ่มต้นโปรแกรมมันจะพยายาม Parallel โดยอ้างอิงจากจำนวน Thread ที่เรามีใน CPU ล้วน ๆ เลย ทำให้ถ้าเราต้องการใช้จำนวนนั้นอยู่แล้ว เราก็ไม่จำเป็นต้องไปเซ็ตอะไรเพิ่มเด้อ
การตั้งค่าจำนวน Thread ที่เรารัน ก็เป็นวิธีหนึ่งในการช่วยให้เรา Optmise การทำงานของโปรแกรมเราได้ (แต่ก็ทำให้มันแย่ลงได้เหมือนกัน) ขึ้นกับลักษณะของโปรแกรมที่เราทำงานด้วยว่า มันมีการรอพวก Disk หรือ Network เยอะขนาดไหน หรือต้องคำนวณตรงไหนเยอะขนาดไหนด้วย ทำให้จำนวน Thread ที่ควรตั้ง มันบอกยากมาก ไม่มี Magic Number ที่แน่นอน ดังนั้น เราจะต้องค่อย ๆ ลองเพื่อหาจำนวน Thread ที่เหมาะสมกับโปรแกรมของเราอีกที
Obsidian เป็นโปรแกรมสำหรับการจด Note ที่เรียกว่า สารพัดประโยชน์มาก ๆ เราสามารถเอามาทำอะไรได้เยอะมาก ๆ หนึ่งในสิ่งที่เราเอามาทำคือ นำมาใช้เป็นระบบสำหรับการจัดการ Todo List ในแต่ละวันของเรา ทำอะไรบ้าง วันนี้เราจะมาเล่าให้อ่านกันว่า เราจัดการะบบอย่างไร...
อะ อะจ๊ะเอ๋ตัวเอง เป็นยังไงบ้างละ เมื่อหลายเดือนก่อน เราไปเล่าเรื่องกันขำ ๆ ว่า ๆ จริง ๆ แล้วพวก Loop ที่เราใช้เขียนโปรแกรมกันอยู่ มันไม่มีอยู่จริง สิ่งที่เราใช้งานกันมันพยายาม Abstract บางอย่างออกไป วันนี้เราจะมาถอดการทำงานของ Loop จริง ๆ กันว่า มันทำงานอย่างไรกันแน่ ผ่านภาษา Assembly...
นอกจากการทำให้ Application รันได้แล้ว อีกเรื่องที่สำคัญไม่แพ้กันคือการวางระบบ Monitoring ที่ดี วันนี้เราจะมาแนะนำวิธีการ Monitor การทำงานของ MySQL ผ่านการสร้าง Dashboard บน Grafana กัน...
จากตอนที่แล้ว เราเล่าในเรื่องของการ Harden Security ของ SSH Service ของเราด้วยการปรับการตั้งค่าบางอย่างเพื่อลด Attack Surface ที่อาจจะเกิดขึ้นได้ หากใครยังไม่ได้อ่านก็ย้อนกลับไปอ่านกันก่อนเด้อ วันนี้เรามาเล่าวิธีการที่มัน Advance มากขึ้น อย่างการใช้ fail2ban...