Strona główna / Artykuły / Optymalizacja inferencji LLM: prefill, dekodowanie oraz enterprise LLMOps

Optymalizacja inferencji LLM: prefill, dekodowanie oraz enterprise LLMOps

Wąskie gardła związane z wypełnianiem uprzednim a dekodowaniem, ciągłe grupowanie danych, FlashAttention, kwantyzacja, PagedAttention, dekodowanie spekulatywne, wypełnianie uprzednie w fragmentach oraz serwisowanie rozproszone.

1809 słów

Projektowanie wydajnych i ekonomicznych rozwiązań do obsługi modeli LLM oznacza projektowanie z uwzględnieniem rzeczywistych ograniczeń sprzętowych – a nie tylko opakowywanie API w nadziei, że karty GPU będą ciągle zajęte.

Początkowo budżety przedsiębiorstw na generatywną sztuczną inteligencję były skupione na szkoleniu i dopracowywaniu modeli. Gdy aplikacje trafiają poza laboratoria, zmienia się charakter kosztów: ciągła inferencja zależna od kart GPU oraz nagłe wzrosty opóźnień, których nie da się przewidzieć.

Eskalacja wymusza kompromisy między aspektami fizycznymi a finansowymi. Szybkie i oszczędne systemy patrzą poza powierzchowne rozwiązania i analizują, jak faktycznie odbywa się inferencja na chipach.

1. Dwie fazy, dwa wąskie gardła

Każda prośba o inferencję dzieli się na krok wypełniania danych i dekodowania. Każda z tych faz napotyka inne ograniczenia sprzętowe.

Wypełnianie danych (zwykle zależne od mocy obliczeniowej)

Prefill przetwarza wszystkie tokeny promptu jednocześnie i równolegle buduje aktywacje typu klucz-wartość. Dość długie prompty powodują, że duże mnożenia macierzy przeciążają zasoby obliczeniowe, więc ta faza jest ograniczona mocą obliczeniową. Krótkie prompty lub małe partie mogą sprawić odwrotną sytuację: niewystarczająca ilość operacji arytmetycznych uniemożliwia ukrycie ruchu pamięci, więc prefill staje się ograniczony zasobami pamięciowymi. Czas przetwarzania przez prefill to pierwsze oczekiwanie użytkownika.

Dekodowanie (zwykle ograniczone przepustowością pamięci)

Gdy prompt zostanie przyjęty, tokeny przychodzą jeden po drugim. Na każdym kroku pełne wagi modelu oraz rosnąca historia klucz-wartość są przenoszone z pamięci o wysokiej przepustowości (HBM) do SRAM GPU. Przy małych rozmiarach partii ten transfer powtarza się tak często, że GPU czeka na przepustowość pamięci zamiast na moc obliczeniową.

Jedna niuans decyduje o reszcie projektu. Zwiększanie wielkości partii umożliwia rozłożenie kosztu pobierania tych samych wag na wiele sekwencji, a proces dekodowania wraca w kierunku ograniczeń obliczeniowych. Właśnie to zjawisko jest powodem istnienia ciągłego grupowania danych: pozwala ono na przeprowadzanie dekodowania w warunkach, w których jednostki arytmetyczne pozostają zajęte.

Równanie opóźnienia

Całkowite opóźnienie żądania dzieli się na część związana z wypełnieniem danych i dekodowaniem:

T_total  =  TTFT  +  (N_tokens − 1) × TPOT
  • TTFT (czas do pierwszego tokena) obejmuje pełne wypełnienie danych wejściowych oraz pierwszy token wyniku – to właśnie on określa początkową szybkość reakcji systemu.
  • TPOT (czas na jeden token wyniku), czyli opóźnienie między tokenami, to stały koszt każdego kolejnego kroku dekodowania.
  • N_tokens to długość końcowego wyniku.

Czynnik (N − 1) jest celowy: pierwszy token znajduje się już wewnątrz TTFT, więc tylko pozostałe są mnożone przez TPOT. Mieszanie surowych obliczeń prefill z TTFT to powszechny błąd. TTFT jest przeznaczony dla użytkowników i musi zawierać ten pierwszy krok dekodowania.

2. Wejście danych przed tym, jak GPU zobaczą ruch

Pikowy ruch wyczerpie zasoby GPU, chyba że wejście danych zostanie najpierw oczyszczone, ocenione i przefiltrowane. Typowy schemat: brama API → wielowarstwowa pamięć semantyczna → w przypadku braku danych – inteligentny router, który ocenia stopień złożoności i kieruje zadanie do kolejki modelu standardowego lub modelu zaawansowanego.

Główne elementy:

  • Wielowarstwowe przechowywanie semantyczne: klucz-wartość z dokładnym dopasowaniem oraz podobieństwo wektorowe przy cos θ ≥ τ. Powtórzenia nigdy nie wpływają na model; koszt i opóźnienie spadają jednocześnie.
  • Inteligentne kierowanie modelem: lekki klasyfikator wysyła dane do klasyfikacji i formatowania do małych modeli, a modele zaawansowane zachowuje na trudne zadania analityczne lub użycie narzędzi wieloetapowych.
  • 3. Silnik wykonywania: ciągłe grupowanie zadań i przepływ pamięci

    Po kierowaniu zadaniami przechodzą one do silnika wykonywania. Statyczne grupowanie marnuje zasoby – cała grupa czeka na najdłuższą sekwencję. Systemy produkcyjne wykorzystują ciągłe grupowanie zadań (planowanie na poziomie iteracji), dzięki czemu sloty pozostają wypełnione, a ukończone sekwencje opuszczają system natychmiast po wysłaniu sygnału zakończenia.

    Ciągłe grupowanie zadań dotyczy nie tylko wydajności. Pomaga ono również przejść z trybu ograniczonego pamięcią do trybu ograniczonego mocą obliczeniową, dzięki czemu GPU może wykonywać operacje arytmetyczne zamiast czekać na dane z HBM.

    4. Bezpośrednie radzenie sobie z wąskimi gardłami na poszczególnych etapach

    Kasowanie w pamięci, routowanie i grupowanie danych zarządzają ruchem. Kolejne mechanizmy modyfikują same fazy przetwarzania.

    Prefill: FlashAttention

    Koszt prefill rośnie wraz z kwadratem długości promptu, gdy klasyczna metoda uwagi generuje pełną macierz ocen o rozmiarze N×N w pamięci HBM. FlashAttention to rdzeń oparty na technologii tile’ów, świadomy ograniczeń wejścia/wyjścia, który oblicza dokładną wartość uwagi poprzez przepływ danych przez pamięć SRAM znajdującą się na chipie, redukując tym samym ruch w pamięci HBM bez konieczności użycia aproksymacji matematycznych. Dzięki temu uzyskuje się dokładny wynik przy krótszym czasie przetwarzania długich promptów. Jest to standard w większości systemów serwowania i łączy się z prefilliem w formie fragmentów oraz mechanizmem dezagregacji.

    Kwantyzacja

    Rozkodowanie wymaga przesłania wag z pamięci HBM do SRAM. Kwantyzacja zmniejsza rozmiar wag – z FP16 do FP8, INT8 lub 4-bit (AWQ, GPTQ) – dzięki czemu każdy token zajmuje mniej bajtów. To zwiększa efektywną przepustowość i pozwala przechowywać więcej sekwencji w pamięci. Należy oczekiwać niewielkiej straty dokładności, którą trzeba sprawdzić poprzez własne testy.

    Paged KV cache (PagedAttention)

    Klucze/wartości z historii powodują rozszerzanie się danych token po tokenie, co dominuje w zużyciu pamięci w czasie rzeczywistym. Fragmentacja alokacji pamięci oraz konieczność jej nadmiernego przydzielania pogłębiają ten problem. PagedAttention przechowuje dane KV w blokach o stałej wielkości, podobnie jak pamięć wirtualna systemu operacyjnego, co eliminuje fragmentację i umożliwia większe rozmiary partii na tym samym sprzęcie. W połączeniu z ciągłym grupowaniem danych zapewnia wysoką współdzielenie zasobów.

    Decodowanie spekulatywne

    Sekwencyjne decodowanie ograniczone zasobami pamięci powoduje, że procesor pozostaje nieaktywny między pobieraniem danych. Decodowanie spekulatywne wykorzystuje ten czas: mały mechanizm proponujący tworzy krótki ciąg tokenów, a główna sieć sprawdza ten ciąg w jednej operacji. Zaakceptowane tokeny wymagają mniej więcej jednego kroku obliczeniowego typowego dla dużych modeli.

    Zachowana zostaje poprawność: dopasowane prefiksy pozostają nienaruszone; pierwsza niezgodność powoduje przerwanie procesu, a celowy model ponownie próbuje wygenerować wynik na podstawie skorygowanej rozkładu. Przy dekodowaniu chciwym tokeny dokładnie odpowiadają modelowi docelowemu; przy próbkowaniu rozkłady są ze sobą statystycznie zgodne. Przyspieszenie zależy od wskaźnika akceptacji wersji roboczych, więc jakość tych wersji ma kluczowe znaczenie.

    5. Planowanie obu faz: uzupełnianie w partiach i dezagregacja

    Proces uzupełniania i dekodowania wymagają różnych zasobów obliczeniowych. Używanie jednego puli GPU sprawia, że długotrwałe uzupełnianie danych monopolizuje zasoby obliczeniowe, co powoduje wzrost opóźnień pomiędzy tokenami we wszystkich pozostałych zapytaniach. Proponuje się dwa komplementarne rozwiązania.

    Uzupełnianie w partiach

    Zamiast jednego dużego operacji wypełniania z góry, podziel prompt na segmenty i łącz je („na grzbiecie”) z krokami dekodowania w tej samej partii (Sarathi / Sarathi-Serve). Dzięki temu zmniejszają się gwałtowne skoki TTFT u sąsiednich zadań, a także mieszają się prace ograniczone mocą obliczeniową z tymi ograniczonymi pamięcią. Ciągłe grupowanie wybiera które żądania mają wspólny krok; podział na fragmenty wypełniania z góry decyduje jak duże operacje wypełniania z góry mogą być wykonywane bez utrudniania dekodowania.

    Służba w formie rozproszonych elementów

    Podział na fragmenty wypełniania z góry zmniejsza zakłócenia; rozproszenie eliminuje je poprzez umieszczenie poszczególnych faz na różnym sprzęcie (DistServe, Splitwise). Wypełnianie z góry odbywa się na pulach optymalizowanych pod kątem obliczeń, a dekodowanie na pulach optymalizowanych pod kątem przepustowości, przy czym dane KV są przesyłane przez sieć łączącą. Każda faza skaluje się na układach scalonych dostosowanych do jej wąskiego gardła.

    Kompromisy są rzeczywistością: transfer danych KV staje się nowym wąskim gardłem, a wagi muszą znajdować się w obu zbiorkach. W dużych skali okazuje się, że dostosowanie rozwiązań do konkretnej fazy przewyższa te dodatkowe koszty. Mniejsze strony często poprzestają jedynie na przedwczesnym wypełnianiu danych w pakietach.

    6. Wpływ systemowy i kompromisy

    W praktyce produkcyjnej osiągi są kompromisem wobec ryzyka operacyjnego i kosztów infrastruktury. Wskaźniki zmieniają się w zależności od sprzętu, modelu, ruchu i konfiguracji — warto sprawdzić własne obciążenie, zamiast kopiować ogólne procenty.

    Multowarstwowe buforowanie semantyczne — dokładne przechowywanie danych KV w połączeniu z porównywaniem wektorowym. W takim przypadku nie ma potrzeby korzystania z modelu. Koszt: wyszukiwanie wektorowe (od kilku do kilkudziesięciu milisekund) oraz nieaktualne lub częściowo trafne odpowiedzi, jeśli wartość τ jest zbyt niska.

    Inteligentne routowanie — klasyfikator złożoności kieruje proste zadania do prostszych modeli. Dzięki temu obniża się średni koszt przetwarzania tokenów; błędne routowanie negatywnie wpływa na jakość.

    Ciągłe grupowanie — łączenie na poziomie iteracji w trakcie generowania. Wyższa wydajność i przeróbka przy jednoczesnym wykonywaniu zadań; poszczególne żądania mogą być umieszczane w kolejce podczas kompletowania. Statyczne grupowanie nadal nadaje się do zadań o dużej przerobce wykonywanych offline.

    Kwantyzacja — wagi o niższej precyzji zmniejszają ilość transferów pomiędzy HBM a SRAM. Więcej zadań może być wykonywanych jednocześnie na jednej karcie GPU; konieczna jest weryfikacja dokładności; wsparcie dla różnych jąder jest zróżnicowane.

    KV strukturyzowane na strony — stałe bloki eliminują fragmentację. Większe rozmiary grup; wymagane jest zarządzanie blokami oraz kompatybilne jądro do obsługi uwagi.

    Dekodowanie spekulatywne — mały proponent wysuwa sugestię; duży model sprawdza ją wspólnie. Wielokrotne tokeny na jeden duży krok; drugi model oraz wrażliwość na wskaźnik akceptacji.

    Gdy potrzebujesz dokładnych danych dotyczących opóźnienia lub kosztów, czerp je z powtarzalnych testów wydajności celowej pracy (opublikowanych badań dotyczących obsługi lub wewnętrznych testów obciążenia), a nie z ustalonych procentowo danych marketingowych.

    7. Zamknięcie pętli

    Przejście od prototypu do produkcji oznacza projektowanie z myślą o sprzęcie. Oddziel proces wypełniania danych przed dekodowaniem, obserwuj, jak wielkość partii wpływa na sposób dekodowania w zależności od tego, czy ograniczeniem jest pamięć, czy obliczenia, przechowuj w cache przewidywalne zapytania, kieruj proste zadania do małych modeli oraz pakuj tokeny przy użyciu ciągłego grupowania. Następnie zwalcz ograniczenia związane z dekodowaniem za pomocą kwantyzacji, struktury KV opartej na stronach oraz dekodowania spekulatywnego, a także łagodź interferencje fazowe poprzez podział na fragmenty i rozdzielenie elementów. Wszystkie te metody pozwalają radzić sobie z szczytowymi obciążeniami bez wyczerpywania GPU.

    Lista kontrolna planowania przepustowości

    Gdy wartość TTFT rośnie, należy sprawdzić rozkłady długości zapytań, wskaźnik trafień do pamięci semantycznej, dostępność technologii FlashAttention oraz to, czy długie zapytania nadal monopolizują GPU jako jedna duża operacja wcześniejszego wypełniania pamięci. Gdy wartość TPOT rośnie przy wysokiej jednoczesności przetwarzania, należy sprawdzić faktyczną wielkość partii danych, czas pozostawania danych w pamięci KV, poziom kwantyzacji oraz to, czy proces dekodowania wrócił do trybu ograniczonego zasobami pamięci.

    Praktyczna cotygodniowa ocena polega na zadaniu trzech pytań: Czy przechowujemy w pamięci przewidywalne zapytania? Czy odsyłamy proste zadania z modeli najnowszej generacji? Czy pakujemy operacje dekodowania tak, aby GPU mogły wykonywać obliczenia zamiast czekać na dostęp do pamięci HBM? Pozytywne odpowiedzi zazwyczaj są lepsze niż zakup kolejnego stojaka serwerowego, zanim zostanie dostosowana cała infrastruktura obsługi.

    Rozbicie na mniejsze części powinno znaleźć się w planie działań dopiero wtedy, gdy już sprawdzą się metody wcześniejszego wypełniania danych w pakietach oraz ciągłe grupowanie zadań. Ruch między elementami sieci i powtarzające się wagi stanowią rzeczywiste koszty; trzeba je uwzględnić, gdy zbiory danych specyficzne dla danej fazy wyraźnie przewyższają efektywność wspólnej floty modeli w danym zestawieniu.

    Zapisz wartość rozmiaru partii danych, przy której dekodowanie staje się ograniczone zasobami obliczeniowymi na twoim sprzęcie. Ta jedna liczba lepiej określa cele związane z ciągłym grupowaniem zadań niż jakikolwiek ogólny wykres z bloga.

    Uwagi operatora

    Kasety semantyczne wymagają regularnej oceny czasu życia danych oraz wartości τ; zbyt niska wartość τ prowadzi do nieprecyzyjnych odpowiedzi. Routerom potrzebne są oznaczone dane dotyczące złożoności, w przeciwnym razie wysyłają one trudne polecenia do małych modeli. Ciągłe grupowanie zadań wymaga określenia standardów opóźnienia w kolejce, aby „wyższa przepustowość” nie maskowała problemów z interaktywnością. Dekodowanie spekulatywne wymaga paneli kontrolnych dla wersji roboczych – jeśli akceptacja tych wersji spada, płacisz za drugi model bez żadnej poprawy szybkości.

    Traktuj artykuły naukowe jako dowody na działanie mechanizmów, a nie jako obietnice procentowych oszczędności. Przed twierdzeniem o możliwościach finansowania przeprowadź testy na swoich kartach graficznych, różnych długościach zapytań oraz w warunkach równoczesnego wykonywania zadań.

    Referencje

    Główne artykuły opisujące te mechanizmy (numery należą do autorów, a nie do Ciebie):

    1. Orca – rozproszony serwer typu transformer — Yu i wsp., OSDI 2022 (USENIX).
    2. PagedAttention – zarządzanie pamięcią — Kwon i wsp., SOSP 2023 (arXiv:2309.06180).
    3. GPTQ – kwantyzacja po szkoleniu — Frantar i wsp., 2022 (arXiv:2210.17323).
    4. AWQ – kwantyzacja wag z uwzględnieniem aktywacji — Lin i wsp., 2023 (arXiv:2306.00978).
    5. FlashAttention – dokładna obsługa operacji wejścia/wyjścia — Dao i wsp., NeurIPS 2022 (arXiv:2205.14135).
    6. Speculative decoding – szybka inferyencja modeli transformer — Leviathan, Kalman, Matias, ICML 2023 (arXiv:2211.17192).
  • Sarathi / Sarathi-Serve z fragmentowanymi wcześniejszymi uzupełnieniami przy użyciu dekodowania typu piggybacking — Agrawal i wsp., 2023–2024 (arXiv:2308.16369).
  • Rozdzielenie procesów wcześniejszego uzupełniania danych i dekodowania w DistServe — Zhong i wsp., OSDI 2024 (USENIX).
  • Dzielenie fazy w Splitwise dla inferencji generatywnej — Patel i wsp., ISCA 2024 (arXiv:2311.18677).