dual-radeon-vllm · write-ups

Two consumer Radeon RX 7900 XT,
measured under ROCm and vLLM

Long-form write-ups from a repository of measurements taken on 2× RX 7900 XT (gfx1100, RDNA3) under ROCm and vLLM. Each article draws its figures from the same committed data the repository verifies, and says on the page which of them can be recomputed and which cannot.

2× RX 7900 XT · gfx1100 · TP=2 VFIO guest · no P2P · PCIe 3.0 cross-die repository

1What this machine does

The charts plot one line per model per machine. Each line reflects that model's fastest measured configuration rather than a common baseline. Four of the six Radeon lines come from a single campaign on one stack and are directly comparable; the other two are drawn from a later stack, because on those two models the campaign is no longer the best this machine can do. The A100 carries five of the same models from its own single-afternoon campaign. Every line marks which stack it runs. The Radeon candidates are the ledger's TP=2 series, 2026-07-25 to 2026-08-29; the September ladders on this pair, which run past 32 000, are Figures 3 and 4's subject.

Figure 1 · decode rate at batch 1, the best configuration per model among the ledger's candidates recomputable from repo
compare

Figure 1. Decode rate at batch 1 against context length, log axis. The default view is every model on 2× RX 7900 XT at tensor parallelism 2; the switch above the chart turns the question round, to one model on every machine, and colour follows the question: the model in one view, the card in the other.

Eleven machines
The pair; one of its cards; an A100 80G, an L4 24G and a Tesla T4 16G from Colab; and six rented on 2026-09-03 — an H100, an H200, a B300, an RTX PRO 6000, and two H100s and two PRO 6000s at TP=2. The checkpoint is what they share — vLLM 0.28.0 on every CUDA card against 0.23.1 on the pair. The split and the stack are not shared, and every line's tooltip carries its own.
The T4
It is here at all only because of vllm#39018: without it the engine dies at kernel load asking 98 304 bytes of shared memory against Turing's 65 536. That patch changes prefill's tile size and nothing else, so this figure reads its line on the same terms as every other. Figure 2 does not, and says so.
Lines that stop
Four do, and each stop is arithmetic rather than an abandoned run: a card that cannot hold the KV for the next rung cannot measure it, and the tooltip says how many tokens its KV held. The shortest is two rungs — gemma-4-31B does not start on the L4 at all with CUDA graphs on, and --enforce-eager buys it 2.51 GiB and a 2 020-token pool.
The switches
Each is named for what the engine resolved rather than for the button. Speculation shows what happens to that line — same day, same kernel — and one arm says DFlash because Muse-Glimmer's drafter is block-diffusion and not MTP. It does not go one way: on these cards both arms start ahead and one stays ahead to 32 K, while on the A100 all four lose at every depth. Qwen3.8's second switch, ROCM_ATTN, adds the backend the runtime picks for itself; the flag this repository pins instead buys decode and sells prefill by more (Figure 2).
Deliberately absent
Two series this repository measured. The L4's Qwen3.8-27B is a different checkpoint from the one drawn — symmetric compressed-tensors against AWQ, 1.27–3.24× apart on decode on gfx1100 — and a legend keyed on the model name would put both behind one entry. Qwen3-8B on one 7900 XT under vLLM 0.27 decodes identically to the line already there, to 0.21 % at every rung, and swapping it in would turn Figure 2's second-card comparison into a stack comparison.
Figure 2 · prefill throughput, every model with a ladder, eleven machines recomputable from repo
compare

Figure 2. The other half of the same question: Figure 1 is how fast each machine generates, this is how fast it reads the prompt. Six models, fifty-two lines, eleven machines — every chart-grade prefill ladder this repository has between 500 and 32 000 tokens, recorded beside decode since July and never drawn until now. Colour and stroke are Figure 1's.

Why two terms
Prefill is not one number. Split the time into a fixed cost, a linear term and attention's quadratic one — T(S) = a + b·S + c·S² — and b is compute while c is how badly attention scales. Hover a point for that line's fitted pair.
Two lines re-measured
2026-09-02. The pair's gemma-4-31B and Qwen3.8-27B lines were first measured on 2026-08-29, when one card had trained at PCIe 3.0 x8 — visible in the host's boot journal, never in the guest's sysfs. A reboot restored it and both arms ran again, same container, same serve arguments: b 868.7 → 722.6 on the 31B and 846.2 → 758.5 on Qwen3.8. Those are the lines drawn.
Card against card
On the dense 12B the A100 is 3.3× on b and 6.7× on c against one 7900 XT, so the gap in attention is twice the gap in compute. The L4 is slower on b (0.90×) and 3.0× better on c, which is why its curve starts below the Radeon's and ends above it.
The second card
On all three models with both topologies it buys 1.2–1.5× on b and 1.9–2.2× on c. Attention parallelises better than the GEMMs because it needs no communication.
The flag is a trade
Pinning Qwen3.8 to TRITON_ATTN, which this repository called free until 2026-08-30, is worth 1.15× of decode at 32 K and costs 1.45× of prefill there.
One line is different
The T4 runs gemma-4-12B only with vllm#39018, which halves TILE_PREFILL on the head_size 512 layers — the quadratic term this figure decomposes. Its c is this card with a different kernel, so it is off until asked for and left out of the ratios above. Its b is not determined by its own ladder either: the 32 K rung came from a second VM, the two agree to 4.61 %, and swapping which supplies it moves b by 29.9 %. This curve is quadratic-dominated where no other here is — 224 s against 97 s at 32 K — so the linear term absorbs the uncertainty, as a does everywhere.
Dropped rungs
Only rungs whose two rounds agree are drawn, and the one each line drops is not one story: six drop their shallowest — on the CUDA cards a cold first request, not discarded until 2026-08-30 — and three Radeon lines drop the 4 000 rung, round 1 slower each time, which this repository does not explain. The MoE on one 7900 XT stops at 12 K for a reason it does know: 16.96 GiB of weights leave 0.93 GiB of KV, 13 149 tokens.

Until 2026-09-03, every ladder on this machine stopped at 32 000 tokens. That appeared to be a hardware boundary, but the limit belonged to the campaign: each arm set max_model_len to 33 000 while its KV pool held several times that. The two figures below ask those same questions out to 128 000 — the pair serves as the subject on a ladder cut that day, with the machines rented the same day one click away as the background.

Figure 3 · decode rate to 128 000 tokens, the pair and the rented machines recomputable from repo
compare

Figure 3. Decode rate at batch 1 against context length, 500 to 128 000, log axis. The default view is 2× RX 7900 XT at tensor parallelism 2 on the 2026-09-03 ladder — the first rows on this machine past 32 000 — with Qwen3.8-27B drawn as Figure 1 draws it, on the fastest configuration it has been measured in rather than on that campaign's stack. The switch above the chart puts one model on every machine rented that day instead. The controls are Figure 1's.

The whole ladder
Sixteen rungs, 500 upward, rather than only the five past 32 000: a curve's shape at depth is only readable against where it started. Where a rung is on both this ladder and Figure 1's, the two are different sittings on different prompt cuts, and the tooltip carries each point's own date.
Qwen3.8-27B is Figure 1's line, measured again
Both figures now draw this checkpoint on TRITON_ATTN and vLLM 0.27.1, so the two are the same configuration in two sittings a week apart, on the narrowed and the restored PCIe link. They agree to 1.78 % at worst across the eleven rungs they share — which is what a cross-sitting control looks like when nothing but the sitting differs. The 0.23.1 ladder that used to be drawn here is not. It is not withdrawn: it is in campaign-2026-09-03, and the difference between it and these lines is the subject of what a retention percentage is made of. It is off this figure because those weights decode 12.33 tok/s at 500 there and 50.52 here, and a front page that draws the first is telling a reader something about this machine that stopped being true. What that ladder does past 32 000 on 0.27 is now measured, to 128 000 and on both attention backends: the depth cost falls from 0.350 to 0.233 microseconds per context token across the version, and to 0.111 with --attention-backend TRITON_ATTN, which is the line drawn here.
Deliberately absent
Qwen3-8B, on every machine. Its own config caps context at 40 960, so its ladder is Figure 1's eleven rungs everywhere it was run — a ceiling the model carries, not the campaign.
Figure 4 · prefill throughput to 128 000 tokens, the pair and the rented machines recomputable from repo
compare

Figure 4. Prefill throughput against prompt length to 128 000, the same lines as Figure 3. T(S) = a + b·S + c·S² is fitted over the whole ladder here, and the note under the chart says on each of the pair's lines where c·S² overtakes b·S and what share of the deepest rung it is.

Two fits for one arm
Figure 2 fits each line on the rungs it draws, to 32 000; this figure fits the sixteen. For the same arm those are two different pairs of coefficients, and each tooltip says which it is carrying.
Dropped rungs
Only rungs whose two rounds agree are drawn, as in Figure 2, and each line's tooltip names the rung it dropped and how far apart the rounds were.

2Articles

Thirteen write-ups, newest first by the date on the card — the last day evidence for that article was taken here, reported upstream or re-read. Under each title is the line that article used to lead with. Beneath it is what the piece establishes, in the words the synthesis article uses to classify it, so the two cannot describe a finding differently.

3What each of these is dated to

The date on an article marks when its evidence was taken, not when it was written. Three of the thirteen are dated to the last week of July, eight to the last week of August and two to September. The machine itself ran on 25, 26 and 28 July and on 1 August, then not again for three weeks — the second cluster is largely re-measurement of the first under conditions the first got wrong. A bar spanning two dates is an article whose evidence comes from two separate sittings, not one that ran throughout.

Figure 5 · the date each article carries the dates the articles carry
Figure 5. Positioned by date, not by publication order. Two of the thirteen are not measurements of this machine: one records the date a defect was reported upstream, and the synthesis re-read sixteen upstream threads rather than running anything. Both are marked as such rather than given a measurement date they do not have.

4How to read these

Each article is a dated record, not standing advice. Two of the largest findings in this repository have already moved since they were measured. An Ubuntu kernel regression was fixed in 7.0.0-30, and an initial loader speed-up of 3.9× to 5.6× was withdrawn when page cache was properly controlled. Every article therefore closes with a section listing what has changed since, appended to rather than rewritten.

Figures carry a provenance marker. recomputable from repo means verify_doc_figures.py checks that value against its source file and fails non-zero if the two disagree — the same check that guards the repository's own documents. raw output not committed means the number is transcribed from a run whose raw output is not in the repository, and cannot be recomputed.

The absolute rates are a floor rather than a benchmark of the hardware. The test environment is a VFIO guest with no peer-to-peer between the cards and a cross-die PCIe 3.0 path, chosen deliberately as the least favourable topology. The shapes transfer; the levels are the worst case.

Source, raw data and the runners that produced the data: github.com/cadamcat/dual-radeon-vllm. Upstream reports and patches are linked from each article.