RDMA punkt-punktam komunikācija LLM sistēmām

Eleģants rīks jaunu LLM komunikācijas modeļu risināšanai

AutoriPerplexity Research

LLM sistēmas tradicionāli ir paļāvušās uz kolektīvo komunikāciju strukturētiem paralēliem modeļiem apmācībā un secināšanā. Tomēr jaunās darba slodzes, piemēram, disaggregētā secināšana, ekspertu maisījuma (MoE) maršrutēšana un asinhronā pastiprinātas mācīšanās precizēšana, pieprasa punkt-punktam komunikācijas elastību.

Mūsu jaunākais pētnieciskais darbs iepazīstinā ar TransferEngine – portējamu uz RDMA balstītu komunikāciju bibliotēku, kas darbojas dažādos tīkla saskarnes kontrolleros (NIC), tostarp NVIDIA ConnectX-7 un AWS Elastic Fabric Adapter (EFA). Mēs demonstrējam tās efektivitāti trīs ražošanas sistēmās uzņēmumā Perplexity AI:

Disaggregēta iepriekšējā aizpilde un atšifrēšana: slānis pa slānim KvCache pārsūtīšana, izmantojot lapās sadalītas rakstīšanas un UVM novērotājus, kas ļauj iepriekšējās aizpildes un atšifrēšanas instancēm mērogot neatkarīgi.

Svara pārsūtīšana RL pēcapmācībai mazāk nekā 2 sekundēs: ārkārtīgi ātra svaru sinhronizācija starp apmācības un secināšanas mezgliem.

Triljonu parametru modeļu iespējošana uz AWS EFA: zema latentuma MoE nosūtīšanas un apvienošanas operācijas, izmantojot izkliedes un barjeras primitīvus, sasniedzot modernāko latentumu uz ConnectX-7 un pirmo dzīvotspējīgo ieviešanu, kas ir saderīga ar AWS EFA.

Šis raksts koncentrējas uz pašu TransferEngine – komunikāciju bibliotēku, kas nodrošina šīs sistēmas. Izlasiet pilnu pētniecisko darbu vietnē arXiv un izpētiet kodu vietnē GitHub.

Motivācija

Kolektīvā komunikācija izceļas strukturētos paralēlos modeļos, piemēram, tensora paralēlismā un datu paralēlismā, taču tā uzliek ierobežojumus, kas ierobežo tās piemērojamību jaunajām darba slodzēm:

Fiksēta dalība: kolektīvi pieprasa globālas komunikācijas vides izveidi inicializācijas laikā, ar sinhronizētu iestatīšanu starp visiem dalībniekiem. Disaggregētai secināšanai tas ir problemātiski, jo iepriekšējās aizpildes un atšifrēšanas instancēm ir dinamiski jāmērojas, reaģējot uz satiksmes modeļiem.

Vienotas tensora formas: kolektīvi pieņem, ka visi dalībnieki apmainās ar tensoriem, kuriem ir identiskas formas un datu tipi. MoE maršrutēšanā katram ekspertam nosūtīto žetonu skaits ir atkarīgs no datiem un tiek atklāts izpildlaikā, piespiežot blīvus all-to-all kolektīvus pielāgoties sliktākā gadījuma bufera izmēriem.

Operāciju secība: kolektīvās API uzspiež sinhronizētu semantiku, pat ja lietojumprogrammām ir jāzina tikai tas, ka operāciju kopums ir pabeigts. Piemēram, KvCache pārsūtīšanā visas lapas no iepriekšējās aizpildes instances uz atšifrēšanas instanci var pienākt jebkurā secībā. Līdzīgi RL svara atjauninājumos visus svara tensorus var pārsūtīt neatkarīgi bez secības ierobežojumiem.

Uz RDMA balstīta punkt-punktam komunikācija dabiski risina šos ierobežojumus, taču trīs barjeras ir kavējušas tās ieviešanu:

Bibliotēkas ierobežojumi: lai gan torch.distributed un NCCL nodrošina punkt-punktam API, piemēram, Send/Recv, šīs operācijas joprojām manto kolektīvo semantiku un ierobežojumus.

Aparatūras dažādība: NVIDIA ConnectX NIC ar uzticamu savienojumu (RC) transportu ir visizplatītākā RDMA iestatīšana, kas garantē piegādi secībā. Mākoņpakalpojumu sniedzēji, piemēram, AWS, izmanto citu aparatūru, Elastic Fabric Adapter (EFA) ar mērogojamu uzticamu datagrammu (SRD) transportu piegādā ziņojumus ārpus secības. EFA arī pieprasa četru 100 Gbps NIC (vai divu 200 Gbps NIC) agregēšanu, lai sasniegtu 400 Gbps joslas platumu.

Programmatūras portējamība: esošās RDMA bibliotēkas cieš no piegādātāja piesaistes. DeepEP pieprasa GPU iniciētu RDMA (IBGDA), kas pieejams tikai uz ConnectX. NVSHMEM uzrāda smagu veiktspējas pasliktināšanos uz EFA. Bibliotēkām, piemēram, Mooncake un NIXL, trūkst EFA atbalsta vai tās atrodas agrīnā izstrādes stadijā. Rezultātā nepastāv dzīvotspējīgs starpprovizoru risinājums LLM secināšanai.

TransferEngine

TransferEngine risina šīs barjeras, izmantojot četras pamatspējas:

Kopējie transporta pieņēmumi: tā vietā, lai mērķētu uz konkrētu NIC, TransferEngine pieņem tikai to, ka pamatā esošais transports ir uzticams, bet nesakārtots, t.i., garantiju krustpunkts, ko nodrošina gan RC, gan SRD transporti.

ImmCounter sinhronizācija: jauns pamatotais primitīvs pabeigšanas paziņojumam, kas darbojas, nepaļaujoties uz ziņojumu secību, ļaujot vienam un tam pašam kodam darboties gan sakārtotos (RC), gan nesakārtotos (SRD) transportos.

Vairāku NIC atbalsts: vairāku NIC caurspīdīga pārvaldība uz katru GPU, automātiski sadalot pārsūtīšanu, lai maksimāli palielinātu joslas platumu.

Minimāla API virsma: neliels operāciju kopums, kas pietiekams, lai izveidotu sarežģītus komunikācijas modeļus.

TransferEngine sistēmas dizains, kurā parādīti pieprasījuma un atpakaļsaukuma ceļi, CPU darbinieki, UVM buferi, GPU un NIC divās GPU/NUMA domēna zonās.

TransferEngine izveido vienu pavediens uz katru GPU. Katrs pavediens pārvalda DomainGroup, kas koordinē 1 līdz 4 RDMA NIC, atkarībā no aparatūras platformas (viens ConnectX-7 nodrošina 400 Gbps, savukārt EFA pieprasa četrus 100 Gbps adapterus vai divus 200 Gbps adapterus). DomainGroup ietvaros katrs Domain ir specifisks aparatūrai un pārvalda vienu NIC, apstrādājot rindu pāru pārvaldību, darba iesniegšanu un pabeigšanas aptauju.

Katra TransferEngine instance atsedz vienu galveno adresi vienaudžu identifikācijai un atklāšanai. Attālinātie vienaudži apmainās ar NetAddr struktūrām, kas kodē tīkla adreses un var tikt serializētas out-of-band komunikācijai. Visi vienaudži sistēmā ir jāizmanto vienāds NIC skaits uz katru GPU. Šī vienotā konfigurācija ļauj TransferEngine inteliģenti sadalīt pārsūtīšanu: uzsākot pārsūtīšanu, gan avots, gan galamērķis precīzi zina, kuri NIC ir pieejami, nodrošinot automātisku slodzes sabalansēšanu visos adapteros.

API dizains

TransferEngine atsedz minimālu API, kas abstrahē prom pamatā esošo RDMA saskarņu sarežģītību.

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 sastāv no vairākām galvenajām sastāvdaļām:

Atmiņas reģistrācija

RDMA operācijas pieprasa, lai atmiņas reģioni būtu reģistrēti ar NIC pirms datu pārsūtīšanas. Funkcija reg_mr ņem rādītāju, garumu un ierīci, atgriežot divus rokturus:

MrHandle: lokāls rokturis, ko izmanto kā avotu pārsūtīšanai

MrDesc: serializējams deskriptors, ar kuru var apmainīties ar attālinātajiem vienaudžiem, lai ļautu tiem rakstīt uz reģistrēto atmiņu

Struktūra MrDesc satur attālinātās tīkla adreses un attālinātās atslēgas sarakstu (pa vienai katram NIC), kas piešķir attālinātu piekļuvi atmiņas reģionam. Šis dizains dabiski apstrādā vairāku NIC iestatījumus.

Divpusēja nosūtīšana/saņemšana

Maziem ziņojumiem un RPC stila kontroles plaknes ziņojumiem TransferEngine nodrošina submit_send un submit_recvs:

submit_send: nosūta ziņojumu uz attālinātu vienaudzi, ko identificē NetAddr. Ziņojums tiek kopēts iekšēji, tāpēc izsaucējs var nekavējoties atkārtoti izmantot vai atbrīvot buferi.

submit_recvs: ievieto saņemšanas buferu baseinu. Kad ziņojums pienāk, tiek izsaukts atzvanījums ar saņemtajiem datiem. Buferis automātiski tiek atgriezts baseinā pēc atzvanījuma pabeigšanas.

Vienpusēja rakstīšana

Masveida datu pārsūtīšanai TransferEngine nodrošina nulles kopēšanas vienpusējas rakstīšanas operācijas:

submit_single_write: pārsūta nepārtrauktu reģionu no avota uz galamērķi

submit_paged_writes: pārsūta nepārtrauktas lapas, ko norāda indeksi, soļi un nobīdes

Abas operācijas pieņem izvēles 32 bitu tūlītējo vērtību, kas tiek piegādāta saņēmējam atomiski pēc pabeigšanas.

Dzinējs automātiski sadala lielas pārsūtīšanas vairākos NIC, lai maksimāli palielinātu joslas platumu.

Vienaudžu grupas un masveida operācijas

Operācijām, kas mērķē uz vairākiem vienaudžiem (bieži sastopamas MoE maršrutēšanā), TransferEngine nodrošina optimizētus primitīvus:

add_peer_group: iepriekš reģistrē vienaudžu adrešu kopu ātrai meklēšanai

submit_scatter: nosūta dažādas datu daļas katram vienaudzim grupā

submit_barrier: nosūta tikai tūlītēju paziņojumu visiem vienaudžiem (nulles baitu pārsūtīšana)

Šīs operācijas ir optimizētas zemam latentumam, kešatmiņā saglabājot iekšējās datu struktūras un šablona veidojot RDMA darba pieprasījumus, izvairoties no atkārtotām iestatīšanas izmaksām.

UVM novērotājs: GPU-to-CPU signalizācija

Galvenais izaicinājums LLM sistēmās ir RDMA pārsūtīšanas koordinēšana ar GPU progresu. Ideālā gadījumā datu pārsūtīšanai vajadzētu sākties, tiklīdz GPU rada izvadi, taču GPU aptauja no CPU ir neefektīva.

TransferEngine to risina ar UVM novērotājiem: vienotām virtuālās atmiņas vietām, kuras var atjaunināt ar GPU kodoliem (pat CUDA grafiku ietvaros) un efektīvi aptaujāt ar CPU, izmantojot GDRCopy. Kad UVM novērotājs ir piešķirts, tiek reģistrēts atzvanījums, kas tiek izsaukts ikreiz, kad GPU atjaunina vērtību.

Tas iespējo tādus modeļus kā slānis pa slānim KvCache pārsūtīšana: pēc katra transformatora slāņa pabeigšanas GPU palielina novērotāju, ierosinot šī slāņa KvCache RDMA pārsūtīšanu, kamēr nākamie slāņi turpina skaitļošanu.

Pabeigšanas paziņojums

TransferEngine nodrošina pabeigšanas paziņojumu mehānismus gan iniciatora pusei (izsaucējam submit_write), gan saņēmēja pusei (rakstīšanas operāciju mērķim).

Iniciatora puses paziņojums: kad rakstīšanas operācija tiek iesniegta, izsaucējs var izvēlēties saņemt paziņojumu pēc pabeigšanas, izmantojot vai nu atzvanīšanas funkciju, vai atomisku karogu. Šis paziņojums apstiprina, ka dati ir veiksmīgi pārsūtīti no avota bufera.

Saņēmēja puses paziņojums: galvenā inovācija ir ImmCounter – komponents, kas izseko skaitītājus, kurus indeksē 32 bitu tūlītējie skaitļi. Kad rakstīšanas operācija nes tūlītēju vērtību, saņēmēja ImmCounter tiek palielināts atomiski pēc datu piegādes. Funkcija expect_imm_count ļauj saņēmējam reģistrēt atzvanījumu, kas iedegas, kad konkrēta tūlītējā vērtība sasniedz mērķa skaitu. Skaitītājus var caurspīdīgi sinhronizēt ar GPU, izmantojot GDRCopy, aptaujāt tieši vai uzraudzīt, izmantojot atzvanījumus.

Šis saņēmēja puses mehānisms nodrošina uzticamu sinhronizāciju, nepieprasot ziņojumu secību. Tas darbojas vienlīdz labi uz ConnectX NIC (kas garantē piegādi secībā pār RC transportu) un EFA NIC (kas piegādā ziņojumus ārpus secības pār SRD transportu).

Ieviešana

TransferEngine ir ieviests Rust valodā, rūpīgi optimizējot piešķīrumus, pavedieni un sinhronizāciju, lai samazinātu latentumu un sasniegtu augstu caurlaidību. Dzinējs izveido vienu pavediens uz katru DomainGroup, katrs piesprausts pie CPU kodola uz NUMA mezgla, kuram ir pievienotas ierīces, samazinot gan plānošanas, gan atmiņas piekļuves latentumu. Datu struktūras, kas raksturīgas domēnam, tiek piešķirtas pēc piespraušanas, lai nodrošinātu, ka atmiņa ir rezervēta pareizajā NUMA mezglā. Viens darbinieka pavediens apstrādā līdz 4 Domain (katrs pārvalda vienu NIC), savukārt cits īpašs pavediens aptaujā GPU, lai atjauninātu UVM novērotājus. Komunikācija starp pavedieniem tiek veikta, izmantojot rindas bez bloķēšanas.

API pārsūta pieprasījumus uz DomainGroup, kas apkalpo attiecīgo ierīci, un pirmais no tiem apkalpo arī resursdatoru. Ciešā ciklā domēna darbinieks aptaujā jaunu darbu, par prioritāti izvirzot jaunu pieprasījumu iesniegšanu. Atkarībā no aparatūras un konfigurācijas pieprasījumi tiek sadalīti un slodze sabalansēta starp pieejamiem Domain. Saliktā pieprasījuma pirmā rakstīšana tiek nekavējoties ievietota NIC sūtīšanas rindā. Kad jauni pieprasījumi ir izsmelti, darbinieks turpina virzīt gaidošās operācijas, ievietojot rakstīšanas darbus, lai aizpildītu aparatūras cauruļvadu. Pēc tam pabeigšanas rindas tiek aptaujātas, lai vaicātu par pabeigtajām pārsūtīšanām un tūlītējiem skaitītāja pieaugumiem. Notikumi tiek agregēti, lai nodrošinātu paziņojumus katrai pārsūtīšanai, nododot pārsūtīšanu īpašam atzvanīšanas pavedienam, ko koplieto visas grupas.

Sadalīšana DomainGroup ietvaros ir elastīga. Pārsūtīšanas var mērķēt uz konkrētiem NIC pēc indeksa. Viena rakstīšana var tikt sadalīta. Lapās sadalītās pārsūtīšanas, izkliedes un barjeras operācijas, kas visas tulko uz vairākām rakstīšanām, var sadalīties visos NIC.

Aparatūrai specifiskas optimizācijas

Domain TransferEngine ietvaros ir specializēti un optimizēti aparatūrai, kuru tie kontrolē:

AWS EFA: EFA ieviešana izmanto libfabric, pārvaldot auduma domēnu katram NIC DomainGroup ietvaros. Galvenie ieviešanas lēmumi un optimizācijas ietver:

Nulles izmēra rakstīšanas: EFA novirzās no RDMA specifikācijas, pieprasot derīgus mērķa deskriptorus pat nulles izmēra rakstīšanām. Līdz ar to TransferEngine uzspiež derīgus deskriptorus visām pārsūtīšanām.

Darba pieprasījuma šablona veidošana: iepriekš aizpilda un saglabā libfabric struktūru kopīgos laukus pirms ievietošanas, samazinot izmaksas uz vienu operāciju masveida pārsūtīšanām un vienaudžu grupas operācijām

NVIDIA ConnectX-7: ConnectX-7 ieviešana izmanto libibverbs ar RC transportu. Galvenie ieviešanas lēmumi un optimizācijas ietver:

UD rindas pāri rokasspiedieniem: katrs Domain izmanto neuzticamu datagrammas (UD) rindas pāri, lai apmainītos ar RC savienojuma rokasspiedieniem.

Atsevišķi RC rindas pāri: izveido 2 RC rindas pārus uz vienu vienaudzi, vienu divpusējām Send/Recv operācijām un otru vienpusējām Write un WriteImm operācijām. Šī atdalīšana ir nepieciešama, jo gan Recv, gan WriteImm pabeigšanas patērē darba pieprasījumus ievietošanas secībā, ļaujot augsta līmeņa Recv semantikai atbalstīt WriteImm bez traucējumiem.

Darba pieprasījuma šablona veidošana: iepriekš aizpilda darba pieprasījuma struktūru kopīgos laukus masveida pārsūtīšanām un vienaudžu grupām.

Darba pieprasījuma ķēdes veidošana: saista līdz 4 darba pieprasījumiem, izmantojot next rādītāju ibv_send_wr, samazinot durvju zvana zvanu skaitu uz NIC.

Relaksēta secība: iespējo IBV_ACCESS_RELAXED_ORDERING, lai atļautu ārpus secības PCIe darījumus starp NIC un GPU atmiņu.

Secinājums

Mērķējot uz garantiju krustpunktu starp dažādiem RDMA transportiem un ieviešot no secības neatkarīgu sinhronizāciju, izmantojot ImmCounter, TransferEngine nodrošina portējamu pamatu punkt-punktam komunikācijai LLM sistēmās. Bibliotēka ir atvērtā koda un pieejama vietnē GitHub, un mēs aicinām kopienu to izpētīt un būvēt uz tās pamata jauniem komunikācijas modeļiem liela mēroga mašīnmācīšanās sistēmās.

Citāts

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