Model nie wykonuje jednej pracy, tylko dwie — i są to prace tak różne, że gdyby nie dzieliły wag, nikt nie nazwałby ich tym samym słowem. Jedna jest równoległa, obciąża rdzenie i przelatuje tysiące tokenów na sekundę. Druga jest ściśle sekwencyjna, obciąża pamięć i wyrabia kilkadziesiąt. Pierwsza odpowiada za to, ile czekasz na początek odpowiedzi. Druga — za to, jak szybko odpowiedź się potem rozwija.
To rozróżnienie jest najważniejszą rzeczą w całej tej serii. Bez niego cennik dostawców API wygląda na kaprys, limity na złośliwość, a wahania czasu odpowiedzi na awarię. Z nim wszystko trzy układa się w jeden mechanizm.
I co najlepsze, tę różnicę widać gołym okiem na własnym laptopie. Wystarczy przestać patrzeć na odpowiedź, a zacząć patrzeć na zegar.
Co się dzieje, zanim padnie pierwsze słowo
Wysyłasz do modelu prompt. Powiedzmy dwie strony tekstu, jakieś tysiąc tokenów. Model ma je streścić.
Zanim wyjdzie pierwsze słowo streszczenia, musi się wydarzyć coś, o czym łatwo zapomnieć: model musi ten tysiąc tokenów przeczytać. Przepuścić przez wszystkie warstwy, policzyć dla każdego tokena jego klucz i wartość, zapisać je do pamięci podręcznej opisanej w poprzednim artykule. Dopiero gdy cała historia jest przetworzona, może zacząć przewidywać, co powinno paść dalej.
Ta faza nazywa się prefill — wypełnienie. Dosłownie chodzi o wypełnienie pamięci podręcznej.
Potem zaczyna się decode. Model generuje jeden token. Dopisuje go do historii, liczy dla niego klucz i wartość, dokłada do pamięci podręcznej. Patrzy na całość i generuje następny token. I tak, po jednym, aż do końca odpowiedzi.
Dwie fazy. Te same wagi, ten sam graf operacji, ta sama matematyka. A zachowują się jak dwa różne programy.
Dlaczego czytanie jest szybkie
Kluczowa własność prefillu: wszystkie tokeny promptu można przetworzyć naraz.
Brzmi banalnie, ale nie jest. Skąd bierze się ta możliwość? Stąd, że cały prompt jest już znany. Model nie musi czekać, aż dowie się, co będzie trzysta pięćdziesiątym tokenem, żeby przetworzyć token dziesiąty. Wszystko leży na stole od początku.
A skoro tak, to zamiast przepuszczać przez warstwę jeden wektor, przepuszczasz przez nią macierz o tysiącu wierszy. Ta sama operacja, ten sam odczyt wag — tylko obsługujący tysiąc tokenów zamiast jednego.
I tu wracamy do wąskiego gardła z artykułu pierwszego. Mówiłem tam, że żeby wygenerować token, trzeba przeczytać cały model z pamięci. To nadal prawda. Ale w prefillu czytasz model raz na tysiąc tokenów, a nie raz na token. Koszt odczytu rozkłada się na tysiąc porcji pracy.
Wąskie gardło przestaje być pamięcią. Zaczyna być liczeniem — bo nagle rdzenie mają co robić. Karta graficzna, ta z tysiącami głupich rdzeni, wreszcie pracuje tak, jak została zaprojektowana: wszystkie na raz, nad jednym wielkim mnożeniem macierzy.
Stąd liczby, które przy pierwszym kontakcie wydają się pomyłką. Model, który generuje trzydzieści tokenów na sekundę, potrafi w prefillu przetwarzać dwa tysiące. Ten sam model, ten sam sprzęt, ta sama sekunda.
Jest jeden haczyk, o którym pisałeś już przy długim kontekście: w prefillu każdy token musi spojrzeć na każdy inny, więc koszt rośnie z kwadratem długości promptu. Przy tysiącu tokenów to nieistotne. Przy stu tysiącach — bardzo istotne. Prefill jest szybki, ale nie jest darmowy i nie skaluje się liniowo.
Dlaczego pisanie jest wolne
Teraz decode, i tu wszystko się odwraca.
Model wygenerował token. Żeby wygenerować następny, musi znać poprzedni — bo to on właśnie zmienił kontekst. Nie da się policzyć tokena numer siedem, zanim nie zna się szóstego. Nie ma tu żadnego marginesu na równoległość. To jest ograniczenie logiczne, nie techniczne: żaden sprzęt świata tego nie obejdzie.
Więc każdy krok decode wygląda tak: weź jeden wektor, przepuść go przez wszystkie warstwy, przeczytaj po drodze wszystkie wagi modelu, wypluj jeden token.
Przeczytaj cztery gigabajty. Żeby wyprodukować jedno słowo.
To jest ta sama arytmetyka, którą liczyliśmy w artykule pierwszym — przepustowość pamięci podzielona przez rozmiar modelu — i teraz widać, że dotyczyła ona wyłącznie tej drugiej fazy. Sufit prędkości z tamtego kalkulatora to sufit decode. Prefill w ogóle nie podlega tej regule.
Marnotrawstwo, które wyjaśnia resztę serii
Zatrzymajmy się tu, bo to najważniejszy akapit tego artykułu.
W trakcie decode karta graficzna prawie nic nie robi. Owszem, przepycha przez siebie cztery gigabajty danych, i to zajmuje jej cały czas. Ale samych obliczeń wykonuje śmiesznie mało — jedno mnożenie wektora przez macierz na warstwę. Rdzenie, wszystkie tysiące, stoją bezczynnie i czekają, aż dane do nich dojadą.
Typowe wykorzystanie mocy obliczeniowej w fazie decode to kilka procent. Reszta się marnuje.
Weź teraz kartę serwerową za trzydzieści tysięcy dolarów, na której w ten sposób działa jedna rozmowa jednego użytkownika, i zadaj sobie pytanie, które zadał sobie każdy dostawca API: skoro rdzenie i tak stoją, co by się stało, gdyby przepuścić przez nie dwieście rozmów naraz?
Odpowiedź brzmi: prawie nic. Wagi odczytane z pamięci są te same dla wszystkich. Jeden odczyt obsłuży dwieście rozmów zamiast jednej, bo to nie odczyt był wąskim gardłem dla rdzeni, tylko rdzenie czekały na odczyt. Dwieście razy więcej pracy w mniej więcej tym samym czasie.
To jedno zdanie wyjaśnia cały tor o API, który zaczyna się w następnym artykule. Kolejki, limity, zmienny czas odpowiedzi, różnica w cenie między wejściem a wyjściem — wszystko to są konsekwencje faktu, że decode marnuje kartę, a prefill nie.
Jak to wygląda na zegarze
Załóżmy typowy laptop, model siedmiomiliardowy w czterech bitach. Prefill przerabia około sześciuset tokenów na sekundę, decode wyrabia około osiemnastu. Różnica trzydziestokrotna i będzie ona rosła wraz z mocą sprzętu, bo mocniejsza karta poprawia prefill znacznie bardziej niż decode.
Krótkie pytanie, długa odpowiedź. Prompt pięćdziesiąt tokenów, odpowiedź osiemset. Prefill trwa niecałą dziesiątą sekundy — pierwsze słowo pojawia się natychmiast. Potem czterdzieści pięć sekund mozolnego wypluwania. Całość jest zdominowana przez decode w ponad dziewięćdziesięciu dziewięciu procentach.
Długi dokument, krótkie streszczenie. Prompt osiem tysięcy tokenów, odpowiedź sto. Prefill trwa trzynaście sekund — tyle patrzysz w pusty ekran, zastanawiając się, czy to się zawiesiło. Potem sześć sekund generowania. Tym razem prefill zjada dwie trzecie czasu.
To są dwa zupełnie różne problemy wydajnościowe i nie ma jednej optymalizacji, która pomaga na oba. Skracanie promptu nie przyspieszy generowania długiej odpowiedzi. Szybsza karta nie skróci znacząco czekania na osiemsetny token. Zanim zaczniesz cokolwiek optymalizować, musisz wiedzieć, w której z dwóch faz tkwi twój czas.
Cztery rzeczy, które teraz mają sens
Dlaczego wejście jest tańsze niż wyjście. Zajrzyj do cennika dowolnego dostawcy: tokeny wejściowe kosztują zwykle trzy do pięciu razy mniej niż wyjściowe. To nie jest polityka cenowa ani zachęta do pisania krótkich promptów. To jest odzwierciedlenie kosztu. Token przetworzony w prefillu zajmuje kartę ułamek tego, co token wygenerowany w decode. Cennik jest mapą fizyki.
Dlaczego istnieje streaming. Nikt nie wymyślił wyświetlania odpowiedzi po kawałku dlatego, że tak ładnie wygląda. Wymyślono to, bo decode trwa i bez strumienia użytkownik siedziałby czterdzieści sekund przed pustym ekranem. Strumień nie przyspiesza niczego — maskuje wolność, której nie da się usunąć. To plaster na ograniczenie fizyczne.
Dlaczego dwie metryki, nie jedna. Czas do pierwszego tokena i tokeny na sekundę mierzą dwie różne fazy i mogą iść w przeciwnych kierunkach. Dostawca, który agresywnie łączy żądania w partie, poprawia przepustowość i pogarsza czas do pierwszego tokena. Który z nich jest „szybszy”, zależy wyłącznie od tego, co robisz. Porównywanie dostawców po jednej liczbie jest bez sensu.
Dlaczego wznowiona rozmowa startuje wolniej. W poprzednim artykule wspomniałem o tym na marginesie, teraz można to nazwać: wznowienie sesji to ponowny prefill całej historii. Pamięć podręczna, którą model zbudował w poprzedniej rundzie, została zwolniona. Musi powstać od nowa. Im dłuższa rozmowa, tym dłuższe to wznowienie — i tym większa pokusa, żeby ten wynik gdzieś zachować. Ta pokusa ma nazwę, jest jedną z najważniejszych optymalizacji w całym torze API i poświęcę jej osobny artykuł.
Zobacz to sam
Widget pod tym akapitem nie generuje tekstu. Robi coś prostszego i bardziej wymownego: rozkłada czas odpowiedzi na dwie fazy i pokazuje je jako pasek.
Ustaw krótki prompt i długą odpowiedź. Prefill jest niewidoczną kreską na początku, cała reszta paska to decode. Teraz odwróć: prompt na osiem tysięcy tokenów, odpowiedź na sto. Pasek przewraca się na drugą stronę.
Zwróć uwagę na licznik wykorzystania karty pod paskiem. W prefillu siedzi wysoko, w decode spada do kilku procent — i to jest wizualizacja tego marnotrawstwa, z którego wyrośnie cały następny tor.
Na koniec przełącz sprzęt z laptopa na kartę serwerową. Wszystko przyspiesza — i prefill, i decode. Spójrz jednak na wykorzystanie karty w fazie decode: spada. Z trzech procent na laptopie do półtora na sprzęcie za trzydzieści tysięcy dolarów. Im droższa karta, tym większa jej część stoi bezczynnie, kiedy model pisze. To jest najkrótsze możliwe podsumowanie tego artykułu i zarazem cały powód, dla którego istnieje następny tor.
Co dalej
Zamykamy tor lokalny. Przez cztery artykuły oglądaliśmy mechanizm z bliska, bo na własnym urządzeniu wszystko jest widoczne: rozmiar wag, przepustowość pamięci, błąd kwantyzacji, rosnąca historia rozmowy, dwie fazy generowania.
Od następnego artykułu ten sam mechanizm przestaje być widoczny. Trafia na cudzy serwer, gdzie twoja prośba jest jedną z dwustu, a jedyne, co możesz zmierzyć, to czas i rachunek.
Ale już wiesz, co tam się dzieje. I wiesz, dlaczego oni to robią akurat tak — bo widziałeś, jak bardzo decode marnuje kartę.
Prefill kontra decode webflux · maszyneria modelu
Ścieżka przez widget
Domyślnie profil zrównoważony. Przesuń prompt na 50 i odpowiedź na 800 — pasek jest niemal w całości turkusowy, prefill to nieczytelna kreska. Teraz prompt 8 tys., odpowiedź 100: pasek przewraca się na drugą stronę i komunikat pod spodem zmienia zalecenie na odwrotne.
Dolny pasek pokazuje, co by było bez pamięci podręcznej z poprzedniego artykułu. Przy domyślnych ustawieniach na laptopie to trzydzieści cztery minuty zamiast pięćdziesięciu sekund.





















