RDMA-punkt-til-punkt-kommunikation for LLM-systemer

Elegant værktøj til at adressere nye LLM-kommunikationsmønstre

ForfatterePerplexity Research

LLM-systemer har traditionelt baseret sig på kollektiv kommunikation med henblik på strukturerede, parallelle mønstre i træning og inferens. Nye arbejdsbelastninger såsom disaggregeret inferens, Mixture-of-Experts (MoE)-routing og asynkron finjustering via forstærkende læring (RL) kræver imidlertid fleksibiliteten ved punkt-til-punkt-kommunikation.

Vores seneste forskningsartikel introducerer TransferEngine, et portabelt RDMA-baseret kommunikationsbibliotek, der fungerer på tværs af forskellige netværkscontroller-enheder (NIC'er), herunder NVIDIA ConnectX-7 og AWS Elastic Fabric Adapter (EFA). Vi demonstrerer dets effektivitet i tre produktionssystemer hos Perplexity AI:

Disaggregeret prefill og decode: Lag-for-lag KvCache-overførsel ved hjælp af paged writes og UVM-overvågninger, hvilket gør det muligt for prefill- og decode-instanser at skalere uafhængigt.

Vægtoverførsel til RL-eftertræning på under 2 sekunder: Lynhurtig synkronisering af vægte mellem trænings- og inferensnoder.

Aktivering af billion-parameter-modeller på AWS EFA: MoE-udsendelse og kombinationsoperationer med lav latens ved hjælp af scatter- og barrier-primitiver, hvilket opnår state-of-the-art latens på ConnectX-7 og den første levedygtige implementering, der er kompatibel med AWS EFA.

Denne artikel fokuserer på selve TransferEngine, det kommunikationsbibliotek, der driver disse systemer. Læs hele forskningsartiklen på arXiv, og udforsk koden på GitHub.

Motivation

Kollektiv kommunikation udmærker sig ved strukturerede, parallelle mønstre som tensor-parallelisme og dataparallelisme, men den pålægger begrænsninger, der begrænser dens anvendelighed på nye arbejdsbelastninger:

Fast medlemskab: Kollektiver kræver etablering af et globalt kommunikationsmiljø på initialiseringstidspunktet med synkroniseret opsætning på tværs af alle deltagere. For disaggregeret inferens er dette problematisk, da antallet af prefill- og decode-instanser skal skalere dynamisk som svar på trafikmønstre.

Ensartede tensorformer: Kollektiver antager, at alle deltagere udveksler tensorer med identiske former og datatyper. I MoE-routing er antallet af tokens, der sendes til hver ekspert, dataafhængigt og opdages ved køretid, hvilket tvinger tætte all-to-all-kollektiver til at udfylde op til værste fald-bufferstørrelser.

Operationsrækkefølge: Kollektive API'er gennemtvinger synkroniserede semantikker, selv når applikationer kun behøver at vide, at et sæt operationer er fuldført. For eksempel kan alle sider i KvCache-overførslen fra prefill-instansen til decode-instansen ankomme i vilkårlig rækkefølge. På samme måde kan alle vægttensorer i RL-vægtopdateringer overføres uafhængigt uden begrænsninger for rækkefølgen.

RDMA-baseret punkt-til-punkt-kommunikation adresserer naturligt disse begrænsninger, men tre barrierer har forhindret dens udrulning:

Biblioteksbegrænsninger: Selvom torch.distributed og NCCL leverer punkt-til-punkt-API'er som Send/Recv, arver disse operationer stadig kollektive semantikker og begrænsninger.

Hardwaremæssig mangfoldighed: NVIDIA ConnectX NIC'er med RC-transport (Reliable Connection) er den mest almindelige RDMA-opsætning, hvilket garanterer levering i den rette rækkefølge. Skyudbydere som AWS bruger forskellig hardware, hvor Elastic Fabric Adapter (EFA) med SRD-transport (Scalable Reliable Datagram) leverer beskeder i vilkårlig rækkefølge. EFA kræver også aggregering af fire 100 Gbps NIC'er (eller to 200 Gbps NIC'er) for at opnå en båndbredde på 400 Gbps.

Software-portabilitet: Eksisterende RDMA-biblioteker lider under leverandørbinding. DeepEP kræver GPU-initieret RDMA (IBGDA), som kun er tilgængelig på ConnectX. NVSHMEM udviser alvorlig forringelse af ydeevnen på EFA. Biblioteker som Mooncake og NIXL mangler EFA-understøttelse eller er stadig i tidlig udvikling. Som følge heraf findes der ingen levedygtig løsning på tværs af udbydere til LLM-inferens.

TransferEngine

TransferEngine adresserer disse barrierer gennem fire kernefunktioner:

Fælles transportantagelser: I stedet for at rette sig mod en specifik NIC antager TransferEngine blot, at den underliggende transport er pålidelig, men uordentlig, dvs. snitmængden af garantier leveret af både RC- og SRD-transporter.

ImmCounter-synkronisering: En ny primitiv til fuldbyrdelsesnotifikation, der fungerer uden at være afhængig af beskedrækkefølge, hvilket gør det muligt at køre den samme kode på både ordnede (RC) og uordnede (SRD) transporter.

Multi-NIC-understøttelse: Transparent administration af flere NIC'er pr. GPU, som automatisk opdeler overførsler (sharding) for at maksimere båndbredden.

Minimal API-flade: Et lille sæt operationer, der er tilstrækkeligt til at opbygge komplekse kommunikationsmønstre.

TransferEngine-systemdesign, der viser anmodnings- og callback-stier, CPU-workers, UVM-buffere, GPU'er og NIC'er på tværs af to GPU/NUMA-domæner.

TransferEngine starter én arbejdetråd pr. GPU. Hver arbejder administrerer en DomainGroup, der koordinerer 1 til 4 RDMA NIC'er, afhængigt af hardwareplatformen (en enkelt ConnectX-7 leverer 400 Gbps, mens EFA kræver fire 100 Gbps-adaptere eller to 200 Gbps-adaptere). Inden for en DomainGroup er hvert Domain hardware-specifikt og administrerer en enkelt NIC, hvilket håndterer køpar-administration, arbejdsindsendelse og afspørgning (polling) af fuldbyrdelser.

Hver TransferEngine-instans eksponerer én enkelt hovedadresse til identifikation og opdagelse af peers. Fjernforbundne peers udveksler NetAddr-strukturer, som koder netværksadresser og kan serialiseres til out-of-band-kommunikation. Alle peers i et system skal bruge det samme antal NIC'er pr. GPU. Denne ensartede konfiguration gør det muligt for TransferEngine at opdele overførsler intelligent: Når en overførsel initieres, ved både kilde og destination præcist, hvilke NIC'er der er tilgængelige, hvilket muliggør automatisk belastningsbalancering på tværs af alle adaptere.

API-design

TransferEngine eksponerer en minimal API, der abstraherer kompleksiteten af de underliggende RDMA-grænseflader væk.

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'en består af flere nøglekomponenter:

Hukommelsesregistrering

RDMA-operationer kræver, at hukommelsesområder registreres hos NIC'en før dataoverførsel. Funktionen reg_mr tager en markør (pointer), længde og enhed og returnerer to håndtag:

MrHandle: Et lokalt håndtag, der bruges som kilde til overførsler

MrDesc: En serialiserbar deskriptor, der kan udveksles med fjerne peers for at sætte dem i stand til at skrive til den registrerede hukommelse

Strukturen MrDesc indeholder en liste over fjernnetværksadresse og fjernnøgle (én pr. NIC), som giver fjernadgang til hukommelsesområdet. Dette design håndterer naturligt opsætninger med flere NIC'er.

Tosidet Send/Recv

Til små beskeder og kontrolplansbeskeder i RPC-stil leverer TransferEngine submit_send og submit_recvs:

submit_send: Sender beskjeden til en fjern peer identificeret af NetAddr. Beskeden kopieres internt, så den der kalder, straks kan genbruge eller frigive bufferen.

submit_recvs: Udstationerer en pool af modtagebuffere. Når en besked ankommer, aktiveres et callback med de modtagne data. Bufferen returneres automatisk til poolen, når callbacket er fuldført.

Ensidet skriveoperation

Til bulk-dataoverførsler leverer TransferEngine zero-copy ensidede skriveoperationer:

submit_single_write: Overfører et sammenhængende område fra kilde til destination

submit_paged_writes: Overfører ikke-sammenhængende sider specificeret af indekser, trinlængder og forskydninger (offsets)

Begge operationer accepterer en valgfrie 32-bit umiddelbar værdi (immediate value), der leveres atomart til modtageren ved fuldbyrdelse.

Motoren opdeler automatisk store overførsler på tværs af flere NIC'er for at maksimere båndbredden.

Peer-grupper og bulk-operationer

For operationer rettet mod flere peers (almindeligt ved MoE-routing) leverer TransferEngine optimerede primitiver:

add_peer_group: Forregistrerer et sæt peer-adresser med henblik på hurtig opslag

submit_scatter: Sender forskellige datasegmenter til hver peer i gruppen

submit_barrier: Sender en notifikation, der udelukkende indeholder en umiddelbar værdi, til alle peers (overførsel på nul bytes)

Disse operationer er optimeret til lav latens ved at cacher interne datastrukturer og skabelonsætte RDMA-arbejdsanmodninger, hvorved gentagne opsætningsomkostninger undgås.

UVM Watcher: GPU-til-CPU-signalering

En central udfordring i LLM-systemer er at koordinere RDMA-overførsler med GPU-fremgang. Ideelt set bør dataoverførsel begynde, så snart GPU'en producerer output, men det er ineffektivt at afspørge GPU'en fra CPU'en.

TransferEngine adresserer dette med UVM Watchers: forenede virtuelle hukommelsesplaceringer, der kan opdateres af GPU-kerner (selv inden for CUDA-grafer) og effektivt afspørges af CPU'en ved hjælp af GDRCopy. Når en UVM-overvåger tildeles, registreres et callback, der aktiveres, hver gang GPU'en opdaterer værdien.

Dette muliggør mønstre som lag-for-lag KvCache-overførsel: efterhånden som hvert transformerlag fuldføres, forhøjer GPU'en overvågeren, hvilket udløser en RDMA-overførsel af dette lags KvCache, mens efterfølgende lag fortsætter med at beregne.

Fuldbyrdelsesnotifikation

TransferEngine leverer mekanismer til fuldbyrdelsesnotifikation for både initiatorsiden (den der kalder submit_write) og modtagersiden (målet for skriveoperationer).

Notifikation på initiatorsiden: Når en skriveoperation indsendes, kan den der kalder, vælge at modtage besked ved fuldbyrdelse via enten en callback-funktion eller et atomart flag. Denne notifikation bekræfter, at dataene er blevet succesfuldt overført fra kildebufferen.

Notifikation på modtagersiden: Den centrale innovation er ImmCounter, en komponent der sporer tællere indekseret af 32-bit umiddelbare værdier. Når en skriveoperation bærer en umiddelbar værdi, forhøjes modtagerens ImmCounter atomart ved datalevering. Funktionen expect_imm_count gør det muligt for modtageren at registrere et callback, der udløses, når en specifik umiddelbar værdi når et målantal. Tællere kan synkroniseres transparent med GPU'en via GDRCopy, afspørges direkte eller overvåges gennem callbacks.

Denne mekanismen på modtagersiden leverer pålidelig synkronisering uden krav om beskedrækkefølge. Den fungerer lige godt på ConnectX NIC'er (som garanterer levering i den rette rækkefølge over RC-transport) og EFA NIC'er (som leverer beskeder i vilkårlig rækkefølge over SRD-transport).

Implementering

TransferEngine er implementeret i Rust og optimerer omhyggeligt tildelinger, trådning og synkronisering for at minimere latens og opnå høj gennemstrømning. Motoren starter én arbejdetråd pr. DomainGroup, hvor hver især er fastgjort til en CPU-kerne på den NUMA-node, som enhederne er tilsluttet, hvilket minimerer både tildelings- og hukommelsesadgangslatens. Datastrukturer, der er specifikke for et domæne, tildeles efter fastgørelse for at sikre, at hukommelsen reserveres på den korrekte NUMA-node. Én arbejdetråd håndterer op til 4 Domain'er (hvor hver administrerer en enkelt NIC), mens en anden dedikeret tråd afspørger GPU'en for at opdatere UVM-overvågninger. Kommunikation på tværs af tråde sker via låsefrie køer.

API'en videresender anmodninger til den DomainGroup, der betjener den relevante enhed, hvor den første også betjener værten. I et stramt loop afspørger domænearbejderen efter nyt arbejde, idet indsendelse af nye anmodninger prioriteres. Afhængigt af hardware og konfiguration opdeles anmodninger (sharding) og balanceres mht. belastning på tværs af de tilgængelige Domain'er. Den første skriveoperation i en sammensat anmodning sendes øjeblikkeligt til NIC'ens sendekø. Når nye anmodninger er opbrugt, fortsætter arbejderen med at fremme verserende operationer ved at indsende skriveoperationer for at fylde hardware-pipelinen op. Efterfølgende afspørges fuldbyrdelseskøer for at forespørge om afsluttede overførsler og forhøjelser af umiddelbare tællere. Hændelser aggregeres for at levere notifikationer pr. overførsel, hvilket overdrager overførslen til en dedikeret callback-tråd delt af alle grupper.

Opdeling (sharding) inde i en DomainGroup er fleksibel. Overførsler kan rettes mod specifikke NIC'er efter indeks. En enkelt skriveoperation kan opdeles. Sidede overførsler, scatter- og barrier-operationer, som alle oversættes til flere skriveoperationer, kan opdeles på tværs af alle NIC'er.

Hardwarespecifikke optimeringer

Domain'erne i TransferEngine er specialiserede og optimeret til den hardware, de kontrollerer:

AWS EFA: EFA-implementeringen bruger libfabric og administrerer et fabric-domæne pr. NIC inden for DomainGroup. Vigtige implementeringsbeslutninger og optimeringer omfatter:

Skriveoperationer med nul størrelse: EFA afviger fra RDMA-specifikationen ved at kræve gyldige måldeskriptorer selv for skriveoperationer med nul størrelse. Derfor håndhæver TransferEngine gyldige deskriptorer for alle overførsler.

Skabelonsætning af arbejdsanmodninger: Forudfylder og bevarer de almindelige felter i libfabric-strukturer før udsendelse, hvilket reducerer overhead pr. operation for bulk-overførsler og peer-gruppe-operationer

NVIDIA ConnectX-7: ConnectX-7-implementeringen bruger libibverbs med RC-transport. Vigtige implementeringsbeslutninger og optimeringer omfatter:

UD-køpar for håndtryk: Hvert Domain bruger et UD-køpar (Unreliable Datagram) til at udveksle RC-forbindelseshåndtryk.

Separate RC-køpar: Opretter 2 RC-køpar pr. peer, én til tosidede Send/Recv-operationer og en anden til ensidede Write- og WriteImm-operationer. Denne adskillelse er nødvendig, fordi både Recv- og WriteImm-fuldbyrdelser forbruger arbejdsanmodninger i opsætningsrækkefølge, hvilket tillader Recv-semantik på højt niveau, mens WriteImm understøttes uden interferens.

Skabelonsætning af arbejdsanmodninger: Forudfylder almindelige felter i arbejdsanmodningsstrukturer til bulk-overførsler og peer-grupper.

Kædning af arbejdsanmodninger: Kæder op til 4 arbejdsanmodninger sammen via next-markøren for ibv_send_wr, hvilket reducerer antallet af doorbell-ringing til NIC'en.

Afslappet rækkefølge: Aktiverer IBV_ACCESS_RELAXED_ORDERING for at tillade PCIe-transaktioner i vilkårlig rækkefølge mellem NIC og GPU-hukommelse.

Konklusion

Ved at rette sig mod snitmængden af garantier på tværs af forskellige RDMA-transporter og introducere rækkefølgeuafhængig synkronisering via ImmCounter leverer TransferEngine et portabelt fundament for punkt-til-punkt-kommunikation i LLM-systemer. Biblioteket er open source og tilgængeligt på GitHub, og vi opfordrer fællesskabet til at udforske og bygge videre på det til nye kommunikationsmønstre i maskinlæringssystemer i stor skala.

Citering

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}
}