กล่องสองเครื่องระดับ DGX Spark โมเดลเดียว: สำเนาบนแต่ละเครื่องแทนการแบ่งโมเดล
ปฏิบัติการ
ตลอดเจ็ดสัปดาห์ โมเดลภาษาท้องถิ่นของเราทำงานแบบแบ่งส่วนบนเครื่อง NVIDIA GB10 สองเครื่องด้วย tensor parallelism เมื่อวันที่ 22 ตุลาคม เราเปลี่ยนมาใช้สำเนาเต็มของโมเดลบนแต่ละเครื่อง พร้อม least-connection proxy อยู่ด้านหน้า อัตราการประมวลผลเพิ่มขึ้นจากประมาณ 180 เป็น 210 token ต่อวินาที และตอนนี้เครื่องใดเครื่องหนึ่งสามารถรีสตาร์ทได้โดยไม่ทำให้โมเดลหายไป
เหตุผลที่เราเคยแบ่งโมเดล
แต่ละเครื่องมีหน่วยความจำ 128 GB ที่ CPU และ GPU ใช้ร่วมกัน ในเดือนกันยายน โมเดลพอดีกับหนึ่งเครื่อง แต่แคชของมันแทบไม่พอ: บิลด์ vLLM ที่เราใช้ไม่สามารถเก็บ KV cache ของโมเดลนี้ในรูปแบบ FP8 ได้ แคชจึงอยู่ในรูปแบบ BF16 และหนึ่งเครื่องมีพื้นที่สำหรับแคชเพียง 499,110 token ซึ่งน้อยกว่าสองคำขอที่บริบทเต็มของโมเดลขนาด 262,144 token เมื่อแบ่งโมเดลบนทั้งสองเครื่องด้วย tensor parallelism ผ่านการเชื่อมต่อเครือข่ายโดยตรง แคชจะขยายได้มากกว่าสามล้าน token เราแบ่งโมเดลเพื่อเพิ่มพื้นที่ ไม่ใช่เพื่อความเร็ว
สิ่งที่เปลี่ยนแปลง
บิลด์ 4-bit เวอร์ชันใหม่กว่าของโมเดลเดียวกัน (NVFP4 สร้างด้วย quantisation-aware distillation) ใช้หน่วยความจำประมาณ 71 GiB และบิลด์ vLLM จากชุมชนสำหรับ GB10 (eugr/spark-vllm-b12x) รันแคชในรูปแบบ FP8 ตอนนี้หนึ่งเครื่องเก็บโมเดลทั้งหมดและแคชขนาด 0.76 ถึง 1.08 ล้าน token ซึ่งรองรับคำขอได้ 2.9 ถึง 4.1 คำขอที่บริบทเต็ม ขึ้นอยู่กับว่าเครื่องจัดสรรหน่วยความจำให้มากน้อยเพียงใด สำเนาทั้งสองใช้ speculative decoding ของโมเดลเอง (MTP, draft token สี่ตัว) ก่อนสลับไปใช้ เราได้รันการทดสอบ tool-calling บนบิลด์ใหม่: ได้คะแนน 86 ส่วนบิลด์เดิมที่ได้คะแนน 75
สิ่งที่เราวัดได้
| แบ่งบนสองเครื่อง | สำเนาบนแต่ละเครื่อง | |
|---|---|---|
| KV cache | มากกว่า 3 ล้าน token | 0.76 และ 1.08 ล้าน token |
| คำขอที่บริบทเต็ม | ประมาณ 12 | 2.9 และ 4.1 |
| อัตราการประมวลผลภายใต้โหลดของเรา | ประมาณ 180 token/วินาที | 210 token/วินาที (111 + 99) |
| การเริ่มระบบ | ประมาณ 20 นาที | 5 ถึง 13 นาทีต่อสำเนา |
| เครื่องหนึ่งรีสตาร์ท | โมเดลใช้งานไม่ได้ | สำเนาอีกเครื่องให้บริการต่อ |
หนึ่งชุดทำงานเดี่ยว วัดจากจำนวนคำขอที่รองรับพร้อมกัน: ด้วยพรอมต์สั้น 1 คำขอให้ 36 โทเคนต่อวินาที 4 คำขอให้ 82, 8 คำขอให้ 109 และ 16 คำขอให้ 150 ส่วนพรอมต์ที่ยาวกว่าของเอเจนต์อีเมลของเรา 4 คำขอให้ 81, 8 คำขอให้ 113 และ 16 คำขอให้เพียง 87: เมื่อเกิน 8 คำขอพร้อมกัน ยิ่งเพิ่มคำขอกลับยิ่งช้าลง
ในช่วงบ่ายหนึ่งวัน ชุดหนึ่งรองรับสูงสุด 12 คำขอพร้อมกัน ส่วนอีกชุดรองรับ 8 คำขอ ชุดแรกให้ผลผลิต 91 ถึง 93 โทเคนต่อวินาที และประมวลผลเสร็จ 26 ถึง 27 คำขอต่อนาที ส่วนชุดที่สองให้ 81 ถึง 83 โทเคนต่อวินาที และ 22 คำขอต่อนาที ตอนนี้ทั้งสองชุดรองรับ 12 คำขอพร้อมกัน
สิ่งที่เราต้องแลกมา
พื้นที่ หน่วยความจำแคช โมเดลแบบแยกส่วนมีแคชเดียวขนาดมากกว่าสามล้านโทเคน ส่วนชุดเครื่องมือมีแคชย่อยสองชุด และคำขอหนึ่งคำขอจะใช้ได้เฉพาะแคชของชุดที่ให้บริการมันเท่านั้น เอเจนต์ของเราส่งคำขอขนาดสั้นและขนาดกลางจำนวนมาก ดังนั้นผลผลิตจึงสำคัญกว่า หากคุณให้บริการบริบทที่ยาวมากเพียงไม่กี่รายการพร้อมกัน การแยกส่วนอาจยังเป็นทางเลือกที่ดีกว่า เราเก็บการตั้งค่าแบบแยกส่วนไว้ และการสลับกลับไปใช้เพียงสคริปต์เดียว
สิ่งที่ผิดพลาดระหว่างการสลับระบบ
- ไม่มีโมเดลให้บริการนาน 11 นาที ตามด้วยข้อผิดพลาด 502 นาน 30 วินาที โมเดลแบบแยกส่วนต้องหยุดทำงานก่อนที่ชุดเครื่องมือจะโหลดขึ้นบนเครื่องเดียวกันได้ ทำให้เอเจนต์ของเราต้องรอประมาณ 11 นาที เมื่อชุดแรกเริ่มทำงาน พร็อกซียังคงตอบกลับ "502 no live upstreams" ต่อไปอีกประมาณ 30 วินาที: เนื่องจากมันทำเครื่องหมายว่าแบ็กเอนด์ล้มเหลวระหว่างที่ระบบหยุดทำงาน (max_fails=3, fail_timeout=30s) และจะไม่ลองเชื่อมต่อใหม่จนกว่าเวลานั้นจะหมดลง
- ไคลเอนต์ที่ข้ามพร็อกซี งานแบทช์หนึ่งของเราตั้งค่าเป็นที่อยู่ของเครื่องแรกไว้ ทำให้โหลดทั้งหมดไปลงที่ชุดเครื่องมือชุดเดียว ในขณะที่อีกชุดยังมีพื้นที่ว่างเหลืออยู่ ตอนนี้ไคลเอนต์ทุกตัวต้องผ่านพร็อกซี ส่วนที่อยู่ของเครื่องจะใช้เฉพาะสำหรับการตรวจสอบสุขภาพระบบเท่านั้น
- ตัวชี้วัดที่กระโดดไปมา ตัวตรวจสอบผลผลิตของเราอ่านค่าจาก vLLM ที่ /metrics ผ่านพร็อกซี เมื่อมีสองแบ็กเอนด์ การอ่านค่าต่อเนื่องจะมาจากคนละชุด ทำให้ตัวนับกระโดดไปมา และตัวตรวจสอบรายงานว่าโมเดลขาดแคลนทรัพยากร ตอนนี้มันอ่านค่าจากแต่ละชุดโดยตรงแล้วนำมารวมกัน
- ตัวตรวจสอบระบบตัวเก่าที่อาจหยุดอินสแตนซ์ใหม่ การตั้งค่าแบบแยกส่วนมีตัวตรวจสอบระบบที่รีสตาร์ทบริการเมื่อโมเดลหยุดตอบกลับ บริการใหม่ประกาศ Conflicts= กับบริการเก่า ดังนั้นการรีสตาร์ทนั้นจะหยุดอินสแตนซ์ใหม่ ต้องปิดการทำงานด้วย
systemctl disable --nowไม่ใช่แค่หยุดการทำงาน เพราะเลเยอร์ปฏิบัติการของเราจะรีสตาร์ทยูนิทที่เปิดใช้งานแต่ไม่ได้ทำงานอยู่ - หน่วยความจำบนเครื่องที่ทำงานอื่นด้วย เครื่องหนึ่งยังรันฐานข้อมูลและสร้างเว็บไซต์ของเราด้วย เมื่อตั้งค่า gpu-memory-utilization ของ vLLM เป็น 0.78 จะเหลือหน่วยความจำเพียง 5 GiB สำหรับงานอื่นทั้งหมด เราจึงลดค่าลงเป็น 0.74 และรีสตาร์ทเฉพาะอินสแตนซ์นั้น ขณะที่อีกอินสแตนซ์รับโหลดต่อ บนเครื่องเหล่านี้ ส่วนแบ่งหน่วยความจำที่คุณให้ vLLM จะใช้พื้นที่เดียวกับงานอื่นทั้งหมด
หากคุณมีเครื่องเหล่านี้สองเครื่อง
- หากโมเดลและแคชที่คุณต้องการพอดีกับเครื่องเดียว ให้รันอินสแตนซ์บนแต่ละเครื่องโดยวางไว้หลังพร็อกซีแบบ least-connection แบ่งโมเดลเฉพาะเมื่อเครื่องเดียวไม่สามารถรองรับแคชที่คุณต้องการได้
- ชี้ทุกไคลเอนต์ไปที่พร็อกซี รวมถึงงานแบบแบทช์และคอนฟิกเก่า
- อ่านเมตริกจากแต่ละอินสแตนซ์โดยตรง
- กำหนดส่วนแบ่งหน่วยความจำให้แต่ละเครื่องตามงานอื่นที่รันอยู่บนเครื่องนั้น
- กำหนดจำนวนคำขอที่อินสแตนซ์หนึ่งๆ ให้บริการพร้อมกันตามทราฟฟิกจริงของคุณ: สำหรับสัดส่วนงานของเรา 12 คำขอดีกว่า 8 คำขอ
- เตรียมพร้อมสำหรับการสลับที่อาจทำให้โมเดลหายไปนานเท่ากับเวลาที่อินสแตนซ์หนึ่งๆ ใช้ในการเริ่มทำงาน และวางแผนรองรับไว้ล่วงหน้า
Factory Floor มาพร้อมพร็อกซีและคอนฟิกการเปิดใช้งานสำหรับหนึ่งหรือสองเครื่อง การตรวจสอบสถานะ และการทดสอบการใช้เครื่องมือที่เราดำเนินการก่อนสลับโมเดล พร้อมตัวตรวจสอบระบบและตัวจำกัดหน่วยความจำสำหรับส่วนอื่นๆ ของเครื่อง