Dlaczego lokalny LLM zwalnia przy wielu użytkownikach
Dowiedz się, dlaczego serwer LLM zwalnia przy pięciu użytkownikach. Wyjaśniamy wpływ parametru num_parallel, limitów KV cache oraz mechanizmu kolejkowania na wydajność modelu.
Dlaczego wydajność lokalnego modelu LLM spada przy większej liczbie użytkowników?
Lokalnie hostowany model LLM zwalnia przy 5 jednoczesnych użytkownikach, ponieważ serwer generuje tylko jedną odpowiedź w danym momencie, a pozostałe cztery żądania oczekują w kolejce. Dokumentacja Ollama jasno określa ustawienie domyślne: OLLAMA_NUM_PARALLEL to „maksymalna liczba równoległych żądań przetwarzanych przez model w tym samym czasie, wartość domyślna wynosi 1”. System działa poprawnie. Czterech z pięciu użytkowników po prostu czeka na swoją kolej.
Rozwiązaniem rzadko jest wymiana sprzętu na mocniejszy. Należy zastosować silnik serwujący, który przetwarza wiele żądań przez model w jednym przebiegu (forward pass), oraz zapewnić wystarczającą ilość wolnej pamięci RAM, aby utrzymać kontekst rozmowy każdego użytkownika podczas przetwarzania. Oba te elementy są istotne, przy czym to drugi z nich faktycznie wyznacza limit wydajności.
Dwie fazy przetwarzania każdego żądania
Prefill odczytuje całe zapytanie jednocześnie i buduje dla niego cache uwagi. Każdy token zapytania przechodzi przez model wspólnie, więc prefill jest jednym dużym mnożeniem macierzy, ograniczonym przez przepustowość obliczeniową. Decode natomiast generuje odpowiedź token po tokenie. Każdy token wymaga ponownego odczytania pełnych wag modelu z pamięci, podczas gdy operacje arytmetyczne dla pojedynczego tokena są znikome. Faza decode jest ograniczona przez przepustowość pamięci.
Ta asymetria jest głównym powodem, dla którego batching działa. Dekodowanie dla jednego użytkownika odczytuje, przykładowo, 5 GB wag na token, pozostawiając większość jednostek obliczeniowych w stanie bezczynności. Dodanie drugiego żądania sprawia, że silnik odczytuje te same 5 GB raz, a następnie oblicza dwa tokeny. Drugi użytkownik nie generuje niemal żadnego dodatkowego czasu oczekiwania. Obsługa żądań ściśle jedno po drugim marnuje ten potencjał.
Dwie wartości opisują odczucia użytkownika. TTFT (time to first token) to czas oczekiwania w kolejce plus czas fazy prefill. ITL (inter-token latency) to odstęp między przesyłanymi strumieniowo tokenami, określany przez fazę decode. Wolny serwer zazwyczaj wykazuje opóźnienia w jednej z tych faz, a metody naprawcze są odmienne. Przed zmianą jakichkolwiek ustawień warto ustalić, z którym problemem mamy do czynienia, a oddzielny pomiar czasu prefill i decode pozwala to zweryfikować.
Statyczne przetwarzanie wsadowe wymusza oczekiwanie na najwolniejszą odpowiedź
Statyczne przetwarzanie wsadowe to rozwiązanie naiwne, które powstaje w przypadku samodzielnego grupowania żądań w kodzie aplikacji. Silnik gromadzi N żądań, uruchamia je jednocześnie i blokuje każdy slot do momentu zakończenia generowania najdłuższej odpowiedzi w grupie.
Użytkownik żądający podsumowania o długości 1200 tokenów blokuje cztery jednowierszowe odpowiedzi wewnątrz partii, ponieważ partia nie zwalnia żadnego slotu, dopóki najwolniejszy element nie zakończy pracy.
Pociąga to za sobą dwa rodzaje kosztów. Zakończone sekwencje nadal zajmują sloty, nie wykonując żadnych użytecznych obliczeń, co powoduje spadek efektywnej przepustowości przy zróżnicowanych długościach wyjściowych, a długości odpowiedzi w czatach są bardzo zmienne. Żądanie, które dotrze krok po utworzeniu partii, czeka na jej całkowite opróżnienie przed rozpoczęciem fazy prefill, co oznacza, że czas TTFT jest determinowany przez długość odpowiedzi innego użytkownika.
Continuous batching przyjmuje i kończy żądania przy każdym tokenie
Continuous batching planuje pracę na poziomie pojedynczego kroku dekodowania. Po każdym kroku harmonogram usuwa sekwencje, które właśnie wygenerowały token stopu, a następnie przyjmuje oczekujące żądania do zwolnionych miejsc. Odpowiedź, która kończy się w kroku 40, zwalnia swoje miejsce w kroku 40, a nie dopiero po zakończeniu całej partii.
Nie jest to rozwiązanie egzotyczne. llama-server dokumentuje -cb, --cont-batching jako „czy włączyć continuous batching (znany również jako dynamic batching) (domyślnie: włączony)”, a vLLM jest zbudowany w oparciu o tę koncepcję. Ollama również obsługuje żądania równoległe. Wartość domyślna ogranicza liczbę żądań do jednego, co jest powodem, dla którego wielu użytkowników błędnie zakłada, że ich sprzęt nie obsługuje współbieżności, podczas gdy w rzeczywistości wynika to z konfiguracji.
Publikowane wyniki dla continuous batching są zazwyczaj mierzone na kartach klasy datacenter, które dysponują zarówno nadmiarem mocy obliczeniowej, jak i dziesiątkami gigabajtów pamięci na cache. Charakterystyka tych wyników przekłada się na lokalne środowisko. Ich skala jednak nie, a sekcja dotycząca pamięci poniżej wyjaśnia dlaczego.
Prefill rywalizuje z dekodowaniem o te same zasoby obliczeniowe
Gdy nowe żądanie dociera w momencie, gdy cztery odpowiedzi są w trakcie strumieniowania, jego prompt musi zostać najpierw przetworzony w fazie prefill, która jest kosztowna obliczeniowo. Jeśli harmonogram zadań przydzieli tej fazie prefill osobny krok, czterej użytkownicy otrzymujący strumień danych nie otrzymają w tym czasie żadnego tokena. W przypadku długiego promptu jest to zauważalna pauza w każdym otwartym oknie. To właśnie to zacięcie użytkownicy nazywają „czkawką” serwera, występującą w momencie, gdy ktoś inny wysyła zapytanie.
Chunked prefill dzieli długi prompt na fragmenty i miesza każdy z nich w tym samym kroku, co trwające dekodowanie. Przewodnik optymalizacji vLLM wprost wskazuje na ten kompromis: mniejsze budżety fragmentów „zapewniają lepszy ITL, ponieważ mniej operacji prefill spowalnia dekodowanie”, podczas gdy wyższe wartości „zapewniają lepszy czas do pierwszego tokena (TTFT), ponieważ w jednej partii można przetworzyć więcej tokenów prefill”. Wybór polega na tym, czyje doświadczenie chronić: osoby czekającej na rozpoczęcie odpowiedzi, czy osób obserwujących strumieniowanie tekstu.
Długość promptu decyduje o skali tego problemu. Prompt o długości 6000 tokenów z odpowiedzią 200 tokenów to 6000 tokenów pracy w fazie prefill wobec 200 kroków dekodowania. Czat z wykorzystaniem Retrieval-augmented generation oraz długie prompty systemowe wprowadzają w ten zakres, przez co prefill przestaje być pomijalnym błędem zaokrąglenia i staje się głównym czynnikiem, na który czekają użytkownicy. Prefix caching pomaga, gdy długa część promptu się powtarza: vLLM udostępnia --enable-prefix-caching, co pozwala na ponowne wykorzystanie pamięci podręcznej dla wspólnego prefiksu promptu zamiast ponownego obliczania go dla każdego żądania.
Pamięć, która kończy się najszybciej, to KV cache
Każdy token w aktywnej konwersacji pozostawia wektor klucza i wektor wartości w każdej warstwie modelu. Jest to KV cache (pamięć podręczna kluczy/wartości), która pozwala procesowi dekodowania uniknąć ponownego obliczania całego promptu dla każdego nowego tokena. Jej rozmiar na token jest określony przez strukturę modelu: 2 (jeden klucz, jedna wartość) razy liczba warstw, razy liczba głowic kluczy/wartości, razy wymiar głowicy, razy liczba bajtów na wartość. Odczytaj te wartości z config.json modelu.
Wykonaj obliczenia raz, a limit przestanie być zagadką. Typowy model 8B z 36 warstwami, 8 głowicami kluczy/wartości i wymiarem głowicy 128, przechowujący cache w formacie 16-bit, kosztuje 2 36 8 128 2 bajtów na token. Daje to 147 456 bajtów, czyli około 144 KiB. Konwersacja o długości 8 192 tokenów wymaga zatem około 1,2 GB pamięci cache. Pięć takich konwersacji potrzebuje około 6 GB, poza wagami modelu, i to jest rzeczywista odpowiedź na pytanie, ilu użytkowników może obsłużyć serwer.
Współbieżność zwielokrotnia kontekst, co jasno komunikują narzędzia. FAQ Ollama podaje: "Równoległe przetwarzanie żądań dla danego modelu skutkuje zwiększeniem rozmiaru kontekstu o liczbę równoległych żądań. Na przykład kontekst 2K przy 4 równoległych żądaniach spowoduje zajęcie kontekstu 8K i dodatkową alokację pamięci". Wymagana pamięć RAM skaluje się wraz z OLLAMA_NUM_PARALLEL pomnożonym przez OLLAMA_CONTEXT_LENGTH. W llama-server kontekst, o który wnioskujesz za pomocą -c, jest dzielony pomiędzy -np slotów, więc samo zwiększenie liczby slotów zmniejsza ilość miejsca dostępnego dla każdego żądania. Odczytaj kontekst na slot z dziennika startowego, zamiast zakładać jego wartość.
vLLM dokonuje prealokacji. --gpu-memory-utilization (domyślnie 0.92) to "frakcja pamięci GPU, która ma zostać użyta przez executor modelu". Wszystko, co pozostanie po załadowaniu wag, staje się pulą stronicowanego KV cache, a gdy pula ta się wyczerpie, scheduler usuwa żądanie zamiast doprowadzić do błędu:
WARNING 05-09 00:49:33 scheduler.py:1057] Sequence group 0 is preempted by PreemptionMode.RECOMPUTE mode because there is not enough KV cache space.W silniku V1 vLLM domyślnym trybem wywłaszczania jest RECOMPUTE, więc usunięte żądanie odrzuca swój cache i ponownie wykonuje prefill po ponownym przyjęciu. Ta praca jest wykonywana dwukrotnie. Dokumentacja ostrzega, że "wywłaszczanie i ponowne obliczenia mogą negatywnie wpływać na opóźnienia end-to-end", a ten wpis w dzienniku jest najlepszym wyjaśnieniem, dlaczego jeden pechowy użytkownik czekał znacznie dłużej niż pozostali, podczas gdy średnie statystyki wyglądały poprawnie. Ustaw disable_log_stats=False, aby logować skumulowaną liczbę, lub odczytaj licznik wywłaszczeń z metryk Prometheus udostępnianych przez vLLM.
Zmiany przy 2, 5 i 20 jednoczesnych użytkownikach
Dwóch użytkowników. Obciążenie jest niemal niezauważalne na GPU z zapasem pamięci podręcznej, ponieważ drugi strumień dekodowania jest przetwarzany równolegle do pierwszego przy minimalnym narzucie czasowym. Na serwerze VPS wyposażonym wyłącznie w CPU i 4 do 8 GB pamięci RAM nie jest to darmowe: oba strumienie współdzielą tę samą pulę vCPU oraz przepustowość pamięci RAM. W rezultacie każdy użytkownik otrzymuje około połowę tokenów na sekundę, a zapotrzebowanie na pamięć podręczną podwaja się przy znacznie mniejszym budżecie zasobów.
Pięciu użytkowników. To moment, w którym ustawienia domyślne przestają wystarczać, a problem zaczyna przypominać kolejkę. Przy OLLAMA_NUM_PARALLEL ustawionym na 1, cztery osoby czekają na zakończenie generowania długiej odpowiedzi przez pierwszą osobę, po czym każda z nich otrzymuje standardową prędkość w momencie nadejścia swojej kolejki. Zwiększenie liczby równoległych zadań zmienia charakter problemu: pięć slotów po 8K kontekstu każdy wymaga znalezienia miejsca na 40K tokenów w pamięci podręcznej. Jeśli nie mieszczą się one w VRAM, silnik przenosi warstwy do pamięci RAM systemu, a jeśli nie mieszczą się w RAM, serwer zaczyna korzystać ze swapu, co powoduje drastyczny spadek liczby tokenów na sekundę.
Dwudziestu użytkowników. Dwudziestu ludzi korzystających z interfejsu czatu zazwyczaj nie generuje dwudziestu jednoczesnych żądań i jest to najważniejsza kwestia, którą należy zrozumieć przed zakupem sprzętu. Człowiek czyta odpowiedź i zastanawia się przez 20 do 60 sekund między kolejnymi turami, więc większość sesji pozostaje w stanie bezczynności. Dwudziestu agentów lub dwadzieścia zadań podsumowywania dokumentów to dwadzieścia rzeczywistych strumieni bez żadnych przerw na bezczynność. To wymaga zupełnie innej maszyny. Programista, który skierował agenta programistycznego na własny serwer Ollama, znajduje się bliżej drugiego przypadku niż pierwszego, ponieważ agent wysyła żądania tak długo, jak trwa zadanie, nie robiąc przerw na czytanie, które wykonuje człowiek.
Czy użytkownicy są aktywni jednocześnie, czy tylko zalogowani?
Przed dobraniem rozmiaru zasobów należy obliczyć liczbę żądań w toku. Arytmetyka jest prosta: liczba żądań w toku równa się liczbie użytkowników pomnożonej przez sekundy generowania na turę i podzielonej przez sekundy między turami.
- Najpierw należy zmierzyć własną prędkość pojedynczego strumienia, uwzględniając zarówno prefill, jak i dekodowanie. Nie należy przyjmować wartości z cudzej karty: zmierz liczbę tokenów na sekundę na własnej maszynie i użyj uzyskanego wyniku.
- Należy oszacować cykl pracy. Dwudziestu użytkowników czatu, 12 sekund generowania na turę, jedna tura co 90 sekund, daje 20 * 12 / 90, czyli około 2.7 żądania w toku.
- Należy ustawić liczbę slotów nieco powyżej tej wartości, a następnie sprawdzić ją pod kątem pamięci: liczba slotów pomnożona przez kontekst pojedynczego żądania musi mieścić się w dostępnych tokenach pamięci podręcznej.
- Kolejka powinna być krótka, aby przepełnienie było widoczne i powodowało szybki błąd.
Dostępne tokeny pamięci podręcznej to wolna pamięć po załadowaniu wag, podzielona przez koszt jednego tokena określony w poprzedniej sekcji. Karta 24 GB obsługująca model 8B w 16-bitach zużywa około 16 GB na wagi i ma około 6 GB użytecznej pamięci podręcznej przy domyślnym wykorzystaniu, co odpowiada mniej więcej pięciu konwersacjom 8K. Aby zmieścić więcej, należy skrócić kontekst na żądanie lub przechowywać pamięć podręczną w 8-bitach (llama-server wymaga --cache-type-k q8_0). Oba rozwiązania zwiększają współbieżność kosztem innych parametrów, dlatego przed zainwestowaniem w sprzęt warto zapoznać się z rzetelną analizą tego kompromisu: kiedy GPU VPS staje się bardziej opłacalny niż tokeny API.
Kiedy domyślne ustawienia Ollama przestają wystarczać
Zwiększ liczbę równoległych zadań poprzez jednostkę serwisową, ponieważ zmienne wyeksportowane w powłoce nie dotrą do demona zarządzanego przez systemd.
sudo systemctl edit ollama.service[Service]
Environment="OLLAMA_NUM_PARALLEL=4"
Environment="OLLAMA_CONTEXT_LENGTH=8192"
Environment="OLLAMA_MAX_QUEUE=64"sudo systemctl daemon-reload
sudo systemctl restart ollama
systemctl show ollama --property=Environment
ollama pssystemctl show powinno wyświetlić trzy zmienne, które właśnie ustawiono. Jeśli tak się nie stanie, plik typu drop-in nie został zapisany i żadne inne działania nie przyniosą efektu. ollama ps następnie wyświetla załadowany model o rozmiarze większym niż same wagi, ponieważ cztery sloty po 8,192 tokeny rezerwują dodatkowo 32,768 tokenów pamięci podręcznej. Kolumna PROCESSOR wskazująca na użycie CPU dla części modelu, mimo oczekiwania pełnego załadowania na GPU, oznacza, że przydzielono więcej pamięci podręcznej, niż karta posiadała w wolnych zasobach. Należy zmniejszyć jedną z tych dwóch wartości. Zmniejszenie kontekstu jest zazwyczaj bezpieczniejszym rozwiązaniem, jednak zbyt małe okno powoduje ciche ucinanie długich promptów zamiast zgłaszania błędu, dlatego warto dobrać num_ctx świadomie, zamiast zmniejszać tę wartość do momentu, aż model się zmieści.
Domyślna kolejka wymaga ponownego rozważenia. Ollama kolejkuje do OLLAMA_MAX_QUEUE żądań, a "wartość domyślna wynosi 512". Powyżej tej liczby serwer odpowiada "błędem 503 wskazującym na przeciążenie serwera". Kolejka o głębokości 512 na maszynie obsługującej cztery zadania jednocześnie to obietnica bez pokrycia, ponieważ klient na 300. pozycji przekroczy limit czasu na długo przed swoją koleją. Krótka kolejka zwraca błąd, który aplikacja może ponowić lub zaraportować, co jest lepszym rozwiązaniem niż nieskończenie trwający proces oczekiwania.
Przeprowadź testy w rzeczywistych warunkach. Wyślij dwa żądania w tym samym momencie z dwóch terminali i obserwuj oba. Jeśli drugie żądanie nie generuje odpowiedzi, dopóki pierwsze się nie zakończy, ustawienie równoległości nie weszło w życie.
Kiedy silnik serwujący zaczyna przynosić korzyści
vLLM uzasadnia dodatkową konfigurację, gdy dysponujesz GPU z zapasem mocy i obsługujesz więcej niż około cztery żądania jednocześnie. Harmonogram działa na poziomie tokenów, pamięć podręczna jest stronicowana, co pozwala na ponowne wykorzystanie wolnych fragmentów, a niewykorzystana pamięć VRAM jest przekształcana w współbieżność zamiast pozostawać bezczynną. Według stanu na sierpień 2026, udokumentowana instalacja i uruchomienie sprowadzają się do dwóch poleceń:
uv pip install vllm --torch-backend=auto
vllm serve Qwen/Qwen2.5-1.5B-Instructcurl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "Qwen/Qwen2.5-1.5B-Instruct",
"messages": [{"role": "user", "content": "Say hello."}]
}'Odpowiedź zawierająca tablicę choices oznacza, że serwer działa, a model został załadowany. Przy obciążeniu kluczowe są dwa parametry: --max-num-seqs, czyli „maksymalna liczba sekwencji przetwarzanych w pojedynczej iteracji” oraz --max-num-batched-tokens, czyli „maksymalna liczba tokenów, które mogą zostać przetworzone w pojedynczej iteracji”. Pierwszy z nich ogranicza współbieżność. Drugi stanowi budżet dla fragmentowanego wstępnego wypełniania (chunked prefill), opisanego wcześniej.
Przy mniej niż czterech żądaniach w toku lub na maszynie bez wspieranego GPU, vLLM generuje niepotrzebną złożoność, oferując niewielki zysk. Silnik wymaga karty klasy CUDA i rezerwuje większość pamięci przy starcie, co jest nieopłacalne w przypadku VPS z 4 do 8 GB RAM. W takiej sytuacji lepszym rozwiązaniem jest mniejszy model z krótszym kontekstem i własna kolejka żądań. różnice między Ollama a vLLM jako silnikami serwującymi szczegółowo omawia ten wybór, a uruchamianie Qwen 3 8B na VPS pokazuje wymagania średniej wielkości modelu, zanim jeszcze dodasz pierwszego użytkownika.
Kompromis pomijany w powszechnych opiniach
Continuous batching zwiększa całkowitą przepustowość i zazwyczaj poprawia medianę opóźnień, ponieważ żądanie w kolejce rozpoczyna się szybciej. Opóźnienia w ogonie rozkładu (tail latency) zachowują się inaczej, a o tym aspekcie wspomina się rzadko.
Każda dodatkowa sekwencja w kroku zwiększa nakład pracy, więc ITL rośnie dla wszystkich w miarę zapełniania się partii. Prefill nowego żądania zajmuje część kroku, który w przeciwnym razie byłby dostępny dla użytkowników korzystających ze streamingu. W warunkach presji na pamięć podręczną (cache pressure) scheduler dokonuje preempcji, co powoduje cofnięcie częściowo wygenerowanego żądania do początku jego fazy prefill.
Interfejs czatu odzwierciedla ogony rozkładu, a nie średnie. Stream, który zatrzymuje się na dwie sekundy w połowie zdania, jest odbierany jako uszkodzony, nawet jeśli całkowity czas zakończenia generowania jest zadowalający. Należy mierzyć p95 TTFT oraz p95 ITL pod spodziewanym obciążeniem i traktować średnią liczbę tokenów na sekundę jako wskaźnik wydajności, a nie opis doświadczenia użytkownika.
Z tego wynikają praktyczne ustawienia. Należy ograniczyć współbieżność nieco poniżej limitów pamięci, aby silnik nigdy nie musiał wykonywać preempcji. Krótka, przewidywalna kolejka jest lepsza niż duża partia powodująca thrashing, ponieważ użytkownik, który czeka cztery sekundy, a następnie otrzymuje płynny stream, jest bardziej zadowolony niż użytkownik, który zaczyna natychmiast, ale dwukrotnie doświadcza zacięć.
Co sprawdzić w przypadku niskiej wydajności
Każdy użytkownik działa poprawnie, ale czas oczekiwania jest długi. To problem kolejkowania, a nie szybkości. W pierwszej kolejności należy sprawdzić ustawienie równoległości. Model obsługuje żądania poprawnie, ale tylko jedno w danym momencie.
Błąd HTTP 503 z Ollama. Kolejka jest pełna. Serwer osiągnął rzeczywisty limit wydajności lub wartość OLLAMA_MAX_QUEUE została celowo ustawiona na niskim poziomie w celu odrzucania nadmiarowego ruchu, co jest pożądanym zachowaniem.
Liczba tokenów na sekundę drastycznie spada przy obciążeniu na serwerze CPU. Uruchom vmstat 1 w trakcie występowania problemu. Wartości inne niż zero w kolumnach si oraz so oznaczają, że maszyna korzysta ze swapu, więc wagi modelu są odczytywane z dysku przy każdym tokenie. Żadna zmiana konfiguracji tego nie naprawi. Należy zmniejszyć rozmiar modelu lub liczbę slotów.
Jeden na dziesięciu użytkowników czeka znacznie dłużej niż pozostali. Przeszukaj dziennik vLLM pod kątem preempted. Najczęstszą przyczyną jest preemption i ponowne obliczenia, co oznacza, że pamięć podręczna jest przeciążona w stosunku do dozwolonej długości kontekstu.
TTFT jest wysokie nawet przy bezczynnym serwerze. To problem fazy prefill, a nie współbieżności. Długie prompty wymagają czasu przed wygenerowaniem pierwszego tokena, dlatego przed analizą sprzętu należy sprawdzić rozmiar promptu oraz prefix caching. Jeśli długi czas oczekiwania dotyczy tylko pierwszej osoby po okresie bezczynności, a kolejni użytkownicy działają poprawnie, przyczyną nie jest prefill, lecz zwalnianie modelu przez Ollama i ponowne wczytywanie wag z dysku. Warto wykluczyć ten scenariusz poprzez utrzymywanie modelu w pamięci między żądaniami.
FAQ
Dlaczego mój samodzielnie hostowany LLM zwalnia, gdy korzysta z niego druga osoba?
Najczęściej nie zwalnia, lecz ustawia się w kolejce. Ollama domyślnie ustawia OLLAMA_NUM_PARALLEL na 1, więc drugie żądanie czeka, aż pierwsze wyemituje swój ostatni token. Rozróżnij te dwa przypadki, mierząc czas strumienia jednego użytkownika podczas oczekiwania drugiego: jeśli liczba tokenów na sekundę jest normalna po rozpoczęciu generowania, oznacza to kolejkę, a zwiększenie liczby równoległych zadań rozwiąże problem. Jeśli oba strumienie działają z połową prędkości, faktycznie współdzielisz przepustowość pamięci, co jest ograniczeniem sprzętowym.
Ilu jednoczesnych użytkowników może obsłużyć jedna mała karta GPU?
Licz pamięć, nie użytkowników. Najpierw wagi, potem pamięć podręczna KV, która kosztuje 2 razy liczba warstw razy liczba głowic klucza/wartości razy wymiar głowicy razy bajty, na token, na aktywną konwersację. Typowy model 8B z 36 warstwami, 8 głowicami klucza/wartości i wymiarem głowicy 128 kosztuje około 144 KiB na token w 16-bitach, więc konwersacja o długości 8,192 tokenów wymaga około 1.2 GB. Karta 24 GB mieszcząca ten model w 16-bitach ma około 6 GB wolnego miejsca na pamięć podręczną, co wystarcza na około pięć konwersacji przy pełnym kontekście lub więcej, jeśli skrócisz kontekst.
Czy ciągłe przetwarzanie wsadowe (continuous batching) spowalnia odpowiedź dla każdego użytkownika?
Mediana opóźnień zazwyczaj się poprawia, ponieważ żądania przestają czekać na zakończenie całej partii. Opóźnienia w ogonie (tail latency) stają się gorsze. Każda dodatkowa sekwencja dodaje pracę do każdego kroku dekodowania, prefill nowego żądania zabiera część kroku użytkownikom strumieniującym, a przerwane żądanie musi być przetwarzane ponownie. Mierz opóźnienie między tokenami na poziomie p95, a nie średnią, ponieważ w oknie czatu pauzy są zauważalne w sposób, który średnie ukrywają.
Czy powinienem zwiększyć OLLAMA_NUM_PARALLEL czy przejść na vLLM?
Najpierw zwiększ liczbę równoległych zadań. Jest to darmowe, wymaga jednego pliku konfiguracyjnego i rozwiązuje typowy przypadek, w którym cztery osoby czekają w kolejce za jedną długą odpowiedzią. Ograniczeniem jest pamięć: równoległe żądania mnożą kontekst, który musisz przechowywać, więc obserwuj, czy warstwy nie są przenoszone do CPU. Przejdź na vLLM, gdy masz GPU z zapasem VRAM i więcej niż około cztery żądania faktycznie w toku, ponieważ to moment, w którym stronicowana pamięć podręczna i szeregowanie na poziomie tokena dają więcej korzyści niż kosztów.
Czy więcej rdzeni CPU naprawi wolny serwer LLM?
Nie w przypadku części, którą użytkownicy zauważają najbardziej. Dekodowanie odczytuje cały model z pamięci dla każdego tokena, więc jest ograniczone przepustowością pamięci RAM, a dodatkowe rdzenie przestają pomagać po nasyceniu przepustowości. Prefill skaluje się wraz z rdzeniami, więc ich większa liczba skraca czas do pierwszego tokena przy długich promptach. Na VPS z 4 do 8 GB pamięci RAM ograniczeniem jest zazwyczaj pojemność pamięci, a skutecznym rozwiązaniem jest mniejszy model lub krótszy kontekst, a nie większa liczba vCPU.