การแยก Prefill และ Decode ออกจากกัน
การแยก prefill และ decode ข้ามอุปกรณ์ช่วยเร่งความเร็วการอนุมาน LLM และปรับปรุงประสิทธิภาพ
เพื่อสร้างโทเค็นเอาต์พุตจากพรอมต์อินพุต กระบวนการอนุมานของ LLM จะแบ่งออกเป็นสองขั้นตอน ได้แก่ prefill และ decode โดย prefill ทำงานกับโทเค็นอินพุตและเติม KV cache ก่อนเข้าสู่ขั้นตอน decode ที่สร้างโทเค็นทีละตัว แม้ว่าการ decode หนึ่งครั้งโดยทั่วไปจะใช้เวลาเพียงหลายสิบมิลลิวินาที แต่ prefill ใช้เวลานานกว่ามาก หากรันบนอุปกรณ์เดียวกัน การผสม prefill เข้ากับ decode จะทำให้ประสิทธิภาพของ decode ลดลง ในบทความนี้ เราจะสำรวจวิธีแก้ปัญหาที่ได้รับการยอมรับในรูปแบบของ disaggregated prefill และ decode โดยรันทั้งสองส่วนบนอุปกรณ์แยกกันเพื่อเพิ่มทั้ง throughput ของ prefill และเวลาแฝงของ decode ให้สูงสุด
ประสิทธิภาพระหว่าง Prefill และ Decode
ในเอ็นจิ้นการให้บริการ LLM ทั่วไป ตัวจัดกำหนดการแบตช์จะเลือกคำขอที่จะประมวลผลในแต่ละขั้นตอนการทำงานของโมเดล เมื่อทำงานบนอุปกรณ์หรือโหนดเดียว ทั้งคำขอ prefill และ decode จะถูกจัดกลุ่มเป็นแบตช์ร่วมกัน ต้นทุนของความสนใจ (attention) ซึ่งรวบรวมตามความยาวของลำดับ จะเพิ่มขึ้นสำหรับทั้ง prefill และ decode โดยเป็นสัดส่วนกับความยาวของรายการในแคช KV (kv_len) โดยปกติแล้วคำขอ decode จะส่งต่อโทเค็นเดี่ยว (qo_len=1) ด้วยต้นทุนที่น้อยที่สุดผ่านเลเยอร์อื่นๆ ที่ทำงานอย่างอิสระกับโทเค็นในลำดับ ส่วนคำขอ prefill จะส่งต่อโทเค็นหลายพันหรือหลายหมื่นโทเค็นด้วยต้นทุนที่สูงผ่านเลเยอร์หนาแน่น (dense layers) (qo_len ขนาดใหญ่)
ความหน่วง (latency) ของฟอร์เวิร์ดพาส (forward pass) ได้รับอิทธิพลอย่างมากจากจำนวนโทเค็นอิสระที่ส่งผ่านเลเยอร์หนาแน่น (qo_len) มากกว่าจำนวนโทเค็นที่ดึงมาจากแคช KV ระหว่างการทำ attention (kv_len) การทำ attention สามารถทำการประมวลผลแบบขนานได้ทั้งจำนวนคำขอและ kv_len ที่เป็นสัดส่วนกับความยาวของลำดับ ทำให้สามารถใช้งานทรัพยากรได้อย่างมีประสิทธิภาพ Prefill นั้นถูกจำกัดด้วยการคำนวณ (compute bound): เนื่องจาก qo_len มีค่าสูง เคอร์เนล GEMM จึงสามารถจัดสรรบล็อกตามมิติ M ได้เพียงพอเพื่อใช้ประโยชน์จากความสามารถในการคำนวณของ GPU สมัยใหม่ได้อย่างเต็มที่ ในขณะที่ Decode ถูกจำกัดด้วยหน่วยความจำ (memory bound): เนื่องจากขนาดแบตช์มักจะต่ำ จำนวนอินพุตตามมิติ M จึงมักจะมีน้อย ซึ่งเพียงพอสำหรับบล็อกเดียวเท่านั้น แม้ว่าเคอร์เนล Split-K GEMM จะสามารถปรับปรุงการใช้งาน SM สำหรับขนาดแบตช์โทเค็นต่ำได้ แต่แคชและหน่วยคูณเมทริกซ์มักจะยังถูกใช้งานไม่เต็มศักยภาพ
เมื่อนำมารวมกัน แบตช์ที่มีคำขอสำหรับ prefill จะส่งผลให้เกิดความหน่วงสูงขึ้นผ่านฟอร์เวิร์ดพาส ซึ่งส่งผลกระทบในเชิงลบต่อปริมาณงาน (throughput) การ decode ของอินสแตนซ์ทั้งหมด แม้ว่าการผสมผสานคำขอ prefill เข้ากับคำขอ decode หรือการใช้ chunked prefill จะช่วยปรับปรุงประสิทธิภาพการ decode เล็กน้อย แต่ก็เป็นเรื่องยากที่จะรักษาปริมาณงาน prefill ให้เพียงพอสำหรับการประมวลผลคำขอจำนวนมากบนอินสแตนซ์เพื่อเพิ่มปริมาณงาน decode ให้สูงสุด ในกรณีของโมเดลขนาดใหญ่ที่มีความยาวเอาต์พุตทั่วไป เพื่อรักษากลุ่มแบตช์ขนาดใหญ่สำหรับการ decode จึงจำเป็นต้องรัน prefill บ่อยครั้งจนทำให้ความหน่วงเฉลี่ยแย่ลงอย่างมากและทำให้เอาต์พุตเกิดอาการสะดุด
ปัญหาเหล่านี้สามารถแก้ไขได้โดยการใช้ชุดโหนดแยกต่างหากเพื่อทำหน้าที่ prefill และ decode การเชื่อมโยงโหนด prefiller เข้ากับโหนด decoder หลายโหนด จะช่วยให้สามารถกำหนดตารางเวลาคำขอ prefill ได้เพียงพอเพื่อเพิ่มปริมาณงานสูงสุด และรักษารักษาจำนวนคำขอพร้อมกันบนโหนด decoder ให้มากพอที่จะเพิ่มปริมาณงาน decode สูงสุดได้เช่นกัน โหนด prefiller จะทำหน้าที่เติมข้อมูลในแคช KV ซึ่งจะถูกถ่ายโอนไปยังโหนด decoder ต่อไป เนื่องจากตัว decoder ไม่จำเป็นต้องหยุดชะงักเพื่อทำ prefill อีกต่อไป ความหน่วงจึงมีความแน่นอนมากขึ้น เนื่องจากผลกระทบโดยรวมจาก kv_len ของคำขอที่ใช้งานอยู่ซึ่งกำลังเติบโตนั้นมีความเด่นชัดน้อยลงมาก โดยแลกมากับการที่ Time to First Token (TTFT) เพิ่มขึ้น เนื่องจากกระบวนการถ่ายโอนแคช KV ผ่านเครือข่ายอาจใช้เวลาตั้งแต่หลักสิบถึงหลายร้อยมิลลิวินาที
KV Messenger
ที่ Perplexity การใช้งานระบบแยก prefill และ decode ของเราสร้างขึ้นโดยรอบ KV messenger ซึ่งทำหน้าที่โต้ตอบกับเอ็นจิ้น LLM เพื่อประสานงานการถ่ายโอนแคช KV จากโหนด prefiller ไปยังโหนด decoder ผ่านเครือข่าย ในฝั่ง prefiller ตัว messenger จะยอมรับคำขอจากโหนด decoder ส่งต่อคำขอเหล่านั้นไปยังตัวจัดกำหนดการแบตช์ และติดตามการทำงานของฟอร์เวิร์ดพาสเพื่อส่งมอบแคช KV ด้วยความหน่วงที่ต่ำที่สุดเท่าที่จะเป็นไปได้ ในฝั่ง decoder หลังจากที่หน้าหน่วยความจำที่ไม่สามารถขับออกได้ (un-evictable pages) ถูกจัดสรรแล้ว ตัว messenger จะบล็อกไม่ให้คำขอถูกกำหนดเวลาสำหรับการ decode จนกว่าจะได้รับการแจ้งเตือนว่าการถ่ายโอนแคช KV และบริบทของ decoder เสร็จสิ้นแล้ว
การแยก prefill จำเป็นต้องมีการเชื่อมต่อที่มีปริมาณงานสูงและมีความหน่วงต่ำ ดังนั้นการใช้งานของเราจึงได้รับการปรับแต่งสำหรับ RDMA โดยรองรับทั้ง EFA และคอนโทรลเลอร์อินเทอร์เฟซเครือข่าย (NIC) แบบ ConnectX KV Messenger ถูกสร้างขึ้นบน libfabric โดยใช้ตัวห่อหุ้ม fabric-lib ของเรา เพื่อให้อนามัยนามธรรม (abstractions) ระดับต่ำที่มีความหน่วงต่ำและอยู่ในระดับที่สูงกว่าเหนือโพรมิทีฟ Remote Direct Memory Access (RDMA) พร้อมทั้งใช้งานการถ่ายโอนหน้าหน่วยความจำและข้อมูลเมตาที่มีประสิทธิภาพ ควบคู่ไปกับการส่งสัญญาณความหน่วงต่ำ ในเบื้องหลัง fabric-lib จะประสานงานระหว่าง GPU และ NIC ที่เชื่อมต่อโดยตรงเพื่อคัดลอกข้อมูลจากโหนด prefiller ไปยังโหนด decoder
เมื่อได้รับคำขอ โหนด prefiller จะจัดสรรชุดหน้าแหล่งที่มาของ KV ที่สอดคล้องกัน และกำหนดเวลาคำขอสำหรับ prefill โดยใช้เอ็นจิ้นในเครื่อง เพื่อลดความหน่วง การถ่ายโอนจะไม่รอให้ฟอร์เวิร์ดพาสเสร็จสิ้น แต่การคัดลอกหน้า KV จะเริ่มต้นทันทีที่โมเดลเสร็จสิ้นการผนวกรายการแคช KV ลงในแคช KV สำหรับแต่ละเลเยอร์ เนื่องจากคำขอ prefill สามารถแบ่งเป็นส่วนย่อยๆ (chunked) ได้ ตัวจัดกำหนดการแบตช์จึงแจ้งเตือน KV messenger เกี่ยวกับส่วนย่อยที่กำลังถูกกำหนดเวลาไว้ก่อนการดำเนินการ เพื่อรองรับกราฟ CUDA พร้อมทั้งสามารถติดตามเลเยอร์ต่างๆ ได้ ตัว messenger จึงรักษาเธรดเฉพาะที่ทำหน้าที่คอยตรวจสอบตัวนับ (counter) ซึ่งจะเพิ่มค่าขึ้นหลังจากการทำเอาต์พุตโปรเจกชันของ attention ตัวนับนี้จะถูกรักษาไว้เฉพาะบนโหนดหลัก (lead node) ในสภาพแวดล้อมแบบชาร์ด (sharded environment): แม้ว่ารายการแคช KV จะถูกต้องหลังจากการผนวกและก่อนการทำ attention แต่เอาต์พุตโปรเจกชันจะถูกลดทอนข้ามแรงก์ (reduced across ranks) เพื่อซิงโครไนซ์กันโดยปริยาย เมื่อตรวจพบการเปลี่ยนแปลงในตัวนับ ตัว messenger จะได้รับการแจ้งเตือนและเรียกใช้งาน fabric-lib เพื่อเริ่มต้นการถ่ายโอนเลเยอร์
หลังจากที่การถ่ายโอนส่วนย่อยสุดท้ายเสร็จสมบูรณ์ ข้อมูลเมตาเพิ่มเติมใดๆ ก็จะถูกคัดลอกไปด้วยเช่นกัน: การถอดรหัสแบบเก็งกำไร (speculative decoding) หรือ MTP จำเป็นต้องมีการย้าย logits และสถานะที่ซ่อนอยู่ (hidden states) ไปยัง decoder การคัดลอกเหล่านี้ดำเนินการผ่าน RDMA ไปยังและมาจากบัฟเฟอร์ที่จัดสรรไว้ล่วงหน้า
เมื่อการถ่ายโอนที่รอดำเนินการทั้งหมดของส่วนย่อยสุดท้ายเสร็จสิ้น โหนด prefiller จะยกเลิกการจัดสรรหน้า KV และทำคำขอนั้นให้เสร็จสิ้น โหนด decoder จะไม่ได้รับการแจ้งเตือนอย่างชัดเจน แต่จะใช้ตัวนับทันที (immediate counters) เพื่อติดตามจำนวนการทำงานที่เสร็จสมบูรณ์ จำนวนการทำงานของ RDMA ในฝั่ง prefiller จะเป็นสัดส่วนกับจำนวนหน้าที่ถูกถ่ายโอน เมื่อการคัดลอกหน้าหน่วยความจำและบริบทตามจำนวนที่ทราบเสร็จสิ้น fabric-lib จะเรียก KV messenger เพื่อระบุว่าคำขอนี้พร้อมสำหรับการ decode แล้ว ตัว messenger จะยกเลิกการจัดสรรบริบทใดๆ และส่งมอบคำขอนั้นให้กับเอ็นจิ้น LLM
การถ่ายโอนแคช KV แบบชาร์ด (Sharded KV Cache Transfers)
หาก prefiller และ decoder พึ่งพา Tensor Parallelism (TP) และทำการชาร์ดหรือจำลองแคช KV เหมือนกันทุกประการ เอ็นจิ้นการถ่ายโอนเดี่ยวจะประสานงานอุปกรณ์หลายเครื่องเพื่อส่งและรับหน้าหน่วยความจำของรีพลิกา (replicas) ทั้งหมด เพื่อให้สามารถใช้ messenger และเอ็นจิ้นการถ่ายโอนเพียงตัวเดียวได้ แม้ว่าตัวรันคำสั่ง (executor) ของโมเดลจะถูกจำลองข้ามอุปกรณ์และกระบวนการหลายรายการก็ตาม cuMem และ cuMemImportFromShareableHandle จะถูกนำมาใช้เพื่อจัดสรรหน่วยความจำอุปกรณ์ที่เป็นเบื้องหลังของแคช KV และแมปเข้าสู่กระบวนการหลัก เอ็นจิ้นการถ่ายโอนจะตรวจสอบโทโพโลยีของโหนดเพื่อค้นหา NIC และ CPU ในโหนด NUMA ที่ใกล้ที่สุดสำหรับใช้ในการถ่ายโอนสไลซ์แคช KV แต่ละรายการ
หากแหล่งที่มาและปลายทางมีการชาร์ดเหมือนกันทุกประการ การถ่ายโอนจะเป็นเรื่องง่ายเนื่องจากมีการแมปแบบหนึ่งต่อหนึ่งจากอุปกรณ์และหน้าหน่วยความจำของแหล่งที่มาและปลายทาง ในสถานการณ์นี้ การทำชาร์ดช่วยลดความหน่วงในการถ่ายโอนโดยปริยาย: การใช้ GPU มากขึ้นจะทำให้สามารถใช้งาน NIC ที่เกี่ยวข้องได้มากขึ้น เข้าใกล้การใช้แบนด์วิดท์เต็มขีดจำกัดมากขึ้น อย่างไรก็ตาม หากมีความไม่ตรงกัน เอ็นจิ้นการถ่ายโอนจะต้องทำการแยกหรือสร้างหน้าหน่วยความจำขึ้นใหม่ ขึ้นอยู่กับอัตราส่วนระหว่างสไลซ์ต้นทางและปลายทาง
หาก prefiller แบ่งแคช KV ข้ามอุปกรณ์จำนวนมากขึ้น หน้าหน่วยความจำแบบเต็มจะถูกสร้างขึ้นใหม่บน decoder โดยการส่งซีกที่สอดคล้องกันออกจากอุปกรณ์ prefiller หาก decoder มีชาร์ดมากกว่า มันจะได้รับหน้าหน่วยความจำจากหลายแหล่ง ตัว decoder จำเป็นต้องทราบรูปแบบการชาร์ดของ prefiller เพื่อที่จะสามารถคำนวณจำนวนการเขียน RDMA ที่คาดว่าจะได้รับ หากเกี่ยวข้องกับการจำลองข้อมูล (replication) prefiller จะจัดกลุ่มอุปกรณ์ออกเป็นชุดรีพลิกา (replica sets) ซึ่งจำลองแคช KV เต็มรูปแบบภายในตัวเอง ชุดรีพลิกาปลายทางจะถูกกำหนดชุดต้นทางแบบสุ่มเพื่อใช้อุปกรณ์ที่มีอยู่ทั้งหมดในการเริ่มต้นการเขียนแบบ RDMA
การถ่ายโอนแบบชาร์ดจำเป็นต้องมีการปรับแต่งแคช KV เล็กน้อย ตามค่าเริ่มต้น FlashInfer จะอาศัยเลย์เอาต์ NHD ซึ่งจัดลำดับโทเค็นภายในหน้าหน่วยความจำให้อยู่ภายในส่วนหัว (heads) เนื่องจากแคชมีแนวโน้มที่จะถูกชาร์ดตามจำนวนความสนใจของเฮด (attention heads) สิ่งนี้จึงสร้างความไม่ต่อเนื่องภายในเฮด การถ่ายโอน RDMA ไม่รองรับการเขียนแบบเว้นช่วง (strided writes) โดยปริยาย จึงจำเป็นต้องใช้หนึ่งการทำงานต่อหนึ่งเฮดในการถ่ายโอน แต่เพื่อลดจำนวนการโต้ตอบกับ libfabric เราจึงจัดระเบียบแคช KV โดยใช้เลย์เอาต์ HND ซึ่งวางมิติของเฮดไว้ก่อนจำนวนโทเค็น วิธีนี้ช่วยให้มั่นใจถึงความต่อเนื่อง ช่วยให้สามารถคัดลอกหน้าหน่วยความจำข้ามไปได้ด้วยการเขียนเพียงครั้งเดียว
การถอดรหัสแบบเก็งกำไร (Speculative Decoding)
การถอดรหัสแบบเก็งกำไรจำเป็นต้องมีการปรับแต่งเล็กน้อยสำหรับการแยก prefill และ decode ในการใช้งานของเรา โหนด prefiller จะไม่ได้รับอนุญาตให้ทำการสุ่มตัวอย่างโทเค็น (sample tokens) เนื่องจากโมเดล Sonar ของ Perplexity รองรับเอาต์พุตที่มีโครงสร้าง เราจึงไม่ต้องการแบกรับความซับซ้อนในการซิงโครไนซ์การใช้งานตัวประมวลผลสกีมา (schema processor) ระหว่าง prefiller และ decoder ในกลไก MTP และการถอดรหัสแบบเก็งกำไร การทำ prefill ของโมเดลร่าง (draft model) จนถึงโทเค็นสุดท้ายจะเกี่ยวข้องกับการสุ่มตัวอย่างโทเค็นจากโมเดลเป้าหมาย (target model)
เพื่อหลีกเลี่ยงปัญหาเหล่านี้ กระบวนการ prefill จะไม่รวมโทเค็นสุดท้ายของลำดับอินพุต แต่จะมีการถ่ายโอนสถานะที่ซ่อนอยู่ (hidden states) หรือ logits จากขั้นตอน prefill ก่อนโทเค็นสุดท้ายแทน และจะปฏิบัติเสมือนเป็นโทเค็น decode ในขั้นตอนถัดไปบน decoder แม้ว่าวิธีนี้จะเพิ่มความหน่วงขึ้นเล็กน้อย เนื่องจากต้องดำเนินการขั้นตอน decode แบบเต็มรูปแบบหลัง prefill เพื่อออกโทเค็นแรก แต่ความซับซ้อนของการใช้งานจะลดลงอย่างมาก
การปรับใช้แบบแยกส่วน (Disaggregated Deployments)
เราได้ทำการปรับใช้หรือทดลองกับการกำหนดค่าแบบแยกส่วนหลายรูปแบบด้วยโมเดลที่แตกต่างกัน เพื่อรองรับทั้งทราฟฟิกในสภาพแวดล้อมการใช้งานจริง (production traffic) หรือภาระงานประเมินภายในองค์กร (in-house evaluation workloads) จากขนาดและกลไกความสนใจของโมเดล เราได้เลือกรูปแบบการชาร์ดที่เหมาะสมสำหรับโหนด prefiller และ decoder เพื่อให้การใช้งาน GPU เกิดประโยชน์สูงสุด
DeepSeek-R1
สำหรับ DeepSeek เราได้พิจารณาการปรับใช้ทั้งแบบ Tensor-Parallel (TP) และ Data-Parallel (DP) ดังที่ได้กล่าวไปแล้วในบล็อกโพสต์ก่อนหน้านี้ การปรับใช้แบบ TP ให้ความหน่วงที่ดีกว่าโดยแลกมากับปริมาณงานที่ต่ำกว่า ซึ่งต้องใช้ GPU มากขึ้นในการรองรับทราฟฟิกที่หนาแน่น การปรับใช้แบบ DP สามารถปรับขนาดตามภาระงานได้ดีกว่ามาก อย่างไรก็ตาม ปริมาณงานสูงสุดจะต่ำกว่าเนื่องจากต้นทุนของการสื่อสารระหว่างอุปกรณ์หรือระหว่างโหนด
DeepSeek อาศัยกลไก Multi-Head Latent Attention ซึ่งทำการบีบอัดแคช KV เนื่องจากเฮด KV ทั้งหมดถูกบีบอัดเป็นเวกเตอร์แฝง (latent vector) เดียว TP จึงไม่สามารถชาร์ดแคช KV ได้ แต่จะต้องทำการจำลองเวกเตอร์แฝงเหล่านั้นบนทุกแรงก์แทน การชาร์ดจะเกิดขึ้นหลังจากการคลายการบีบอัด (decompression) เนื่องจากแต่ละแรงก์สามารถดึงเฮดที่แตกต่างกันจากตัวแทนแฝง (latent representation) เดียวกันได้ ส่งผลให้ชาร์ดแคช KV ทั้งหมดเหมือนกันทุกประการทั้งในฝั่ง prefiller และ decoder
ด้วยการตั้งค่า TP ภายในโหนดเดียว ทั้ง prefiller และ decoder จะถูกชาร์ดเหมือนกันทุกประการ การถ่ายโอนจะถูกส่งมาจากทุกแรงก์เพื่อให้สามารถใช้ประโยชน์จาก NIC ที่มีอยู่ทั้งหมดได้อย่างเต็มที่ อย่างไรก็ตาม ด้วยการปรับใช้แบบ DP ซึ่งขนาดแรงก์ TP ต่ำกว่าหรือแต่ละแรงก์ DP ถูกกำหนดให้กับ GPU เดี่ยว อุปกรณ์ prefiller ใดๆ ที่ถือสำเนาจำลองของแคช KV สามารถทำการส่งต่อได้ เพื่อสร้างความสมดุลให้กับคำขอข้าม NIC ที่มีอยู่ทั้งหมด เราจึงสุ่มเลือก GPU และ NIC เพื่อส่งแคช KV จาก prefiller ไปยัง decoder
ในกรณีที่ใช้ prefill-decode แบบผสม การปรับใช้ R1 ของเราประสบปัญหาในการรักษาเสถียรภาพให้เกิน 50 TPS อย่างต่อเนื่อง เนื่องจากมีการขัดจังหวะการทำ prefill บ่อยครั้งในระดับหลายร้อยมิลลิวินาที ในทางตรงกันข้าม การแยก prefill ออกมาทำให้เราต้องเสียต้นทุนเพิ่มขึ้นประมาณ 100ms สำหรับ TTFT ในแต่ละคำขอ แต่โหนด prefiller เดี่ยวสามารถรักษากลุ่มแบตช์ที่สม่ำเสมอบนโหนด decoder 3 โหนด ส่งมอบปริมาณงานที่สูงกว่า 90 TPS ในขณะที่จัดการภาระงานประมาณ 1 QPS ต่อโหนด decoder สำหรับการปรับใช้แบบ data-parallel ค่า TPS จะต่ำกว่าเล็กน้อยอยู่ที่ประมาณ 50 อย่างไรก็ตาม อินสแตนซ์สามารถรองรับภาระงานได้ 1 QPS ต่อแรงก์ โดยมี 8 แรงก์ต่อหนึ่งโหนด
Qwen3-Coder
โมเดลขนาด 480B นี้ใช้ Grouped-Query Attention (GQA) ดังนั้นความสนใจ (attention) จึงสามารถชาร์ดได้อย่างง่ายดายและได้รับประโยชน์จาก tensor parallelism โดยไม่ต้องสูญเสียหน่วยความจำสำหรับแคช KV ส่งผลให้เราสามารถชาร์ดโมเดลข้าม GPU 8 ตัวสำหรับทั้ง prefill และ decode โดยจับคู่โหนด decoder ประมาณ 3 โหนดกับโหนด prefiller 1 โหนด เนื่องจาก attention ถูกชาร์ด เราจึงพึ่งพาเลย์เอาต์แคช KV แบบ HND เพื่อชาร์ดแคช KV ของ prefiller และ decoder โดยจับคู่แรงก์ prefiller กับแรงก์ decoder และใช้ประโยชน์จาก NIC ทั้งหมดอย่างเต็มที่เพื่อถ่ายโอนสไลซ์แบบคู่ขนาน
การอ้างอิง
@online{perplexity2025DisaggregatedPrefillAndDecode,
author = {{Perplexity} and Nandor Licker and Lequn Chen and Kevin Hu and Erin McKnight and Phi Hoang},
title = {{Disaggregated prefill and decode}},
date = {2025-08-01},
url = {https://perplexity.coolkeypoint.com/hub/blog/disaggregated-prefill-and-decode}
}