RDMA point-to-point communicatie voor LLM-systemen
Elegant hulpmiddel voor LLM-communicatiepatronen
LLM-systemen vertrouwden traditioneel op collectieve communicatie voor gestructureerde parallelle patronen bij training en inferentie. Opkomende workloads, zoals gedesaggregeerde inferentie, Mixture-of-Experts (MoE)-routering en asynchrone fine-tuning van reinforcement learning, vereisen echter de flexibiliteit van point-to-point communicatie.
Ons nieuwste onderzoekspaper introduceert TransferEngine, een draagbare, op RDMA gebaseerde communicatiebibliotheek die werkt met verschillende netwerkinterfacecontrollers (NIC's), waaronder NVIDIA ConnectX-7 en AWS Elastic Fabric Adapter (EFA). Wij demonstreren de effectiviteit ervan in drie productiesystemen bij Perplexity AI:
Gedesaggregeerde Prefill en Decode: KvCache-overdracht per laag met behulp van gepagineerde schrijfacties en UVM-watchers, waardoor prefill- en decode-instanties onafhankelijk kunnen schalen.
Gewichtsoverdracht voor RL Post-Training in minder dan 2 seconden: Razendsnelle gewichtssynchronisatie tussen trainings- en inferentieknooppunten.
Triljoen-parameter modellen op AWS EFA mogelijk maken: Low-latency MoE-dispatch en combine-operaties met behulp van scatter- en barrier-primitieven, waarmee de modernste latentie op ConnectX-7 wordt bereikt en de eerste levensvatbare implementatie die compatibel is met AWS EFA.
Dit artikel richt zich op TransferEngine zelf, de communicatiebibliotheek die deze systemen aandrijft. Lees het volledige onderzoekspaper op arXiv en verken de code op GitHub.
Motivatie
Collectieve communicatie blinkt uit in gestructureerde parallelle patronen zoals Tensor Parallelism en Data Parallelism, maar legt beperkingen op die de toepasbaarheid ervan op opkomende workloads beperken:
Vast lidmaatschap: Collectieven vereisen het opzetten van een globale communicatiewereld bij initialisatie, met gesynchroniseerde instelling over alle deelnemers heen. Voor gedesaggregeerde inferentie is dit problematisch omdat het aantal prefill- en decode-instanties dynamisch moet schalen als reactie op verkeerspatronen.
Uniforme tensorvormen: Collectieven gaan ervan uit dat alle deelnemers tensoren met identieke vormen en datatypes uitwisselen. Bij MoE-routering is het aantal tokens dat naar elke expert wordt verzonden afhankelijk van de data en wordt dit tijdens runtime ontdekt, wat dichte all-to-all collectieven dwingt tot opvulling naar de slechtst mogelijke buffergroottes.
Volgorde van bewerkingen: Collectieve API's dwingen gesynchroniseerde semantiek af, zelfs wanneer applicaties alleen hoeven te weten dat een set bewerkingen is voltooid. Bij KvCache-overdracht kunnen bijvoorbeeld alle pagina's van de prefill-instantie naar de decode-instantie in willekeurige volgorde aankomen. Op dezelfde manier kunnen bij RL-gewichtupdates alle gewichtstensoren onafhankelijk worden overgedragen zonder volgordebeperkingen.
Op RDMA gebaseerde point-to-point communicatie pakt deze beperkingen op natuurlijke wijze aan, maar drie barrières hebben de adoptie ervan verhinderd:
Bibliotheekbeperkingen: Hoewel torch.distributed en NCCL point-to-point API's zoals Send/Recv bieden, erven deze bewerkingen nog steeds collectieve semantiek en beperkingen.
Hardware-diversiteit: NVIDIA ConnectX NIC's met Reliable Connection (RC)-transport zijn de meest voorkomende RDMA-setup en garanderen levering in de juiste volgorde. Cloudproviders zoals AWS gebruiken andere hardware; Elastic Fabric Adapter (EFA) met Scalable Reliable Datagram (SRD)-transport levert berichten in willekeurige volgorde. EFA vereist ook het samenvoegen van vier 100 Gbps NIC's (of twee 200 Gbps NIC's) om een bandbreedte van 400 Gbps te bereiken.
Software-portabiliteit: Bestaande RDMA-bibliotheken lijden onder vendor lock-in. DeepEP vereist GPU-geïnitieerde RDMA (IBGDA), die alleen beschikbaar is op ConnectX. NVSHMEM vertoont ernstige prestatievermindering op EFA. Bibliotheken zoals Mooncake en NIXL missen EFA-ondersteuning of bevinden zich in een vroege ontwikkelingsfase. Als gevolg hiervan bestaat er geen levensvatbare cross-provider oplossing voor LLM-inferentie.
TransferEngine
TransferEngine pakt deze barrières aan via vier kerncapaciteiten:
Algemene transportaannames: In plaats van zich op een specifieke NIC te richten, gaat TransferEngine er alleen van uit dat het onderliggende transport betrouwbaar maar ongeordend is, d.w.z. het snijpunt van garanties die worden geboden door zowel RC- als SRD-transporten.
ImmCounter-synchronisatie: Een nieuw primitief voor voltooiingskennisgeving dat werkt zonder afhankelijk te zijn van berichtvolgorde, waardoor dezelfde code kan worden uitgevoerd op zowel geordende (RC) als ongeordende (SRD) transporten.
Multi-NIC-ondersteuning: Transparant beheer van meerdere NIC's per GPU, waarbij overdrachten automatisch worden geshard om de bandbreedte te maximaliseren.
Minimaal API-oppervlak: Een kleine set bewerkingen die voldoende is om complexe communicatiepatronen op te bouwen.
TransferEngine start één worker-draad per GPU. Elke worker beheert een DomainGroup die 1 tot 4 RDMA NIC's coördineert, afhankelijk van het hardwareplatform (een enkele ConnectX-7 biedt 400 Gbps, terwijl EFA vier 100 Gbps-adapters of twee 200 Gbps-adapters vereist). Binnen een DomainGroup is elk Domain hardware-specifiek en beheert het een enkele NIC, waarbij het queue pair-beheer, werkindiening en voltooiingspolling worden afgehandeld.
Elke TransferEngine-instantie stelt één hoofdadres bloot voor peer-identificatie en -detectie. Externe peers wisselen NetAddr-structs uit die netwerkadressen coderen en kunnen worden geserialiseerd voor out-of-band communicatie. Alle peers in een systeem moeten hetzelfde aantal NIC's per GPU gebruiken. Deze uniforme configuratie stelt TransferEngine in staat om overdrachten intelligent te sharden: bij het initiëren van een overdracht weten zowel bron als bestemming precies welke NIC's beschikbaar zijn, wat automatische taakverdeling over alle adapters mogelijk maakt.
API-ontwerp
TransferEngine stelt een minimale API bloot die de complexiteit van de onderliggende RDMA-interfaces abstraheert.
#[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>;
}De API bestaat uit verschillende belangrijke componenten:
Geheugenregistratie
RDMA-bewerkingen vereisen dat geheugenregio's bij de NIC worden geregistreerd vóór gegevensoverdracht. De reg_mr-functie neemt een pointer, lengte en apparaat en retourneert twee handles:
MrHandle: Een lokale handle die wordt gebruikt als bron voor overdrachten
MrDesc: Een serialiseerbaar descriptor die kan worden uitgewisseld met externe peers om hen in staat te stellen naar het geregistreerde geheugen te schrijven
De MrDesc-structuur bevat een lijst met extern netwerkadres en externe sleutel (één per NIC) die externe toegang tot de geheugenregio verlenen. Dit ontwerp behandelt multi-NIC-setups op natuurlijke wijze.
Tweezijdige Send/Recv
Voor kleine berichten en RPC-stijl controleplane-berichten biedt TransferEngine submit_send en submit_recvs:
submit_send: Verzendt het bericht naar een externe peer geïdentificeerd door NetAddr. Het bericht wordt intern gekopieerd, zodat de aanroeper de buffer onmiddellijk kan hergebruiken of vrijgeven.
submit_recvs: Plaats een pool van ontvangstbuffers. Wanneer een bericht aankomt, wordt een callback aangeroepen met de ontvangen data. De buffer wordt automatisch teruggegeven aan de pool nadat de callback is voltooid.
Een-zijdige Write
Voor bulkoverdrachten biedt TransferEngine zero-copy een-zijdige write-bewerkingen:
submit_single_write: Draagt een aaneengesloten regio over van bron naar bestemming
submit_paged_writes: Draagt niet-aaneengesloten pagina's over die zijn gespecificeerd door indexen, strides en offsets
Beide bewerkingen accepteren een optionele 32-bits onmiddellijke waarde die atomair aan de ontvanger wordt geleverd na voltooiing.
De engine shardt automatisch grote overdrachten over meerdere NIC's om de bandbreedte te maximaliseren.
Peergroepen en bulkbewerkingen
Voor bewerkingen die op meerdere peers zijn gericht (gebruikelijk bij MoE-routering), biedt TransferEngine geoptimaliseerde primitieven:
add_peer_group: Registreert vooraf een set peer-adressen voor snelle lookup
submit_scatter: Verzendt verschillende dataslices naar elke peer in de groep
submit_barrier: Verzendt een alleen-onmiddellijke kennisgeving naar alle peers (zero-byte overdracht)
Deze bewerkingen zijn geoptimaliseerd voor lage latentie door interne datastructuren te cachen en RDMA-werkaanvragen te templatiseren, waardoor herhalende overhead voor instellingen wordt vermeden.
UVM Watcher: GPU-naar-CPU signalering
Een belangrijke uitdaging in LLM-systemen is het coördineren van RDMA-overdrachten met GPU-voortgang. Idealiter zou gegevensoverdracht moeten beginnen zodra de GPU output genereert, maar het pollen van de GPU vanaf de CPU is inefficiënt.
TransferEngine pakt dit aan met UVM Watchers: verenigde virtuele geheugenlocaties die kunnen worden bijgewerkt door GPU-kernels (zelfs binnen CUDA-grafieken) en efficiënt kunnen worden gepolled door de CPU met behulp van GDRCopy. Wanneer een UVM-watcher wordt toegewezen, wordt een callback geregistreerd die wordt aangeroepen wanneer de GPU de waarde bijwerkt.
Dit maakt patronen mogelijk zoals KvCache-overdracht per laag: nadat elke transformer-laag is voltooid, verhoogt de GPU de watcher, wat een RDMA-overdracht van de KvCache van die laag activeert terwijl volgende lagen doorgaan met berekenen.
Voltooiingskennisgeving
TransferEngine biedt mechanismen voor voltooiingskennisgeving voor zowel de initiatorszijde (de aanroeper van submit_write) als de ontvangerszijde (het doelwit van Write-bewerkingen).
Initiator-zijde kennisgeving: Wanneer een write-bewerking wordt ingediend, kan de aanroeper ervoor kiezen om bij voltooiing op de hoogte te worden gesteld via een callback-functie of een atomaire vlag. Deze kennisgeving bevestigt dat de data succesvol is overgedragen vanuit de bronbuffer.
Ontvanger-zijde kennisgeving: De belangrijkste innovatie is de ImmCounter, een component die counters bijhoudt die worden geïndexeerd door 32-bits onmiddellijke getallen. Wanneer een write-bewerking een onmiddellijke waarde bevat, wordt de ImmCounter van de ontvanger atomair verhoogd bij levering van de data. De expect_imm_count-functie stelt de ontvanger in staat om een callback te registreren die wordt geactiveerd wanneer een specifieke onmiddellijke waarde een doel-count bereikt. De counters kunnen transparant worden gesynchroniseerd met de GPU via GDRCopy, direct worden gepolled of worden gecontroleerd via callbacks.
Dit mechanisme aan de ontvangerszijde biedt betrouwbare synchronisatie zonder dat berichtvolgorde vereist is. Het werkt even goed op ConnectX NIC's (die levering in de juiste volgorde garanderen over RC-transport) en EFA NIC's (die berichten in willekeurige volgorde afleveren over SRD-transport).
Implementatie
TransferEngine is geïmplementeerd in Rust, waarbij toewijzingen, threading en synchronisatie zorgvuldig worden geoptimaliseerd om latentie te minimaliseren en een hoge doorvoer te bereiken. De engine start één worker-draad per DomainGroup, elk vastgepind op een CPU-kern op de NUMA-node waaraan de apparaten zijn gekoppeld, waardoor zowel scheduling- als geheugentoegangslatentie worden geminimaliseerd. Datastructuren specifiek voor een domein worden toegewezen na het vastpinnen om ervoor te zorgen dat geheugen wordt gereserveerd in de juiste NUMA-node. Eén worker-draad behandelt tot 4 Domains (elk beheer van een enkele NIC), terwijl een andere toegewezen draad de GPU pollt om UVM-watchers bij te werken. Cross-thread communicatie gebeurt via lock-free queues.
De API stuurt aanvragen door naar de DomainGroup die het juiste apparaat bedient, waarbij de eerste ook de host bedient. In een strakke lus pollt de domein-worker naar nieuw werk, waarbij de indiening van nieuwe verzoeken prioriteit krijgt. Afhankelijk van de hardware en configuratie worden verzoeken geshard en verdeeld over de beschikbare Domains. De eerste Write van een samengesteld verzoek wordt onmiddellijk geplaatst in de verzend-wachtrij van de NIC. Zodra nieuwe verzoeken zijn uitgeput, gaat de worker verder met het voortzetten van lopende bewerkingen en plaatst schrijfacties om de hardware-pipeline te vullen. Vervolgens worden voltooiingswachtrijen gepolled om te vragen naar voltooide overdrachten en onmiddellijke counter-verhogingen. Gebeurtenissen worden geaggregeerd om kennisgevingen per overdracht te leveren, waarbij de overdracht wordt overgedragen aan een toegewezen callback-draad die door alle groepen wordt gedeeld.
Sharding binnen een DomainGroup is flexibel. Overdrachten kunnen zich op specifieke NIC's richten op basis van index. Een enkele Write kan worden gesplitst. Gepagineerde overdrachten, scatter- en barrier-bewerkingen, die allemaal vertalen naar meerdere Writes, kunnen over alle NIC's sharden.
Hardware-specifieke optimalisaties
De Domains binnen TransferEngine zijn gespecialiseerd en geoptimaliseerd voor de hardware onder hun controle:
AWS EFA: De EFA-implementatie gebruikt libfabric en beheert een fabric-domein per NIC binnen de DomainGroup. Belangrijke implementatiebeslissingen en optimalisaties omvatten:
Nul-grote schrijfacties: EFA wijkt af van de RDMA-specificatie door geldige doel-descriptors te vereisen, zelfs voor schrijfacties van nul grootte. Daarom dwingt TransferEngine geldige descriptors af voor alle overdrachten.
Template-werkaanvragen: Vult vooraf de gemeenschappelijke velden van libfabric-structs in en behoudt deze voordat ze worden gepost, waardoor de overhead per operatie voor bulkoverdrachten en peergroepbewerkingen wordt verminderd
NVIDIA ConnectX-7: De ConnectX-7-implementatie gebruikt libibverbs met RC-transport. Belangrijke implementatiebeslissingen en optimalisaties omvatten:
UD queue pairs voor handshakes: Elk Domain gebruikt een Unreliable Datagram (UD) queue pair om RC-verbindingshandshakes uit te wisselen.
Afzonderlijke RC queue pairs: Creëert 2 RC queue pairs per peer, één voor tweezijdige Send/Recv-bewerkingen en een andere voor een-zijdige Write- en WriteImm-bewerkingen. Deze scheiding is noodzakelijk omdat zowel Recv- als WriteImm-voltooiingen werkaanvragen verbruiken in de volgorde van indiening, wat hoogwaardige Recv-semantiek mogelijk maakt terwijl WriteImm wordt ondersteund zonder interferentie.
Template-werkaanvragen: Vult vooraf algemene velden van werkaanvraagstructuren in voor bulkoverdrachten en peergroepen.
Koppeling van werkaanvragen: Koppelt maximaal 4 werkaanvragen via de next-pointer van ibv_send_wr, waardoor het aantal deurbel-belletjes naar de NIC wordt verminderd.
Ontspannen volgorde: Schakelt IBV_ACCESS_RELAXED_ORDERING in om ongeordende PCIe-transacties tussen de NIC en het GPU-geheugen toe te staan.
Conclusie
Door zich te richten op het snijpunt van garanties over verschillende RDMA-transporten en door onafhankelijke synchronisatie via ImmCounter te introduceren, biedt TransferEngine een draagbare basis voor point-to-point communicatie in LLM-systemen. De bibliotheek is open source en beschikbaar op GitHub, en wij moedigen de gemeenschap aan om deze te verkennen en voort te bouwen op opkomende communicatiepatronen in grootschalige machine learning-systemen.
Citatie
@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}
}