Skip to content
Blog

Supplement · One node, eight H200s

Growing one conversation to a million tokens

A supplementary report on cached, growing conversations: per-turn latency, retrieval checks and the difference from a fresh million-token prompt.

IndieRouter Team · · updated · 13 min read

The fresh-document tests left a practical question unanswered: what happens when a conversation grows instead of arriving all at once? We took GLM-5.3 through ten turns toward a million tokens. Its longest first-token wait was 88.4 seconds, against roughly ten and a half minutes for a fresh million-token prompt. We then added a second conversation and watched much of that improvement disappear for the user waiting behind another long prefill.

GLM-5.3 · FP8 weights · FP8 KV vLLM 0.31.0, TP8 + DCP8 prefix caching on

Why a ramp

A fresh million-token document is one valid workload. A growing coding session or document conversation is another. To test the latter, we ran a ramp: one conversation that starts at 32K tokens and grows over ten turns to 1,000,000 (32K, 64K, 128K, then +131K per turn up to 917K, then 1M). Each turn re-sends the whole history plus new text and asks for 128 output tokens. A needle is planted in the first turn and asked for on every turn.

With prefix caching enabled, the engine can reuse compatible KV blocks from earlier requests instead of prefilling the entire history again. We expected that reuse to reduce the work needed for later turns, and checked the engine counters alongside latency. vLLM exposes prefix-cache queries and hits on its metrics endpoint, which we sampled after each turn to get a hit rate [S35].

One conversation, ten turns

Time to first token for each turn (bars, left axis) and new tokens per turn (orange line, right axis). The red line is the one-shot 1M-token prompt with nothing cached.
Time to first token for each turn (bars, left axis) and new tokens per turn (orange line, right axis). The red line is the one-shot 1M-token prompt with nothing cached. Open full-size chart
TurnTotal contextNew tokensTime to first tokenPrefill on new tokensCache hit rateNeedle
T132,74732,72319.8 s1,651 tok/s0%found
T265,52132,73620.1 s1,629 tok/s49%found
T3131,05765,50538.6 s1,695 tok/s50%found
T4262,129131,04178.2 s1,676 tok/s50%found
T5393,200131,04079.1 s1,658 tok/s67%found
T6524,273131,04280.8 s1,622 tok/s75%found
T7655,345131,04182.7 s1,584 tok/s80%found
T8786,417131,04185.5 s1,532 tok/s83%found
T9917,488131,04088.4 s1,483 tok/s86%found
T10999,98582,46657.6 s1,432 tok/s92%found

Each turn cost about what its new tokens cost: 131K new tokens took 79-88 s whether the conversation was at 262K or at 917K. The last turn added 82K tokens to reach 1,000,000 and took 57.6 s. Summed over all ten turns, time to first token was 631 s, about the same as the one-shot prompt (628 s), consistent with reuse of the earlier prompt blocks and a similar effective prefill rate. Summed first-token waits are a latency measure, not a direct measurement of total compute. What changes is the experience: the longest single wait was 88 s instead of 10.5 minutes.

Decode stayed at 34.7 tokens/s for this single conversation on every turn, matching the 28.9 ms/token of the one-shot run. At 1M the conversation used 17.7% of the KV cache, consistent with the engine's estimate that about five such conversations fit.

Prefix-cache hit rate per turn (engine metrics) against the share of each prompt that had been sent before. The two curves closely track each other, consistent with substantial reuse of earlier prompt blocks. Ticks mark the needle being found.
Prefix-cache hit rate per turn (engine metrics) against the share of each prompt that had been sent before. The two curves closely track each other, consistent with substantial reuse of earlier prompt blocks. Ticks mark the needle being found. Open full-size chart

The hit rate tracks the share of each prompt that was already sent, 0% on turn 1 and 92% on turn 10. Those counters support the reuse explanation; they do not trace every kernel or prove that no work was repeated. The needle from turn 1 was found on all ten turns. As with every needle test, that shows retrieval at depth, not reasoning over the whole conversation.

Two conversations at once

Then we ran the same ramp with two conversations side by side. This exposed interference that the single-conversation run could not show.

Two ramps in parallel. On every turn one session waits roughly twice as long as a session alone (black ticks), consistent with the two long prefills competing for the same serving resources.
Two ramps in parallel. On every turn one session waits roughly twice as long as a session alone (black ticks), consistent with the two long prefills competing for the same serving resources. Open full-size chart

The latency traces were asymmetric: one session waited much longer than the other. We did not collect a scheduler trace, so the measurements establish interference rather than an exact ordering of every prefill chunk. On every turn one session waited about as long as a single session, and the other about twice as long. At the last turn that was 114.8 s and 62.7 s. Decode suffered too. The session served first decoded at only 1.5 tokens/s (median), while the other conversation was still prefilling. That timing is consistent with decode/prefill interference. The second session, decoding alone afterwards, ran at 23.0 tokens/s. Mixing long prefills into decode steps is a known source of latency spikes in vLLM. A GLM-5.3-Flash user on GB200 traced every slow decode step to a step that also carried prefill tokens vLLM issue #56975. With two sessions the KV cache peaked at 35.4%.

A second model: MiniMax-M3

Publication boundary: the MiniMax FP8-KV server below passed only two of three fresh-prompt retrieval checks at 1M. Its ramp results remain provisional, even though the planted fact was recovered on every ramp turn. We include them to explain the experiment, not as a validated model ranking.

MXFP8 weights (W8A16 on Hopper) · FP8 KV vLLM nightly 1a001d58, TP8 + EP8 prefix caching on

MiniMax-M3 ran the same ramp on the same 8 GPUs (Part 5 has its launch story). Every turn was faster than GLM-5.3's, and the shape is the same: each turn costs roughly what its new tokens cost.

Time to first token per turn, one conversation, GLM-5.3 vs MiniMax-M3. Same prompts, same turn sizes.
Time to first token per turn, one conversation, GLM-5.3 vs MiniMax-M3. Same prompts, same turn sizes. Open full-size chart

MiniMax-M3's longest turn took 28.2 s, and the last turn, which reached 1,000,000 tokens, took 20.1 s, against 157.7 s for the same million tokens sent in one shot. Summed over all ten turns, time to first token was 169 s. Prefill on the new tokens slowed as the context grew, from 9,587 tokens/s on turn 1 to 4,098 on turn 10, so for this model attention cost does grow with depth. Decode held at 56-62 tokens/s on every turn, the hit rate reached 92% on turn 10, and the needle from turn 1 was found on all ten turns. That does not erase its earlier miss on one of three fresh-prompt retrieval checks. This FP8-KV run remains provisional under the campaign's publication gate; Part 5 records the failed gate.

With two conversations at once, MiniMax-M3 showed the same pattern as GLM-5.3. One session waited for the other's prefill (last turn: 36.6 s and 21.1 s), and the session decoding during the other's prefill slowed to a median of 6.5 tokens/s, against 35.6 for the one decoding on its own. The needle was found on every turn in both sessions.

Every model on the same ramp (updated 11 Oct)

By the end of the campaign we had completed the ramp for the models below: one conversation, the same ten turns from 32K to 1,000,000 tokens, prefix caching on, 128 output tokens per turn. The 4-GPU models ran on one quad; the giants on all eight GPUs.

ModelGPUsLongest turnLast turn (1M)Sum of all turnsDecode tok/sCache hit, turn 10Turn-1 needle foundRun
GLM-5.3888.4 s57.6 s631 s35-3592%10 / 10glm53__tp8_dcp8__1024k__vllm__1010-053939
MiniMax-M3828.2 s20.1 s169 s56-6292%10 / 10minimax_m3__fx_tep8_recipe__1024k__vllm__1010-081133
DeepSeek-V4-Pro-0813844.9 s34.5 s260 s43-4692%10 / 10dsv4_pro__tep8__1024k__vllm__1010-142942
GLM-5.3-Flash410.4 s7.4 s63 s144-14792%1 / 10glm53_flash__tp4_a__1024k__vllm__1011-020514
Qwen3.8-Flash-Next418.1 s14.2 s97 s125-14791%9 / 10qwen38_flash_next__tep4_b__1024k__vllm__1011-000332
DeepSeek-V4-Flash-0731 FP8 (AtlasCloud)426.3 s19.4 s132 s129-148n/a (SGLang)6 / 10dsv4_flash_0731_fp8__tp4_a__1024k__sglang__1011-012409
DeepSeek-V4-Flash FP8 (sgl-project)426.6 s19.7 s134 s129-148n/a (SGLang)8 / 10dsv4_flash_fp8__tp4_b__1024k__sglang__1011-003258
Qwen3.6-35B-A3B447.1 s33.8 s217 s58-18392%8 / 10qwen36_35b__dp4_b__1024k__vllm__1011-010614

The 4-GPU models are much faster per turn than the giants, and the linear-attention GLM-5.3-Flash is the fastest of all. But the needle column matters as much as the time columns. GLM-5.3, MiniMax-M3 and DeepSeek-V4-Pro found the turn-1 needle on every turn. GLM-5.3-Flash found it on only one of ten turns of this ramp, even though it found the needle in all one-shot 1M tests in Part 4, and the two DeepSeek-V4-Flash builds and Qwen3.6 missed it on some turns. A needle is a retrieval check, not a reasoning test, so a miss here is a warning sign for multi-turn recall at depth rather than a full verdict. SGLang doesn't expose the prefix-cache hit counters we sampled from vLLM, so the DeepSeek-V4-Flash hit rates are not available.

How it scales with users

The ramp ran with one conversation, then with two at the same time on the same server. That is only two concurrency points. It tests interference between long sessions, not a complete capacity curve. By our derivation from the logged KV pool, GLM-5.3 fits 5 1M-token sequences (sequence cap 8) and MiniMax-M3 fits 3 (cap 16). Two sessions are below both estimated capacity limits. KV exhaustion therefore does not explain these particular waits, though the shared scheduler and GPU work can still delay either session.

The mechanism, according to the vLLM docs: decode steps are scheduled first, and prompt tokens fill the rest of each step's token budget in chunks (vLLM docs: Optimization and Tuning). When one session sends a 131K-token turn, the other session's decode shares every step with those prefill chunks. The numbers are our measurement.

One conversation (solid) vs two at once (dashed = the slower session, dotted = the faster). Left: time to first token per turn. Middle: decode speed per user. Right: all output tokens of the turn divided by the wall time until the last session finished. Source: results/ramp1m.csv, our measurements.
One conversation (solid) vs two at once (dashed = the slower session, dotted = the faster). Left: time to first token per turn. Middle: decode speed per user. Right: all output tokens of the turn divided by the wall time until the last session finished. Source: results/ramp1m.csv, our measurements. Open full-size chart
ModelSessionsFirst token (faster / slower)Decode tok/s per user (faster / slower)Output tok/s, whole turnPeak KV use
GLM-5.3157.6 s34.62.117.7%
GLM-5.3262.7 s / 114.8 s22.6 / 2.22.135.4%
MiniMax-M3120.1 s56.25.726.7%
MiniMax-M3221.1 s / 36.6 s33.7 / 6.66.353.4%

Turn 10 (1,000,000-token context), 128 output tokens per session. Our measurement. A second session at 1M left GLM throughput roughly unchanged and increased MiniMax throughput only modestly. The small aggregate gain did not prevent much longer first-token waits for one session or much slower decode while another long prefill was in flight.

What this does and doesn't show

Two models in one setup each: GLM-5.3 in TP8 + DCP8 across both quads, MiniMax-M3 in TP8 + EP8 on a nightly build, 128 output tokens per turn. The ramp is synthetic: random text plus a planted needle, not a real document. A different layout could change the prefill rate a lot (see the PCP note in the giants post). Separating prefill and decode onto different GPU groups is one proposed way to reduce this interference, with transfer and resource-allocation costs of its own [S33]. Runs of it started on Sunday on a short grid (status below).

Status of the other ramps (updated Sun 11 Oct)

  • Added to the table above: DeepSeek-V4-Pro (TEP8), GLM-5.3-Flash, both DeepSeek-V4-Flash builds, Qwen3.6-35B-A3B and Qwen3.8-Flash-Next, one conversation each.
  • Qwen3.8-27B ramped on four replicas to turn 9 (917K tokens) before the run's time cap stopped it; it found the turn-1 needle on turns 1-7 and missed it on turns 8-9. It's not in the table above because it never reached the 1M turn. Part 2 has its numbers.
  • Dropped for time when the deadline moved forward: a two-conversation Qwen3.8-27B ramp, a ramp of the DeepSeek-V4-Flash-0731 FP4 release on SGLang's W4A8 path, and a repeat of the MiniMax-M3 ramp. None of them ran, so there are no numbers for them.
  • Prefill/decode split (prefill on one quad, decode on the other): the GLM-5.3-Flash split finished (run glm53_flash__pd_1p1d__256k__vllm__1011-063501, provisional because no 256K needle check exists for that model) and so did the Qwen3.8-27B split (run qwen38_27b__pd_1p1d__256k__vllm__1011-073653). Both used the short parallelism grid, not this ramp, so they can't answer the two-session interference question directly. Details in Part 8.
Why the MiniMax-M3 ramp carries a warning

Every result we publish has to come from a server that passed a needle check at the same context length with a score of at least 0.67, with no failed requests. The MiniMax-M3 server that ran this ramp (minimax_m3__fx_tep8_recipe__1024k__vllm__1010-081133, FP8 KV) found the one-shot 1M needle 2 times out of 3. That's 0.667, just under the bar, so by our own rule its rows, including all of its ramp rows, are unpublishable. We keep the MiniMax-M3 curves above because the ramp checked the needle on every turn itself and found it on all ten, but read them as provisional. A later MiniMax-M3 server with BF16 KV found the 1M needle 3/3 and passes the bar (Part 13), but it didn't run the ramp, and the planned repeat was dropped for time.

What we learned
  • Prefix caching turns a 10-minute wait into ten waits of 20-90 seconds. The sum of first-token waits was similar in this GLM comparison, but the longest individual wait was much shorter.
  • Reuse changes the cost of the next turn, with GLM effective prefill rates around 1.4-1.7K new tokens/s across the ramp. The cache counters and timings support substantial reuse; they are not proof of zero recomputation.
  • Concurrent long sessions queue behind each other's prefill, and the session already decoding slows to a crawl (1.5 tok/s on GLM-5.3) while another one prefills. MiniMax-M3 does the same.
  • Built up turn by turn, recall held. Both models found the turn-1 needle on all ten turns, including MiniMax-M3, which missed one in three on one-shot 1M prompts.

We left this experiment with two separate requirements for a useful long-context service: keep prior work reusable, and protect a user who is already decoding from another user’s large prefill. The ramp exposed both questions; it did not settle the best scheduler or layout. Part 8 follows the short-prompt split experiments, and the afterword adds the missing GLM replica baseline. Neither is a completed million-token split test.

Sources

Bracketed ids [S..] refer to the campaign bibliography (docs/SOURCES.md). Numbers not attributed to a source are our own measurements on this node.

  1. [S35] vLLM project, Production metrics (docs)
  2. [S33] PKU / UCSD (Zhong et al.), DistServe: Disaggregating Prefill and Decoding (arXiv 2401.09670, OSDI 2024)
  3. NolenLiang, https://github.com/vllm-project/vllm/issues/56975 (vLLM issue #56975)
  4. https://docs.vllm.ai/en/latest/configuration/optimization.html (vLLM docs: Optimization and Tuning)

← Back to Blog