Razdvojeni pretpuni i dekodiranje
Odvajanje pretpunjenja i dekodiranja na uređajima ubrzava LLM zaključivanje i poboljšava performanse
Kako bi se generirali izlazni tokeni iz ulaznog upita, LLM zaključivanje je podijeljeno u dvije faze: pretpuni i dekodiranje. Pretpuni se izvodi na ulaznim tokenima, popunjavajući KV predmemorije, prije ulaska u fazu dekodiranja koja generira tokone jedan po jedan. Dok jedan korak dekodiranja obično traje desetke milisekundi, pretpuni traje znatno dulje. Ako se izvode na istim uređajima, miješanje pretpunjenja s dekodiranjem pogoršava performanse dekodiranja. U ovom članku istražujemo utvrđeno rješenje u obliku razdvojenog pretpunjenja i dekodiranja, pokrećući ih na odvojenim uređajima kako bi se maksimizirala propusnost pretpunjenja i latencije dekodiranja.
Pretpuni naspram performansi dekodiranja
U tipičnom LLM poslužiteljskom mehanizmu, planer serija odabire zahtjeve za obradu u svakom koraku izvršavanja modela. Kada se izvodi na jednom uređaju ili čvoru, i zahtjevi za pretpuni i za dekodiranje se grupiraju u serije. Trošak pažnje, koji se akumulira duž duljine sekvence, raste i za pretpuni i za dekodiranje, proporcionalno duljini unosa u KV predmemoriji (kv_len). Zahtjevi za dekodiranje obično prosljeđuju jedan token (qo_len=1), uz minimalan trošak kroz druge slojeve koji rade neovisno na tokenima sekvence. Zahtjevi za pretpuni prosljeđuju tisuće ili desetke tisuća tokena uz značajan trošak kroz guste slojeve (veliki qo_len).
Latencija prolaza naprijed više ovisi o broju neovisnih tokena proslijeđenih kroz guste slojeve (qo_len) nego o broju tokena dohvaćenih iz KV predmemorije tijekom pažnje (kv_len). Pažnja može paralelizirati i preko broja zahtjeva i kv_len proporcionalno duljinama sekvenci, postižući dobru iskorištenost. Pretpuni je ograničen računalnom snagom: uz visok qo_len, GEMM jezgre mogu dodijeliti dovoljno blokova duž M dimenzije kako bi u potpunosti iskoristile računalne mogućnosti modernih GPU-ova. Dekodiranje je ograničeno memorijom: zbog obično malih veličina serija, broj ulaza duž M obično je mali, dovoljan za samo jedan blok. Iako Split-K GEMM jezgre mogu poboljšati iskorištenost SM-a za male veličine serija tokena, predmemorije i jedinice za množenje matrica obično ostaju neiskorištene.
Kada se pomiješaju, serije koje sadrže zahtjeve za pretpuni imaju veće latencije kroz prolaz naprijed, što negativno utječe na propusnost dekodiranja cijele instance. Iako miješanje zahtjeva za pretpuni sa zahtjevima za dekodiranje ili korištenje iscjepkanog pretpunjenja može neznatno poboljšati performanse dekodiranja, teško je održati dovoljnu propusnost pretpunjenja za obradu dovoljno zahtjeva na instanci kako bi se maksimizirala propusnost dekodiranja. U slučaju velikih modela, s tipičnim izlaznim duljinama, za održavanje velike veličine serije za dekodiranje, pretpuni se mora obavljati dovoljno često da značajno narušava prosječnu latenciju i uzrokuje trzaje u izlazu.
Ti se problemi mogu riješiti korištenjem zasebnog skupa čvorova za obradu pretpunjenja i dekodiranja. Udruživanjem čvora za pretpuni s višestrukim čvorovima za dekodiranje, mogu se zakazati dovoljni zahtjevi za pretpuni kako bi se maksimizirala propusnost i održao dovoljno velik broj istovremenih zahtjeva na čvorovima za dekodiranje kako bi se također maksimizirala propusnost dekodiranja. Čvorovi za pretpuni popunjavaju KV predmemorije, koje se zatim prenose na čvorove za dekodiranje. Budući da dekoderi više ne moraju raditi pauze za pretpuni, latencije postaju mnogo determinističkije, budući da je ukupni utjecaj rasta kv_len aktivnih zahtjeva mnogo manje izražen. Cijena se plaća u povećanju vremena do prvog tokena (TTFT), budući da prijenos KV predmemorija preko mreže može trajati od desetaka do stotina milisekundi.
KV Messenger
U tvrtki Perplexity, naša implementacija za razdvojeni pretpuni i dekodiranje izgrađena je oko KV glasnika koji komunicira s LLM mehanizmom kako bi orkestrirao prijenose KV predmemorije s čvorova za pretpuni na čvorove za dekodiranje putem mreže. Na strani pretpunjenja, glasnik prihvaća zahtjeve s čvorova za dekodiranje, prosljeđujući ih planeru serija i prateći izvršavanje prolaza naprijed kako bi otpremio KV predmemorije uz što manju latenciju. Na strani dekodiranja, nakon što se dodijele stranice koje se ne mogu izbaciti, glasnik blokira zakazivanje zahtjeva za dekodiranje sve dok ne primi obavijest o dovršetku prijenosa KV predmemorije i konteksta dekodiranja.
Razdvajanje pretpunjenja zahtijeva veze visoke propusnosti i niske latencije, stoga je naša implementacija prilagođena za RDMA, podržavajući EFA i ConnectX mrežne sučeljne kartice (NIC). KV Messenger je izgrađen na libfabric, koristeći naše fabric-lib omotače za pružanje apstrakcija više razine s niskom latencijom preko Remote Direct Memory Access (RDMA) primitiva, implementirajući učinkovite prijenose stranica i metapodataka, zajedno s usmjeravanjem niske latencije. U pozadini, fabric-lib koordinira GPU i njegove izravno povezane mrežne kartice za kopiranje podataka s čvora za pretpuni na čvor za dekodiranje.
Nakon primitka, čvor za pretpuni dodjeljuje odgovarajući skup izvornih KV stranica i zakazuje zahtjev za pretpuni koristeći svoj lokalni mehanizam. Kako bi se minimizirala latencija, prijenosi ne čekaju prolaz naprijed: umjesto toga, kopije KV stranica pokreću se čim model završi s dodavanjem unosa KV predmemorije u KV predmemoriju za pojedinačne slojeve. Budući da zahtjevi za pretpuni mogu biti iscjepkani, planer serija obavještava KV glasnik o trenutno zakazanim odsječcima prije izvršavanja. Kako bi podržao CUDA grafove uz istovremeno praćenje slojeva, glasnik održava namjenski nit koji anketira brojač inkrementiran nakon izlazne projekcije pažnje. Brojač se održava samo na vodećem čvoru u okruženju sa šardovima: iako su unosi KV predmemorije valjani nakon dodavanja i prije pažnje, izlazna projekcija se svodi na razine, implicitno ih sinkronizirajući. Jednom kada se uoči promjena u brojaču, glasnik prima obavijest i poziva fabric-lib za pokretanje prijenosa sloja.
Nakon što je prijenos posljednjeg odsječka završen, svi dodatni metapodaci se također kopiraju: spekulativno dekodiranje ili MTP zahtijevaju premještanje logits i skrivenih stanja na dekoder. Ta se kopiranja također obavljaju putem RDMA, u i iz unaprijed dodijeljenih međuspremnika.
Po dovršetku svih prihvaćenih prijenosa posljednjeg odsječka, čvor za pretpuni poništava dodjelu KV stranica i dovršava zahtjev. Čvor za dekodiranje ne dobiva izravnu obavijest: umjesto toga, koristi neposredne brojače za praćenje broja dovršenih operacija. Broj RDMA operacija na strani pretpunjenja proporcionalan je broju prenesenih stranica. Po dovršetku poznatog broja kopiranja stranica i konteksta, fabric-lib poziva KV glasnik kako bi naznačio da je zahtjev spreman za dekodiranje. Glasnik poništava dodjelu bilo kojeg konteksta i predaje zahtjev LLM mehanizmu.
Prijenosi šardiranih KV predmemorija
Ako se pretpuni i dekoder oslanjaju na paralelizam tenzora (TP) i dijele na šardove ili repliciraju KV predmemorije identično, jedan mehanizam za prijenos koordinira više uređaja za slanje i primanje stranica svih replika. Kako bi se mogao koristiti jedan glasnik i mehanizam za prijenos unatoč činjenici da je izvršitelj modela repliciran na više uređaja i procesa, cuMem i cuMemImportFromShareableHandle se koriste za dodjeljivanje memorije uređaja koja podržava KV predmemorije i njeno mapiranje u glavni proces. Mehanizam za prijenos pregledava topologiju čvora kako bi pronašao mrežne kartice i CPU-ove u najbližem NUMA čvoru koji će se koristiti za prijenose svakog od odsječaka KV predmemorije.
Ako se izvor i odredište identično dijele na šardove, prijenosi su trivijalni jer postoji preslikavanje jedan na jedan s uređaja i stranica izvora i odredišta. U ovoj situaciji, šardiranje implicitno pomaže latencijama prijenosa: korištenjem više GPU-ova mogu se zaposliti povezane mrežne kartice, dosežući bliže potpunoj iskorištenosti propusnosti. Međutim, ako postoji neusklađenost, mehanizam za prijenos mora podijeliti ili rekonstruirati stranice ovisno o omjeru između izvornih i odredišnih odsječaka.
Ako pretpuni dijeli KV predmemoriju na više uređaja, pune stranice se rekonstruiraju na dekoderu slanjem odgovarajućih polovica s uređaja za pretpuni. Ako dekoder ima više šardova, prima stranice iz više izvora. Dekoder treba znati shemu šardiranja pretpunjenja kako bi mogao izračunati broj RDMA zapisivanja koja se očekuju primiti. Ako je uključena replikacija, pretpuni grupira uređaje u skupove replika koji unutar sebe repliciraju punu KV predmemoriju. Odredišni skupovi replika nasumično se dodjeljuju jednom od izvornih skupova kako bi se koristili svi dostupni uređaji za pokretanje RDMA zapisivanja.
Šardirani prijenosi zahtijevaju malu prilagodbu KV predmemorija. Zadano, FlashInfer se oslanja na NHD raspored, koji uređuje tokone unutar stranice unutar glava. Budući da su predmemorije najvjerojatnije podijeljene na šardove duž broja glava pažnje, to stvara diskontinuitet unutar glave. RDMA prijenosi implicitno ne podržavaju preskocima raspoređena zapisivanja, zahtijevajući jednu operaciju po glavi za izvršenje prijenosa. Umjesto toga, kako bi se smanjio broj interakcija s libfabric, organiziramo KV predmemorije koristeći HND raspored koji postavlja dimenziju glave prije broja tokena. To osigurava kontinuitet, dopuštajući da se stranica kopira jednim zapisivanjem.
Spekulativno dekodiranje
Spekulativno dekodiranje zahtijeva male prilagodbe za razdvojeni pretpuni-dekodiranje. U našoj implementaciji, čvorovima za pretpuni nije dopušteno uzorkovanje tokena. Budući da Sonar modeli tvrtke Perplexity podržavaju strukturirani izlaz, ne želimo ulaziti u složenost sinkronizacije implementacija procesora sheme između pretpunjivača i dekodera. U mehanizmima MTP-a i spekulativnog dekodiranja, pretpunjenje nacrta modela do posljednjeg tokena uključuje uzorkovanje tokena iz ciljnog modela.
Kako bi se zaobišli ovi problemi, pretpuni ne uključuje posljednji token ulazne sekvence. Umjesto toga, skrivena stanja ili logits iz pretpunjenja koja prethode posljednjem tokenu se prenose i tretiraju se kao token dekodiranja u sljedećem koraku na dekoderu. Iako ovo neznatno povećava latencije, budući da se puni korak dekodiranja mora izvršiti nakon pretpunjenja kako bi se izdao prvi token, složenost implementacije je znatno smanjena.
Razdvojene primjene
Primijenili smo ili eksperimentirali s višestrukim razdvojenim konfiguracijama s različitim modelima, za podršku produkcijskom prometu ili internim radnim opterećenjima procjene. Na temelju veličine i mehanizma pažnje modela, odabrali smo prikladne sheme šardiranja za čvorove pretpunjenja i dekodiranja kako bismo najbolje iskoristili GPU-ove.
DeepSeek-R1
S DeepSeekom smo razmotrili i primjene s tenzorskim paralelizmom (TP) i s paralelizmom podataka (DP). Kao što je raspravljeno u prethodnim objavama na blogu, TP primjene pružaju bolju latenciju uz cijenu niže propusnosti, zahtijevajući više GPU-ova za posluživanje teškog prometa. DP primjene se mnogo bolje skaliraju s opterećenjem, međutim njihova vršna propusnost je niža zbog troška komunikacije između uređaja ili čvorova.
DeepSeek se oslanja na latentnu pažnju s više glava, komprimirajući KV predmemorije. Budući da su sve KV glave komprimirane u jedan latentni vektor, TP ne može dijeliti KV predmemorije na šardove, jer umjesto toga mora replicirati latentne vektore na svim razinama. Šardiranje se događa nakon dekompresije, budući da svaka razina može izdvojiti različite glave iz iste latentne reprezentacije. Posljedično, svi šardovi KV predmemorije identični su i na šardovima pretpunjenja i dekodiranja.
S unutarčvornom TP postavkom, i pretpunjivači i dekoderi su identično podijeljeni na šardove. Prijenosi se otpremaju sa svih razina kako bi se u potpunosti iskoristile sve dostupne mrežne kartice. Međutim, s DP primjenom, gdje je veličina TP razine niža ili je svaka DP razina dodijeljena jednom GPU-u, bilo koji uređaj za pretpuni koji drži repliciranu kopiju KV predmemorije može je otpremiti. Kako bismo uravnotežili zahtjeve na svim dostupnim mrežnim karticama, nasumično biramo GPU i mrežnu karticu za slanje KV predmemorije s pretpunjivača na dekoder.
S miješanim pretpunjenjem i dekodiranjem, naša R1 primjena se borila da dosljedno premaši 50 TPS zbog čestih prekida pretpunjenja u rasponu od stotina milisekundi. Suprotno tome, odvajanjem pretpunjenja, imali smo kaznu od oko 100 ms za TTFT za svaki zahtjev, ali jedan čvor za pretpuni mogao je održavati dosljedne veličine serija na 3 čvora dekodera, isporučujući propusnost veću od 90 TPS dok je podnosio opterećenje od oko 1 QPS po čvoru dekodera. S primjenama paralelizza podataka, TPS je bio nešto niži na oko 50, međutim instance su mogle podnijeti opterećenje od 1 QPS po razini, s 8 razina na jednom čvoru.
Qwen3-Coder
Ovaj model od 480B koristi pažnju s grupiranim upitima (GQA), tako da se pažnja može lako podijeliti na šardove i može imati koristi od tenzorskog paralelizma bez žrtvovanja memorije za KV predmemorije. Posljedično, mogli smo podijeliti model na 8 GPU-ova i za pretpuni i za dekodiranje, uparujući oko 3 čvora dekodera s jednim čvorom pretpunjenja. Budući da je pažnja podijeljena na šardove, oslanjamo se na HND KV raspored predmemorije za dijeljenje KV predmemorija pretpunjenja i dekodiranja na šardove, uparujući razine pretpunjenja s razinama dekodiranja i u potpunosti koristeći sve mrežne kartice za prijenos odsječaka u paralelno.
Citiranje
@online{perplexity2025DisaggregatedPrefillAndDecode,
author = {{Perplexity} and Nandor Licker and Lequn Chen and Kevin Hu and Erin McKnight and Phi Hoang},
title = {{Disaggregated prefill and decode}},
date = {2025-08-01},
url = {https://perplexity.coolkeypoint.com/hub/blog/disaggregated-prefill-and-decode}
}