ทำไม AI ช้าตอน context ยาว · เบนช์มาร์ก 13 โมเดลที่ 65K-128K พบว่า prefill กินเวลา 94-99% ไม่ใช่ token ต่อวินาที
มีคนลองวัดความเร็วโมเดลโอเพนซอร์ส 13 ตัวที่ context ยาวถึง 131K แล้วพบว่าตัวเลข token ต่อวินาทีที่ค่ายชอบโชว์ แทบไม่เกี่ยวกับความเร็วจริงตอนใช้งาน context ยาว เพราะเวลาเกือบทั้งหมดที่เรารอหมดไปกับ prefill และตัวชี้วัดที่ควรดูจริง ๆ คือจำนวน KV head ไม่ใช่จำนวนพารามิเตอร์

มีคนลองเอาโมเดลโอเพนซอร์ส 13 ตัวมาไล่วัดความเร็ว ตั้งแต่ context สั้น ๆ ไปจนถึง context ยาว 131,000 token บนการ์ดจอสำหรับผู้ใช้ทั่วไปเพียงใบเดียว รวมเวลารันราว 21 ชั่วโมง แล้วผลลัพธ์กลับสวนความเข้าใจของคนส่วนใหญ่ที่รันโมเดล AI ในเครื่องตัวเอง ในที่นี้ context คือข้อมูลทั้งหมดที่เราป้อนให้โมเดลอ่านก่อนตอบ ทั้งคำสั่ง เอกสาร และประวัติแชต ส่วน token คือหน่วยย่อยของข้อความ ประมาณคำหรือเศษคำ
ตัวเลข "token ต่อวินาที" ที่ทุกค่ายชอบยกมาโชว์ตอนเปิดตัวโมเดล แทบไม่มีความหมายเมื่อ context ยาวขึ้น พอเราป้อนเอกสารยาว ๆ ให้ AI อ่าน หรือให้มันทำงานแบบ agent หรือ AI ที่ทำงานเป็นขั้นตอนและเรียกใช้เครื่องมือเองได้ โดยสะสม context ไว้เยอะ เวลาส่วนใหญ่กลับหมดไปกับ prefill ซึ่งเป็นตอนที่โมเดลอ่าน context ทั้งก้อนและประมวลผลก่อนจะเริ่มพิมพ์คำตอบ ที่ context ระดับ 65,000 token ขึ้นไป prefill กินเวลาที่เรานั่งรอไปถึง 94-99% ส่วนการพิมพ์คำตอบจริง ๆ เหลือแค่ 1-5% เท่านั้น
ตัวเลขที่ค่ายโชว์ วัดผิดช่วง

เวลาโมเดลตอบเรา มันทำงานสองช่วงที่ต่างกันชัดเจน ช่วงแรกคือ prefill ตอนที่โมเดลอ่าน context ทั้งหมด ทั้งคำสั่ง เอกสาร และประวัติแชตที่เราป้อนเข้าไป แล้วประมวลผลให้เสร็จก่อน ช่วงที่สองคือ decode ตอนที่โมเดลค่อย ๆ พิมพ์คำตอบออกมาทีละ token
ตัวเลข token ต่อวินาทีที่เห็นในหน้าสเปกหรือกราฟเปรียบเทียบ ซึ่งในเบนช์มาร์กเรียกว่า tg128 วัดแค่ช่วง decode คือความเร็วตอนพิมพ์คำตอบ ปัญหาคือมันไม่ได้บอกอะไรเลยว่า prefill เร็วแค่ไหน
ลองนึกภาพงาน agent ทั่วไปที่ป้อน context เข้าไป 65,000 token แล้วให้ตอบสั้น ๆ ราว 300 token เพื่อเรียกใช้เครื่องมือ ในการทดลองนี้ ทั้ง 12 โมเดลที่มีข้อมูลระดับ 65K เวลาที่หมดไปกับ prefill อยู่ที่ราว 94-98% ของทั้งหมด แปลว่าต่อให้โมเดลพิมพ์คำตอบเร็วขึ้นเป็นสองเท่า เวลารวมที่เรารอก็แทบไม่ขยับ เพราะส่วนที่พิมพ์คำตอบมันเล็กจนแทบมองไม่เห็นอยู่แล้ว
สำหรับงานที่ context ยาวแต่ตอบสั้น อย่าง agent ที่คอยเรียกเครื่องมือ หรือระบบ RAG ที่ดึงเอกสารมาป้อนให้โมเดลตอบ ตัวเลขที่ควรดูจึงเป็นความเร็ว prefill ที่ context เท่างานจริง ซึ่งในเบนช์มาร์กคือ pp65K หรือ pp131K ไม่ใช่ tg128 การเอา tg128 มาเป็นตัวชูโรงเลยทำให้เราเทียบโมเดลกันผิดจุดมาตลอด
เล็กกว่า แต่เร็วกว่า 4.4 เท่า

คำถามถัดมาคือ แล้วอะไรทำให้ prefill ของโมเดลหนึ่งเร็วหรือช้า คนส่วนใหญ่เดาว่าตัวการคือจำนวนพารามิเตอร์ ยิ่งโมเดลใหญ่ยิ่งช้า หรือไม่ก็เป็นเรื่องสถาปัตยกรรมว่าเป็นแบบ MoE ที่เปิดใช้พารามิเตอร์แค่บางส่วนต่อการประมวลผลหนึ่งครั้ง หรือแบบ dense ที่ใช้พารามิเตอร์ทั้งหมด แต่การทดลองนี้ชี้ไปที่ตัวแปรที่คนมองข้าม นั่นคือจำนวน KV head
อธิบายง่าย ๆ ตอนโมเดลอ่าน context มันจะเก็บบันทึกย่อของทุก token ไว้ในสิ่งที่เรียกว่า KV cache เพื่อไม่ต้องคำนวณ token เดิมซ้ำ กองบันทึกนี้ใหญ่ขึ้นเรื่อย ๆ ตาม context ที่ยาวขึ้น และทุกครั้งที่โมเดลประมวลผล token ใหม่ มันต้องกวาดอ่าน KV cache ทั้งกองอีกรอบ ส่วนจำนวน KV head ก็คือจำนวนชุดของบันทึกที่โมเดลเก็บต่อหนึ่ง token ยิ่งมี KV head เยอะ ข้อมูลที่ต้องกวาดอ่านต่อ token ก็ยิ่งมาก
ตัวอย่างจากการทดลองชัดมาก โมเดล dense ขนาด 9B ที่มี 4 KV head เร็วกว่าโมเดล dense ขนาด 15B ที่มี 8 KV head ถึง 4.4 เท่าที่ context 128K ทั้งที่ตัวเล็กกว่าเกือบครึ่ง เหตุผลคือ 8 KV head แปลว่ามีข้อมูลให้กวาดอ่านต่อ token มากกว่าราว 2.5 เท่าเมื่อเทียบกับ 4 KV head พอ context ยาว งานกวาดอ่านตรงนี้จึงเพิ่มขึ้นมหาศาล
ขนาดของ KV cache ไม่ใช่เรื่องเล่น ๆ โมเดล 24B ตัวหนึ่งที่มี 8 KV head รันเทสต์ที่ 131K ไม่จบด้วยซ้ำ เพราะแค่ KV cache อย่างเดียวก็กินหน่วยความจำไปราว 21GB แล้ว
สถาปัตยกรรมที่ prefill แทบไม่ตก
ถ้า KV cache ที่โตขึ้นเรื่อย ๆ คือต้นตอของความช้า จะเป็นยังไงถ้ามีโมเดลที่ไม่ต้องสะสม KV cache แบบนั้น
นั่นคือสิ่งที่โมเดลตระกูล Mamba2 hybrid ทำได้ ในการทดลองมีตัวหนึ่งชื่อ Granite-4.0-H-Small ของ IBM ที่ประกอบด้วยเลเยอร์แบบ attention ซึ่งเป็นเลเยอร์ที่ต้องสะสม KV cache แค่ 4 ชั้น กับเลเยอร์ Mamba2 อีก 36 ชั้น โมเดลนี้ยังรักษาความเร็ว prefill ไว้ได้ถึง 69% เมื่อ context ยาวถึง 131K ขณะที่โมเดลแบบ transformer ล้วน ๆ ซึ่งเป็นสถาปัตยกรรมกระแสหลักในตอนนี้ ความเร็วตกลงเหลือต่ำกว่า 42% กันทุกตัว
เหตุผลคือเลเยอร์ Mamba2 เก็บสถานะแบบ recurrent ซึ่งเป็นความจำขนาดคงที่ ไม่พองตาม context เหมือน KV cache จึงเหลือแค่เลเยอร์ attention 4 ชั้นเท่านั้นที่ทำให้ KV cache โต ความช้าตอน context ยาวเลยแทบไม่เกิด
แต่ทางลัดแบบนี้ก็ต้องแลกมาด้วยข้อจำกัด Granite-4.0-H-Small พิมพ์คำตอบช้า อยู่ที่ราว 71 token ต่อวินาที และความสามารถในการคิดวิเคราะห์ค่อนข้างอ่อน มันจึงไม่ใช่โมเดลสำหรับงานคิดหนัก ๆ แต่สำหรับงานแบบป้อน context มหาศาลแล้วให้ตอบสั้น ซึ่งก็คือลักษณะงาน agent พอดี ข้อได้เปรียบเรื่อง prefill ที่ไม่ตกนี้คือของจริง
คำแนะนำเรื่อง quantize ที่อาจให้ผลตรงข้าม
มีคำแนะนำยอดฮิตในวงการ local LLM หรือการรันโมเดล AI ในเครื่องตัวเอง ว่าให้ quantize KV cache คือบีบบันทึกในแคชให้เล็กลง เช่นจากความละเอียด F16 ลงมาเหลือ Q8 หรือ Q4 เพื่อประหยัดหน่วยความจำและเพิ่มความเร็ว แต่การทดลองนี้พบว่าตอน context ยาว มันอาจให้ผลตรงข้ามได้
เพราะการบีบแคชให้เล็กลง ก็ต้องแลกมาด้วยการแปลงข้อมูลในแคชกลับเป็นความละเอียดเต็มทุกครั้งที่โมเดลใช้งาน และงานแปลงกลับนี้เองก็เป็นภาระประมวลผลที่โตตาม context พอ context ยาวถึงจุดหนึ่ง เวลาที่เสียไปกับการแปลงกลับ อาจมากกว่าเวลาที่ประหยัดได้จากแคชที่เล็กลง
ผลที่ context 65K คือ KV cache แบบ F16 ที่ไม่บีบเลย กลับเร็วกว่าแบบบีบในหลายกรณี มันเร็วกว่าตั้งแต่ 11% ถึง 53% กับโมเดล MoE และเร็วกว่าราว 21% กับโมเดล dense ตัวเล็กที่มี KV head น้อย ในทางกลับกัน F16 ช้าลงกับโมเดล dense ตัวใหญ่ที่มี KV head ตั้งแต่ 8 ขึ้นไป โดยช้าลงได้ตั้งแต่ 18% ไปจนถึง 66%
ประเด็นสำคัญคือผลตรงนี้ขึ้นอยู่กับฮาร์ดแวร์และ backend ที่ใช้ ซึ่งก็คือซอฟต์แวร์เบื้องหลังที่สั่งการ์ดจอให้รันโมเดล ไม่ใช่กฎตายตัว ทางที่ปลอดภัยคือให้ลองทั้ง F16 และแบบบีบบนเครื่องตัวเองด้วย context เท่างานจริง แล้ววัดเทียบดู อย่าเพิ่งเชื่อว่าบีบแล้วเร็วกว่าเสมอ
แล้วจะเลือกโมเดลสาย context ยาวยังไง
ลองรวบสิ่งที่การทดลองนี้ชี้ไว้เป็นลำดับสำหรับเลือกโมเดลในงาน context ยาวหรืองาน agent แบบหยิบไปใช้ได้จริง
- ดูความเร็ว prefill ที่ context เท่างานจริง คือ pp65K หรือ pp131K ไม่ใช่ token ต่อวินาที (tg128)
- เปิดไฟล์ config เช็กค่า n_kv_heads กับ head_dim ก่อนดูจำนวนพารามิเตอร์ เพราะโมเดลที่มี KV head น้อยกว่ารับมือ context ยาวได้ดีกว่า
- ถ้างานคือ context มหาศาลแต่ตอบสั้น ลองพิจารณา Mamba2 hybrid อย่าง Granite-4.0-H-Small โดยยอมแลกกับ decode ที่ช้าและการคิดวิเคราะห์ที่อ่อนลง
- ลอง F16 KV cache เทียบกับแบบบีบบนเครื่องตัวเอง อย่าเหมาว่าบีบแล้วเร็วกว่า
ในภาพรวม โมเดลแบบ MoE มักเป็นจุดลงตัวสำหรับงาน agent ที่ใช้ context ยาวบนการ์ดจอสำหรับผู้ใช้ทั่วไป เพราะตอน prefill มันประมวลผลเฉพาะพารามิเตอร์ที่ active หรือส่วนที่ถูกเรียกใช้จริง เช่นราว 3B จากทั้งหมด 35B ปริมาณข้อมูลที่ต้องขนไปมาระหว่างการ์ดจอกับแรมจึงน้อย แถม decode ก็เร็ว
เมื่อนำความเร็วที่วัดได้ไปรวมกับคะแนนความฉลาดจากดัชนี Artificial Analysis Intelligence Index เป็นคะแนนเดียว โมเดล MoE ก็ขึ้นมานำ ส่วนโมเดลที่ฉลาดที่สุดในเทสต์ซึ่งเป็น dense กลับตกไปอยู่อันดับ 6 เพราะใช้เวลานานกว่าราว 3 เท่า ขณะที่โมเดล MoE ตัวนำได้คะแนนความฉลาดราว 87% ของตัวที่ฉลาดสุด แต่เร็วกว่าถึง 3 เท่า สำหรับงาน agent ที่ต้องวนแก้งานหลายรอบ ความเร็วที่ต่างกัน 3 เท่ามีค่ามากกว่าความฉลาดที่ต่างกันนิดเดียว
อ่านอย่างมีสติ
ก่อนจะเอาตัวเลขเหล่านี้ไปใช้ มีบริบทที่ต้องรู้ ตัวเลขทั้งหมดมาจากการวัดของผู้ใช้คนเดียวที่แชร์ไว้ในกลุ่ม r/LocalLLaMA บน Reddit รันผ่าน llama.cpp บนการ์ด RX 7900 XT เพียงใบเดียว ผ่าน backend Vulkan รวมเวลาราว 21 ชั่วโมง จึงไม่ใช่ผลจากห้องแล็บที่วัดหลายเครื่องหลายการ์ด
สุดท้ายแล้ว โจทย์ของงาน context ยาวไม่ได้อยู่ที่โมเดลพิมพ์คำตอบเร็วแค่ไหน แต่อยู่ที่มันอ่าน context ทั้งก้อนให้จบได้เร็วแค่ไหน และนั่นคือตัวเลขที่ไม่มีค่ายไหนเอาขึ้นสไลด์ตอนเปิดตัว
ที่มา: โพสต์ I benchmarked 13 models at 65K-128K context to find out what actually matters for agentic workloads จาก r/LocalLLaMA



