นักพัฒนาคนหนึ่งได้ค้นพบปัญหาคอขวดด้านประสิทธิภาพที่สำคัญในเครื่องมือประมวลผล LLM ยอดนิยม และพบวิธีแก้ไขที่เรียบง่ายอย่างน่าประหลาดใจซึ่งสามารถเพิ่มความเร็วในการประมวลผลได้เป็นสองเท่า ความก้าวหน้านี้เกิดขึ้นหลังจากใช้เวลาหลายวันในการแก้ไขปัญหาว่าทำไม llama.cpp ถึงมีปัญหากับการประมวลผลแบบกลุ่ม ในขณะที่ vLLM สามารถจัดการงานเดียวกันได้อย่างง่ายดาย
ปัญหาการจัดเรียงหน่วยความจำที่ซ่อนอยู่
ปัญหาไม่ได้อยู่ที่การใช้งาน GPU หรือพลังการคำนวณ แต่อยู่ที่วิธีการจัดเรียงข้อมูลในหน่วยความจำ ขณะที่ทำการทดสอบทั้ง llama.cpp และ vLLM บนการ์ดกราฟิก RTX 4070 เดียวกัน นักพัฒนาสังเกตเห็นว่า llama.cpp เริ่มทำงานช้าลงอย่างรุนแรงเมื่อประมวลผล 8 คำสั่งหรือมากกว่าพร้อมกัน แม้ว่าการใช้งาน GPU จะดูปกติ ในขณะเดียวกัน vLLM สามารถจัดการงานแบบกลุ่มเดียวกันได้โดยไม่มีปัญหา
สาเหตุหลักอยู่ที่วิธีการจัดระเบียบข้อมูล key-value cache ในหน่วยความจำของแต่ละระบบ Llama.cpp ใช้การจัดเรียงหน่วยความจำแบบเรียบที่ทำงานได้ดีสำหรับคำสั่งเดียว แต่สร้างรูปแบบการเข้าถึงหน่วยความจำที่ไม่มีประสิทธิภาพเมื่อจัดการหลายคำสั่งพร้อมกัน ทำให้ GPU ต้องทำการอ่านหน่วยความจำแบบกระจัดกระจายและไม่ต่อเนื่อง ซึ่งเป็นการสูญเสียแบนด์วิดท์ที่มีค่า
Key-value cache: เทคนิคการปรับปรุงหน่วยความจำที่เก็บค่า attention ที่คำนวณไว้แล้วเพื่อหลีกเลี่ยงการคำนวณซ้ำ ซึ่งมีความสำคัญต่อการประมวลผลโมเดลภาษาอย่างมีประสิทธิภาพ
การเปรียบเทียบโครงสร้างหน่วยความจำ:
- โครงสร้างเดิมของ llama.cpp : [sequence, head, dimension] - ทำให้เกิดการเข้าถึงหน่วยความจำแบบ strided
- โครงสร้างที่ปรับปรุงแล้ว: [head, sequence, dimension] - ช่วยให้การอ่านหน่วยความจำเป็นแบบ coalesced
- การปรับปรุงประสิทธิภาพ: เร็วขึ้น 2 เท่าด้วยการดำเนินการเดียวกัน
วิธีแก้ไขง่ายๆ ที่เปลี่ยนทุกอย่าง
วิธีแก้ไขเกี่ยวข้องกับการปรับรูปร่างวิธีการเก็บข้อมูลใน memory tensors โดยการเปลี่ยนการจัดเรียงจาก [sequence, head, dimension] เป็น [head, sequence, dimension] นักพัฒนาได้จัดโครงสร้างข้อมูลให้สอดคล้องกับวิธีที่ streaming multiprocessors ของ GPU ต้องการเข้าถึงหน่วยความจำตามธรรมชาติ การเปลี่ยนแปลงที่ดูเล็กน้อยนี้ทำให้สามารถอ่านหน่วยความจำแบบ coalesced ได้อย่างเต็มที่โดยไม่มี strided jumps
GPU ไม่เคยเป็นคอขวด มันคือการจัดเรียงหน่วยความจำที่ไม่สอดคล้องกับ access stride ที่ SM คาดหวัง
การปรับเปลี่ยนนี้ทำให้รูปแบบการเข้าถึงหน่วยความจำของ llama.cpp เข้าใกล้วิธีการของ vLLM มากขึ้น ซึ่งใช้ paged key-value cache ที่มีการจัดเรียงที่ปรับให้เหมาะสมกับสถาปัตยกรรมหน่วยความจำ GPU นี่อธิบายได้ว่าทำไม vLLM จึงปรับขนาดได้ดีกว่าเมื่อมีขนาดกลุ่มที่ใหญ่ขึ้น - มันถูกออกแบบตั้งแต่เริ่มต้นให้ทำงานกับวิธีที่ GPU สมัยใหม่จัดการหน่วยความจำ
Streaming multiprocessors (SMs): หน่วยประมวลผลหลักใน GPU ของ NVIDIA ที่ดำเนินการคำนวณแบบขนานอย่างมีประสิทธิภาพมากที่สุดเมื่อเข้าถึงตำแหน่งหน่วยความจำที่ต่อเนื่องกัน
ประสิทธิภาพของ Inference Engine:
เครื่องมือ | การจัดการ Batch Size | รูปแบบการจัดเก็บในหน่วยความจำ | การขยายประสิทธิภาพ |
---|---|---|---|
llama.cpp | แย่เมื่อเกิน batch 8 | รูปแบบ Flat layout | จำกัด |
vLLM | ยอดเยี่ยม | Paged KV cache | เหนือกว่า |
sglang | ยอดเยี่ยม | ปรับให้เหมาะสม | เหนือกว่า |
เกินกว่าการครอบงำของ NVIDIA
แม้ว่าการปรับปรุงนี้จะเน้นไปที่ฮาร์ดแวร์ NVIDIA แต่การอภิปรายในวงกว้างเผยให้เห็นการพัฒนาที่น่าสนใจในภูมิทัศน์ GPU Instinct MI300 ของ AMD เสนอลักษณะประสิทธิภาพที่แตกต่างกันด้วยประสิทธิภาพ FP32 สูงถึง 160 TFLOPS คู่กับแบนด์วิดท์ HBM3 6 TB/s นี่สร้าง ridge-point ประมาณ 27 FLOPs ต่อไบต์ ซึ่งมากกว่า A100 ของ NVIDIA ที่ 13 FLOPs ต่อไบต์ประมาณสองเท่า
อย่างไรก็ตาม ข้อได้เปรียบของระบบนิเวศซอฟต์แวร์ของ NVIDIA ยังคงแข็งแกร่ง แม้ว่า AMD จะมีข้อกำหนดฮาร์ดแวร์ที่แข่งขันได้และตัวเลือกหน่วยความจำบนแพ็คเกจที่ใหญ่กว่า (128-256 GB) แต่การขาดเครื่องมือซอฟต์แวร์ที่เป็นผู้ใหญ่ทำให้นักพัฒนาส่วนใหญ่ยังคงอยู่ในระบบนิเวศของ NVIDIA
ลักษณะประสิทธิภาพของ GPU:
- NVIDIA A100: จุดสันปันน้ำ 13 FLOPs/byte
- AMD Instinct MI300: จุดสันปันน้ำ 27 FLOPs/byte, 160 TFLOPS FP32, แบนด์วิดท์ HBM3 ประมาณ 6 TB/s
- ความจุหน่วยความจำ: AMD มีหน่วยความจำบนแพ็กเกจ 128-256 GB เทียบกับการกำหนดค่าที่เล็กกว่าของ NVIDIA
ภาพรวมใหญ่
การค้นพบนี้เน้นย้ำบทเรียนสำคัญสำหรับนักพัฒนา AI: คอขวดด้านประสิทธิภาพมักซ่อนอยู่ในที่ที่ไม่คาดคิด แม้ว่ากราฟการใช้งาน GPU อาจดูสมบูรณ์แบบ แต่ข้อจำกัดที่แท้จริงอาจแอบซ่อนอยู่ในรูปแบบการเข้าถึงหน่วยความจำหรือตัวเลือกการจัดเรียงข้อมูล กระบวนการแก้ไขข้อบกพร่องสองวันที่นำไปสู่ความก้าวหน้านี้แสดงให้เห็นว่าการมองเกินตัวชี้วัดระดับผิวเผินมีความสำคัญเพียงใดเมื่อปรับปรุงงาน AI
ประสบการณ์ของนักพัฒนายังแสดงให้เห็นคุณค่าของการเปรียบเทียบเอนจิ้นการประมวลผลที่แตกต่างกัน โดยการทำงานเดียวกันบนทั้ง llama.cpp และ vLLM พวกเขาสามารถระบุได้ว่าปัญหาไม่ได้เกิดจากฮาร์ดแวร์หรืองานนั้นเอง แต่อยู่ที่วิธีที่เครื่องมือหนึ่งจัดระเบียบโครงสร้างข้อมูล