- กฎของ Moore กำลังชนข้อจำกัด ทำให้ฮาร์ดแวร์พัฒนาจากการเร่งความเร็วคอร์เดี่ยวไปสู่การเน้น มัลติคอร์และการประมวลผลแบบขนาน
- การประมวลผลแบบขนาน แบ่งได้หลายรูปแบบ เช่น data parallelism, model parallelism และ pipeline parallelism และถูกนำมาใช้ผสมกันในระบบดีปเลิร์นนิงสมัยใหม่
- มีการทำงานแบบขนานในหลายชั้น ตั้งแต่ SIMD (data parallelism ระดับคำสั่ง), การขนานระดับเธรด/คอร์, ไปจนถึง การขนานขนาดใหญ่บน GPU
- ภาษา·ไลบรารี·เครื่องมือสำหรับการประมวลผลแบบขนาน ส่วนใหญ่เป็นส่วนขยายที่ “นำไปต่อเพิ่ม” บนภาษาลำดับเดิม จึงมีแนวโน้มที่ได้รับความสนใจคือการผสานความขนานเข้าเป็น native ของภาษาโดยตรง (เช่น Mojo)
- งานสำคัญด้านการปรับแต่งประสิทธิภาพจริง ได้แก่ การปรับ false sharing ของ cache line (ปฏิสัมพันธ์ที่ไม่จำเป็น), การแบ่งหน่วยความจำอย่างมีประสิทธิภาพ, และ การทำ automatic vectorization
แรงจูงใจของความขนานและวิวัฒนาการของฮาร์ดแวร์
- ในช่วงแรก ประสิทธิภาพดีขึ้นตามธรรมชาติจากการย่อทรานซิสเตอร์และการเพิ่มความถี่สัญญาณนาฬิกา แต่สุดท้ายก็ชน ข้อจำกัดด้านความร้อน/กระบวนการผลิต จนถึงขีดจำกัดทางกายภาพ
- หลังจากนั้น สถาปัตยกรรมมัลติคอร์ ก็กลายเป็นมาตรฐาน โดย CPU หนึ่งตัวมีตั้งแต่หลายคอร์ไปจนถึงหลายร้อยคอร์
รูปแบบทั่วไปของความขนาน
- Data parallelism: ใช้การคำนวณแบบเดียวกันกับข้อมูลจำนวนมากพร้อมกัน (เช่น การบวกเวกเตอร์)
- Model parallelism: กระจายโมเดลหนึ่งตัวไปวางบนหลายอุปกรณ์
- Pipeline parallelism: แบ่งการคำนวณออกเป็นหลายขั้น และให้แต่ละขั้นทำงานพร้อมกัน
SIMD (single instruction, multiple data) และการทำ vectorization
- SIMD คือวิธีประมวลผลข้อมูลหลายตัว (เวกเตอร์) ด้วยคำสั่งเดียว โดยมีการรองรับใน ISA หลายแบบ เช่น ARM NEON และ x86 SSE/AVX
- สามารถควบคุมการคำนวณเวกเตอร์แบบชัดเจนผ่าน C/C++ intrinsics ได้ และคอมไพเลอร์ก็รองรับ automatic vectorization เช่นกัน แต่ยังมีข้อจำกัด
- ในการใช้งานจริง มักประมวลผลซ้ำตามความยาวเวกเตอร์ก่อน แล้วใช้การคำนวณแบบสเกลาร์มาจัดการข้อมูลส่วนที่เหลือ
การทำงานแบบขนานบน CPU
- ใช้ เธรด เพื่อรันแบบขนานบนมัลติคอร์ โดยมี API ตามภาษาและการรองรับจากตัวจัดตารางงานของ OS
- เนื่องจากต้นทุนในการสร้าง/ทำลายเธรดสูง การแบ่งงานด้วยจำนวนเธรดที่เหมาะสมกับขนาดข้อมูล (และใกล้เคียงจำนวนคอร์) จึงมีประสิทธิภาพกว่า
- การปรับ cache line 'false sharing' ให้เหมาะสมมีความสำคัญมาก (ประสิทธิภาพลดลงเมื่อเธรดต่าง ๆ เข้าถึงตัวแปรอิสระที่อยู่ใน cache line เดียวกัน) โดยอาจใช้ C++17 เช่น
std::hardware_destructive_interference_size
- ควรทำ padding/alignment เพื่อให้แต่ละเธรดเขียนลงในพื้นที่ข้อมูลของตัวเองแยกจากกัน
การทำงานแบบขนานบน GPU
- GPU ถูกออกแบบมาเฉพาะทางสำหรับ การประมวลผลแบบขนานของข้อมูลขนาดใหญ่ ผ่านคอร์ขนาดเล็กจำนวนหลายพันคอร์
- CUDA/OpenCL: รันฟังก์ชันเคอร์เนลในระดับเธรด/บล็อกตั้งแต่หลายสิบไปจนถึงหลายหมื่นหน่วย โดยภายในใช้โมเดล SIMT (single instruction, multiple threads)
- ทำงานเป็นหน่วย work group/warp และการลด branch divergence ให้ต่ำที่สุดมีความสำคัญต่อประสิทธิภาพอย่างมาก
- โครงสร้างลำดับชั้นของหน่วยความจำ: ต้องปรับให้เหมาะสมในหลายระดับ เช่น thread register, block shared memory และ global memory ทั้งระบบ
Triton: GPU kernel DSL บน Python
- Triton เป็น DSL ที่ฝังอยู่ใน Python รองรับการคอมไพล์แบบ JIT และหลายแบ็กเอนด์ (MLIR/LLVM/PTX เป็นต้น)
- สามารถเขียนโค้ดเคอร์เนลด้วย Python ระดับสูง และรองรับการทำให้ขนานอัตโนมัติ การแบ่งงาน และการ masking
- ให้ประสิทธิภาพได้ราว 75~90% ของระดับ NVIDIA cuDNN พร้อมลดความซับซ้อนในการพัฒนาได้มาก
ทำให้ความขนานเป็น native ของภาษา: Mojo
- Mojo เป็นภาษาใหม่ที่สร้างโดย Chris Lattner ผู้พัฒนา LLVM/MLIR โดยรองรับความขนานและการคอมไพล์เฉพาะฮาร์ดแวร์ในระดับภาษา
- มีการฝังความขนานไว้ใน ระบบชนิดข้อมูลและโครงสร้างภาษา เช่น SIMD vector type, ฟังก์ชัน vectorization และการแยกหน่วยความจำ host/device
- แม้แต่ลูปสไตล์ Python ก็ยังทำ automatic vectorization ได้ ช่วยให้ได้ประสิทธิภาพโดยไม่ต้องควบคุมระดับล่างอย่างชัดเจน
บทสรุปและแนวโน้ม
- การเขียนโปรแกรมแบบขนานสมัยใหม่ประกอบขึ้นจากการผสมผสานของฮาร์ดแวร์และโมเดลความขนานหลายแบบ และการรองรับจากตัวภาษาเองก็ยิ่งสำคัญขึ้นเรื่อย ๆ
- การมาของ ภาษา·เครื่องมือสำหรับความขนานยุคถัดไป เช่น Mojo, Triton และ JAX กำลังทำให้การเขียนโปรแกรมแบบขนานพัฒนาไปในทิศทางที่เข้าใจง่ายและมีผลิตภาพมากขึ้น
- การเขียนโปรแกรมแบบขนานจะดึงประสิทธิภาพสูงสุดออกมาได้จริง ก็ต่อเมื่อโครงสร้างฮาร์ดแวร์ การปรับหน่วยความจำให้เหมาะสม และการรองรับในระดับภาษาทำงานร่วมกันอย่างเป็นระบบ
ยังไม่มีความคิดเห็น