Repository navigation
[Engine] sm_80 tuning measured on a CMP 170HX (GA100): prompts up to 2.0x, decode +20 %, conversation switches 5-10x faster; five PRs to follow #3712
Description
Area
[Engine] (speed, kernels, models, the server and API)
What do you want to do?
Make Strata fast on sm_80 (GA100-class) cards with every expert resident. Today none of the engine's per-card tables has an sm_80 entry: the wide dense dequant default, the MMVQ-IL rows and the fused prompt-expert thread shapes. #3252 reports A100 slowdowns, and you said you have no sm_80 card. We have one and spent the last weeks tuning for it. We would like to agree on scope here before opening five PRs. The PRs are written and build on CUDA sm_80, HIP gfx1100 and HIP gfx906. Their per-PR GPU runs are in progress.
Card and setup. CMP 170HX: GA100, sm_80, 70 SMs, 64 GB HBM2e, PCIe Gen2 x4 (the engine's probe reads 1.6 GB/s host to device). The card runs at a 1650 MHz clock ceiling with a +250 MHz V/F offset (an undervolt), and its power cap was 235 W or 250 W across these runs.
- Model: Qwen3.8-Flash-Next IQ3_S native pack, every expert resident (24,576 slots, 46.84 GiB).
- Engine args:
--kv int8 --spec 3 --mtp --prefill 8192 --max-context 262144 --conversation-cache-mib 8192. - Software: Linux, driver 610, CUDA 13.3. Measured against 9de04cd (0.2.1.4). The PR branches are now rebased onto main 011c9e6 (0.2.2, engine 0.1.47). None of the 238 commits since 9de04cd overlaps these changes. The rebase had two textual conflicts, each resolved by keeping both sides. This box has two sockets, so 0.2.2's NUMA-split CPU expert pool turns itself on here. The rerun below sets
STRATA_NUMA_POOL=0in both arms, because the automatic split on the arena path gives remote reads and an unstable decode (NUMA pool split on a two-socket Xeon (2x 6226R, 2x R9700): on/off numbers, and the "arena path" limitation #3708).
Overall result. Fresh engine per run, same box.
| stock 0.2.1.4 | our branch | ||
|---|---|---|---|
| prompt, ~3K words (tok/s) | 2,071 / 2,004 / 2,001 | 4,136 / 4,145 / 4,042 | 2.0x |
| prompt, ~12K words | 2,911 / 2,768 | 4,464 / 4,452 | 1.57x |
| prompt, ~48K words (51.8K tokens) | 3,114 | 4,363 | 1.40x |
| decode, 384 tokens after a ~3K-word prompt | 106.6 / 110.3 | 129.4 / 131.1 | 1.20x |
| park a 3.3K-token conversation (ms) | 212-217 | 21.5-40.1 | 5-10x |
| restore (ms) | 96-126 | 28-47; 6.5-12.6 from the second restore of a run |
How the two columns were set up:
- Stock is the release binary with
STRATA_PF_FUSED=1 STRATA_OWNED_PRICE=exact. - Our branch turns on the switches of all five PRs and several existing upstream switches, for example
STRATA_SIDE_MIXER=2,STRATA_GR_UP75=1andSTRATA_QIL=1. - It also turns on two switches that are in no PR:
STRATA_GDN_OUT_NORM_LOOPandSTRATA_MTP_PRIO_ALWAYS. Neither showed a gain. - The park and restore "before" times are from our builds with the new switches off. We have no conversation-check run of the stock binary.
- Greedy answers (11 prompts, 256 tokens, temperature 0) are identical to stock for every bitwise change.
A same-session rerun is queued and will replace this table. It runs on the rebased branches and has three rows:
- stock;
- stock plus upstream's existing switches;
- the five PRs merged, with their switches.
Switches. Every switch is opt-in except the two sm_80 defaults in PR 1, which are bitwise. The effects below were measured on our working builds. Per-switch off/on pairs on the PR branches themselves are in progress, and the table will be updated with them.
| switch | PR | default | output | effect on the CMP 170HX |
|---|---|---|---|---|
wide dense dequant on sm_80 (STRATA_DEQUANT_WIDE) |
1 | on for sm_80 | bitwise | 3,259-token prompt GPU time 857 -> 792 ms; prompts +6.3..+7.2 % at 3.3K tokens, +3.9 % at 52K |
MMVQ-IL sm_80 row tables (STRATA_MMVQ_IL_SM80=0 restores the shared table) |
1 | on for sm_80 | bitwise | decode +2.7..+3.2 % over the shared table |
STRATA_PF_FUSED_NT=256 (256-thread fused IQ prompt experts) |
1 | off | bitwise | in progress |
STRATA_PF_BATCH=N (whole-layer launches, up to 512 experts) |
1 | off | bitwise | in progress |
STRATA_PLE_COMPACT=1 (raw IQ4_NL PLE rows, dequantized on the device) |
2 | off | bitwise | prompts +1.0..+1.7 % |
STRATA_PLE_COPY_STREAM=1, STRATA_EMBED_VRAM=1, STRATA_DEQUANT_SIDE=1 |
2 | off | bitwise | 51.8K-token prompt, measured as a bundle: "ple" 95-103 -> 47 ms, "embed+steps" 191-201 -> 70 ms |
STRATA_PF_FUSED_F16=1 / STRATA_PF_FUSED_WS=1 (f16 mma fused experts) |
3 | off | rounding | prompts +2..+6 % over the 256-thread kernels; 1 of 11 greedy answers changes |
STRATA_GDN_TC=1 (DeltaNet prompt recurrence as a chunked tensor-core scan) |
3 | off | rounding | recurrence 105 -> 34 ms at 3.3K tokens, 1,705 -> 547 ms at 52K; prompts +6.6..+7.6 %; within 2.3e-6 of FP32 in gdn_rec_parity; 0 of 11 greedy answers change |
STRATA_CKPT_ASYNC=1, STRATA_CKPT_VRAM_SLOTS=N |
4 | off | bitwise | turn checkpoint 71.4 -> 0.9 ms; restore 114-126 -> 28-47 ms |
STRATA_PARK_ASYNC=1, STRATA_CKPT_POOL=N, STRATA_CKPT_GATHER=1 |
4 | off | bitwise | park at 3.3K tokens 212-217 -> 21.5-40.1 ms |
STRATA_PARK_VRAM_MIB=N |
4 | off | bitwise | later restores 6.5-12.6 ms |
STRATA_GRAPH_PRIO=1 (verify-window graph nodes keep their stream priority) |
5 | off | bitwise | decode +1.8 % |
STRATA_MMVQ_IL_PF=1 (hoisted-load integer MMVQ-IL kernels for Q6_K) |
5 | off | bitwise | decode +1.4 % |
The five PRs. Each PR has one commit per feature, so any of them can be split further.
- sm_80 defaults: the two bitwise default changes plus
PF_FUSED_NTandPF_BATCH. Upstream's default fused kernels keep identical SASS. Cost: the 256-thread instances add 365,632 bytes of sm_80 device code to every CUDA build. - Prefill over a slow host link: four opt-in switches. Metal gets stubs for the new entry points.
- Tensor-core prefill: f16 fused experts and the GDN scan. Both are opt-in and change rounding. Stacked on PR 1. Teacher-forced KL is not run yet.
- Conversation cache: background copies, a ready checkpoint pool, VRAM slots. Opt-in and CUDA-only. Compatibility: an existing
STRATA_CKPT_POOL=1now also pre-allocates 2 ready checkpoints (+236 MB of host RSS). - Decode: graph-node priorities and the Q6_K hoisted-load GEMV. Stacked on PR 1, because its table builds on PR 1's sm_80 rows.
PR 1's MMVQ-IL table is the sm_80 counterpart of #3453 (sm_89). The two touch the same table list.
Validation so far.
- Rebased on 011c9e6, every commit of PRs 1-4 and their branch tips build on CUDA sm_80, HIP gfx1100 (ROCm 7.14) and HIP gfx906, with no new warning in any touched file compared with 011c9e6 built the same way. PR 5's builds are finishing.
- Before the rebase, PR 3's sources also built for the Dockerfile's architecture list (75, 80, 86, 89, 120). That check has not been redone on 011c9e6.
- Not run: SYCL and Metal builds, any AMD hardware, CUDA 12, and other sm_80 cards.
Questions.
- Five thematic PRs, or a finer split?
- Is the 365 KB of sm_80 device code acceptable in every CUDA build, or should it go behind a CMake option?
- PRs 3 and 5 are stacked on PR 1. Should we open them as drafts naming PR 1, or wait for PR 1 to merge?
What do you do today instead?
We run our fork's branch (github.com/linxuhao/Strata, opt-sm80 and opt-sm80-hip) with the switches above.
🤖 Generated with Claude Code
Activity
linxuhao commentedon Oct 22, 2026
Draft with all five features, one commit each, rebased on 011c9e6: #3779. Per-feature GPU runs on 011c9e6 are in progress; results will follow here.
🤖 Generated with Claude Code
linxuhao commentedon Oct 23, 2026
Results on main 011c9e6, same box,
STRATA_NUMA_POOL=0in both arms. The full tables and method are in #3779 #3779 (comment).The five PRs merged, with their switches, against stock: 4K prompts 1,980 → 4,131 tok/s (2.09x), 16K 2,889 → 4,458 (1.54x), 52K 3,154 → 4,367 (1.38x), decode 108.6 → 129.5 (1.19x).
Against stock with upstream's own switches turned on, these PRs add 1.80x / 1.45x / 1.35x on prompts and +8 % on decode. The greedy answers are identical and the A/B/A conversation check is identical.
🤖 Generated with Claude Code
Dense dequant: the wide kernels by default on sm_80 too, bitwise (#3779…
MMVQ-IL: sm_80 row tables, bitwise (#3779, #3712)
Prefill fused IQ experts: opt-in 256-thread kernels STRATA_PF_FUSED_N…
Prefill PLE: opt-in STRATA_PLE_COMPACT=1 uploads the raw IQ4_NL rows …
Prefill: opt-in STRATA_PLE_COPY_STREAM=1 uploads the compact PLE rows…
Native head: opt-in STRATA_EMBED_VRAM=1 keeps the token-embedding tab…
Prefill: opt-in STRATA_DEQUANT_SIDE=1 dequantizes the dense weights o…
Prefill fused IQ experts: opt-in f16 mma path STRATA_PF_FUSED_F16=1 a…
Prefill GDN: opt-in STRATA_GDN_TC=1 runs the DeltaNet recurrence as a…
Conversation checkpoints: opt-in STRATA_CKPT_ASYNC (background device…
Conversation cache: opt-in STRATA_PARK_ASYNC=1 parks the K/V in the b…
Conversation cache: opt-in ready checkpoint pool STRATA_CKPT_POOL_REA…
Conversation cache: opt-in STRATA_PARK_VRAM_MIB=N keeps parked conver…
docs: conversation cache copies on a slow host link, STRATA_CKPT_ASYN…
Decode window: opt-in STRATA_GRAPH_PRIO=1 gives the window graph's ke…
mmvq_il_parity: the L2-residency arms (--hot / --gap / --persist / --…
Decode: opt-in STRATA_MMVQ_IL_PF=1 hoisted-load, exact-integer interl…
docs: the sm_80 defaults and the opt-in sm_80 switches of #3779, STRA…
Niko1221 commentedon Oct 27, 2026
Thank you. #3779 is ported in 0.2.7 with your name on the 18 commits. The bitwise-identical parts (wider dequantisation, the MMVQ tables for sm_80) are on by default for sm_80 (off: STRATA_DEQUANT_WIDE=0, STRATA_MMVQ_IL_SM80=0); the options that change the summation order, need 101 KB of shared memory or have no unit test stay off, and STRATA_DEFAULTS=026 restores 0.2.6. We have no sm_80 card (only sm_86 and sm_120 here); please try 0.2.7 and post your results in the pull request.
Release: https://github.com/Niko1221/Strata/releases/tag/v0.2.7
pool: the 0.2.7 opt-ins for a split - one setting on the coordinator,…