ลดความหน่วงและเพิ่มปริมาณงานด้วยการปรับใช้ DeepSeek แบบหลายโหนด
การปรับใช้แบบ Multi-GPU ช่วยเพิ่มประสิทธิภาพโมเดล MoE ทั้งด้านความเร็วและสเกลไปพร้อมๆ กัน
ในระบบส่วนใหญ่ ความหน่วง (Latency) และปริมาณงาน (Throughput) มักจะเป็นเป้าหมายที่ขัดแย้งกัน ซึ่งต้องมีการประนีประนอมในระหว่างขั้นตอนการออกแบบและการปรับใช้ ตัวอย่างเช่น ในโมเดลภาษาขนาดใหญ่ (Large Language Models) แบบหนาแน่น (Dense) การเพิ่มขนาดแบตช์ (Batch Size) สามารถช่วยเพิ่มปริมาณงานได้ แต่ก็จะทำให้ความหน่วงเพิ่มขึ้นด้วย ขณะที่การเพิ่มเทนเซอร์พาราเรลลึซึม (Tensor Parallelism) ภายในเครื่องเดี่ยวสามารถลดความหน่วงลงได้ แต่จะลดจำนวนเรพพลิกกา (Replicas) ส่งผลให้ปริมาณงานลดลง
โมเดล Mixture of Experts (MoE) เช่น DeepSeek-V3/R1 ได้แสดงให้เห็นถึงความสามารถของโมเดลและประสิทธิภาพในการดำเนินงานที่ยอดเยี่ยมเมื่อเร็วๆ นี้ ตัวอย่างเช่น โมเดล DeepSeek-V3/R1 มีพารามิเตอร์รวมทั้งหมด 671B แต่โทเค็น (Token) แต่ละตัวใช้พารามิเตอร์เพียง 37B เท่านั้นในระหว่างการอนุมาน (Inference) สถาปัตยกรรมโมเดลนี้สร้างทั้งความท้าทายและโอกาสสำหรับระบบอนุมาน
บทความนี้แสดงให้เห็นว่า ตรงกันข้ามกับระบบทั่วไป โมเดล MoE เช่น DeepSeek-V3/R1 สามารถ บรรลุปริมาณงานที่สูงขึ้นและ,ความหน่วงที่ต่ำลงไปพร้อมๆ กัน เมื่อใช้งาน GPU จำนวนมากขึ้นในการปรับใช้แบบหลายโหนด (Multi-node Deployments) ในเกือบทุกสถานการณ์
สถาปัตยกรรมสำหรับการปรับใช้
เนื่องจากโมเดลมีผู้เชี่ยวชาญขนาดเล็ก (Experts) จำนวนมาก การปรับใช้จึงต้องกระจายไปยังอุปกรณ์หลายเครื่อง เราได้พิจารณาการปรับใช้ทั้งแบบโหนดเดี่ยวบนเครื่องโหนดเดียวที่มี 8xH200 GPU และการปรับใช้แบบหลายโหนดบน 8xH100 GPU
สถาปัตยกรรมสำหรับการปรับใช้ทั้งสองแบบใช้ประโยชน์จาก Data Parallelism ซึ่งประสานงานผ่านตัวจัดตารางเวลาคำขอ (Request Scheduler) ภายในของเรา การใช้งาน Data Parallelism เกี่ยวข้องกับการเปิดใช้งานอินสแตนซ์ของเอ็นจิ้นการอนุมาน (Inference Engine Instances) หลายอินสแตนซ์ โดยแต่ละอินสแตนซ์ทำงานอย่างอิสระเพื่อให้บริการและจัดการคำขอ ตัวจัดตารางเวลาคำขอซึ่งโต้ตอบกับเอ็นจิ้นผ่าน gRPC มีหน้าที่กระจายคำขอให้เท่า ๆ กันที่สุดเท่าที่จะเป็นไปได้ พร้อมทั้งอำนวยความสะดวกในการนำ KV Cache กลับมาใช้ซ้ำ (KV Re-use) โดยส่งคำขอที่มีเพฟิกซ์ (Prefix) ที่ตรงกันบางส่วนไปยังเซิร์ฟเวอร์ที่มีแคชอยู่ อินสแตนซ์ของเอ็นจิ้นจะไม่ครอบคลุมหลายโหนด แต่อาจเลือกใช้ Tensor Parallelism เพื่อแชร์ (Shard) แอตเทนชัน (Attention) ข้ามอุปกรณ์หลายเครื่องได้ อินสแตนซ์เหล่านี้เชื่อมต่อถึงกันผ่าน NVLink ในกรณีที่เป็นโหนดเดี่ยว หรือผ่าน InfiniBand ในกรณีที่เป็นหลายโหนด เพื่อส่งและรวบรวมผู้เชี่ยวชาญ (Experts)
การกำหนดค่าการปรับใช้แบบโหนดเดี่ยวให้ความหน่วงที่เหนือกว่าเมื่อใช้ขนาดแบตช์ขนาดเล็ก อย่างไรก็ตาม ประสิทธิภาพจะลดลงอย่างรวดเร็วภายใต้สภาวะภาระงานที่เพิ่มขึ้น
ในการปรับใช้เอ็นจิ้นการให้บริการ เราจะเปิดใช้งานพอด (Pod) หนึ่งพอดต่อโหนดซึ่งโฮสต์อินสแตนซ์เอ็นจิ้นหลายตัว PyTorch มีหน้าที่ตั้งค่าการสื่อสารแบบกระจายและเจรจาการเริ่มต้นใช้งาน NVSHMEM สำหรับการสื่อสาร เราพึ่งพา Custom CUDA Kernels ที่อธิบายไว้ใน โพสต์บล็อก ก่อนหน้านี้ การใช้งานการปรับใช้ทั้งสองแบบแทบจะเหมือนกันทุกประการ โดยที่โมเดลจะเลือกเคอร์เนลที่ถูกต้องมาใช้งานตามโครงสร้างที่ใช้ Expert Parallelism
เทคนิคการทำขนาน
ก่อนที่จะเจาะลึกการเปรียบเทียบประสิทธิภาพของเรา จำเป็นอย่างยิ่งที่จะต้องทำความเข้าใจกลยุทธ์หลักในการทำพาราเรลลึซึมที่ทำให้การปรับใช้โมเดล MoE ขนาดมหึมาอย่าง DeepSeek-V3/R1 เป็นไปได้
Tensor Parallelism
ในการอนุมานของ LLM (LLM Inference) มักจะใช้ Tensor Parallelism (TP) เพื่อลดการใช้หน่วยความจำและการประมวลผลต่อ GPU ซึ่งจะช่วยลดความหน่วง โดยปกติแล้ว เราสามารถแชร์ Linear Projections ในเลเยอร์ Attention และ MLP ตามมิติแถวหรือคอลัมน์ และแชร์การดำเนินการ Attention ตามมิติของเฮดแอตเทนชัน (Attention Head Dimension)
ด้วย TP สถาปัตยกรรม Llama-3 จึงไม่มีการคำนวณที่ซ้ำซ้อนสำหรับการดำเนินการ Linear Projection และ Attention ข้าม GPU ซึ่งเป็นวิธีการแชร์ที่เหมาะสมที่สุด อย่างไรก็ตาม ในโมเดล DeepSeek-V3/R1 นั้น TP ไม่สามารถบรรลุผลเช่นนี้ได้
โมเดล DeepSeek-V3/R1 ใช้ Multi-Latent Attention (MLA) เลเยอร์ MLA จะใช้ Linear Projection kv_a_proj ก่อนเพื่อคำนวณเวกเตอร์แฝง (Latent Vector) จากนั้นจึงใช้ Linear Projection อีกตัวหนึ่ง kv_b_proj เพื่อแปลงเวกเตอร์ดังกล่าวให้อยู่ในสเปซของเฮดแอตเทนชันแต่ละตัว เนื่องจากเฮดแอตเทนชันทั้งหมดใช้เวกเตอร์แฝงร่วมกัน TP จึงไม่สามารถแชร์เวกเตอร์แฝงได้ ดังนั้น TP Ranks ทั้งหมดจึงจำเป็นต้องจำลองพารามิเตอร์และการคำนวณของ kv_a_proj และ kv_b_proj ในทำนองเดียวกัน เนื่องจาก MLA จัดเก็บเวกเตอร์แฝงไว้ใน KV Cache แต่ละ TP Rank จึงจัดเก็บสำเนา KV Cache ที่เหมือนกันทุกประการ
แม้ว่าจะมีความซ้ำซ้อนบางประการใน MLA แต่ Tensor Parallelism ก็ยังคงช่วยลดความต้องการในการประมวลผลลงได้บางส่วน ทำให้มีประโยชน์สำหรับสถานการณ์ที่ต้องการความเร็วในการส่งออกสูง
Expert Parallelism
โมเดล DeepSeek-V3/R1 แทนที่เลเยอร์ MLP ด้วยเลเยอร์ MoE เลเยอร์ MoE มีผู้เชี่ยวชาญแบบเส้นทาง (Routed Experts) 256 ราย และผู้เชี่ยวชาญแบบใช้ร่วมกัน (Shared Expert) 1 ราย โทเค็นแต่ละตัวจะถูกส่งไปยังผู้เชี่ยวชาญแบบเส้นทางที่แตกต่างกัน 8 รายเพื่อทำการประมวลผล และผลลัพธ์จะถูกรวมเข้าด้วยกันโดยการถ่วงน้ำหนัก (Weighted Summed) นอกจากนี้ โทเค็นแต่ละตัวยังทำการประมวลผลในผู้เชี่ยวชาญแบบใช้ร่วมกัน และนำผลลัพธ์ที่ได้ไปบวกกับผลลัพธ์จากผู้เชี่ยวชาญแบบเส้นทางด้วย
Expert Parallelism (EP) ทำหน้าที่เป็นแนวทางในการแชร์ทั่วไปสำหรับเลเยอร์ MoE โดย GPU แต่ละตัวจะจัดการผู้เชี่ยวชาญแบบเส้นทาง 256 / EP ราย ในขณะที่ยังคงรักษาสำเนาของผู้เชี่ยวชาญแบบใช้ร่วมกันไว้ เมื่อเทียบกับ TP แล้ว ข้อดีของ EP คือสามารถกระจายการประมวลผลไปยัง GPU จำนวนมากขึ้น ซึ่งช่วยลดการประมวลผลและการใช้หน่วยความจำต่อ GPU ลงได้
ก่อนที่จะดำเนินการประมวลผลของผู้เชี่ยวชาญ GPU ทั้งหมดจำเป็นต้องทำการสื่อสารแบบ AllToAll เพื่อส่งโทเค็นไปยัง GPU ที่มีผู้เชี่ยวชาญที่สอดคล้องกันอยู่ หลังจากประมวลผลของผู้เชี่ยวชาญแล้ว จะต้องมีการสื่อสารแบบ AllToAll อีกครั้งเพื่อรวบรวมผลลัพธ์การประมวลผลจาก GPU ต่างๆ และทำการรวมแบบถ่วงน้ำหนัก เราได้ใช้งานเคอร์เนลการสื่อสาร AllToAll ทั้งสองนี้ในเวอร์ชันที่ได้รับการปรับแต่ง ซึ่งได้แก่ Dispatch และ Combine โดยใช้ NVSHMEM ใน โพสต์บล็อก ก่อนหน้านี้ เราได้อธิบายรายละเอียดเกี่ยวกับการใช้งาน และเคอร์เนลของเราได้ถูก เปิดซอร์ส (Open-sourced) บน GitHub แล้ว
Data Parallelism
ด้วย EP เราสามารถกระจายการประมวลผล MoE ไปยัง GPU 128 ตัวหรือมากกว่านั้นได้ อย่างไรก็ตาม การประมวลผลของ MLA ไม่สามารถแบ่งส่วนด้วย EP ได้ ณ จุดนี้ เราจึงสามารถนำ Data Parallelism (DP) มาใช้ได้ DP Group แต่ละกลุ่มจะมีสำเนาเลเยอร์ MLA ที่สมบูรณ์ DP Group แต่ละกลุ่มจะยอมรับอินพุตที่แตกต่างกันและทำการประมวลผลเลเยอร์ MLA อย่างอิสระ
DP และ TP ของเลเยอร์ MLA สามารถรวมเข้าด้วยกันได้ โดยที่ DP Group หนึ่งกลุ่มจะถูกแบ่งออกเป็น TP Ranks หลายตัว EP ของเลเยอร์ MoE สามารถรวมเข้ากับ DP/TP ของเลเยอร์ MLA ได้เช่นกัน EP = DP * TP ตัวอย่างเช่น บนเครื่อง 16 เครื่อง EP128 DP32 TP4 หมายถึงการกระจายผู้เชี่ยวชาญแบบเส้นทางไปยัง GPU 128 ตัว โดยทุกๆ 4 GPU จะรวมกันเป็นหนึ่ง DP Group รวมเป็น 32 DP Group ที่เป็นอิสระต่อกัน
โหนดเดี่ยว vs หลายโหนด
พารามิเตอร์ 671B ของ DeepSeek เกินความจุหน่วยความจำของเครื่อง H100 แบบ 8-GPU เครื่องเดียว (80 GB * 8) แต่เครื่อง H200 แบบ 8-GPU เครื่องเดียวสามารถรองรับโมเดลทั้งหมดได้อย่างสมบูรณ์ (141 GB * 8) เมื่อใช้การกำหนดค่า EP8 DP8 TP1 โมเดลจะใช้หน่วยความจำประมาณ 100 GB ต่อ GPU ซึ่งเหลือพื้นที่ประมาณ 40 GB สำหรับ KV Cache และผลลัพธ์ขั้นกลางอื่นๆ โทเค็นหนึ่งตัวใช้พื้นที่ KV Cache 70,272 ไบต์ สมมติว่าแต่ละคำขอมี 5,000 โทเค็น GPU แต่ละตัวจะสามารถรองรับคำขอได้ประมาณ 100 คำขอ
เราต้องการทำความเข้าใจความแตกต่างของประสิทธิภาพระหว่างการปรับแบบโหนดเดี่ยวและการปรับแบบหลายโหนดภายใต้การกำหนดค่าที่แตกต่างกัน เราใช้เครื่อง H200 หนึ่งเครื่องสำหรับการปรับใช้แบบโหนดเดี่ยว และใช้เครื่อง H100 สูงสุด 16 เครื่องสำหรับการปรับใช้แบบหลายโหนด สำหรับแต่ละสภาพแวดล้อมการปรับใช้ เราใช้การผสมผสานระหว่าง TP 1, 2, 4, 8 และขนาดแบตช์ต่อ GPU เป็น 1, 2, 4, 8, 16, 32, 64, 128 เราสมมติว่าแต่ละคำขอมีความยาว KV Cache 5,000 โทเค็น นอกจากนี้เรายังสมมติว่า Multi-Token Prediction (MTP) ทำนายเพิ่มอีก 1 โทเค็น (กล่าวคือ ความยาวคิวรี่ (Query Length) ของแต่ละคำขอคือ 2) และสมมติอัตราการยอมรับ (Acceptance Rate) อย่างอนุรักษ์นิยมที่ 60% แผนภูมิด้านล่างแสดงปริมาณงานและความเร็วในการส่งออกสำหรับการกำหนดค่าต่างๆ
แกนนอนแสดงความเร็วในการส่งออกต่อคำขอเป็นโทเค็น/วินาที แกนนตั้งใช้มาตราส่วนลอการิทึมเพื่อแสดงปริมาณงานต่อเครื่องเป็นโทเค็น/วินาที เราได้ทำเครื่องหมาย Pareto Frontier สำหรับการกำหนดค่า EP แต่ละแบบด้วยเส้นที่มีสีแตกต่างกัน
ในสถานการณ์ที่มีข้อกำหนดความเร็วในการส่งออกสูงมาก การใช้ EP8 DP1 TP8 แบบโหนดเดี่ยวที่มีขนาดแบตช์เป็น 1 สามารถทำความเร็วในการส่งออกได้เกิน 100 โทเค็น/วินาที แต่ปริมาณงานจะต่ำมาก ซึ่งเทียบเท่ากับความเร็วในการส่งออก ในสถานการณ์นี้ ทั้งแบตช์จะมีโทเค็นเพียง 2 ตัวเท่านั้น ซึ่งสามารถส่งไปยังผู้เชี่ยวชาญได้มากที่สุด 2*8=16 ราย และเปิดใช้งานพารามิเตอร์รวมกันได้มากที่สุด 57B
ในช่วงความเร็วในการส่งออก 80-40 โทเค็น/วินาที เมื่อปริมาณงานเพิ่มขึ้น ความเร็วในการส่งออกจะลดลงอย่างมาก ในทางตรงกันข้าม EP128 มีปริมาณงานสูงกว่าการปรับใช้แบบโหนดเดี่ยวประมาณ 5 เท่า ณ ความเร็วในการส่งออกที่เท่ากัน
ปรากฏการณ์นี้สามารถอธิบายได้โดยการตรวจสอบพฤติกรรมการทำงานของการปรับใช้แบบโหนดเดี่ยว นั่นคือ การเพิ่มขนาดแบตช์จะสัมพันธ์โดยตรงกับการเพิ่มขึ้นของผู้เชี่ยวชาญที่ถูกเปิดใช้งาน เมื่อขนาดแบตช์เป็น 1 จำนวนเฉลี่ยของผู้เชี่ยวชาญที่ถูกเปิดใช้งานต่อ GPU จะเท่ากับ 2 * 8 / 8 = 2 เมื่อขนาดแบตช์ใหญ่เพียงพอ ผู้เชี่ยวชาญทั้งหมดจะถูกเปิดใช้งาน ซึ่งหมายความว่า GPU แต่ละตัวจะเปิดใช้งานผู้เชี่ยวชาญ 256 / 8 = 32 ราย การเปิดใช้งานผู้เชี่ยวชาญมากขึ้นหมายความว่า GPU จำเป็นต้องอ่านพารามิเตอร์จากหน่วยความจำมากขึ้น ซึ่งจะเพิ่มแรงกดดันต่อแบนด์วิดท์หน่วยความจำ (Memory Bandwidth) อย่างมาก เนื่องจากเฟสการถอดรหัส (Decode Phase) ของโมเดลภาษาขนาดใหญ่ประสบปัญหาคอขวดที่แบนด์วิดท์หน่วยความจำอยู่แล้ว ไม่ใช่ที่ประสิทธิภาพการประมวลผล การเพิ่มขนาดแบตช์ในการปรับใช้แบบโหนดเดี่ยวจึงช่วยลดความเร็วในการส่งออกลงอย่างมาก
การเปรียบเทียบการกำหนดค่าการปรับใช้แบบหลายโหนดทั้งสี่แบบ (EP16, EP32, EP64 และ EP128) เผยให้เห็นว่า ค่า EP ที่สูงขึ้นจะช่วยเลื่อน Pareto Frontier ไปสู่การปรับปรุงทั้งปริมาณงานและความเร็วในการส่งออกพร้อมๆ กัน
การใช้ค่า EP ที่สูงขึ้นหมายความว่า GPU แต่ละตัวจะได้รับจัดสรรผู้เชี่ยวชาญน้อยลง ตัวอย่างเช่น EP128 หมายความว่า GPU แต่ละตัวรับผิดชอบผู้เชี่ยวชาญ 256 / 128 = 2 ราย ดังนั้นแรงกดดันต่อแบนด์วิดท์หน่วยความจำจึงลดลงอย่างมาก กล่าวอีกนัยหนึ่ง การใช้ค่า EP ที่ใหญ่ขึ้นจะทำให้เราได้รับแบนด์วิดท์หน่วยความจำเพิ่มขึ้นอย่างมีประสิทธิภาพ เมื่อขนาดแบตช์ต่อ GPU น้อยกว่า 64 การเพิ่มขนาดแบตช์จะไม่ส่งผลกระทบอย่างมีนัยสำคัญต่อความเร็วในการประมวลผลของผู้เชี่ยวชาญ เนื่องจากการเพิ่มจำนวนอินพุตไม่ได้เพิ่มแรงกดดันต่อแบนด์วิดท์หน่วยความจำอย่างมีนัยสำคัญ ดังนั้น เราจึงสังเกตเห็นว่าเมื่อใช้ EP128 การเพิ่มขนาดแบตช์จะไม่ส่งผลกระทบต่อความเร็วในการส่งออกอย่างรุนแรงนัก
ที่น่าสนใจคือ ที่ขนาดแบตช์ที่ใหญ่ขึ้น (64 คำขอต่อ GPU) เราสังเกตเห็นปรากฏการณ์ใหม่ นั่นคือ ปริมาณงานของการปรับใช้แบบโหนดเดี่ยวนั้นสูงกว่าการปรับใช้แบบหลายโหนดเล็กน้อย ส่วนหนึ่งเป็นเพราะ NVLink ภายในโหนดมีแบนด์วิดท์ที่สูงกว่า InfiniBand ระหว่างโหนด และอีกส่วนหนึ่งเป็นเพราะข้อจำกัดในการใช้งานของเรา เราจะวิเคราะห์ปรากฏการณ์นี้ในรายละเอียดเพิ่มเติมภายหลัง
เนื่องจากข้อจำกัดด้านความจุหน่วยความจำ การกำหนดค่า EP8 DP8 TP1 จึงไม่สามารถรองรับขนาดแบตช์ 128 ต่อ GPU ได้ ดังนั้นการปรับใช้แบบหลายโหนดจึงยังคงเป็นตัวเลือกที่ดีกว่าในสถานการณ์ที่ต้องการปริมาณงานที่สูงกว่า
การซ้อนทับกันของการประมวลผลและการสื่อสาร
ตามที่ได้แนะนำไปสั้นๆ ด้านบนเกี่ยวกับ Expert Parallelism โดยปกติแล้ว GPU จะว่างงานในระหว่างการสื่อสารของเลเยอร์ MoE เพื่อลดความสูญเปล่าและลดความหน่วง เราจำเป็นต้องค้นหางานประมวลผลที่ไม่ขึ้นต่อกันของข้อมูลมาเติมเต็มเวลาว่างเหล่านี้
ส่วนบนของแผนภูมิด้านข้างแสดงขั้นตอนการประมวลผลของเลเยอร์หนึ่ง การประมวลผล MoE จะขึ้นอยู่กับ Dispatch และการประมวลผลของเลเยอร์ถัดไปจะขึ้นอยู่กับผลลัพธ์ของ Combine
เราวางผู้เชี่ยวชาญแบบใช้ร่วมกันไว้บน GPU แต่ละตัว ด้วยวิธีนี้ การประมวลผลของผู้เชี่ยวชาญแบบใช้ร่วมกันจึงไม่ต้องอาศัยการสื่อสารแบบ AllToAll ดังนั้น เราจึงสามารถดำเนินการประมวลผลของผู้เชี่ยวชาญแบบใช้ร่วมกันได้ทันทีหลังจาก Dispatch Send เสร็จสิ้น จากนั้นจึงรอให้ Dispatch Recvเสร็จสมบูรณ์ เราเรียกรูปแบบการซ้อนทับนี้ว่า "Dispatch Overlap"
Dispatch Overlap มีรูปแบบการใช้งานที่ตรงไปตรงมาและสามารถนำไปประยุกต์ใช้ได้กว้างขวาง เทคนิคนี้ช่วยซ่อนเวลาในการประมวลผลของผู้เชี่ยวชาญแบบใช้ร่วมกันในทุกๆ ขนาด EP และขนาดแบตช์
เพื่อเพิ่มการซ้อนทับกันของการประมวลผลและการสื่อสารให้ดียิ่งขึ้น เราได้ใช้ Micro Batching ที่กล่าวถึงในรายงานทางเทคนิคของ DeepSeek เพื่อทำลายการพึ่งพาอาศัยกันของข้อมูล ดังแสดงในส่วนล่างของแผนภูมิ เราได้แบ่งการประมวลผลของ Transformer Layer หนึ่งออกเป็น 5 ขั้นตอน:
ขั้นตอนที่ 1: InputNorm, QKVProj, AppendKV, BMM
ขั้นตอนที่ 2: BMM, Attn, OProj, PostNorm, Gate
ขั้นตอนที่ 3: Dispatch Send, Shared Expert
ขั้นตอนที่ 4: Dispatch Recv, MoE, Combine Send
ขั้นตอนที่ 5: Combine Recv
ใน Dense Transformer Layers 3 เลเยอร์แรก เราจะใช้แบตช์เต็ม ใน 58 MoE Transformer Layers ที่ตามมา เราจะแบ่งแบตช์ออกเป็นสอง Micro Batches เท่าๆ กัน Micro Batches ทั้งสองนี้จะทำงานสลับกันโดยเหลื่อมกันอยู่ 3 ขั้นตอน เนื่องจากไม่มีการพึ่งพาอาศัยกันของข้อมูลระหว่าง Micro Batches ทั้งสองนี้ เราจึงสามารถสลับไปทำการประมวลผลของอีก Micro Batch หนึ่งได้ทันทีหลังจาก Dispatch Send และหลังจาก Combine Send
การแจกแจงความหน่วง
ต่อไป เราจะเปรียบเทียบผลลัพธ์ของการซ้อนทับผ่านการทดลอง รวมถึงเปรียบเทียบความแตกต่างด้านประสิทธิภาพระหว่างการปรับใช้แบบโหนดเดี่ยว EP8 และการปรับใช้แบบหลายโหนด EP128 เพื่อความสะดวกในการเปรียบเทียบ เราจึงใช้ H100 GPU สำหรับการทดลองต่อไปนี้ โดยใช้ TP1, ขนาดแบตช์ 128 ต่อ GPU, ความยาว Query เป็น 2 ต่อคำขอ และความยาว KV Cache เป็น 5000
แผนภูมิด้านบนแสดงเวลาทั้งหมดที่ใช้ไปกับ MoE Transformer Layer หนึ่งเลเยอร์ และสัดส่วนความหน่วงของเคอร์เนลประเภทต่างๆ ยกเว้น Dispatch, Combine และ GroupGEMM แล้ว เวลาในการทำงานของเคอร์เนลอื่นๆ ควรจะเท่ากันในชุดการทดลอง EP8, EP128 NoOverlap และ EP128 DispatchOverlap เนื่องจากมีขนาดแบตช์เท่ากัน
การซ้อนทับ (Overlapping)
มาเปรียบเทียบผลลัพธ์ของวิธีการซ้อนทับทั้งสามวิธีกันก่อน NoOverlap ใช้เวลาทั้งหมด 2667 µs, DispatchOverlap ใช้เวลา 2651 µs ซึ่งช่วยประหยัดไปได้ 16 µs หรือคิดเป็นเพียง 0.6% เท่านั้น MicroBatch แสดงให้เห็นถึงการปรับปรุงที่ดีขึ้นอย่างมาก โดยใช้เวลา 1896 µs ซึ่งเร็วขึ้น 29% ทั้งเวลาของ Dispatch และ Combine ลดลงอย่างเห็นได้ชัด โดย Dispatch ลดลงจาก 593 µs เหลือ 367 µs และ Combine ลดลงจาก 1012 µs เหลือ 237 µs
โปรดสังเกตว่า สำหรับเคอร์เนลการประมวลผล การแบ่งแบตช์ขนาด 128 ออกเป็นสองแบตช์ที่มีขนาด 64 จะทำให้เวลาในการทำงานรวมเพิ่มขึ้น ดังนั้น แม้ว่าเวลาที่ใช้ในการสื่อสารจะลดลง 1001 µs แต่เวลาโดยรวมกลับลดลงเพียง 771 µs เท่านั้น เราจะอธิบายเหตุผลโดยใช้โมเดล Roofline ในหัวข้อถัดไป
ด้วยเหตุนี้ การทำ Microbatching จึงไม่ได้ช่วยปรับปรุงประสิทธิภาพเสมอไป
แผนภูมิด้านบนแสดงการปรับปรุงประสิทธิภาพของ Microbatch เมื่อเทียบกับ DispatchOverlap สำหรับขนาดแบตช์ 4-128 เมื่อขนาดแบตช์น้อยกว่า 32 Microbatch จะทำให้ประสิทธิภาพลดลง 5%-40% แต่เมื่อขนาดแบตช์มากกว่าหรือเท่ากับ 32 Microbatch สามารถช่วยเพิ่มประสิทธิภาพได้ 10%-35%
EP8 vs EP128
กลับมาที่แผนภูมิก่อนหน้านี้และเปรียบเทียบระหว่าง EP8 และ EP128 Microbatch โดย EP8 ใช้เวลาทั้งหมด 1802 µs ซึ่งน้อยกว่า 1896 µs ของ EP128 เล็กน้อย นอกเหนือจากเวลาในการทำงานของเคอร์เนลที่เพิ่มขึ้นจากการใช้ Microbatch ที่กล่าวไปข้างต้นแล้ว ความแตกต่างหลักๆ จะอยู่ที่ GroupGEMM ที่ใช้สำหรับการประมวลผล MoE และเคอร์เนลการสื่อสารอีกสองตัว ได้แก่ Dispatch และ Combine
GroupGEMM ของ EP8 ใช้เวลา 555 µs ในขณะที่ GroupGEMM ของ EP128 ใช้เวลา 270 µs ซึ่งลดลงครึ่งหนึ่ง นี่คือข้อได้เปรียบหลักของการปรับใช้แบบหลายโหนด
น่าเสียดายที่เวลาที่ใช้ในการสื่อสารเพิ่มขึ้น 213 µs ซึ่งหักล้างข้อได้เปรียบของ GroupGEMM ไปมาก ใน การทดสอบประสิทธิภาพ แยกต่างหากสำหรับเคอร์เนลการสื่อสารของเรา เราพบว่าเคอร์เนลเหล่านี้สามารถบรรลุแบนด์วิดท์ของ InfiniBand ได้เพียงครึ่งเดียวเท่านั้น เราจะยังคงปรับแต่งเคอร์เนลการสื่อสารของเราต่อไป
เคอร์เนลอีกตัวที่ล้าหลังอย่างเห็นได้ชัดคือ GEMM โดย Microbatch ทำให้ GEMM ใช้เวลาเพิ่มขึ้น 95 µs เราจะวิเคราะห์ GEMM ให้ละเอียดยิ่งขึ้นในหัวข้อ Roofline ด้านล่าง เราเชื่อว่าการใช้งาน GEMM ในปัจจุบันยังไม่บรรลุประสิทธิภาพที่ดีที่สุด
Roofline
Roofline Model เป็นเครื่องมือที่ดีสำหรับการวิเคราะห์ประสิทธิภาพของเคอร์เนล แกนนอนคือ Arithmetic Intensity ซึ่งเป็นอัตราส่วนของ FLOP ต่อไบต์ I/O ของหน่วยความจำ ค่าแกนนอนนี้สามารถคำนวณได้โดยตรงจากความหมายเชิงเซแมนติกของเคอร์เนล แกนตั้งแสดงประสิทธิภาพที่ทำได้ ซึ่งคำนวณโดยการหาร FLOP ด้วยเวลาแฝงของเบนช์มาร์ก
ขีดจำกัดบนทางทฤษฎีของประสิทธิภาพเคอร์เนลถูกกำหนดโดยตรงจากข้อมูลจำเพาะของ GPU ประสิทธิภาพสูงสุด FP8 ของ H100 คือ 1979 TFLOP/s ซึ่งแสดงเป็นเส้นแนวนอนในโมเดล Roofline แบนด์วิดท์หน่วยความจำของ H100 คือ 3.35 TB/s ซึ่งแสดงเป็นความชันของเส้นที่ลากผ่านจุดกำเนิด เส้นทั้งสองนี้ระบุขีดจำกัดประสิทธิภาพสำหรับเคอร์เนลที่จำกัดด้วยการประมวลผล (Compute-bound) และจำกัดด้วยหน่วยความจำ (Memory-bound) ตามลำดับ
ด้านล่างนี้ เราจะอภิปรายเกี่ยวกับประสิทธิภาพของเคอร์เนล GroupGEMM และ GEMM
GroupGEMM
เคอร์เนล GroupGEMM ใน MoE จะดำเนินการคำนวณดังต่อไปนี้: มีกลุ่มทั้งหมด g กลุ่ม กลุ่มที่ i มีโทเค็นจำนวน m_i โทเค็น โดยทำการคูณเมทริกซ์ [m_i, k] x [k, n] -> [m_i, n] ในการทดสอบประสิทธิภาพ เราสมมติว่าจำนวนโทเค็นในแต่ละกลุ่มเท่ากัน โดยกำหนดให้เป็น m_i = m จากนั้นจำนวน FLOP สำหรับ GroupGEMM จะเท่ากับ 2 * g * m * k * n และไบต์ I/O หน่วยความจำคือ g * (m * k + n * k + m * n)
ในโมเดล DeepSeek-V3/R1 มีผู้เชี่ยวชาญ 256 ราย และโทเค็นแต่ละตัวจะถูกส่งไปยังผู้เชี่ยวชาญ 8 รายเพื่อทำการประมวลผล สมมติว่าขนาดแบตช์เท่ากับ 128, ความยาว Query เท่ากับ 2, และใช้การกำหนดค่า EP128 DP128 จำนวนเฉลี่ยของโทเค็นที่ผู้เชี่ยวชาญแต่ละรายได้รับ (กล่าวคือ m) จะเท่ากับ 128 * 2 * 8 * 128 / 256 = 1024 ในทำนองเดียวกัน เราสามารถคำนวณค่า m สำหรับการกำหนดค่าและขนาดแบตช์อื่นๆ ได้
เราใช้การใช้งาน GroupGEMM ของ DeepGEMM สำหรับการทดสอบประสิทธิภาพ จุดทดสอบครอบคลุมการผสมผสานของการกำหนดค่า EP8, EP16, EP32, EP64, EP128 ร่วมกับ TP1 และขนาดแบตช์ 1-128
แผนภูมิด้านบนแสดงโมเดล Roofline สำหรับ GroupGEMM ภายใต้การกำหนดค่า EP ที่แตกต่างกัน EP ที่ต่างกันจะสอดคล้องกับจำนวนกลุ่มที่ต่างกัน แผนภูมิแสดงเส้นประสิทธิภาพที่ทับซ้อนกันเกือบสนิท ซึ่งบ่งชี้ว่าประสิทธิภาพของ GroupGEMM ถูกกำหนดเป็นหลักโดยจำนวนโทเค็นรวม (แสดงเป็น g * m)
ดาวระบุจุดข้อมูลที่สอดคล้องกับขนาดแบตช์ 128 ต่อ GPU สำหรับการกำหนดค่า EP แต่ละแบบ เมื่อเปรียบเทียบจุดข้อมูลที่มีดาวเหล่านี้ เราจะเห็นว่าเมื่อ EP เพิ่มขึ้น (และ DP เพิ่มขึ้นพร้อมๆ กัน) จำนวนโทเค็นต่อผู้เชี่ยวชาญ m ก็จะเพิ่มขึ้นด้วย ที่ EP8 ค่า m=128 ในขณะที่ที่ EP128 ค่า m=2048
เมื่อ m เพิ่มขึ้น Arithmetic Intensity ก็จะเพิ่มขึ้นด้วย ในการกำหนดค่าส่วนใหญ่ GroupGEMM จะถูกจำกัดด้วยแบนด์วิดท์หน่วยความจำ ดังนั้นการเพิ่มค่า m จึงช่วยปรับปรุงประสิทธิภาพให้ดีขึ้น
GEMM
เคอร์เนล GEMM จะสอดคล้องกับ Linear Projections ในโมเดล เช่น Q/K/V/O Projection สำหรับการคูณเมทริกซ์ของ [m, k] x [k, n] -> [m, n] จำนวน FLOP จะเท่ากับ 2 * m * k * n และไบต์ I/O หน่วยความจำคือ m * k + n * k + m * n เรายังสามารถทดสอบความหน่วงสำหรับขนาดแบตช์ 1-128 ได้อีกด้วย
แผนภูมิด้านบนแสดงโมเดล Roofline สำหรับ GEMM ภายใต้การกำหนดค่า EP ที่แตกต่างกัน เราจะเห็นได้ว่าประสิทธิภาพของ GEMM ถูกจำกัดด้วยแบนด์วิดท์หน่วยความจำ เมื่อขนาดแบตช์เพิ่มขึ้น Arithmetic Intensity ก็จะเพิ่มขึ้นเช่นกัน ซึ่งช่วยปรับปรุงประสิทธิภาพให้ดีขึ้น
Microbatch
เมื่อใช้ Microbatching เราจะแบ่งแบตช์ออกเป็นสองส่วนเท่าๆ กัน จากแผนทั้งสองด้านบน เราจะเห็นได้ว่าเมื่อ m กลายเป็น m/2 ประสิทธิภาพของการคูณเมทริกซ์จะลดลง ดังนั้น การดำเนินการคูณเมทริกซ์ขนาด m/2 สองครั้งจึงใช้เวลานานกว่าการดำเนินการคูณเมทริกซ์ขนาด m เพียงครั้งเดียว
Multi-Token Prediction
ตลอดบทความนี้ เราได้สมมติว่ามีการใช้ Multi-Token Prediction (MTP) สำหรับ Speculative Decoding โดย MTP จะเปลี่ยนความยาว Query ต่อคำขอจาก 1 เป็น 2 สำหรับการคูณเมทริกซ์แล้ว สิ่งนี้เทียบเท่ากับการเปลี่ยน m เป็น m * 2 ซึ่งช่วยเพิ่มประสิทธิภาพของการคูณเมทริกซ์ ในทางกลับกัน หากเราวาดโมเดล Roofline สำหรับ MLA เราจะพบว่าการเพิ่มความยาว Query จะช่วยเพิ่มประสิทธิภาพของเคอร์เนล MLA อย่างมีนัยสำคัญ
ดังนั้น การใช้ MTP จึงมีบทบาทสำคัญต่อประสิทธิภาพของโมเดล
การใช้งาน & การปรับให้เหมาะสม
ในหัวข้อนี้ เราจะแนะนำรายละเอียดการใช้งานและการปรับแต่งบางส่วนสำหรับโมเดล DeepSeek-V3/R1 ของเรา
การควอนไทเซชัน (Quantization)
DeepSeek-V3/R1 ได้รับการฝึกอบรมแบบเนทีฟบน FP8 โดยใช้รูปแบบการควอนไทเซชันแบบต่อบล็อก (Per-block Quantization Scheme) ซึ่งน้ำหนัก (Weights) จะถูกควอนไทเซชันแบบคงที่ (Statically) และการเปิดใช้งาน (Activations) จะถูกควอนไทเซชันแบบทันทีทันใด (On-the-fly) แทนที่จะคำนวณปัจจัยสเกล (Scaling Factor) ต่อแชนเนลหรือต่อเมทริกซ์แบบคงที่ แต่จะคำนวณปัจจัยสเกลข้ามเวกเตอร์ 128-element สำหรับการเปิดใช้งาน และ 128x128 element tiles สำหรับเมทริกซ์ ซึ่งช่วยจำกัดการสูญเสียความแม่นยำอันเนื่องมาจากการควอนไทเซชัน
ที่ Perplexity เราพึ่งพาการผสมผสานระหว่างเคอร์เนล CUDA และ Triton เพื่อรองรับการอนุมาน โดย CUDA ถูกใช้สำหรับเคอร์เนลที่มีความละเอียดอ่อนต่อประสิทธิภาพสูงสุดและมีการแก้ไขไม่บ่อยนัก (เช่น แอตเทนชันและ GEMM) ในขณะที่ Triton ใช้สำหรับใช้งานเคอร์เนลการเปิดใช้งาน (Activation) การปรับมาตรฐาน (Normalization) และยูทิลิตี้ที่หลากหลาย Triton ช่วยให้เราสามารถปรับเคอร์เนลให้เข้ากับรูปแบบการควอนไทเซชันแบบบล็อกได้อย่างรวดเร็ว
สำหรับเลเยอร์ Linear และ MoE เราผสมผสานเคอร์เนล Deep GEMM เข้ากับเคอร์เนล Triton GEMM ของเราเอง เนื่องจากเราสังเกตเห็นว่าสำหรับมิติของเมทริกซ์บางประเภทและขนาดแบตช์ที่ต่ำ Split-K จะให้ความหน่วงที่ต่ำกว่า หากเลเยอร์ที่ไม่ได้ควอนไทเซชันทำการคูณแบบ (M, K) x (K, N) เลเยอร์นั้นจะต้องการปัจจัยสเกล (M x ceil_div(K, 128)) x (ceil_div(K, 128), ceil_div(N, 128)) สำหรับการควอนไทเซชันแบบบล็อก สำหรับการควอนไทเซชันแบบบล็อก ปัจจัยสเกลสำหรับการเปิดใช้งานจะถูกคำนวณแบบทันทีทันใด แทนที่จะเป็นการปรับเทียบร่วงหน้า (Pre-calibrated) เนื่องจากปัจจัยสเกลของการเปิดใช้งานจะถูกรวมกลุ่มตามมิติ K เท่านั้น และไม่ใช่ตามมิติ M เคอร์เนลจึงต้องการการปรับเปลี่ยนเพียงเล็กน้อยเท่านั้นเพื่อรองรับรูปแบบนี้
ฟังก์ชันการเปิดใช้งาน SiLU ที่ใช้โดย DeepSeek-V3/R1 จำเป็นต้องมีการเปลี่ยนแปลงครั้งใหญ่เพื่อรองรับ CUDA Graphs, การควอนไทเซชันแบบบล็อก และจำนวนโทเค็นที่มีเส้นทางแบบไดนามิก การควอนไทเซชันแบบบล็อกอาจทำให้เกิดปัญหาได้เนื่องจากทำให้เกิดการลดทอนในแนวนอน (Horizontal Reductions) อย่างไรก็ตาม เคอร์เนลได้ทำการแบ่งการเปิดใช้งานตามมิติซ่อนเร้น (Hidden Dimension) ออกเป็นบล็อกขนาด 1024 elements ไว้ล่วงหน้าแล้ว ภายในหนึ่งบล็อก เทนเซอร์ที่จะถูกควอนไทเซชันจะถูกแบ่งย่อยออกเป็นบล็อกขนาด 128 เพื่อคำนวณค่าสัมบูรณ์ที่ใหญ่ที่สุด โดยที่ Triton จะสร้างการลดทอนค่าสูงสุดแบบ cross-warp (Cross-warp Max Reductions) ที่มีประสิทธิภาพ ซึ่งเพิ่ม Overhead น้อยมาก
เพื่อรองรับการกำหนดเส้นทาง MoE ภายใต้ CUDA Graphs เคอร์เนลจะต้องรับรู้ข้อมูลการกำหนดเส้นทางที่ระบุจำนวนโทเค็นต่อผู้เชี่ยวชาญ แทนที่จะจัดตารางเวลางานตามขนาดของบัฟเฟอร์ที่ถูกจัดสรรไว้เพื่อเก็บขีดจำกัดบนของจำนวนโทเค็น เราไม่สามารถแบ่งปัญหาตามมิติตนเซอรร์อินพุตได้ ดังนั้นเราจึงเปิดใช้งานเคอร์เนลแบบเพอร์ซิสเทนต์ (Persistent Kernels) จำนวนคงที่ ซึ่งทำหน้าที่อ่านข้อมูลการกำหนดเส้นทางเพื่อพิจารณาว่ามีโทเค็นถูกเติมอยู่เท่าใด และแบ่งงานในการประมวลผลการเปิดใช้งานระหว่างเคอร์เนลเหล่านั้นแบบไดนามิก
เราได้นำเคอร์เนลบางส่วนของเราไปรวมไว้ในโปรเจกต์ FlashInfer (Upstreamed) แล้ว และในอนาคตเราจะทำการเปิดซอร์สโค้ดของเราเพิ่มเติมอีก
เลเยอร์ MLA
เราใช้ FlashInfer สำหรับการคำนวณ MLA โดย FlashInfer รองรับการตั้งค่า Page Table ที่ยืดหยุ่นและมีประสิทธิภาพสูงมาก
เราได้รวม q_a_proj และ kv_a_proj เข้าเป็น qkv_a_proj เดียวกัน ส่งผลให้ความหน่วงลดลงจาก 15.4 µs + 14.8 µs = 30.2 µs เหลือ 16.7 µs
เราได้แยก kv_b_proj ออกเป็นสองเมทริกซ์ ได้แก่ k_b_proj และ v_b_proj พร้อมทั้งเขียนเคอร์เนล FP8 Block Quantized BMM สำหรับการคำนวณที่เกี่ยวข้องกับเมทริกซ์ทั้งสองนี้
CUDA Graph
CUDA Graph สามารถช่วยลด Overhead ในการเรียกใช้เคอร์เนลได้อย่างมาก ซึ่งมีความสำคัญอย่างยิ่งต่อประสิทธิภาพ เราสร้าง CUDA Graph สำหรับแต่ละขนาดแบตช์
ก่อนที่จะพัฒนา AllToAll Kernel ของเราเอง เราเคยใช้ torch.all_to_all_single() สำหรับการสื่อสารแบบ AllToAll การดำเนินการนี้กำหนดให้ GPU ทั้งหมดต้องใช้ขนาดแบตช์เท่ากัน อย่างไรก็ตาม DP Groups ที่แตกต่างกันอาจรันด้วยขนาดแบตช์ที่แตกต่างกันได้
เพื่อให้แน่ใจว่า all_to_all_single() สามารถใช้งานร่วมกับ DP Groups ที่มีขนาดแบตช์ต่างกันได้ ก่อนอื่นเราจึงใช้การดำเนินการ allreduce() ก่อนที่โมเดลจะเริ่มทำงานแต่ละครั้ง เพื่อหาขนาดแบตช์ที่ใหญ่ที่สุดในบรรดา DP Groups ทั้งหมด จากนั้นเราจึงทำให้ DP Groups ทั้งหมดใช้ขนาดแบตช์นี้ในการทำงาน
แม้ว่าวิธีนี้จะทำให้เราสามารถใช้ CUDA Graph ได้ แต่ก็มีข้อเสีย 3 ประการ ประการแรก มันต้องมีการดำเนินการ allreduce() เพิ่มเติม ประการแรก DP Groups ที่มีขนาดแบตช์เล็กกว่าจะถูกบังคับให้ต้องทำการแพดดิง (Padding) และประการที่สาม มันทำให้โค้ดการใช้งานของเรามีความซับซ้อน
หลังจากที่เราใช้งาน AllToAll Kernel ของเราเองแล้ว เราก็ไม่ต้องกำหนดให้ GPU ทั้งหมดต้องใช้ขนาดแบตช์เท่ากันอีกต่อไป ดังนั้นเราจึงไม่ต้องทำการดำเนินการ allreduce() เพิ่มเติมหรือทำการแพดดิงขนาดแบตช์อีกต่อไป
MoE Router
MoE router ถูกใช้งานใน Triton โดยอาศัยอัลกอริทึมการเรียงลำดับ (Sort) ที่ดัดแปลงมาจากไลบรารีมาตรฐาน ซึ่งจะคอยติดตามดัชนี (Indices) ขององค์ประกอบที่ถูกเรียงลำดับด้วย การใช้งานนี้ถูกแชร์ร่วมกันในโมเดล MoE ทุกรุ่น เนื่องจากเส้นทางของ Mixtral เป็นกรณีพิเศษของเส้นทาง DeepSeek ซึ่งกลุ่ม Top-K จะเหมือนกับกลุ่มของผู้เชี่ยวชาญทั้งหมด เคอร์เนลแบบสัปดน (Sparse Kernels) จะใช้ดัชนีและคะแนน Top-K โดยตรง ในขณะที่รูปแบบ dispatch/combine แบบหนาแน่นที่อาศัย all-to-all จะต้องรวบรวมข้อมูลการกำหนดเส้นทางแบบรายผู้เชี่ยวชาญแทนที่จะเป็นแบบรายโทเค็น
งานในอนาคต
ในงานในอนาคต เราวางแผนที่จะปรับปรุงประสิทธิภาพของโมเดล DeepSeek ให้ดียิ่งขึ้นไปอีก
การปรับแต่งที่สำคัญที่สุดในลำดับถัดไปคือ Prefill Disaggregation เฟส Prefill และเฟส Decode ของโมเดล DeepSeek-V3/R1 มีลักษณะการคำนวณที่แตกต่างกันอย่างมาก ทั้งสองเฟสสามารถใช้กลยุทธ์การปรับแต่งและรูปแบบการปรับใช้ที่แตกต่างกันได้
สำหรับเลเยอร์ MLA ในเฟส Decode เราใช้ Matrix Absorption เพื่อลดจำนวน FLOP ของการคำนวณ MLA ในเฟส Prefill การฉายเวกเตอร์แฝงลงในสเปซ K/V ก่อน แล้วจึงทำการคำนวณในรูปแบบ Multi-Head Attention (MHA) จะให้ผลลัพธ์ที่ดีกว่า
หาก Prefill และ Decode ทำงานบน GPU เดียวกัน เพื่อลดผลกระทบของ Prefill ต่อความเร็วในการส่งออกของ Decode โดยทั่วไปเราจะใช้ Chunked Prefill เพื่อแบ่ง Query ออกเป็นหลายๆ ชิ้น (Chunks) สำหรับการทำ Prefill เนื่องจาก KV Cache ทำการจัดเก็บเวกเตอร์แฝงไว้ จึงทำให้การแปลง MLA เป็นรูปแบบ MHA กลายเป็นเรื่องยาก
สำหรับเลเยอร์ MoE ในเฟส Decode เราจะใช้ EP และ DP ให้ใหญ่ที่สุดเท่าที่จะเป็นไปได้เพื่อเพิ่มจำนวนโทเค็นอินพุตต่อผู้เชี่ยวชาญ ซึ่งจะช่วยปรับปรุงประสิทธิภาพของ GroupGEMM ในเฟส Prefill เนื่องจากจำนวนโทเค็นมีมากพออยู่แล้ว GroupGEMM จึงถูกจำกัดด้วยการประมวลผล (Compute-bound) อยู่แล้ว ดังนั้น สำหรับ Prefill เราจึงสามารถใช้ EP และ DP ที่มีขนาดเล็กลงได้
หาก Prefill และ Decode ทำงานบน GPU เดียวกัน ตราบใดที่ DP Group ใดๆ กำลังดำเนินการ Prefill อยู่ ความหน่วงของเลเยอร์ MoE บน GPU ทั้งหมดจะเพิ่มขึ้น ซึ่งจะส่งผลกระทบอย่างมากต่อความเร็วในการส่งออกของ Decode
นอกจากการแยก Prefill Disaggregation แล้ว เรายังมีแผนที่จะปรับแต่งในส่วนอื่นๆ ดังต่อไปนี้ด้วย:
ประสิทธิภาพของ AllToAll: ปัจจุบันเคอร์เนล AllToAll ของเราสามารถบรรลุแบนด์วิดท์ของ InfiniBand ได้เพียง 1/3 เท่านั้น เราจะยังคงปรับแต่งเคอร์เนลนี้ต่อไป
Speculative Decoding สไตล์ EAGLE: ในข้อมูลด้านบน เราสมมติว่าใช้ Speculative Decoding เพื่อทำนาย 1 โทเค็น EAGLE สามารถใช้โครงสร้างแบบต้นไม้เพื่อทำนายโทเค็นหลายตัวได้ ซึ่งจะช่วยเพิ่มความยาวในการยอมรับ (Acceptance Length) และสามารถเพิ่มความเร็วในการส่งออกได้อย่างมาก
GEMM Kernel: ในโมเดล Roofline ที่แสดงไว้ก่อนหน้านี้ เราจะเห็นว่าประสิทธิภาพของเคอร์เนล GEMM ยังคงห่างไกลจากขีดจำกัดทางทฤษฎีอยู่มาก เราจะยังคงปรับแต่งเคอร์เนลนี้ต่อไป
GB200 NVL72: ในโซลูชัน GB200 NVL72 ล่าสุดของ NVIDIA มีการเชื่อมต่อระหว่าง Blackwell GPU จำนวน 72 ตัวผ่าน NVLink ความเร็วสูง สำหรับโมเดลที่มีสถาปัตยกรรม MoE นี่ถือเป็นโอกาสและความท้าทายครั้งใหญ่
บทสรุป
การปรับใช้โมเดล DeepSeek MoE แบบหลายโหนดสามารถบรรลุสิ่งที่โดยปกติแล้วเป็นไปไม่ได้สำหรับ Dense LLMs นั่นคือ การปรับปรุงทั้งปริมาณงานและความหน่วงไปพร้อมๆ กัน ด้วยการกระจายผู้เชี่ยวชาญไปยัง GPU จำนวนมากขึ้น เราจึงสามารถลดแรงกดดันต่อแบนด์วิดท์หน่วยความจำต่ออุปกรณ์ลงได้ ซึ่งช่วยให้ประมวลผลได้เร็วขึ้นและมีปริมาณงานของระบบที่สูงขึ้น การทดลองของเราแสดงให้เห็นว่าการกำหนดค่า EP128 สามารถบรรลุปริมาณงานที่สูงขึ้นได้สูงสุดถึง 5 เท่าที่ความเร็วในการส่งออกเทียบเท่ากัน เมื่อเทียบกับการปรับใช้แบบโหนดเดี่ยว
เทคนิคการซ้อนทับกันของการประมวลผลและการสื่อสาร เช่น Micro-batching ช่วยลด Overhead การสื่อสารแบบหลายโหนดได้อย่างมาก โดยการใช้งานของเราแสดงให้เห็นว่ามีความเร็วเพิ่มขึ้นสูงสุดถึง 40% เคอร์เนลการสื่อสาร AllToAll แบบกำหนดเองและการใช้งานเคอร์เนลที่ได้รับการปรับแต่งของเราได้ช่วยให้สามารถปรับใช้โมเดลที่มีพารามิเตอร์ 671B ได้อย่างมีประสิทธิภาพ
เนื่องจากสถาปัตยกรรม MoE ได้รับความนิยมมากขึ้นเรื่อยๆ จากความสามารถของตัวโมเดล กลยุทธ์การปรับใช้เหล่านี้จึงมอบข้อมูลเชิงลึกที่มีคุณค่าสำหรับการปรับขนาดโมเดลลักษณะดังกล่าวให้มีประสิทธิภาพ
ข้อมูลอ้างอิง
ภาพรวมระบบอนุมาน DeepSeek-V3/R1
Megatron-LM: การฝึกอบรมโมเดลภาษาพารามิเตอร์หลายพันล้านโดยใช้ Model Parallelism
EAGLE: Speculative Sampling ต้องอาศัยการทบทวนความไม่แน่นอนของฟีเจอร์ใหม่
การอ้างอิง
@online{perplexity2025LowerLatencyAndHigherThroughputWithMultiNodeDeepseekDeployment,
author = {{Perplexity} and Lequn Chen and Nandor Licker and Erin McKnight},
title = {{Lower latency and higher throughput with multi-node DeepSeek deployment}},
date = {2025-04-18},
url = {https://perplexity.coolkeypoint.com/hub/blog/lower-latency-and-higher-throughput-with-multi-node-deepseek-deployment}
}