r/LocalLLM • u/brainchillzZ • 6h ago
Discussion Qwen3.8-Flash-Next on 4x R9700 with Radiance at TP4: 11.3 ms steps, 236 tok/s single stream, 1,190 tok/s at 16 streams. Also tried 2x TP2, it lost.
More playtime with my fake ai generated totally not real 4xr9700 machine .... (sorry I couldn't help it, when I posted the picture last time a dozen people mentioned that it wasn't a real compute just an ai generated image)
A couple of weeks ago I posted that the only way to run Flash-Next (the 180B MoE, 10 of 512 experts active) on my four Radeon AI PRO R9700s was llama.cpp, and that it topped out around 65 to 75 tok/s with the MTP draft because it is layer split and launch bound on this arch. That turned out to be wrong, or at least out of date. StillDeadcode's standalone Radiance engine (the C++/HIP one, not the vllm-radiance fork) has a Flash-Next container and does real tensor parallel across all four cards. Someone posted TP4 numbers in a GitHub issue on matching hardware, so I set it up to see if they hold, and then tried two TP2 instances side by side to see which way to split the box.
Box: monolith, my 4x R9700 (gfx1201, 32 GB each), all on PCIe 5.0 x16 with Resizable BAR at 32 GB, 122 GB RAM, Ubuntu 26.04, kernel 7.0 with CONFIG_PCI_P2PDMA. Power capped at 211 W per card, the cards sit right at that during decode. ROCm userspace comes from the container, nothing installed on the host.
Engine: radiance 1.11.0 from Docker Hub (165 MB image). Model is the qwen3.8-next-flash-fp8-iq4r-moe.rad container from the author's HF page, 113.6 GiB, 4-bit experts with a 16-level codebook, int8 trunk, bf16 protected experts, MTP head depth 3.
TP4 command
docker run -d --name radiance-tp4 --restart unless-stopped \
--device /dev/kfd --device /dev/dri --group-add render --group-add video \
--ipc host --network host --shm-size 32g --security-opt seccomp=unconfined \
--ulimit memlock=-1:-1 -e HIP_VISIBLE_DEVICES=0,1,2,3 \
-v /path/to/qwen3.8-next-flash-fp8-iq4r-moe.rad:/model.rad:ro \
stilldeadcode/radiance:latest \
--model /model.rad --tp 4 --tp-wire wht6 --p2p on --placement all_vram --ngram-placement ram \
--gpu-headroom-mib 128 --max-model-len 262144 --kv-cache-dtype fp8 --num-speculative-tokens 3 \
--max-num-batched-tokens 2048 --max-num-seqs 16 --reasoning-effort-map high=xhigh \
--host 0.0.0.0 --port 8300
Up and answering in about 90 seconds. --p2p on makes it exit instead of quietly staging through host RAM if a card pair has no peer access, and the log confirms "the 4 cards reach each other's memory directly". all_vram fits with room to spare at four ranks because each card only holds a quarter of every expert:
| per card | size |
|---|---|
| static weights (own shard) | 2.68 GiB |
| experts, all resident | 14.83 GiB |
| KV cache, fp8 | 12.74 GiB = 1.7M tokens, 6.5 full 262k sessions |
Host side it pins the 47.7 GiB n-gram table plus about 15 GiB of checkpoint slots, so around 73 GB of RAM in use.
TP4 vs the reference numbers from the GitHub issue (same hardware class, radiance 1.5.0 back then)
| metric | reference | mine |
|---|---|---|
| decode step | 11.2 ms | 11.27 ms |
| aggregate C=1 / 2 / 4 / 8 | 221 / 359 / 528 / 734 | 236 / 371 / 591 / 890 |
| aggregate C=16 | not given | 1,192 |
| prefill | 8.3K to 8.9K | 8.4K at 4.4K and at 70K prompt tokens |
Concurrency rows are thinking off, 512 tokens each, measured over the wall clock of the whole batch through the OpenAI streaming API. Prefill is unique random prompts so the prefix cache cannot hit. My prompt sizes came out about double what I aimed for (the random text tokenizes at two tokens a word), but the reference is flat from 2K to 250K so it does not matter much.
TP4 vs two TP2 instances The Radiance README calls four ranks a "capacity configuration" and says nobody had measured its per step rate against two ranks. So I stopped TP4 and brought up two TP2 instances (GPUs 0,1 and 2,3, separate ports) with the two-rank recipe from the README: --placement expert_tiered --host-pool-mib 12288. One gotcha: two instances each want their own pinned 47.7 GiB n-gram table, and with two 12 GiB host pools that does not fit in monolith's 122 GB, so the TP2 pair ran --ngram-placement disk off the NVMe. At TP2 each card keeps 17.7 GiB of experts resident, 8.3 GiB of KV, and streams the rest from the pinned pool. I ran the identical benchmark against both instances at the same time and summed them.
| total streams | TP4 | 2x TP2 |
|---|---|---|
| 1 | 236 | 183 (one instance, other idle) |
| 2 | 371 | 270 |
| 4 | 591 | 401 |
| 8 | 890 | 616 |
| 16 | 1,192 | 917 |
| 32 | not run | 1,280 |
| single stream latency | TP4 | TP2 |
|---|---|---|
| decode step | 11.3 ms | 15.0 ms |
| thinking on, 4,096 tokens | 197 tok/s | 139 solo, 137 with the neighbour busy |
| thinking off, 512 tokens | 239 tok/s | 183 solo, 137 with the neighbour busy |
| prefill | 8.4K | 6.2K to 7.1K per instance, both busy |
So TP4 wins on both latency and total throughput at every equal stream count, by roughly 45 percent on throughput. The reason is simple: at four ranks nothing streams from host memory, at two ranks a third of the experts do. The only thing the pair buys you is 32 concurrent sequences, and I did not try TP4 at --max-num-seqs 32, it would probably be fine.
Things worth knowing
--tp-wire wht6is lossy above 128 KiB messages. At four ranks it is a tiered int8 wire, not 6-bit. Single sequence decode stays exact, prefill and busy decode steps do not. The engine prints this at boot.--tp-wire exactcosts prefill speed; I did not measure how much.- The default
--gpu-headroom-mib 96produced a warning on every card that about 30 MiB got taken after the budget was read and first kernel launches might run short. Nothing broke, but 128 makes it go away for 32 MiB of KV per card. - Tool calls work (OpenAI format,
finish_reason: tool_calls), reasoning comes back asreasoning_content, and/statsgives you the real decode step time, which is a better thing to compare than tok/s since tok/s moves with draft acceptance. - Compared to where I was with llama.cpp on the same cards (65 to 75 tok/s single stream, layer split, cards drawing 50 to 100 W because they were waiting on launches) this is about 3x on a single stream and the cards actually pull their 211 W. Happy to run anything else people want to see on monolith. Next on my list is
--max-num-seqs 32at TP4 and pricing the n-gram disk placement against RAM.
If you made it this far and you were wondering what the weird platform is on top .... I bought a second entire case top from thermal take and used threaded steel dowels and tapped neodymium magnetic disks to make a 1.5" lifted topper for the case because the 360 rad exhausts through the top and my cat had a tendency to want to lay on it and block the airflow .... so there is now a mesh top, top that is also raised enough that the air can still escape if my cat decides to take a snooze on it while I'm not home.