Repository navigation
RX 7900 XTX (gfx1100): 925 -> 2,503 tok/s prompt read (+171%) - the method, and the 26% cliff below full lend coverage #1389
Description
gfx1100 (RX 7900 XTX): 100% lend coverage is most of the prefill story, and STRATA_RESIDENT_HEADROOM_GIB is undocumented
Measurements of the prompt path on a single RX 7900 XTX, with what helped, and the dead ends so nobody repeats
them. No request attached: one observation you may want to act on, plus the method behind a ~2.5K tok/s prompt read.
Everything below was measured on v0.1.40.2, which needs no local patch on either ROCm — #1180 covers
both of the clang-22 problems I had worked around on 0.1.40 (NW_LB=1 for clang >= 22, and the missing
__syncthreads() after put(raw, (s + 1) & 1) in native_w11_kernel, now at moe_fused_iq.cu:828 with the
comment "the writes of every thread are in LDS before the next stage reads them (#1180)").
Setup
Ubuntu 24.04 in a privileged LXC (8 vCPU, 50 GB RAM, no swap), RX 7900 XTX 24 GB (gfx1100). Engine v0.1.40.2
built with -DSTRATA_ENABLE_HIP=ON -DSTRATA_PREFILL_MMQ=ON -DCMAKE_HIP_ARCHITECTURES=gfx1100. Model: Swift 1.5
IQ3_XXS (native pack, 48 layers, IQ2_XXS/IQ2_XS/IQ3_XXS/IQ3_S/IQ2_S gate-up + Q2_0/IQ4_NL down).
Engine arguments: --expert-cache auto --prefill auto:32768 --spec 4 --mtp <rt> --max-context 524288 --kv int8 --kv-resident 65536 --resident-experts --vision --vram-reserve-mib 700, with
STRATA_RESIDENT_HEADROOM_GIB set, and STRATA_HIPBLASLT_TUNING pointing at the table that matches the
installed hipBLASLt.
ROCm versions: the method is not version-specific, the numbers are
I report both because they differ, and because I expect most readers are on the wheels setup.py pins:
| ROCm | hipBLASLt / table | 25K | 74K | 150K | |
|---|---|---|---|---|---|
setup.py default |
7.10.0a20251120 | 1.2.0 / gfx1100-hipblaslt-100200.txt |
2,409 | 2,158 | 1,914 |
| what I run now | 10.2.0a20261003 (TheRock nightly) | 1.5.0 / gfx1100-hipblaslt-100500.txt |
2,744 | 2,559 | 2,339 |
- The subject of this issue — the lend coverage,
wait copy, the file reads — is engine-side and reproduces
on both. The good/bad tables below are from the 10.2 run, but the 7.10 run swings the same way (I saw 1,518 /
1,646 / 1,668 tok/s at 74K on restarts that landed short). - The 7.10 → 10.2 move needed
STRATA_RESIDENT_HEADROOM_GIB8 → 7, because the newer runtime keeps roughly
1 GiB more host RAM. That is exactly the kind of thing item 3 below is about. - One caveat if you are on 7.10: the 10.2 long-prompt numbers above came with the matching
100500table. On
any ROCm the engine refuses a table whose version does not match the library and prefills on plain hipBLAS,
which cost about 45% here (926 vs 1,687 tok/s on a 131K prompt) — keep the pairing.
For reference, the same box/model with the 0.1.40 defaults (no --resident-experts, no fused gate/up,
--prefill auto, pageable expert cache) read a 97,488-token prompt at 925 tok/s.
The method, by measured contribution
| lever | effect |
|---|---|
fused int8 WMMA gate/up + down (STRATA_PF_FUSED=1, STRATA_PF_GEMM=1, STRATA_PF_SWITCH_MIN_T=4096) |
925 → 1,092 tok/s; removes the 16-expert host grouping that was 37.3% of the stage sum |
--resident-experts with a page-locked complement |
1,092 → 2,503 tok/s; wait copy 45,604 ms (52.2%) → 24 ms (0.1%) |
--prefill auto:32768 |
+23% at 74K (fewer chunks, less expert re-streaming) |
| the matching hipBLASLt table | see the caveat above: without it the engine drops to plain hipBLAS |
The observation: the resident set is decided by MemAvailable at start, and below 100% lend coverage the prompt read silently drops ~26%
FileExpertSource prints the number that decides everything:
FileExpertSource: 6201 of the prompt path's 6201 lendable slots keep their experts in RAM too
FileExpertSource: 5093 of the prompt path's 6202 lendable slots keep their experts in RAM too
(the others are read from the file when lent: not enough RAM for them)
When that line is not N of N, the uncovered experts are read from the 76 GB pack during the prompt, and
wait copy — which otherwise sits at 0.1% — swallows the run. Same 74K prompt, same binary, only the startup
differed:
| resident | coverage | file blobs | wait copy |
74K prompt | |
|---|---|---|---|---|---|
| good start | 33.24 GiB | 6201 / 6201 | 0 | 134–485 ms (0.2%) | 2,583 tok/s |
| bad start | 31.47 GiB | 5093 / 6202 | 11,090 | 37,019 ms (36.8%) | 1,810 tok/s |
The stage breakdown of the bad run is unambiguous — every other stage is unchanged:
| stage (ms) | good | bad |
|---|---|---|
wait copy |
485 (1.7%) | 11,552 (26.2%) |
gemm gate/up |
6,741 | 7,692 |
combine |
2,553 | 5,494 |
| qsa attn / select / gdn / hc read | 3,244 / 2,988 / 2,460 / 2,588 | 3,402 / 3,126 / 2,458 / 2,688 |
| KV streaming | 79.00% hit VRAM, 83.4 MiB from RAM | 78.97%, 83.5 MiB — identical |
Why it is length-dependent: a short prompt only routes through part of the experts, so the uncovered slots are
rarely touched (25K reads the same either way: 2,443 vs 2,409 tok/s). A 74K+ prompt routes through nearly all of
them, so the misses repeat. That is the whole "short prompts look fine, long prompts collapse" pattern.
Why it varies at all: the complement is sized from MemAvailable at start. On this 50 GB box the resident set
landed anywhere between 31.5 and 33.2 GiB across restarts, i.e. 75–100% coverage, with nothing else changed.
The only knob is STRATA_RESIDENT_HEADROOM_GIB (8 for the 7.10 wheels, 7 after moving to 10.2). It is not in
--help, not in docs/, and searching the issues for HEADROOM_GIB returns 0 hits, so I expect most people
never find it: they just get a slower long prompt.
What I would ask for
- Warn loudly when the line is not
N of N— oneWARNINGwith the measured cost ("this prompt path will
read K experts from the file; expect roughly X% less prompt speed") would have saved me a day of looking at
wait copy. Right now it is one INFO line among ~40 at start. - Make the resident set deterministic, or retry when it lands below full coverage.
#1250already paces
the pinning; the same pacing could re-run the sizing (or hold back a little RAM) until the complement covers
the prompt path, since the outcome swings ±26% on a coin flip. - Document
STRATA_RESIDENT_HEADROOM_GIBindocs/AMD_HIP.md, including that it needs re-tuning when the
ROCm version changes (7.10 → 10.2 needed 8 → 7 here). A one-line note "check theFileExpertSourceline
after a restart" would cover it.
Dead ends (measured, so nobody repeats them)
- KV placement does not matter.
--kv-resident20,480 / 65,536 / 131,072 with 100% coverage: 25K
2,481 / 2,453 / 2,454, 74K 2,553 / 2,577 / 2,567, 150K 2,365 / 2,351 / 2,347 tok/s, and the KV streaming
stats are identical (97.7–98.0% hit VRAM, ~250 MiB from RAM). Moving the KV slice out of VRAM buys +331
expert slots (0.53 GiB) and no speed. Note--kv-resident 0is the sentinel for all cells in VRAM, not
"none" — it costs ~6 GiB of free VRAM and 11% of decode. - A bigger prefill chunk does not help.
--prefill 65536/131072vsauto:32768: 25K 2,454 / 2,460 vs
2,465, 74K 2,571 / 2,568 vs 2,585, 150K 2,351 / 2,348 vs 2,363. (Also,generate.cpp:1672capsautoat
32,768, soauto:32768is already the maximum.) - A self-calibrated hipBLASLt 1.5.0 small-T table does nothing (only relevant if you are on a 1.5.0 build).
I took the geometry set of the newgfx1100-hipblaslt-100401.txt(45 rows, small-T buckets) and re-ran
tune_hipblasltagainst hipBLASLt 1.5.0 on this card: 45 rows, 12 of them small-T, headergfx1100 100500.
A/B with a warm-up: 512 tokens 794 vs 696 (the 696 had a 605 outlier; the other sample was 787), 1K 1,140 vs
1,146, 2K 1,842 vs 1,843, 74K 2,528 vs 2,539 — i.e. flat. The reason is visible in the diff: the tuner picks
the same solution ids the heuristic already used (bf16 10240 2560 10240is 1036 at T=128…6144, exactly
what the old table has at 4096/8192). So the +32% you measured for the 100401 table looks specific to that
hipBLASLt version's old table, and does not transfer to 1.5.0. Short prompts here are also dominated by fixed
per-request cost, not the GEMMs: 512 tokens reads at ~700–790 tok/s, 4.2K at ~2,400. --specabove 4 is flat. With full coverage, spec 3/4/5/6: window 22.5/24.9/27.0/27.9 ms, tokens per
window 2.00/2.26/2.29/2.37, decode 87.9/91.4/89.8/91.4 tok/s. More drafts cost per step and buy tokens per
step at about the same rate, so it saturates at 4.STRATA_SPEC_GUMBEL=1under greedy decoding: 70.1 vs 72.2 tok/s (no gain; it is a sampled-decoding
feature, and it does change the output).pin=N/strata_prefixworks and is worth documenting harder. With{"strata_prefix": {"messages": 1}}
on a 100K-token document,tools/research_run.pygoes from 101 s (median first token 14.8 s) to 19 s
(0.81 s) — with no--conversation-cache-mibat all. It does stop working the moment another request is
interleaved (even a 31-token one), which is where the conversation cache is needed.
Reproduction
# build (either ROCm; no patch needed on 0.1.40.2)
cmake -S . -B build-hip -G Ninja -DCMAKE_BUILD_TYPE=Release -DSTRATA_ENABLE_HIP=ON \
-DSTRATA_PREFILL_MMQ=ON -DCMAKE_HIP_ARCHITECTURES=gfx1100
ninja -C build-hip strata
# the check that matters, after the first request of a start
grep -o "FileExpertSource: .* of the prompt path's .*" strata-*.log | tail -1
# 6201 of the prompt path's 6201 -> good; anything else -> the prompt path reads the packThen read the same 74K prompt twice, once with the coverage full and once with it short, and diff the
wait copy share. On this box the difference is 0.2% vs 36.8%, and 2,583 vs 1,810 tok/s.
Activity
[-]RX 7900 XTX (gfx1100, ROCm 10.2): 100% lend coverage is most of the prefill story, and STRATA_RESIDENT_HEADROOM_GIB is undocumented[/-][+]RX 7900 XTX (gfx1100): 925 -> 2,503 tok/s prompt read (+171%) - the method, and the 26% cliff below full lend coverage[/+]Warn when the prompt path's lend coverage is short; document STRATA_R…
fix the escape in the lend-coverage warning (#1389)
Niko1221 commentedon Oct 8, 2026
Thank you, this is the clearest write-up of the long-prompt path we have had. What is in 0.1.41:
FileExpertSource ... lendable slots keep their experts in RAM tooline now also prints a WARNING when it is not N of N, with the share of slots that will read the pack and a pointer toSTRATA_RESIDENT_HEADROOM_GIB(your ask 1).docs/AMD_HIP.mddocumentsSTRATA_RESIDENT_HEADROOM_GIB, the 'check the line after a restart' step, the ROCm 7.10 -> 10.2 retune, your measured levers and your dead ends, all as your measurements on a 7900 XTX (ask 3 and the dead-end list).On gfx1201 (R9700) the prompt-side switches in your table do not apply (
STRATA_PF_GEMMis gfx11-only and falls through there), so we cannot confirm the 925 -> 2,503 tok/s on our side. Could you A/B our port (the warning plus the docs, nothing else) on your 7900 XTX and tell us whether the warning fires on a bad start and stays quiet on a good one? Five or more interleaved starts, each with its FileExpertSource line, would be ideal.zhhhn commentedon Oct 8, 2026
Tested on the 7900 XTX (RX 7900 XTX 24 GB, gfx1100, 50 GB RAM, ROCm 10.2.0a20261003, hipBLASLt 1.5.0), v0.1.41
built from the tag with no local patches (
NW_LB=1and theput()barrier are in). Both answers first, theevidence under them:
headroom 7 and 15, each start preceded by the same condition (
MemAvailable49 GiB — the headroom is the onlyvariable): 3/3 short starts warned, 3/3 full starts were silent. No false positive, no false negative.
docs/AMD_HIP.md: 925 -> 1,092, 1,092 -> 2,503,auto:32768+23%at 74K, the 8 -> 7 retune, the table pairing, the flat dead ends).
Oracle:
hip_prefill_fused_iq20/20 (default 4 pairs) and 10/10 (--only=IQ3_XXS).The six starts. Each row is that start's own
FileExpertSourceline, and "warning" is whether that startadded one (I counted the line before and after each start, not the file):
wait copy6201 of 62011872 of 62014329 of 6201 (70%)6201 of 62011788 of 62014413 of 6201 (71%)6201 of 62011933 of 62014268 of 6201 (69%)The warning's text, verbatim from start 2:
Two notes on the cost, since they bear on how the warning reads:
against 2,466), while the 2.9%-uncovered start in our original report cost 26%. So the number in the warning
is fine as "one measurement", but I would not let it read as a rule of thumb — which slots are uncovered seem to
matter more than how many (the prompt path crosses nearly every expert either way, and the hot early-layer ones
are the ones that hurt). If you prefer, "expect a slower prompt read on 50K+ token prompts" alone carries the
message; we can keep the number for scale.
wait copyandfiles N blobsare from the same run).On ask 2 (a deterministic sizing / retry): yes, we will A/B it — and the data says it is worth trying.
In the production log on this box (headroom 7, nothing else changed, 17 recorded starts) 13 were
6201 of 6201and 4 were short, by 1.5-12.6% (6110, 6080, 5801, 5423). One of those short starts (6020/6201, 2.9% uncovered)
read its long prompt 26% slower. So the failure is real, it is not rare (~1 start in 4), and a retry that lands on
N of Nis the right target — the shares in the table above show the outcome is not smooth in the size, so I wouldnot try to compensate by resizing, only to re-run the sizing once.
On gfx1201 (R9700): agreed, and thanks for checking —
STRATA_PF_GEMMis gfx11-only, so the 925 -> 2,503 paircannot be confirmed there. Two things that do carry over, if you want to test the one that matters on the R9700:
STRATA_HIP_WMMA=1is the RDNA4 equivalent of the fused lever, and the page-locked resident complement itselfhas no architecture gate (
src/core/expert_source.cpp), which is where 1,092 -> 2,503 comes from. The sameinterleaved-start check with
--resident-expertsand a headroom sweep would show it on that card: with thecomplement full,
wait copyis 0.1-1.6% and 0 file blobs; with it short, the blobs and the warning appeartogether.
Happy to run any further start counts you want (a longer series, or a fixed seed so the same prompt is used every
time), and I will A/B the retry build whenever you have one.
RAM の写し: prompt の経路に貸すスロットのエキスパートが入りきらないときに警告する(upstream 14d9d2e、f780…
Niko1221 commentedon Oct 13, 2026
Thanks for the report. To keep the tracker current we are closing issues older than three days: 0.1.44 is out with a long list of fixes, and many older reports are covered by it or by 0.1.43.1.
If this still happens on 0.1.44, please leave a comment here with your engine log (and the version line it prints), and we will reopen it. A new issue that links this one works too.
Warn when the prompt path's lend coverage is short; document STRATA_R…
fix the escape in the lend-coverage warning (Niko1221#1389)
CullenShane commentedon Oct 22, 2026
@Niko1221 I am still seeing this behavior on 0.1.45 (0.2.1 docker) but the logging is so different I don't know how to get you logs. What do I have to run to get the logs you're asking for?