Dwie maszyny klasy DGX Spark, jeden model: kopia na każdej bije podział

Operacje

Przez siedem tygodni nasz lokalny model językowy działał podzielony na dwóch maszynach NVIDIA GB10 z równoległością tensorową. 22 października zastąpiliśmy to pełną kopią modelu na każdej maszynie i proxy z równoważeniem obciążenia po metodzie least-connection przed nimi. Przepustowość wzrosła z około 180 do 210 tokenów na sekundę, a każda z maszyn może teraz zostać uruchomiona ponownie bez utraty modelu.

Dlaczego go podzieliliśmy

Każda maszyna ma 128 GB pamięci współdzielonej przez CPU i GPU. We wrześniu model mieścił się na jednej maszynie, ale jego cache ledwo: używana kompilacja vLLM nie potrafiła utrzymać cache KV tego modelu w FP8, więc cache był w BF16, a jedna maszyna miała miejsce na 499 110 tokenów. To mniej niż dwa żądania przy pełnym kontekście modelu wynoszącym 262 144 tokeny. Po podziale na obie maszyny z równoległością tensorową przez ich bezpośrednie łącze sieciowe cache urósł do ponad trzech milionów tokenów. Podzieliliśmy model dla miejsca, nie dla szybkości.

Co się zmieniło

Nowsza kompilacja 4-bitowa tego samego modelu (NVFP4, utworzona z destylacji z uwzględnieniem kwantyzacji) zajmuje około 71 GiB pamięci, a społecznościowa kompilacja vLLM dla GB10 (eugr/spark-vllm-b12x) uruchamia jego cache w FP8. Teraz jedna maszyna mieści cały model i cache od 0,76 do 1,08 miliona tokenów, czyli miejsce na 2,9 do 4,1 żądań przy pełnym kontekście, w zależności od tego, ile pamięci maszyna mu przydzieli. Obie kopie wykorzystują własne spekulatywne dekodowanie modelu (MTP, cztery tokeny robocze). Przed przełączeniem uruchomiliśmy nasz test wywoływania narzędzi na nowej kompilacji: uzyskała 86 punktów, a zastąpiona przez nią kompilacja 75.

Co zmierzyliśmy

Podział na dwie maszynyKopia na każdej maszynie
Cache KVponad 3 miliony tokenów0,76 i 1,08 miliona tokenów
Żądania przy pełnym kontekścieokoło 122,9 i 4,1
Przepustowość pod naszym obciążeniemokoło 180 tokenów/s210 tokenów/s (111 + 99)
Uruchomienieokoło 20 minut5 do 13 minut na kopię
Restart jednej maszynymodel jest niedostępnydruga kopia obsługuje żądania

Jedna kopia sama w sobie, w zależności od liczby obsługiwanych jednocześnie żądań: przy krótkich promptach 1 żądanie dawało 36 tokenów na sekundę, 4 dawały 82, 8 dawały 109, a 16 dawało 150. Przy dłuższych promptach naszego agenta e-mailowego 4 dawały 81, 8 dawały 113, a 16 tylko 87: powyżej ośmiu takich żądań więcej oznaczało wolniejsze działanie.

Przez jedno popołudnie jedna kopia obsługiwała maksymalnie 12 żądań jednocześnie, a druga 8. Pierwsza generowała od 91 do 93 tokenów na sekundę i kończyła od 26 do 27 żądań na minutę; druga od 81 do 83 tokenów na sekundę i 22 żądania na minutę. Teraz obie obsługują po 12.

Z czego zrezygnowaliśmy

Z miejsca. Model podzielony miał jedną pamięć podręczną o pojemności ponad trzech milionów tokenów; kopie mają dwie mniejsze, a żądanie może korzystać tylko z pamięci podręcznej tej kopii, która je obsługuje. Nasi agenci wysyłają wiele krótkich i średnich żądań, więc dla nich przepustowość była ważniejsza. Jeśli obsługujesz kilka bardzo długich kontekstów jednocześnie, podział może nadal być lepszym wyborem. Zachowaliśmy konfigurację z podziałem, a powrót do niej to jeden skrypt.

Co poszło nie tak podczas przełączania

  1. Jedenaście minut bez modelu, potem 30 sekund błędów 502. Model podzielony musiał się zatrzymać, zanim kopia mogła się załadować na tych samych maszynach, więc nasi agenci czekali około jedenastu minut. Gdy pierwsza kopia wstała, proxy nadal odpowiadało „502 no live upstreams” przez około 30 sekund dłużej: oznaczyło backendy jako awaryjne, gdy były wyłączone (max_fails=3, fail_timeout=30s) i nie próbowało ich ponownie, aż ten czas upłynął.
  2. Klient, który pominął proxy. Jedno z naszych zadań wsadowych miało w konfiguracji adres pierwszej maszyny, więc cały jego ruch trafił do jednej kopii, podczas gdy druga miała zapas mocy. Teraz każdy klient przechodzi przez proxy; adresy samych maszyn służą tylko do kontroli stanu.
  3. Skaczące metryki. Nasz monitor przepustowości czytał /metrics z vLLM przez proxy. Przy dwóch backendach kolejne odczyty pochodziły z różnych kopii, liczniki skakały tam i z powrotem, a monitor zgłaszał model jako wygłodzony. Teraz czyta każdą kopię bezpośrednio i sumuje wyniki.
  4. Stary watchdog, który zatrzymałby nową kopię. Konfiguracja rozdzielona miała watchdoga, który restartował usługę, gdy model przestawał odpowiadać. Nowa usługa deklaruje Conflicts= ze starą, więc taki restart zatrzymałby nową kopię. Trzeba ją było wyłączyć poleceniem systemctl disable --now, a nie tylko zatrzymać, ponieważ nasza warstwa operacyjna restartuje jednostki, które są włączone, ale nie działają.
  5. Pamięć na maszynie wykonującej inne zadania. Jedna maszyna obsługuje również nasze bazy danych i budowanie stron. Przy ustawieniu gpu-memory-utilization dla vLLM na 0,78 pozostawało tylko 5 GiB na wszystko inne, więc obniżyliśmy je do 0,74 i zrestartowaliśmy tylko tę kopię, podczas gdy druga przejęła obciążenie. Na tych maszynach udział pamięci przydzielany vLLM pochodzi z tej samej pamięci co wszystko inne.

Jeśli masz dwie takie maszyny

Factory Floor zawiera proxy i konfiguracje uruchomienia dla jednej lub dwóch maszyn, kontrolę stanu oraz test użycia narzędzi, które wykonujemy przed przełączeniem modeli, wraz z watchdogami i zabezpieczeniami pamięci dla reszty maszyny.

Zaktualizowano: