Simon KimSo far, we've optimized prefill and decoding separately. Could looking at the nature of the work in each stage help us understand the assumptions that existing infrastructure was designed around?
Jeongkyu ShinYes. Let's follow a single request from the moment it arrives until the answer is complete. We can use a typical autoregressive transformer with a KV cache as our reference. I don't know Astra's undisclosed architecture either, but I can explain how existing models generate text.
Suppose a user asks, “Read this report and summarize the key risks.” The service assembles an input that includes not just the question, but also the system instructions, any necessary conversation history, and the report. It converts that into token IDs and assigns the work to a server. If the server is busy, there will be a wait as well. We don't call this entire preparation process prefill.
Prefill comes next. As the model processes the input tokens, it computes internal representations and stores keys and values in the KV cache for use during subsequent generation. The cache stores intermediate results so the model doesn't have to repeat all the preceding computation from scratch every time. Since the input is already available, computation at many positions can be handled together. But there are still constraints that prevent each position from seeing future tokens, and computations across layers still have to follow a particular order.
The output from the final position in prefill is used to calculate the probabilities for the first new token, and one is selected according to the generation rules. If prefill is the stage where the model reads the report, this is where the first piece of the answer is determined. It's not as though the model has already composed a complete answer in its head and is simply releasing it a little at a time.
Simon KimSo decoding isn't the process of revealing a predetermined answer on the screen. It's recalculating each time what to write next, following the answer generated so far. Does it also recompute the entire report each time?
Jeongkyu ShinIt reuses the keys and values from the input it has already processed. The token just selected is fed into the model, which uses the previously stored information to calculate the probabilities for the next token. The new token's key and value are added to the cache too. The token selected this way then becomes the input for the next computation.
That repeated process is decoding. In standard generation, the preceding token has to be determined before the next one can be computed, so there is a sequential dependency within a single answer. But that doesn't mean the entire GPU processes only one token at a time. Requests from multiple users can be batched and run together, and there are optimizations such as speculative decoding, which proposes multiple candidate tokens and then verifies them.
Generation ends when an end token appears or a condition is met, such as a configured length limit or the user stopping the response. The service converts the generated tokens into text and delivers it, then frees up any per-request memory that is no longer needed. With streaming, it starts sending portions of the content while generation is still underway; otherwise, it collects the results and sends them together. The chunks of text you see on the screen may not correspond exactly to the tokens the model processes internally.
Simon KimSo when a user feels that “the response takes too long to start” versus “the response comes out slowly,” those are different problems. What timing metrics should we look at for each?
Jeongkyu ShinThe time it takes to produce the first token is called TTFT, or Time to First Token. Depending on where measurement starts and ends, it can include request transmission and queuing, input preparation, prefill, and selection of the first token. So you shouldn't immediately conclude that prefill is slow just because TTFT is long. For models that generate reasoning tokens first, there can also be a gap between the first internal token and the first answer the user sees.
Once generation starts, we look at the interval between tokens, or ITL, and the number of tokens generated per second. ITL stands for Inter-Token Latency. For a request that supplies a long report and asks for a short answer, input processing may account for a large share of the work. If a short question prompts a long piece of writing, the cumulative decoding time can be substantial. The total time the user waits includes both stages, along with queuing, transmission, and so on.
Simon KimThen when you ask follow-up questions about the same long document, does prefill have to start from scratch every time? I'm also curious about where caching changes the cost structure.
Jeongkyu ShinIf the server is implemented to reuse the KV cache for an identical input prefix, it can reduce computation for the portion it has already processed. This is called prefix caching. Newly added input still has to be processed, and conditions have to be met: the cache must still be available, and the model and input must match. Just because earlier content is visible in the conversation doesn't necessarily mean the server still holds that cache.
Keeping a cache around longer reduces recomputation, but it occupies memory. You have to weigh the benefits of reuse against the space needed to handle more requests simultaneously. Ultimately, you have to manage the whole process together—from receiving requests through prefill, decoding, cache management, and completion—to understand the actual costs and response speed.
Simon KimWhy do input processing and token generation have different bottlenecks even when they run on the same GPU? Should we think of it in terms of differences in the amount of computation and data movement?
Jeongkyu ShinA bottleneck is the part that most constrains the overall speed. To use a kitchen analogy, you might have plenty of ingredients, but cooking takes a long time. Or the cook might be fast, but the ingredients arrive late.
Prefill processes a lot of input together, so compute capacity often becomes important. Decoding generates tokens sequentially, so the speed at which the necessary data can be fetched from memory often becomes important. We call the former compute-bound and the latter a memory-bandwidth bottleneck. But it depends on factors such as batch size and model design, so it isn't an absolute distinction.
Simon KimSo you're saying that running loops internally could also shift where the bottleneck is.
Jeongkyu ShinYes, because it changes which computations are repeated and which data is read again. We have to start by revisiting what the existing optimizations were designed to speed up.