การฝังตัวที่รวดเร็วบน GPU
การค้นหน้าที่รวดเร็วและแม่นยำมีความสำคัญอย่างยิ่งต่อ Perplexity ทั้งหมด ตั้งแต่ Search และ Computer ไปจนถึงแพลตฟอร์ม API ของเรา เบื้องหลังการทำงานหนักจะดำเนินการโดยโมเดลการฝังตัวและการจัดอันดับ ซึ่งช่วยให้ระบบของเราidentify ผลลัพธ์ที่เกี่ยวข้องมากที่สุดสำหรับคำค้นหาที่กำหนด เราบรรลุสถานะของรัฐ
การค้นหน้าที่รวดเร็วและแม่นยำมีความสำคัญอย่างยิ่งต่อ Perplexity ทั้งหมด ตั้งแต่ Search และ Computer ไปจนถึงแพลตฟอร์ม API ของเรา เบื้องหลังการทำงานหนักจะดำเนินการโดยโมเดลการฝังตัวและการจัดอันดับ (Embedding and Ranking Models) ซึ่งช่วยให้ระบบของเราidentify ผลลัพธ์ที่เกี่ยวข้องมากที่สุดสำหรับคำค้นหาที่กำหนด เราบรรลุคุณภาพและความหน่วงระดับแนวหน้าด้วยการฝึกฝนและให้บริการโมเดลของเราเอง เช่น pplx-embed
บทความนี้นำเสนอภาพเบื้องหลังของโครงสร้างพื้นฐานการให้บริการของ Perplexity สำหรับโมเดลประเภทพิเศษนี้ เราอภิปรายถึงเทคนิคของเราในการตอบสนองความต้องการด้านการอนุมานของการค้นหาแบบ AI-native อย่างมีประสิทธิภาพ ช่วยให้สามารถสร้างต้นแบบและประเมินผลโมเดลได้อย่างรวดเร็ว พร้อมทั้งขับเคลื่อน ดัชนีการค้นหาขนาด exabyte ของเรา เทคนิคเหล่านี้ร่วมกันขยายขอบเขต Pareto ของคุณภาพและประสิทธิภาพการค้นหา ช่วยให้เราสามารถให้บริการตัวแทน (Agents) และผู้ใช้ด้วยผลลัพธ์ที่ดีที่สุดเท่าที่จะเป็นไปได้ด้วยต้นทุนและความหน่วงที่ต่ำที่สุด
การฝังตัวสำหรับการค้นหา (Embeddings for Search)
ในการตั้งค่าการค้นหาทั่วไป เอกสารที่ทำดัชนีแล้วจะถูกแมปไปยังสเปซเวกเตอร์ที่มีมิติสูงโดยใช้โมเดลการฝังตัวและจัดเก็บไว้ในฐานข้อมูลเวกเตอร์ ด้วยการฝังตัวคำค้นหาโดยใช้โมเดลเดียวกัน เอกสารที่คล้ายคลึงกันจึงสามารถค้นหาได้โดยการหาเวกเตอร์ที่ใกล้เคียงที่สุดกับเวกเตอร์ของคำค้นหานั้น สิ่งนี้นำไปสู่รูปแบบการจราจรสองรูปแบบที่แตกต่างกันสำหรับเอ็นจิ้นการอนุมานในการให้บริการ:
- การฝังตัวแบบแบตช์ (Batch Embedding): เมื่อสร้าง ขยาย หรือทำดัชนีฐานข้อมูลใหม่ เอกสารจำนวนมากจะต้องถูกฝังตัวลงในสเปซเวกเตอร์ โดยเพิ่มปริมาณงานให้สูงสุดเพื่อลดต้นทุนให้น้อยที่สุด
หลังจากการค้นหาเวกเตอร์ เอกสารจำนวนมากจะต้องถูกให้คะแนน (Scored) โดยสร้างความสมดุลระหว่างปริมาณงานและความหน่วง
- การฝังตัวแบบออนไลน์ (Online Embedding): เมื่อทำการสืบค้นฐานข้อมูล จะต้องฝังตัวคำค้นหาสั้นๆ เพื่อใช้ในการค้นหา (Lookups) ซึ่งช่วยลดความหน่วงให้น้อยที่สุด
เราสร้างโครงสร้างพื้นฐานการอนุมานขึ้นมาเพื่อใช้ประโยชน์จากคอมโพเนนต์ส่วนกลางให้ได้มากที่สุดในหลากหลายกรณีการใช้งาน เนื่องจากโดยทั่วไปเราใช้โมเดล Transformer ขนาดเล็กเพื่อสร้างการฝังตัว (Embeddings) เราจึงใช้การทำงานส่วนใหญ่ร่วมกับโค้ดการอนุมาน LLM ของเรา: แบตช์การฝังตัวจะคล้ายกับการเติมล่วงหน้าแบบจำกัดการคำนวณ (Compute-bound Prefill) ในขณะที่การฝังตัวแบบออนไลน์ ซึ่งมักจะทำงานบนโทเค็นจำนวนน้อย จะคล้ายคลึงกับการถอดรหัสแบบจำกัดหน่วยความจำ (Memory-bound Decode) ในเชิงการคำนวณ ดังนั้นเราจึงนำ เคอร์เนลการเติมล่วงหน้าและการถอดรหัสที่ได้รับการปรับแต่ง ของเรากลับมาใช้ใหม่เพื่อให้บริการโมเดลการฝังตัว ส่งผลให้เราสามารถบรรลุปริมาณงานการอนุมานแบบแบตช์ขนาดใหญ่ด้วยงานทางวิศวกรรมเพิ่มเติมที่น้อยที่สุด ในขณะที่ยังคงรักษาความหน่วงต่ำสำหรับเวิร์กโหลดการฝังตัวแบบออนไลน์
Tulips, Roses และ Ivy บางส่วน
เราแสดงผลการอนุมานผ่าน API ที่เป็นมาตรฐาน ทั้งภายในและภายนอกผ่านแพลตฟอร์ม API ของเรา เบื้องหลัง มีบริการหลายรายการที่เกี่ยวข้องกับการประมวลผลคำขอการฝังตัว:
- Ivy คือเกตเวย์ HTTP ของ Rust ที่บริการของ Perplexity เรียกใช้งาน
ทำหน้าที่จัดการงานฝั่ง CPU สำหรับคำขอต่างๆ เช่น การแยกวิเคราะห์ JSON, การ ทำโทเค็น (Tokenization), เทมเพลตอินพุต และการแบ่งแบตช์ พร้อมทั้งแปลงคำขอให้เป็นโปรโตคอล gRPC แบบกำหนดเองสำหรับเซิร์ฟเวอร์ปลายทาง การแยกส่วนนี้ช่วยให้เราสามารถกำหนดค่าพารามิเตอร์บางอย่างเกี่ยวกับการทำโทเค็นและการจัดรูปแบบอินพุตได้โดยไม่ต้องไปยุ่งกับอินสแตนซ์การอนุมานที่หนักกว่า
- Tulip คืออินเทอร์เฟซของเซิร์ฟเวอร์การอนุมาน
เป็นเซิร์ฟเวอร์ gRPC ที่ใช้งานด้วย Rust, tokio และ `tonic` Tulip รับคำขอการอนุมาน gRPC พร้อมทั้งจัดการการจัดตารางเวลาและการจัดกลุ่มแบตช์ จากนั้นจะส่งแบตช์ไปยังเอ็นจิ้น ROSE และส่งคืนการตอบสนองที่เสร็จสมบูรณ์ไปยังไคลเอนต์
- **ROSE** (Runtime-Optimized Serving Engine) ทำหน้าที่ประมวลผลการอนุมานโมเดล (Model Inference)
โดยหลักแล้วถูกกำหนดใน Python โดยมี เคอร์เนล, เลเยอร์ และคำนิยามสำหรับโมเดลที่หลากหลาย ROSE ทำหน้าที่นำทางส่งผ่าน (Forward Passes) ผ่านโมเดล พร้อมทั้งให้บริการการจัดการกราฟ CUDA ที่เชี่ยวชาญพิเศษสำหรับการฝังตัว (Embeddings) ซึ่งเชื่อมต่อกับ Tulip ผ่านฟังก์ชัน step() ซึ่งรับแบตช์และส่งคืนการอ้างอิงถึงการคำนวณที่ดำเนินการบนตัวเร่งความเร็ว (Accelerator)

ให้ความสนใจมากกว่าแค่ตัวเคอร์เนล (Paying Attention Beyond the Kernel)
ทั้งโมเดลที่อิงตาม Transformer และสถาปัตยกรรม Hopper/Blackwell เบื้องล่างต่างก็เป็นเทคโนโลยีที่โตเต็มที่แล้ว ดังนั้นการฝังตัวการอนุมาน (Embedding Inference) ฝั่ง GPU จึงหลอมรวมไปสู่การนำไปใช้งานที่เหมาะสมที่สุดในเอ็นจิ้นการอนุมานต่างๆ ถึงกระนั้น เราก็ได้ค้นพบโอกาสเพิ่มเติมในการปรับปรุงรันไทม์และฮาร์เนสที่แสดงผลโมเดลแบบ end-to-end ให้กับไคลเอนต์ โดยเฉพาะอย่างยิ่ง เราพบว่าเราสามารถปรับปรุงความหน่วง (Latencies) ได้ด้วยการจัดการกราฟ CUDA อย่างระมัดระวัง และด้วยการสร้างนามธรรม LazyTensor เพื่อติดตามผลลัพธ์ฝั่ง GPU แบบอะซิงโครนัสในเอ็นจิ้น Rust ดั้งเดิม เราได้นำฟีเจอร์เหล่านี้ไปใช้ใน Tulip เพื่อให้สามารถเชื่อมต่อกับอิมปิลิเมนต์ชันโมเดลของ ROSE ได้อย่างมีประสิทธิภาพ
Tulip
เราออกแบบ Tulip ให้เป็นอินเทอร์เฟซที่มีน้ำหนักเบาที่สุดเท่าที่จะเป็นไปได้เหนือการให้บริการโมเดลของเรา โดยจะจัดการคำขาที่เข้ามาในงานอะซิงโครนัสของ Tokio, รักษาพูลของคำขอที่ติดตามและจัดกำหนดการแบตช์เพื่อส่งไปยังตัวเร่งความเร็ว กลไกการกำหนดเวลาใน Tulip นั้นเรียบง่ายมาก: คำขอจะสะสมในขณะที่ Tulip กำลังส่งงานหรือรอผลลัพธ์ จากคำขอที่สะสมเหล่านี้ ลำดับจะถูกเลือกตามลำดับก่อนหลัง (First-come, First-served) เพื่อรันผ่านโมเดล
กลไกการกำหนดเวลาอย่างง่ายนี้มีแรงจูงใจมาจากการสังเกตประสิทธิภาพของโมเดล สำหรับโมเดลการฝังตัวขนาดเล็ก ที่ความยาวลำดับที่เราให้บริการ เราสังเกตเห็นว่าต้นทุนเชิงเส้นของเลเยอร์หนาแน่นมีมากกว่าต้นทุนกำลังสองของแอตเทนชัน ดังนั้น ความหน่วงจึงเป็นสัดส่วนกับจำนวนโทเค็นเป็นส่วนใหญ่ ไม่ใช่จำนวนลำดับ ด้วยเหตุนี้ เมื่อแบตช์มีขนาดใหญ่พอที่จะทำให้ GPU อิ่มตัว ซึ่งอยู่ที่ประมาณ 512 โทเค็นบนโมเดลที่มีพารามิเตอร์ต่ำกว่าหนึ่งพันล้าน การอัดลำดับเข้าไปในแบตช์มากขึ้นจึงไม่ได้ช่วยปรับปรุงประสิทธิภาพแต่อย่างใด
เพื่อให้สามารถเชื่อมต่อกับโมเดลได้อย่างมีประสิทธิภาพ Tulip จึงอาศัยกราฟ CUDA และการติดตามผลลัพธ์แบบลาซีเพื่อทำให้งานของ GPU และ CPU ซ้อนทับกัน และใช้งานทรัพยากรที่มีอยู่ได้อย่างเต็มที่
การจัดการกราฟ CUDA
การรันการส่งผ่านไปข้างหน้า (Forward Pass) ของโมเดลเกี่ยวข้องกับการทำงานทั้งฝั่ง CPU และฝั่ง GPU CPU มีหน้าที่กำหนดตารางเวลาแบตช์และเรียกใช้เคอร์เนลด้วยพารามิเตอร์ที่เหมาะสม ในขณะที่ GPU จะดำเนินการคูณเมทริกซ์, แอตเทนชัน (Attention), นอร์ม (Norm) หรือเคอร์เนลการเปิดใช้งาน (Activation Kernels) ที่เกี่ยวข้อง สำหรับเวิร์กโหลดที่มีปริมาณงานสูง เช่น การฝึกฝนและการทำดัชนีซ้ำ โอเวอร์เฮดฝั่ง CPU จะมีน้อยมากเนื่องจากขนาดแบตช์และความหน่วงฝั่ง GPU มีขนาดใหญ่ อย่างไรก็ตาม สำหรับขนาดแบตช์ที่เล็กกว่า การทำงานฝั่ง CPU อาจมีมากกว่าการทำงานฝั่ง GPU

เพื่อบรรเทาโอเวอร์เฮด แทนที่จะเปิดใช้เคอร์เนลที่เป็นอิสระ กราฟ CUDA สามารถสร้างขึ้นเพื่อบันทึกเมตาดาต้าที่จำเป็นในการเปิดใช้งานเคอร์เนลทั้งหมดของการส่งผ่านไปข้างหน้าด้วยการเรียกเพียงครั้งเดียวไปยังไดรเวอร์ CUDA ซึ่งช่วยลดความจำเป็นในการรันโค้ด Python และ PyTorch ที่มีราคาแพงซ้ำสำหรับการกำหนดค่าที่สามารถบันทึกกราฟ CUDA ได้
ในแต่ละโมเดล เราจะติดตามจุดเปลี่ยน (Inflection Point) โดยกำหนดจำนวนโทเค็นขั้นต่ำที่การทำงานของ GPU มีราคาแพงกว่าการเรียกใช้เคอร์เนลฝั่ง CPU เนื่องจากโมเดลการฝังตัวมีขนาดเล็ก เราจึงสังเกตเห็นว่าจุดเปลี่ยนนี้เกิดขึ้นที่แบตช์ขนาดหลายพันโทเค็นและหลายสิบลำดับ อิมปิลิเมนต์ชัน Attention บางอย่างอาศัยอินพุตฝั่งโฮสต์แบบไดนามิกเพื่อกำหนดค่าการเรียกใช้เคอร์เนล ซึ่งขัดขวางกราฟ CUDA แบบเติมล่วงหน้า/หนาแน่นสำหรับโมเดลเต็มรูปแบบ เราได้ upstream การเปลี่ยนแปลงไปยังเคอร์เนลที่เกี่ยวข้องเพื่อให้สามารถใช้งานได้ในเอ็นจิ้นการอนุมานของเรา
เพื่อแก้ไขปัญหาโอเวอร์เฮด เราสร้างกราฟ CUDA สำหรับโมเดลทั้งหมดสำหรับโมเดลการฝังตัวทุกรุ่น และทำให้งานของ CPU ทับซ้อนกับงานของ GPU เนื่องจากกราฟ CUDA ช่วยลดโอเวอร์เฮดฝั่ง CPU เมื่อเปิดใช้งานกราฟแล้ว เราจึงมีเวลาว่างในการเริ่มต้นและเข้าคิวการดำเนินการของแบตช์ถัดไปเมื่อใดก็ตามที่พร้อมใช้งาน ผลลัพธ์ของแบตช์ที่รอดำเนินการจะถูกติดตามด้วย LazyTensor ซึ่งช่วยให้งานแบบอะซิงโครนัสใน Rust สามารถบล็อกจนกว่าแบตช์ก่อนหน้าจะดำเนินการเสร็จสิ้น กราฟ CUDA ช่วยในการให้บริการที่มีความหน่วงต่ำโดยรับประกันว่าเราจะไม่ถูกขัดขวางด้วยต้นทุนของการเรียกใช้เคอร์เนล และอำนวยความสะดวกในการจัดตารางเวลาที่ดีขึ้นในกรณีที่มีปริมาณงานสูง เนื่องจากการปลดปล่อย CPU ให้ทำงานในแบตช์ถัดไปได้เร็วยิ่งขึ้น

กราฟ CUDA จะต้องถูกบันทึกไว้สำหรับการกำหนดค่าแต่ละแบบที่แตกต่างกัน ซึ่งสำหรับการฝังตัวหมายถึงกราฟหนึ่งชุดต่อการรวมกันของจำนวนลำดับและจำนวนโทเค็น เนื่องจากตารางนี้กว้างขวางมาก เราจึงทำการแพดจำนวนโทเค็นไปยังบั๊กเก็ตที่เป็นพหุกูณของ 64 หรือ 256 แต่วิธีนี้ก็ยังคงส่งผลให้มีกราฟหลายพันรายการซึ่งอาจใช้เวลาหลายนาทีในการบันทึกสำหรับโมเดลทั่วไป ต้นทุนของการบันทึกมาจากสองแหล่ง: การส่งผ่านไปข้างหน้าแบบกระตือรือร้น (Eager Forward Pass) ที่ต้องดำเนินการเพื่อคอมไพล์เคอร์เนลและตั้งค่าบัฟเฟอร์สำหรับเคอร์เนลต่างๆ ที่ต้องการ ตามด้วยการรันการบันทึกซึ่งรันโค้ด Python ซ้ำ
เราบรรเทาต้นทุนการเริ่มต้นระบบด้วยการบันทึกกราฟ CUDA แบบลาซีในขณะที่เอ็นจิ้นให้บริการ เราติดตามการกำหนดค่าแต่ละรายการและมั่นใจว่าผ่านการรันวอร์มอัพแบบกระตือรือร้นก่อนที่จะทริกเกอร์การบันทึกและการเล่นซ้ำของกราฟในการเรียกใช้ครั้งที่สอง การดำเนินการทั้งหมดที่ตามมาของกำหนดค่ากราฟเดียวกันจะผ่านการเล่นซ้ำกราฟ CUDA การบันทึกกราฟแบบลาซีส่งผลกระทบต่อความหน่วง p99 ในระหว่างการเริ่มต้นระบบ อย่างไรก็ตาม มีประโยชน์ในการกระจายงานแบบกระตือรือร้นหลายนาทีให้กระจายออกไปเป็นหลายชั่วโมง เวลาเริ่มต้นที่เร็วยขึ้นช่วยให้เราสามารถปรับขนาดและจัดการการปรับใช้การฝังตัวได้ดียิ่งขึ้น
เทนเซอร์แบบลาซี (Lazy Tensors)
ผ่านทาง CUDA การทำงานของ GPU จะเป็นแบบอะซิงโครนัส เนื่องจากการเปิดใช้งานเคอร์เนลแบบอะซิงโครนัสจะนำไปต่อคิวบนสตรีม โค้ดโฮสต์จึงต้องทำการซิงโครไนซ์อย่างชัดเจนเพื่ออ่านเวกเตอร์ผลลัพธ์ออกมา เพื่ออำนวยความสะดวกในระดับความขนานที่สูงขึ้นและสามารถเริ่มแบตช์ในอนาคตได้ในขณะที่รอให้แบตช์ก่อนหน้าทำงานบนอุปกรณ์เสร็จสิ้น เราจึงอาศัยนามธรรม LazyTensor ในการติดตามค่าต่างๆ
LazyTensor จะติดตามบัฟเฟอร์โฮสต์ในหน่วยความจำแบบล็อกหน้า (Page-locked Memory) และการดำเนินการ cudaMemcpyAsync ผ่านอีเวนต์ที่คัดลอกข้อมูลจากอุปกรณ์ โดยจะเริ่มทำงานหลังจากเปิดตัวการส่งผ่านไปข้างหน้าบนสตรีมเดียวกัน เนื่องจากการดำเนินการคัดลอกต้องรอให้เคอร์เนลก่อนหน้าทั้งหมดบนสตรีมดำเนินการเสร็จสิ้น อีเวนต์ที่เกี่ยวข้องจึงติดตามทั้งการเสร็จสิ้นของการส่งผ่านไปข้างหน้าและความพร้อมใช้งานของผลลัพธ์บน CPU

เราใช้ประโยชน์จาก LazyTensor ในเอ็นจิ้นตัวเข้ารหัส ROSE เพื่อให้การทำงานของ GPU และ CPU ซ้อนทับกันได้ แทนที่การเรียกใช้งาน step() แต่ละครั้งจะรันกราฟ CUDA และรอจนกว่าจะเสร็จสิ้น ฟังก์ชัน step() จะส่งคืน LazyTensor เพื่อติดตามผลลัพธ์แบบอะซิงโครนัส เมื่อทำงานร่วมกับกราฟ CUDA วิธีนี้ช่วยให้เราบรรลุความหน่วงต่ำและแบนด์วิดท์ที่ดีขึ้น

ROSE
เราได้ปรับแต่งเอ็นจิ้น ROSE ซึ่งเดิมสร้างขึ้นสำหรับการให้บริการ LLM เพื่อจัดการกับการทำงานของโมเดลการฝังตัวด้วย เพื่อลดความพยายามที่จำเป็นในการรองรับโมเดลการฝังตัว ROSE จึงนำโค้ดกลับมาใช้ใหม่อย่างจริงจังระหว่าง LLM และการฝังตัว ตัวอย่างเช่น การให้บริการ pplx-embed และการถอดรหัส LLM Qwen3.5 ล้วนผ่านเคอร์เนลเดียวกัน การแชร์นี้ช่วยให้เราสามารถให้บริการโมเดลการฝังตัวที่เดิมได้รับการ fine-tune จาก LLM ได้อย่างง่ายดายสำหรับการสร้างต้นแบบ, การประเมินผล และการอนุมานในการผลิต
สำหรับเลเยอร์แบบหนาแน่น (Dense Layers) การอนุมานการฝังตัวและ LLM จะเหมือนกันเนื่องจากเวกเตอร์โทเค็นได้รับการประเมินผลอย่างอิสระ ในเลเยอร์แอตเทนชัน ความแตกต่างจะถูกจัดการโดยการเพิ่มการรองรับอินพุตแบบเรกเก็ด (Ragged Inputs) ควบคู่ไปกับการตั้งค่าการเติมล่วงหน้าและการถอดรหัสแบบแบ่งหน้า (Paged Prefill and Decode Setups) ที่ LLM ต้องการ เมื่อให้บริการโมเดลการฝังตัว เราจะไม่สร้างอินสแตนซ์ KV cache แต่จะส่งต่อไปยังรูปแบบต่างๆ ของเคอร์เนลแอตเทนชันที่รองรับรูปแบบเรกเก็ดเพื่อหลีกเลี่ยงการแพดดิง (Padding) รูทีนการแปลงและการสอบเทียบที่สนับสนุนก็จะถูกใช้ร่วมกับ LLM ด้วย
Ivy
Ivy ซึ่งเป็นเลเยอร์พร็อกซี HTTP สำหรับการอนุมานของเรา ก็มีบทบาทสำคัญในด้านประสิทธิภาพเช่นกัน เนื่องจากเพย์โหลดของคำขอมีความแตกต่างกันในการผลิต การส่งเส้นทางคำขอแต่ละรายการไปยังเรพ리카แต่ละตัวอาจทำให้เกิดความไม่สมดุลของภาระงาน (Load Imbalance) Ivy จะแบ่งคำขอแบตช์ขนาดใหญ่ออกเป็นส่วนย่อยๆ และปรับสมดุลภาระงานระหว่างเรพ리카 ซึ่งช่วยปรับปรุงการใช้งานและทำให้ความหน่วงราบรื่นขึ้น งานล่าสุดของเราเกี่ยวกับการ ทำโทเค็น Unigram ภายในองค์กร ซึ่งนำมาใช้เต็มรูปแบบใน Ivy ช่วยปรับปรุงความหน่วงได้อย่างมากเมื่อเทียบกับตัวทำโทเค็นที่มีอยู่ทั่วไป (Off-the-shelf Tokenizers)
...แต่เคอร์เนลก็ยังคงมีความสำคัญ
ROSE รองรับแบ็ก엔ด์แอตเทนชันที่หลากหลาย เคอร์เนลที่แตกต่างกันอาจเหมาะกับขนาดปัญหาที่เฉพาะเจาะจง เมื่อเวลาผ่านไป เราได้ผสานรวมเคอร์เนล FlashInfer 2, FlashInfer 3 และ FlashAttention 4 เพื่อใช้งานเรกเก็ดแอตเทนชัน (Ragged Attention)

โดยทั่วไป เราสังเกตเห็นว่า FlashAttention 4 ทำงานได้เร็วกว่า อย่างไรก็ตาม FlashInfer 3 มีประสิทธิภาพเหนือกว่าบนโมเดลที่อิงตาม Qwen เมื่อใช้ความยาวลำดับ (Sequence Lengths) ที่ยาวมากๆ เนื่องจากประสิทธิภาพและการปรับแต่งอาจแตกต่างกันไปตามจำนวนและมิติของ Attention Heads เราจึงยังคงรองรับการกำหนดค่าที่หลากหลายและตัดสินใจเป็นกรณีๆ ไปเมื่อให้บริการ
การเปรียบเทียบมาตรฐาน (Benchmarks)
เราทำการเปรียบเทียบกับ vLLM v0.22.0 โดยรันการอนุมานด้วยความแม่นยำ BF16 บนน้ำหนักโมเดลจริงและอินพุตที่ได้จากชุดข้อมูลการประเมิน การรันการจับเวลาทั้งหมดนำหน้าด้วยการรันวอร์มอัพ (Warmup Runs) ซึ่งตรวจสอบว่าความเบี่ยงเบนในความคล้ายคลึงโค사인 (Cosine Similarity) อยู่ภายใน 0.1%
การฝังตัวความหน่วงต่ำ (p50 / p90 / p99 / สูงสุด ms)
เรา گزارش รันไทม์สำหรับขนาดแบตช์คำขอที่ทำโทเค็นล่วงหน้า 1 รายการ, คำขอแบบต่อเนื่องอย่างสมบูรณ์, ความยาวลำดับที่ 128, 512 และ 4096 โทเค็น

การให้คะแนนความหน่วงต่ำ (p50 / p90 / p99 / สูงสุด ms)
ขนาดแบตช์คำคนที่ทำโทเค็นล่วงหน้าแล้ว 5, 25 และ 50, ความยาวลำดับ 512 โทเค็น

การฝังตัวปริมาณงานสูง (emb/s)
ขนาดแบตช์คำขอ (Request Batch Size) 100, มีกระบวนการทำงานพร้อมกันสี่กระบวนการที่ส่งคำขอ, และมีความยาวลำดับที่ 512, 1024 และ 4096 โทเค็น

การฝังตัวที่มีความพร้อมกันสูง (p50 / p90 / p99 / สูงสุด ms)
ความยาวลำดับ 512, ขนาดแบตช์ 1 แต่เราส่งคำขอพร้อมกัน 1, 2, 4, 8 และ 16 คำขอ การเปรียบเทียบมาตรฐานนี้ยังรวมถึงต้นทุนการทำโทเค็นผ่าน Ivy ควบคู่ไปกับโอเวอร์เฮดของเครือข่ายระหว่าง Ivy และ Tulip ด้วย

บทสรุปและงานในอนาคต
โครงสร้างพื้นฐานการให้บริการที่ประกอบด้วย Ivy, Tulip และ ROSE ช่วยให้เราสามารถให้บริการการฝังตัวสำหรับ Perplexity ด้วยความหน่วงที่ต่ำลงและปริมาณงานที่ดีขึ้น ส่งผลให้การค้นหามีความแม่นยำมากขึ้นในต้นทุนที่ลดลงเมื่อเทียบกับโซลูชันสำเร็จรูป
ด้วยการมุ่งเน้นไปที่โมเดลเฉพาะและรับผิดชอบสเตกทั้งหมด เราจึงได้รับอิสระที่จำเป็นในการสร้างสมดุลที่มีประสิทธิภาพระหว่างประสิทธิภาพและความยืดหยุ่น โดยผสมผสานพริมิทิฟ Rust ที่นำกลับมาใช้ใหม่ได้สูงและมีประสิทธิภาพเข้ากับโค้ดการสร้างแบบจำลอง Python ทั่วไป เอ็นจิ้นการอนุมานโอเพนซอร์สจำนวนมาก เช่น vLLM, SGLang และ TokenSpeed กำลังผสานรวมภาษาอย่าง Rust และ C++ เข้ากับสเตกของตน เราได้ลงทุนใน Rust ตลอดสองปีที่ผ่านมาและได้รับผลตอบแทนที่คุ้มค่าในแง่ของประสิทธิภาพและความสามารถในการบำรุงรักษา ด้วยการใช้การทำงานของการฝังตัวส่วนใหญ่ร่วมกับสเตกการให้บริการ LLM ของเรา เราจึงได้รับปริมาณงาน (Throughput) ที่เพิ่มขึ้นเช่นกัน โดยไม่ต้องใช้ความพยายามทางวิศวกรรมอย่างมากในการบำรุงรักษาโมเดลการฝังตัว
ในขณะที่โมเดลพัฒนาขึ้น เราจะยังคงปรับปรุงแต่ละเลเยอร์ของสเตกของเราอย่างต่อเนื่องเพื่อลดความหน่วงทั้งที่ถูกจำกัดด้วย CPU และจำกัดด้วย GPU โปรโตคอลที่ใช้ gRPC แบบกำหนดเองของเราภายใน Ivy and Tulip ช่วยให้เราสามารถปรับแต่งการสื่อสารเพื่อลดความหน่วงของเครือข่าย ในขณะที่ ROSE เป็นพื้นฐานในการปรับปรุงปริมาณงานในการคำนวณ นอกจากนี้ เนื่องจากการรองรับ Python แบบ free-threaded เติบโตขึ้นทั่วทั้งระบบนิเวศ เราจึงจะสามารถปรับปรุงความสามารถในการทำงานร่วมกันระหว่าง Python-Rust ให้ดียิ่งขึ้นเพื่อลดโอเวอร์เฮด