Două cutii de clasă DGX Spark, un model: o copie pe fiecare, fără împărțire
Operațiuni
Timp de șapte săptămâni, modelul nostru local de limbaj a rulat împărțit pe două mașini NVIDIA GB10 cu paralelism de tensori. Pe 22 octombrie am înlocuit această configurație cu o copie completă a modelului pe fiecare mașină și un proxy cu cea mai mică conexiune în față. Randamentul a crescut de la aproximativ 180 la 210 tokeni pe secundă, iar oricare dintre mașini poate acum reporni fără a întrerupe modelul.
De ce îl împărțisem
Fiecare mașină are 128 GB de memorie partajată între CPU și GPU. În septembrie, modelul încăpea pe o mașină, dar memoria cache abia dacă încăpea: buildul vLLM folosit nu putea păstra memoria cache KV a acestui model în FP8, așa că memoria cache era în BF16, iar o mașină avea loc pentru 499.110 de tokeni. Aceasta este mai puțin decât două cereri la contextul complet al modelului de 262.144 de tokeni. Împărțit pe ambele mașini cu paralelism de tensori prin legătura lor de rețea directă, memoria cache a crescut la peste trei milioane de tokeni. Împărțisem modelul pentru spațiu, nu pentru viteză.
Ce s-a schimbat
Un build mai nou de 4 biți al aceluiași model (NVFP4, realizat prin distilare conștientă de cuantizare) ocupă aproximativ 71 GiB de memorie, iar un build comunitar vLLM pentru GB10 (eugr/spark-vllm-b12x) rulează memoria cache în FP8. Acum, o mașină găzduiește întregul model și o memorie cache de 0,76 până la 1,08 milioane de tokeni, loc pentru 2,9 până la 4,1 cereri la context complet, în funcție de câtă memorie îi alocă mașina. Ambele copii utilizează decodarea speculativă a modelului (MTP, patru tokeni de ciornă). Înainte de comutare, am rulat testul nostru de apelare a instrumentelor pe noul build: a obținut 86, iar buildul înlocuit 75.
Ce am măsurat
| Împărțit pe două mașini | O copie pe fiecare mașină | |
|---|---|---|
| Memorie cache KV | peste 3 milioane de tokeni | 0,76 și 1,08 milioane de tokeni |
| Cereri la context complet | aproximativ 12 | 2,9 și 4,1 |
| Randament sub sarcina noastră | aproximativ 180 tokeni/s | 210 tokeni/s (111 + 99) |
| Pornire | aproximativ 20 de minute | 5 până la 13 minute pe copie |
| Repornirea unei mașini | modelul este indisponibil | cealaltă copie servește |
O singură copie, luată individual, în funcție de numărul de cereri servite simultan: cu prompturi scurte, 1 cerere a oferit 36 de tokeni pe secundă, 4 au oferit 82, 8 au oferit 109 și 16 au oferit 150. Cu prompturile mai lungi ale agentului nostru de e-mail, 4 au oferit 81, 8 au oferit 113 și 16 doar 87: peste opt astfel de cereri, mai multe încetinesc sistemul.
Într-o după-amiază, o copie a preluat cel mult 12 cereri simultan, iar cealaltă 8. Prima a produs 91 până la 93 de tokeni pe secundă și a finalizat 26 până la 27 de cereri pe minut; a doua a produs 81 până la 83 de tokeni pe secundă și 22 de cereri pe minut. Acum ambele preiau câte 12.
La ce am renunțat
Spațiu. Modelul partajat avea un cache de peste trei milioane de tokeni; copiile au două cache-uri mai mici, iar o cerere poate folosi doar cache-ul copiei care o servește. Agenții noștri trimit multe cereri scurte și medii, iar pentru ele debitul era mai important. Dacă serviți câteva contexte foarte lungi simultan, partajarea poate rămâne alegerea mai bună. Am păstrat configurația partajată, iar revenirea la ea se face printr-un singur script.
Ce a mers prost în timpul trecerii
- Eleven minute fără model, apoi 30 de secunde de erori 502. Modelul partajat trebuia oprit înainte ca o copie să se încarce pe aceleași mașini, astfel încât agenții noștri au așteptat aproximativ unsprezece minute. Când prima copie a pornit, proxy-ul a continuat să răspundă „502 no live upstreams” timp de încă aproximativ 30 de secunde: acesta marcase backend-urile ca eșuate în timp ce erau oprite (max_fails=3, fail_timeout=30s) și nu le-a mai testat până la expirarea acestui timp.
- Un client care a sărit peste proxy. Una dintre sarcinile noastre în lot avea adresa primei mașini în configurație, astfel încât întreaga sa sarcină a mers către o singură copie, în timp ce cealaltă avea capacitate disponibilă. Acum, fiecare client trece prin proxy; adresele proprii ale mașinilor sunt folosite doar pentru verificări de sănătate.
- Metrici care au fluctuat. Monitorul nostru de debit citea /metrics din vLLM prin proxy. Cu două backend-uri, citirile consecutive veneau de la copii diferite, contoarele oscilau, iar monitorul raporta modelul ca înfometat. Acum citește fiecare copie direct și le însumează.
- Un watchdog vechi care ar fi oprit noua copie. Configurarea divizată avea un watchdog care repornea serviciul atunci când modelul nu mai răspundea. Noul serviciu declară Conflicts= cu cel vechi, astfel încât repornirea ar fi oprit noua copie. A trebuit dezactivat cu
systemctl disable --now, nu doar oprit, deoarece stratul nostru de operațiuni repornește unitățile care sunt activate, dar nu rulează. - Memoria de pe mașina care face și alte lucrări. O mașină rulează și bazele noastre de date și build-urile site-urilor. Cu gpu-memory-utilization al vLLM la 0,78, rămâneau doar 5 GiB pentru restul, așa că l-am coborât la 0,74 și am repornit doar acea copie, în timp ce cealaltă prelua sarcina. Pe aceste mașini, cota pe care o acorzi vLLM provine din aceeași memorie cu tot restul.
Dacă ai două astfel de mașini
- Dacă modelul și cache-ul de care ai nevoie încap pe o singură mașină, rulează o copie pe fiecare, în spatele unui proxy cu cea mai mică conexiune. Împarte modelul doar când o mașină nu poate susține cache-ul de care ai nevoie.
- Indreaptă fiecare client către proxy, inclusiv joburile batch și configurațiile vechi.
- Citește metricile de la fiecare copie direct.
- Alocă fiecărei mașini propria cotă de memorie, în funcție de ce altceva rulează pe ea.
- Setează câte cereri deservește o copie simultan, în funcție de traficul tău: pentru mixul nostru, 12 a fost mai bun decât 8.
- Așteaptă-te ca comutarea să scoată modelul din funcțiune pentru durata de pornire a unei copii și planifică acest lucru.
Factory Floor include proxy-ul și configurațiile de lansare pentru una sau două mașini, verificarea de sănătate și testul de utilizare a instrumentelor pe care le rulăm înainte de a comuta modelele, împreună cu watchdog-urile și mecanismele de siguranță a memoriei pentru restul mașinii.