การสื่อสารแบบจุดต่อจุด RDMA สำหรับระบบ LLM

เครื่องมือที่ยอดเยี่ยมในการจัดการกับรูปแบบการสื่อสาร LLM ที่เกิดขึ้นใหม่

ผู้เขียนPerplexity Research

โดยทั่วไปแล้ว ระบบ LLM อาศัยการสื่อสารแบบกลุ่ม (collective communications) สำหรับรูปแบบขนานที่มีโครงสร้างในการฝึกและการอนุมาน อย่างไรก็ตาม ภาระงานที่เกิดขึ้นใหม่ เช่น การอนุมานแบบแยกส่วน (disaggregated inference) การจัดเส้นทาง Mixture-of-Experts (MoE) และการปรับแต่งการเรียนรู้เสริมแรงแบบอะซิงโครนัส (asynchronous reinforcement learning fine-tuning) ต้องการความยืดหยุ่นของการสื่อสารแบบจุดต่อจุด (point-to-point communication)

บทความวิจัยล่าสุดของเราแนะนำ TransferEngine ซึ่งเป็นไลบรารีการสื่อสารแบบ RDMA ที่มีความสามารถในการพกพา ซึ่งทำงานร่วมกับคอนโทรลเลอร์อินเทอร์เฟซเครือข่าย (NIC) ที่แตกต่างกัน รวมถึง NVIDIA ConnectX-7 และ AWS Elastic Fabric Adapter (EFA) เราแสดงให้เห็นถึงประสิทธิภาพในระบบการผลิตสามระบบที่ Perplexity AI:

การแยกส่วนระหว่างการเตรียมข้อมูลล่วงหน้าและการถอดรหัส (Disaggregated Prefill and Decode): การถ่ายโอน KvCache ทีละเลเยอร์โดยใช้การเขียนแบบแบ่งหน้า (paged writes) และตัวติดตาม UVM ซึ่งช่วยให้อินสแตนซ์การเตรียมข้อมูลล่วงหน้าและการถอดรหัสสามารถปรับขนาดได้อย่างอิสระ

การถ่ายโอนน้ำหนักสำหรับการฝึก RL ภายหลังในเวลาน้อยกว่า 2 วินาที: การซิงโครไนซ์น้ำหนักที่รวดเร็วเป็นพิเศษระหว่างโหนดการฝึกและการอนุมาน

การเปิดใช้งานโมเดลระดับล้านล้านพารามิเตอร์บน AWS EFA: การดำเนินการจัดส่งและรวม MoE ที่มีลาเทนซีต่ำโดยใช้พริมิทิฟ scatter และ barrier ซึ่งบรรลุลาเทนซีระดับล้ำสมัยบน ConnectX-7 และเป็นการใช้งานจริงครั้งแรกที่เข้ากันได้กับ AWS EFA

บทความนี้มุ่งเน้นไปที่ TransferEngine ซึ่งเป็นไลบรารีการสื่อสารที่ขับเคลื่อนระบบเหล่านี้ อ่านบทความวิจัยฉบับเต็มได้ที่ arXiv และสำรวจโค้ดได้ที่ GitHub

แรงจูงใจ

การสื่อสารแบบกลุ่มมีความโดดเด่นในรูปแบบขนานที่มีโครงสร้าง เช่น Tensor Parallelism และ Data Parallelism แต่มีข้อจำกัดที่ลดทอนความสามารถในการนำไปใช้กับภาระงานที่เกิดขึ้นใหม่:

ความเป็นสมาชิกแบบคงที่: การสื่อสารแบบกลุ่มกำหนดให้ต้องสร้างสภาพแวดล้อมการสื่อสารส่วนกลางในเวลาเริ่มต้น พร้อมกับการตั้งค่าที่ซิงโครไนซ์กันทั่วทั้งผู้เข้าร่วมทั้งหมด สำหรับการอนุมานแบบแยกส่วน สิ่งนี้สร้างปัญหาเนื่องจากจำนวนอินสแตนซ์การเตรียมข้อมูลล่วงหน้าและการถอดรหัสจะต้องปรับขนาดแบบไดนามิกเพื่อตอบสนองต่อรูปแบบการรับส่งข้อมูล

รูปร่างของเทนเซอร์แบบสม่ำเสมอ: การสื่อสารแบบกลุ่มสันนิษฐานว่าผู้เข้าร่วมทั้งหมดแลกเปลี่ยนเทนเซอร์ที่มีรูปร่างและประเภทข้อมูลเหมือนกันทุกประการ ในการจัดเส้นทาง MoE จำนวนโทเค็นที่ส่งไปยังผู้เชี่ยวชาญแต่ละรายขึ้นอยู่กับข้อมูลและถูกค้นพบขณะรันไทม์ ซึ่งบังคับให้การสื่อสารแบบ all-to-all แบบหนาแน่นต้องทำการแพด (pad) ให้มีขนาดบัฟเฟอร์ในกรณีที่แย่ที่สุด

การจัดลำดับการดำเนินการ: API การสื่อสารแบบกลุ่มบังคับใช้ความหมายเชิงซิงโครไนซ์ แม้ว่าแอปพลิเคชันจะต้องการทราบเพียงชุดการดำเนินการเสร็จสิ้นแล้วก็ตาม ตัวอย่างเช่น ในการถ่ายโอน KvCache ทุกเพจจากอินสแตนซ์การเตรียมข้อมูลล่วงหน้าไปยังอินสแตนซ์การถอดรหัสสามารถมาถึงในลำดับใดก็ได้ ในทำนองเดียวกัน ในการอัปเดตน้ำหนัก RL เทนเซอร์น้ำหนักทั้งหมดสามารถถ่ายโอนได้อย่างอิสระโดยไม่มีข้อจำกัดด้านลำดับ

การสื่อสารแบบจุดต่อจุดที่ใช้ RDMA แก้ไขข้อจำกัดเหล่านี้ได้อย่างเป็นธรรมชาติ แต่มีอุปสรรคสามประการที่ขัดขวางการนำไปใช้งาน:

ข้อจำกัดของไลบรารี: แม้ว่า torch.distributed และ NCCL จะมี API แบบจุดต่อจุดเช่น Send/Recv แต่การดำเนินการเหล่านี้ยังคงสืบทอดความหมายและข้อจำกัดของการสื่อสารแบบกลุ่ม

ความหลากหลายของฮาร์ดแวร์: NVIDIA ConnectX NIC พร้อมการรับส่งข้อมูลแบบ Reliable Connection (RC) คือการตั้งค่า RDMA ที่พบมากที่สุด ซึ่งรับประกันการส่งมอบตามลำดับ ผู้ให้บริการคลาวด์เช่น AWS ใช้ฮาร์ดแวร์ที่แตกต่างกัน โดย Elastic Fabric Adapter (EFA) พร้อมการรับส่งข้อมูลแบบ Scalable Reliable Datagram (SRD) จะส่งข้อความแบบไม่เรียงลำดับ นอกจากนี้ EFA ยังต้องรวม NIC ขนาด 100 Gbps สี่ตัว (หรือ NIC ขนาด 200 Gbps สองตัว) เพื่อให้ได้แบนด์วิดท์ 400 Gbps

ความสามารถในการพกพาของซอฟต์แวร์: ไลบรารี RDMA ที่มีอยู่ประสบปัญหาการผูกขาดกับผู้จำหน่าย DeepEP ต้องการ RDMA ที่เริ่มต้นโดย GPU (IBGDA) ซึ่งมีให้ใช้งานบน ConnectX เท่านั้น NVSHMEM แสดงการเสื่อมถอยของประสิทธิภาพอย่างรุนแรงบน EFA ไลบรารีเช่น Mooncake และ NIXL ขาดการสนับสนุน EFA หรือยังอยู่ในขั้นตอนการพัฒนาเริ่มต้น ส่งผลให้ไม่มีโซลูชันข้ามผู้ให้บริการที่ใช้งานได้สำหรับการอนุมาน LLM

TransferEngine

TransferEngine แก้ไขอุปสรรคเหล่านี้ผ่านความสามารถหลักสี่ประการ:

ข้อสมมติฐานการรับส่งข้อมูลร่วมกัน: แทนที่จะกำหนดเป้าหมายไปที่ NIC เฉพาะ TransferEngine สมมติเพียงว่าการรับส่งข้อมูลเบื้องต้นมีความน่าเชื่อถือแต่ไม่มีการจัดลำดับ กล่าวคือ จุดตัดของข้อรับประกันที่มีให้โดยการรับส่งข้อมูลทั้งแบบ RC และ SRD

การซิงโครไนซ์ ImmCounter: พริมิทิฟใหม่สำหรับการแจ้งเตือนความสำเร็จที่ทำงานโดยไม่ต้องพึ่งพาลำดับของข้อความ ช่วยให้โค้ดชุดเดียวกันสามารถทำงานได้ทั้งบนการรับส่งข้อมูลแบบมีลำดับ (RC) และแบบไม่มีลำดับ (SRD)

การรองรับหลาย NIC: การจัดการ NIC หลายตัวต่อ GPU อย่างโปร่งใส โดยทำการแบ่งส่วน (sharding) การถ่ายโอนโดยอัตโนมัติเพื่อเพิ่มแบนด์วิดท์สูงสุด

พื้นผิว API ที่น้อยที่สุด: ชุดการดำเนินการขนาดเล็กที่เพียงพอต่อการสร้างรูปแบบการสื่อสารที่ซับซ้อน

แผนผังการออกแบบระบบ TransferEngine แสดงเส้นทางคำขอและการเรียกกลับ เวิร์กเกอร์ CPU บัฟเฟอร์ UVM GPU และ NICs ครอบคลุมสองโดเมน GPU/NUMA

TransferEngine จะสปอนเธรด (spawn) เธรดทำงานหนึ่งเธรดต่อ GPU หนึ่งตัว เธรดทำงานแต่ละตัวจะจัดการ DomainGroup ที่ประสานงานกับ RDMA NIC จำนวน 1 ถึง 4 ตัว ขึ้นอยู่กับแพลตฟอร์มฮาร์ดแวร์ (ConnectX-7 เดี่ยวให้แบนด์วิดท์ 400 Gbps ในขณะที่ EFA ต้องใช้อะแดปเตอร์ 100 Gbps สี่ตัวหรืออะแดปเตอร์ 200 Gbps สองตัว) ภายใน DomainGroup แต่ละ Domain จะเฉพาะเจาะจงกับฮาร์ดแวร์และจัดการ NIC ตัวเดียว โดยดูแลการจัดการคิวคู่ (queue pair) การส่งงาน และการโพลล์ความสำเร็จ

อินสแตนซ์ TransferEngine แต่ละรายการจะแสดง แอดเดรสหลัก เพียงหนึ่งรายการสำหรับการระบุและค้นหาเพียร์ เพียร์ระยะไกลจะแลกเปลี่ยนโครงสร้าง NetAddr ที่เข้ารหัสเน็ตเวิร์กแอดเดรสและสามารถซีเรียลไลซ์สำหรับการสื่อสารแบบ out-of-band ได้ เพียร์ทั้งหมดในระบบต้องใช้จำนวน NIC ต่อ GPU เท่ากัน การกำหนดค่าที่สม่ำเสวีนี้ช่วยให้ TransferEngine สามารถแบ่งส่วนการถ่ายโอนได้อย่างชาญฉลาด: เมื่อเริ่มการถ่ายโอน ทั้งต้นทางและปลายทางจะทราบแน่ชัดว่ามี NIC ใดบ้างที่ใช้งานได้ ช่วยให้สามารถกระจายโหลดอัตโนมัติทั่วทั้งอะแดปเตอร์ทั้งหมด

การออกแบบ API

TransferEngine นำเสนอ API ขนาดเล็กที่ซ่อนความซับซ้อนของอินเทอร์เฟซ RDMA เบื้องหลัง

rust
#[serde] struct NetAddr(Bytes); #[serde] struct MrDesc{ ptr: u64, rkeys: Vec<(NetAddr, u64)> } struct MrHandle(NonNull<c_void>); type Offset = u64; struct Pages{ indices: Vec<u32>, stride: u64, offset: Offset } struct PeerGroupHandle(u64); struct ScatterDst{ len: u64, src: Offset, dst: (MrDesc,Offset)} enum OnDone { Callback(fn () -> ()), Flag(Atomic<bool>) } trait TransferEngine { fn main_address() -> NetAddr; // Memory Region Management fn reg_mr(ptr, len, device) -> (MrHandle, MrDesc); // Two-sided Send/Recv fn submit_send(addr: NetAddr, msg: &[u8], cb: fn () -> ()); fn submit_recvs(len: u64, cnt: u64, cb: fn (&[u8]) -> ()); // One-sided Write fn expect_imm_count(imm: u32, count: u32, cb: fn () -> ()); fn submit_single_write(len: u64, imm: Option<u32> src: (MrHandle, Offset), dst: (MrDesc, Offset), OnDone); fn submit_paged_writes(page_len: u64, imm: Option<u32>, src: (MrHandle, Pages), dst: (MrDesc, Pages), OnDone); // One-sided Write to a group of peers fn add_peer_group(addrs: Vec<NetAddr>) -> PeerGroupHandle; fn submit_scatter(h: Option<PeerGroupHandle>, OnDone, imm: Option<u32>, src: MrHandle, dst: Vec<ScatterDst>); fn submit_barrier(h: Option<PeerGroupHandle>, OnDone, imm: u32, dst: Vec<MrDesc>); // Watcher for CPU-GPU synchronization fn alloc_uvm_watcher(cb: fn(u64,u64) -> ()) -> NonNull<u64>; }

API ประกอบด้วยส่วนประกอบสำคัญหลายส่วน:

การลงทะเบียนหน่วยความจำ

การดำเนินการ RDMA กำหนดให้ต้องลงทะเบียนบริเวณหน่วยความจำกับ NIC ก่อนการถ่ายโอนข้อมูล ฟังก์ชัน reg_mr จะรับพอยเตอร์ ความยาว และอุปกรณ์ และคืนค่าตัวจัดการ (handles) สองตัว:

MrHandle: ตัวจัดการในเครื่องที่ใช้เป็นต้นทางสำหรับการถ่ายโอน

MrDesc: ตัวอธิบายที่สามารถซีเรียลไลซ์ได้ ซึ่งสามารถแลกเปลี่ยนกับเพียร์ระยะไกลเพื่อให้เพียร์เหล่านั้นสามารถเขียนลงในหน่วยความจำที่ลงทะเบียนไว้ได้

โครงสร้าง MrDesc ประกอบด้วยรายการเน็ตเวิร์กแอดเดรสระยะไกลและคีย์รีโมท (หนึ่งรายการต่อ NIC) ที่ให้สิทธิ์การเข้าถึงระยะไกลไปยังบริเวณหน่วยความจำ การออกแบบนี้จัดการกับการตั้งค่าแบบหลาย NIC ได้อย่างเป็นธรรมชาติ

Send/Recv สองทาง

สำหรับข้อความขนาดเล็กและข้อความควบคุมสไตล์ RPC TransferEngine มีฟังก์ชัน submit_send และ submit_recvs:

submit_send: ส่งข้อความไปยังเพียร์ระยะไกลที่ระบุโดย NetAddr ข้อความจะถูกคัดลอกภายใน ดังนั้นผู้เรียกจึงสามารถนำกลับมาใช้ใหม่หรือปล่อยบัฟเฟอร์ได้ทันที

submit_recvs: โพสต์พูลของบัฟเฟอร์รับ เมื่อข้อความมาถึง คอลแบกจะถูกเรียกใช้พร้อมกับข้อมูลที่ได้รับ บัฟเฟอร์จะถูกส่งกลับไปยังพูลโดยอัตโนมัติหลังจากคอลแบกเสร็จสิ้น

Write ทางเดียว

สำหรับการถ่ายโอนข้อมูลจำนวนมาก TransferEngine มีการดำเนินการ Write แบบทางเดียวแบบ zero-copy:

submit_single_write: ถ่ายโอนบริเวณข้อมูลที่ต่อเนื่องจากต้นทางไปยังปลายทาง

submit_paged_writes: ถ่ายโอนเพจที่ไม่ต่อเนื่องกันซึ่งระบุโดยอินเด็กซ์ สไตรด์ (strides) และออฟเซ็ต

การดำเนินการทั้งสองยอมรับค่า immediate แบบ 32 บิตเสริม ซึ่งจะถูกส่งไปยังผู้รับแบบอะตอมมิกเมื่อเสร็จสิ้น

เอนจินจะทำการแบ่งส่วนการถ่ายโอนขนาดใหญ่ข้าม NIC หลายตัวโดยอัตโนมัติเพื่อให้แบนด์วิดท์สูงสุด

กลุ่มเพียร์และการดำเนินการจำนวนมาก

สำหรับการดำเนินการที่กำหนดเป้าหมายไปยังเพียร์หลายราย (พบได้ทั่วไปในการจัดเส้นทาง MoE) TransferEngine มีพริมิทิฟที่ได้รับการปรับแต่ง:

add_peer_group: ลงทะเบียนแอดเดรสของเพียร์ล่วงหน้าเพื่อการค้นหาที่รวดเร็ว

submit_scatter: ส่งชิ้นส่วนข้อมูลที่แตกต่างกันไปยังเพียร์แต่ละรายในกลุ่ม

submit_barrier: ส่งการแจ้งเตือนแบบ immediate-only ไปยังเพียร์ทั้งหมด (การถ่ายโอนขนาดศูนย์ไบต์)

การดำเนินการเหล่านี้ได้รับการปรับแต่งสำหรับลาเทนซีต่ำโดยการแคชโครงสร้างข้อมูลภายในและสร้างเทมเพลตคำ่างาน RDMA หลีกเลี่ยงค่าใช้จ่ายในการตั้งค่าซ้ำๆ

UVM Watcher: การส่งสัญญาณจาก GPU ไปยัง CPU

ความท้าทายหลักในระบบ LLM คือการประสานงานการถ่ายโอน RDMA กับความคืบหน้าของ GPU ตามอุดมคติ การถ่ายโอนข้อมูลควรเริ่มต้นทันทีที่ GPU ผลิตผลลัพธ์ แต่การโพลล์ GPU จาก CPU นั้นไม่มีประสิทธิภาพ

TransferEngine แก้ไขปัญหานี้ด้วย UVM Watchers: ตำแหน่งหน่วยความจำเสมือนแบบรวม (unified virtual memory) ที่สามารถอัปเดตได้โดยเคอร์เนล GPU (แม้ภายในกราฟ CUDA) และโพลล์ได้อย่างมีประสิทธิภาพโดย CPU โดยใช้ GDRCopy เมื่อมีการจัดสรร UVM watcher คอลแบกจะถูกลงทะเบียนซึ่งจะถูกเรียกใช้ทุกครั้งที่ GPU อัปเดตค่า

สิ่งนี้ช่วยให้เกิดรูปแบบต่างๆ เช่น การถ่ายโอน KvCache ทีละเลเยอร์: หลังจากเลเยอร์ทรานส์ฟอร์เมอร์แต่ละเลเยอร์เสร็จสิ้น GPU จะเพิ่มค่า watcher ซึ่งทริกเกอร์การถ่ายโอน RDMA ของ KvCache ของเลเยอร์นั้นในขณะที่เลเยอร์ถัดๆ ไปยังคงทำการคำนวณต่อไป

การแจ้งเตือนความสำเร็จ

TransferEngine มีกลไกการแจ้งเตือนความสำเร็จสำหรับทั้งฝั่งผู้เริ่มต้น (ผู้เรียก submit_write) และฝั่งผู้รับ (เป้าหมายของการดำเนินการ Write)

การแจ้งเตือนฝั่งผู้เริ่มต้น: เมื่อส่งการดำเนินการ write ผู้เรียกสามารถเลือกที่จะรับการแจ้งเตือนเมื่อเสร็จสิ้นผ่านฟังก์ชันคอลแบกหรือแฟล็กแบบอะตอมมิก การแจ้งเตือนนี้ยืนยันว่าข้อมูลได้รับการถ่ายโอนจากบัฟเฟอร์ต้นทางเรียบร้อยแล้ว

การแจ้งเตือนฝั่งผู้รับ: นวัตกรรมหลักคือ ImmCounter ซึ่งเป็นคอมโพเนนต์ที่ติดตามตัวนับซึ่งจัดทำดัชนีด้วยตัวเลข immediate 32 บิต เมื่อการดำเนินการ write มีค่า immediate ตัวนับ ImmCounter ของผู้รับจะถูกเพิ่มค่าแบบอะตอมมิกเมื่อส่งมอบข้อมูล ฟังก์ชัน expect_imm_count ช่วยให้ผู้รับสามารถลงทะเบียนคอลแบกที่จะทำงานเมื่อค่า immediate เฉพาะถึงจำนวนเป้าหมาย ตัวนับเหล่านี้สามารถซิงโครไนซ์กับ GPU ได้อย่างโปร่งใสผ่าน GDRCopy, โพลล์โดยตรง หรือตรวจสอบผ่านคอลแบก

กลไกฝั่งผู้รับนี้ให้การซิงโครไนซ์ที่เชื่อถือได้โดยไม่ต้องกำหนดให้ต้องมีการจัดลำดับข้อความ ทำงานได้ดีไม่แพ้กันบน ConnectX NIC (ซึ่งรับประกันการส่งมอบตามลำดับผ่านการรับส่งข้อมูล RC) และ EFA NIC (ซึ่งส่งมอบข้อความแบบไม่เรียงลำดับผ่านการรับส่งข้อมูล SRD)

การใช้งาน

TransferEngine ถูกนำมาใช้งานด้วย Rust โดยปรับแต่งการจัดสรรหน่วยความจำ การทำงานแบบเธรด และการซิงโครไนซ์อย่างพิถีพิถัน เพื่อลดความหน่วงและให้ได้อัตราการส่งผ่านข้อมูลสูงสุด เอนจินนี้สร้างเธรดผู้ปฏิบัติงานหนึ่งเธรดต่อ DomainGroup แต่ละเธรดถูกผูกไว้กับคอร์ CPU บนโหนด NUMA ที่อุปกรณ์ต่าง ๆ เชื่อมต่ออยู่ เพื่อลดทั้งความหน่วงจากการจัดตารางงานและการเข้าถึงหน่วยความจำ โครงสร้างข้อมูลเฉพาะของโดเมนจะถูกจัดสรรหลังจากการผูกคอร์เพื่อให้แน่ใจว่าหน่วยความจำถูกสำรองไว้ในโหนด NUMA ที่ถูกต้อง เธรดผู้ปฏิบัติงานหนึ่งเธรดรองรับ Domain ได้สูงสุด 4 รายการ (แต่ละรายการจัดการ NIC เพียงหนึ่งตัว) ขณะที่อีกเธรดเฉพาะจะโพล GPU เพื่ออัปเดต UVM watcher การสื่อสารข้ามเธรดทำผ่านคิวแบบไม่ล็อก

API จะส่งต่อคำขอไปยัง DomainGroup ที่ให้บริการอุปกรณ์ที่เหมาะสม โดยตัวแรกจะทำหน้าที่ให้บริการโฮสต์ด้วย ในลูปแบบกระชับ (tight loop) โดเมนเวิร์กเกอร์จะโพลล์ค่างานใหม่ โดยให้ความสำคัญกับการส่งคำขอใหม่ก่อน ขึ้นอยู่กับฮาร์ดแวร์และการกำหนดค่า คำขอจะถูกแบ่งส่วนและกระจายโหลดข้าม Domain ที่ใช้งานได้ การเขียน (Write) ครั้งแรกของคำขอแบบคอมโพสิตจะถูกโพสต์ไปยังคิวส่งของ NIC ทันที เมื่อคำขอใหม่หมดลง เธรดทำงานจะดำเนินการต่อด้วยการดำเนินงานที่ค้างอยู่ โดยโพสต์การเขียนเพื่อเติมเต็มไพป์ไลน์ฮาร์ดแวร์ จากนั้น คิวความสำเร็จจะถูกโพลล์เพื่อสอบถามเกี่ยวกับการถ่ายโอนที่เสร็จสิ้นและการเพิ่มขึ้นของตัวนับ immediate เหตุการณ์ต่างๆ จะถูกรวบรวมเพื่อส่งการแจ้งเตือนต่อการถ่ายโอน โดยส่งมอบการถ่ายโอนไปยังเธรดคอลแบกเฉพาะที่แชร์โดยทุกกลุ่ม

การแบ่งส่วนภายใน DomainGroup มีความยืดหยุ่น การถ่ายโอนสามารถกำหนดเป้าหมายไปยัง NIC เฉพาะด้วยอินเด็กซ์ การเขียนครั้งเดียวสามารถถูกแยกได้ การถ่ายโอนแบบใช้เพจ (paged transfers) การสแคทเทอร์ (scatter) และการดำเนินการแบร์ริเออร์ (barrier) ซึ่งทั้งหมดแปลงเป็นการเขียนหลายครั้ง สามารถแบ่งส่วนข้าม NIC ทั้งหมดได้

การปรับแต่งเฉพาะฮาร์ดแวร์

Domain ภายใน TransferEngine มีความเชี่ยวชาญและได้รับการปรับแต่งสำหรับฮาร์ดแวร์ภายใต้การควบคุมของตน:

AWS EFA: การใช้งาน EFA ใช้ libfabric โดยจัดการโดเมนแฟบริกหนึ่งโดเมนต่อ NIC ภายใน DomainGroup การตัดสินใจในการใช้งานและการปรับแต่งที่สำคัญประกอบด้วย:

การเขียนขนาดศูนย์: EFA แตกต่างจากข้อกำหนด RDMA โดยกำหนดให้ต้องมีตัวอธิบายเป้าหมายที่ถูกต้องแม้กระทั่งสำหรับการเขียนขนาดศูนย์ ดังนั้น TransferEngine จึงบังคับใช้ตัวอธิบายที่ถูกต้องสำหรับการถ่ายโอนทั้งหมด

การสร้างเทมเพลตคำ่างาน: เติมข้อมูลล่วงหน้าและคงฟิลด์ทั่วไปของโครงสร้าง libfabric ไว้ก่อนทำการโพสต์ ซึ่งช่วยลดค่าใช้จ่ายต่อการดำเนินการสำหรับการถ่ายโอนจำนวนมากและการดำเนินการกลุ่มเพียร์

NVIDIA ConnectX-7: การใช้งาน ConnectX-7 ใช้ libibverbs พร้อมการรับส่งข้อมูลแบบ RC การตัดสินใจในการใช้งานและการปรับแต่งที่สำคัญประกอบด้วย:

คิวคู่ UD สำหรับการจับมือ (handshakes): Domain แต่ละตัวใช้คิวคู่ Unreliable Datagram (UD) เพื่อแลกเปลี่ยนการจับมือเชื่อมต่อ RC

คิวคู่ RC แยกต่างหาก: สร้างคิวคู่ RC 2 คิวต่อเพียร์ หนึ่งคิวสำหรับการดำเนินการ Send/Recv สองทาง และอีกคิวสำหรับการดำเนินการ Write และ WriteImm แบบทางเดียว การแยกนี้มีความจำเป็นเนื่องจากการเสร็จสิ้นของทั้ง Recv และ WriteImmใช้คำ่างานตามลำดับการโพสต์ ซึ่งช่วยให้มีความหมายระดับสูงของ Recv ในขณะที่ยังคงรองรับ WriteImm ได้โดยไม่มีการรบกวน

การสร้างเทมเพลตคำ่างาน: เติมข้อมูลล่วงหน้าในฟิลด์ทั่วไปของโครงสร้างคำ่างานสำหรับการถ่ายโอนจำนวนมากและกลุ่มเพียร์

การเชื่อมโยงคำ่างาน: เชื่อมโยงคำ่างานสูงสุด 4 รายการผ่านพอยเตอร์ next ของ ibv_send_wr ซึ่งช่วยลดจำนวนครั้งในการส่งสัญญาณ doorbell ไปยัง NIC

การจัดลำดับแบบผ่อนคลาย: เปิดใช้งาน IBV_ACCESS_RELAXED_ORDERING เพื่ออนุญาตให้ธุรกรรม PCIe แบบไม่เรียงลำดับระหว่าง NIC และหน่วยความจำ GPU

บทสรุป

ด้วยการกำหนดเป้าหมายไปที่จุดตัดของการรับประกันข้ามการรับส่งข้อมูล RDMA ที่แตกต่างกัน และแนะนำการซิงโครไนซ์ที่ไม่ขึ้นกับลำดับผ่าน ImmCounter ทำให้ TransferEngine เป็นพื้นฐานที่พกพาได้สำหรับการสื่อสารแบบจุดต่อจุดในระบบ LLM ไลบรารีนี้เป็นโอเพนซอร์สและใช้งานได้บน GitHub และเราขอสนับสนุนให้ชุมชนสำรวจและต่อยอดจากไลบรารีนี้สำหรับรูปแบบการสื่อสารที่เกิดขึ้นใหม่ในระบบการเรียนรู้ของเครื่องขนาดใหญ่

การอ้างอิง

BibTeX
@online{perplexity2025RdmaPointToPointCommunicationForLlmSystems,
  author = {{Perplexity} and Nandor Licker and Kevin Hu and Vladimir Zaytsev and Lequn Chen},
  title = {{RDMA point-to-point communication for LLM systems}},
  date = {2025-11-05},
  url = {https://www.perplexity.ai/hub/blog/rdma-point-to-point-communication-for-llm-systems}
}