34 KiB
Research: fastest inference on 2x R9700 (27B Q8_K_XL vs Flash-Next, multi-GPU modes, RAM/SSD offload, Strata)
Date: 2026-10-29
Question: A second Radeon AI PRO R9700 arrives in about a week. What gives the fastest decode (and prefill) for agentic coding on this box: Qwen3.8-27B-UD-Q8_K_XL, Qwen3.8-Flash-Next-UD-Q4_K_XL, or something else? Which llama.cpp multi-GPU mode helps, does RAM/SSD offload work with 32 GB of RAM, and is Strata worth switching to?
Scope note: research only. Nothing in docker-compose.yml or .env.example was changed, so nothing needs deploying.
Hardware assumed: Ryzen 9 5950X, 32 GB DDR4 dual channel (reported ~94 % used, swap in use), ASRock B550M Pro4, 1 TB NVMe, Ubuntu 24.04, kernel 6.8, 1x R9700 now and 2x soon.
TL;DR
| # | Finding | Confidence |
|---|---|---|
| 1 | The second card does not make the dense 27B decode faster with -sm layer. Layer split runs the cards one after the other, so one token still reads every weight at one card's bandwidth. It only adds VRAM. |
High (llama.cpp docs) |
| 2 | UD-Q8_K_XL is a quality move, not a speed move. It reads ~30.1 GB per token against ~16.8 GB for today's UD-Q4_K_XL. Estimated decode is ~15 t/s without speculation and ~25 t/s with DFlash2, against ~46 t/s measured today. |
Medium (estimate, math below) |
| 3 | -sm tensor is the only mode that can speed up one stream, and on ROCm it is immature. The one measured 2-card ROCm result for this exact model is 1.31x one card (2x 7900 XTX). DFlash2 crashes with -sm tensor (open bug), and MTP plus -sm tensor has lockup reports. Your second slot is PCIe 3.0 x4 behind the chipset, which hurts the per-layer all-reduces. |
Medium |
| 4 | Best use of the second card for 2 parallel agents: two independent single-GPU llama-server replicas, one per card, each --parallel 1. Each agent keeps today's ~46 t/s, the decode drop when the other slot prefills goes away, and each slot gets the full 262144 tokens instead of 131072. Needs a second backend connection in OmniRoute with per-conversation stickiness. |
Medium-high |
| 5 | Flash-Next UD-Q4_K_XL (111.3 GB) does not fit. Even with the 28.8 GB n-gram table left on SSD, 82.5 GB of weights remain for 64 GB of VRAM. The ~20-24 GB of experts left over would come from NVMe, because RAM is full. Estimated decode is then ~6-12 t/s. Only the all-in-VRAM quants (UD-Q2_K_XL, UD-IQ3_XXS) are realistic, at a large quality cost (mean KLD 0.22 / 0.17 vs 0.047). |
Medium |
| 6 | RAM is the binding constraint, and compose itself is a likely cause of the swap. --cache-ram is set to 24576 MiB (24 GiB) on a 32 GB host that also runs OmniRoute with mem_limit: 10g. |
High for the config, inferred for the swap |
| 7 | Strata: a real, fast-moving MIT engine built around Flash-Next expert tiering. It has R9700 HIP builds and a layer split for 2 cards, but its design wants all experts in RAM (~88 GB for UD-Q4_K_XL), it does not run the dense 27B at all, and its numbers are self-reported on hosts with 47-503 GB of RAM. Not worth it on 32 GB. Revisit after a RAM upgrade (board max 128 GB). | Medium |
1. Platform facts that drive everything
- R9700: 32 GB GDDR6, 256-bit, 20 Gbps, 640 GB/s, PCIe 5.0 x16, 300 W (Puget Systems listing, Tom's Hardware launch). AMD's own product page timed out on fetch, so this is from third-party listings.
- Effective bandwidth, calibrated from this repo's data: no-spec decode for the 27B Q4 on one R9700 is 25-30 t/s across three outside sources (
qwen3.8-27b-throughput-tuning.md). 16.8 GB/token x ~26.4 t/s ≈ ~440 GB/s effective (~69 % of peak). All estimates below use 420-450 GB/s. - ASRock B550M Pro4 slots, from the ASRock manual (PDF) spec page with a Matisse/Vermeer CPU: "2 x PCI Express x16 Slots (PCIE1: Gen4x16 mode; PCIE3: Gen3 x4 mode)", plus one PCIe 3.0 x1 slot (PCIE2). M2_1 is "up to Gen4x4", M2_2 "up to Gen3 x2", and M2_2 shares lanes with SATA3_5_6. Memory is dual channel, max 128 GB. So the second R9700 runs at PCIe 3.0 x4 (~3.9 GB/s per direction).
- PCIE3 is behind the chipset. The manual does not say so explicitly. It follows from the AM4 lane budget: the CPU gives x16 to the GPU, x4 to the CPU-attached M.2, and x4 as the chipset uplink. B550's own lanes and uplink are PCIe 3.0 (ComputerBase B550 test, Guru3D). Card 2 therefore shares the chipset's PCIe 3.0 x4 uplink with M2_2, SATA, USB and LAN. GPU-to-GPU traffic crosses CPU root complex → chipset → slot. Whether ROCm P2P works across that path was not verified. Assume it falls back to staging through host memory.
- DDR4: dual-channel DDR4-3200 peaks at 2 x 8 B x 3200 MT/s = 51.2 GB/s. Plan for ~35-45 GB/s effective (estimate).
- RAM budget today:
docker-compose.ymlsets--cache-ram ${LLAMA_CACHE_RAM:-24576}(24 GiB) and OmniRoutemem_limit: 10g(OMNIROUTE_MEMORY_MB=8192). That is 34 GiB of permitted use on a 32 GB host before the OS, qdrant and page cache.--cache-ramis a ceiling that fills up over time, which fits the reported 94 % plus swap.docs/architecture.mdalready says to "keep it well below the host's free RAM". - Physical and power, unverified: two 300 W cards plus a 142 W-PPT 5950X need a PSU of roughly 850 W or more.
500k-context-…roadmap.mdalready flagged PSU headroom as the limit. A dual-slot card in the bottom slot of a micro-ATX board can also need a fifth rear bracket in the case. Check both before the card arrives.
2. Qwen3.8-27B UD-Q8_K_XL
File facts (HF tree API plus the GGUF header, parsed from the first 12 MB of each file):
| UD-Q4_K_XL (today) | UD-Q8_K_XL | |
|---|---|---|
| File size | 17.56 GB | 31.46 GB |
| Tensor types | mixed IQ4/Q4_K-Q6_K, Q8_0 for small tensors | Q8_0 FFN/attention, BF16 attn_q/k/v and output |
token_embd (row lookup, not streamed) |
0.72 GB | 1.35 GB |
| Bytes streamed per token | ~16.8 GB | ~30.1 GB (1.79x) |
Sources: HF tree API, file page. Both files carry the built-in MTP head (qwen35.block_count = 65, nextn_predict_layers = 1).
Architecture (unsloth config.json): dense, Qwen3_5ForConditionalGeneration, 64 layers, hybrid. 48 Gated DeltaNet (linear_attention) layers and 16 full_attention layers (full_attention_interval: 4), 24 Q heads, 4 KV heads, head_dim 256, 262144 native context. Every weight is read for every token.
Fit on 2x32 GB. KV is 16 layers x 2 x 4 heads x 256 x 1.0625 B (q8_0) ≈ 34.8 KB/token, so ~9.1 GB at 262144 tokens. 31.5 GB weights + 1.1 GB DFlash2 Q4_K_M (z-lab GGUF tree) + 9.1 GB KV + ~3-5 GB compute buffers per card comes to ~46-50 GB of 64 GB (estimate). It fits with room. It does not fit on one card.
Decode estimate (bandwidth-bound, ~440 GB/s per card):
| Config | Math | Est. decode, no spec | With speculation (x1.7, the ratio this repo measures for DFlash2: 45.9 / ~27) |
|---|---|---|---|
| Q4_K_XL, 1 card (today) | 16.8 / 440 = 38 ms | ~26 t/s (measured 25-30) | 45.9 t/s measured |
Q8_K_XL, -sm layer |
30.1 / 440 = 68 ms, cards in series | ~15 t/s | ~25 t/s |
Q8_K_XL, -sm tensor at 1.31x |
15 x 1.31 | ~19 t/s | DFlash2 crashes here (see §3). MTP x1.5 gives ~29 t/s if stable |
Q4_K_XL, -sm tensor at 1.31x |
26 x 1.31 | ~34 t/s | MTP x1.5 gives ~50 t/s if stable |
All of these except the measured row are estimates. Q8_K_XL roughly halves single-stream speed against today. Pick it only if Q4 quality is shown to be the bottleneck on your own tasks.
3. llama.cpp multi-GPU on ROCm
Source: docs/multi-gpu.md and common/arg.cpp on master (fetched 2026-10-29). --split-mode takes none, layer, row, tensor.
| Mode | What it does | Decode on bandwidth-bound cards | Status |
|---|---|---|---|
layer (default) |
Pipeline: each GPU owns a contiguous block of layers and their KV | No single-stream gain. One token walks GPU0 then GPU1, each reading only its half, so the total time is the same. Prefill can overlap ubatches across GPUs. The docs say: "priority is a fast prefill. Can tolerate slow interconnect speeds" | Mature |
row |
Old row-split of dense matmuls | Docs: "Deprecated … Superseded by tensor". PR #19378: needs "a synchronization after every single operation", only worth it on old slow GPUs |
Avoid |
tensor |
Meta-backend tensor parallelism: weights and KV split, all-reduce at sync points | Both cards read their half of each layer at the same time, so it is the only mode with a decode win. Docs: "Performance should be good for multiple NVIDIA GPUs using the CUDA backend, no guarantees otherwise" | EXPERIMENTAL. Merged in #19378 (2026-04-09) |
Measured -sm tensor numbers that are relevant here:
- Qwen3.8-27B Q4_K_XL, 2x RX 7900 XTX, ROCm: 47.06 t/s = 1.31x one card. Per-card DRAM utilization drops from 65.9 % to 43.0 % when split (#28863, closed without a fix). This is the best proxy for a 2x R9700 result.
- The #19378 author on ROCm (RX 6800 + MI50, RX 9060 XT + MI100): "the performance is bad vs. the
-sm layerbaseline". On 2x RTX 4090, the dense Gemma4 31B decoded 1.36x faster, while pp2048 fell to 0.61x and the MoE model got slower. - #27825 (merged 2026-09-15) turned on the internal AllReduce for HIP. On an RX 9070 at x16 plus an RX 6800 XT at PCIe 4.0 x4, Gemma4 31B Q6_K: tg 23.62 → 24.15 (+2 %), pp +16 %. RCCL is opt-in at build time (
-DGGML_HIP_RCCL=ON), "disabled because … it was not universally beneficial" (multi-gpu.md). Whether the officialserver-rocmimage builds with RCCL was not checked. - 2x R9700 user, Gemma4 26B-A4B Q8_0:
-sm tensorf16 KV 29.7 t/s vs-sm layer19 t/s (#23567). Same card, a different (MoE) model. - A 5950X + dual-R9700 user runs "Qwen 3.6 27B Q8 … with tensor split" and reports trouble mixing tensor-split and layer-split processes on ROCm (#28582, open).
Constraints and bugs that apply to this stack:
- Architecture support:
qwen35andqwen4expare not on the deny list inllm_arch_supports_sm_tensor(src/llama-arch.cpp, master).-sm tensorwas re-enabled for qwen4exp in #28569. -sm tensorneeds flash-attn on (llama-context.cpp). Quantized KV is supported since #23792 (2026-06-01). llama.cpp'sdocs/multi-gpu.mdstill says it errors, so the doc is stale there.--fitis "not supported withtensor", so set--ctx-sizeby hand.- DFlash2 +
-sm tensorcrashes:GGML_ASSERT(src_ss[0].axis != GGML_BACKEND_SPLIT_AXIS_0)(#28777, open). The ROCm variant (#27829) was closed as stale, and the fix PR #27858 is open. Its tester still hit a different assert, and a commenter says "this is already a problem for ROCm". The workaround in #28777 is-sm layer. - MTP +
-sm tensor: reproducible lockups on CUDA (#27122, closed as stale). One ROCm report (2x V620, Q8_K_XL) saw one hard abort in ~67 h. - gfx1201 flash-attn garbage at some context lengths for Qwen3.8-27B (head_dim 256): immediate EOS in bands, reproduced on 2x R9700, Linux,
--tensor-split 1.7,1(#30884, open). Another commenter could not reproduce it through/v1/chat/completions. Run a needle test on any new config.
PCIe x4 chipset slot, estimated effects:
-sm layerdecode: one hidden-state hop per token (5120 x 4 B ≈ 20 KB). Negligible. Prefill: ~10 MB per 512-token ubatch hop, ~3 ms at 3.9 GB/s. Small.-sm tensordecode: ~2 all-reduces per layer x 64 layers ≈ 128 small reductions per token. These are latency-bound, not size-bound. At an assumed 20-50 µs each through host memory, that is 2.5-6.4 ms per token, 10-25 % of a 25 ms token (estimate). This is the likely reason #28863 lands at 1.31x and not ~1.9x, and an x4-through-chipset link will not do better.-sm tensorprefill: ~2 x tokens x 5120 x 2 B per layer. For a 2048-token ubatch that is ~2.7 GB over 64 layers, ~0.7 s at 3.9 GB/s, so prefill is capped at roughly 3000 t/s plus compute (estimate). Expect prefill to get worse than layer split, as in the #19378 tables.
Flags:
-ts/--tensor-splitsets proportions. Withlayerthe default split follows free memory. Withtensorthe default is an even split.-mg/--main-gpuapplies only to-sm none(pick the card) orrow(intermediate results). It does nothing forlayer/tensor.- Draft placement:
--spec-draft-device/-devd(default: follows--device),-ngld,-otd(common/arg.cpp). Underlayer, the output head sits on the last GPU. Place the drafter on one card and A/B both cards. No source documents which placement is better for DFlash2, which reads hidden states from several target layers (n_extract=5) spread over both cards.
4. Qwen3.8-Flash-Next UD-Q4_K_XL
Model facts (unsloth model card, GGUF header of shard 1, and shard headers 2-4):
general.architecture = qwen4exp, described as "A Preview of the Qwen4 Architecture". MoE: 125B total, 6B activated, plus a 51B n-gram embedding and a 4B MTP head. 48 layers, hidden size 2560, layout 12 x (3 x Gated DeltaNet + 1 x Qwen Sparse Attention), each layer followed by an MoE block. 512 experts, 10 routed + 1 shared, expert FFN dim 640. Native context 262144. Licenseqwen-community-1.0. The 27B is Apache 2.0 perqwen3.8-27b-quant.md.- UD-Q4_K_XL = 111.33 GB in 4 shards (0.01 + 49.86 + 49.38 + 12.09 GB, HF tree API). It splits into:
per_layer_token_embd(n-gram table): 28.80 GB IQ4_NL, read a few rows per token. The same size in every UD quant.- routed experts
ffn_{gate,up,down}_exps: 77.02 GB - everything else (attention/DeltaNet, shared experts, routers, hyper-connections, output head): ~4.8 GB, mostly Q8_0
- Bytes per token: 4.8 + 77.02 x 10/512 (1.50) ≈ ~6.3 GB, 2.7x less than the 27B Q4. The all-in-VRAM ceiling is 6.3 / 440 → ~70 t/s. Batch-1 MoE kernels gather scattered experts and reach less of peak, so the estimate is ~40-60 t/s if it fit in VRAM. For scale, Strata reports 63 (prose) / 87 (code) t/s on one R9700 with this file and its own engine (§6).
- Quality (Qwen card, BF16): SWE-bench Pro 62.5 vs 61.7 for the 27B, SWE-bench Multilingual 81.0 vs 73.8, DeepSWE 58.7 vs 42.2, Toolathlon 73.5 vs 67.1. Unsloth's quant table (run guide) gives mean KLD: UD-Q2_K_XL 0.225, UD-IQ3_XXS 0.165, UD-Q3_K_XL 0.107, UD-IQ4_XS 0.084, UD-Q4_K_XL 0.047.
Fit in 64 GB VRAM (non-PLE = total − 28.8 GB, the PLE left on SSD via --lazy-mode, §5):
| Quant | Total | Non-PLE (needs VRAM) | Result on 2x32 GB |
|---|---|---|---|
| UD-Q4_K_XL | 111.3 | 82.5 | ~20-24 GB of experts must live off-GPU. Does not fit |
| UD-IQ4_XS | 93.7 | 64.9 | ~6-9 GB off-GPU after KV and buffers |
| UD-Q3_K_XL | 90.0 | 61.2 | Borderline, a few GB off-GPU |
| UD-IQ3_XXS | 82.0 | 53.2 | Fits, ~10 GB headroom |
| UD-Q2_K_XL | 78.9 | 50.1 | Fits, ~13 GB headroom |
KV is small. Only the 12 QSA layers have one: 12 x 2 KV heads x 256 x 2 (K+V) x 2 B ≈ 24.6 KB/token f16, ~6.4 GB at 262144 tokens, ~3.4 GB at q8_0 (estimate; ignores indexer keys).
llama.cpp support status:
-
Merged: model support #27742 (2026-08-27), MTP #29761 (2026-10-01),
-sm tensorre-enabled #28569, many QSA fixes through 2026-10-27 (e.g. #31243). -
Open bugs that hit this setup:
- #29149: HIP livelock during load on an R9700, with both UD-IQ3_XXS and UD-Q4_K_XL. The root-cause comment places it in the mmap upload path; "the non-mmap path stages through pinned buffers instead". Try
--load-mode none. - #29811 and #30860: MTP asserts at startup.
- #30887: the DFlash2 drafter crashes on the first prompt (R9700, ROCm).
- #30815: QSA chunking, still open.
Usable, but rough on HIP.
- #29149: HIP livelock during load on an R9700, with both UD-IQ3_XXS and UD-Q4_K_XL. The root-cause comment places it in the mmap upload path; "the non-mmap path stages through pinned buffers instead". Try
5. Offloading to DDR RAM and SSD
What llama.cpp offers (master common/arg.cpp):
-ot/--override-tensor <regex>=<buft>places matching tensors, e.g.blk\.[0-9]+\.ffn_.*_exps=CPU.-cmoe/--cpu-moeputs all experts on the CPU.-ncmoe/--n-cpu-moe Nputs the experts of the first N layers on the CPU. These are MoE-only, so they do nothing for the dense 27B (already noted indocs/architecture.md).--moe-cache-mib N: an LRU cache in VRAM for host-resident experts, merged in #29887 (2026-10-07). On Flash-Next Q4_0 the PR measured 4090 25.0 → 39.4 t/s and 5090 30.8 → 67.8 t/s, on an EPYC host with RAM for all experts. "Small batches only (<= 32 tokens)".-lzm/--lazy-mode {on,auto,off}: "read the rows of such tensors from disk on demand instead of keeping them resident (requires mmap)".autoapplies to tensors over 4 GiB, which covers the 28.8 GB PLE table by default (renamed in #27969). Per #30772 (closed unmerged), mapped PLE pages still accumulate in RSS and single-threadedmadvisecan bottleneck NVMe.-lm/--load-mode {auto,none,mmap,…}. With mmap, a tensor that fits nowhere is paged from disk implicitly.- Pipeline parallelism turns off as soon as any tensor override is used (
!has_tensor_overrides()inllama-context.cpp, as described in the open PR #29963). So-ot/-ncmoeplus two GPUs also loses the layer-split prefill overlap.
Numbers for this box:
- Dense 27B: no useful offload. Every weight is read every token, and DDR4 is ~51 GB/s against ~440 GB/s VRAM. Each GB moved to RAM costs ~20-25 ms/token on the CPU (estimate). With two cards the Q8_K_XL fits in VRAM anyway. The only RAM tier that helps is
--cache-ram(prompt cache), as recorded inqwen3.8-27b-throughput-tuning.md. - Flash-Next UD-Q4_K_XL, if RAM were free (hypothetical): ~22 GB of experts on CPU = 29 % of experts, ~0.43 GB read per token from DDR4 at ~40 GB/s ≈ 11 ms. The GPU part ≈ (4.8 + 1.07) / 440 ≈ 13 ms. Layers are sequential, so the parts add up: ~24 ms ≈ ~40 t/s, more with
--moe-cache-mib(estimate). Prefill uploads the host experts once per ubatch: 22 GB over PCIe 4.0 x16 at ~25 GB/s ≈ 0.9 s per ubatch, so ~18 s of transfer for a 40k-token prompt at-ub 2048(estimate). - Flash-Next UD-Q4_K_XL, on this box: RAM is full (§1). Even with
--cache-ramcut to near zero, 32 GB minus OS, OmniRoute (up to 10 GB) and qdrant leaves maybe ~15 GB (estimate), less than the ~22 GB needed. The rest is paged from the CPU-attached Gen4 x4 NVMe. At ~0.43 GB/token and an assumed 3-6 GB/s for ~1-3 MB expert reads, that is ~70-140 ms/token, ~6-12 t/s. Prefill re-reads ~22 GB per ubatch from disk, so minutes per 40k-token prompt. The page cache will also compete with the swap that is already happening. An outside data point: an RX 9060 XT 16 GB with 64 GB DDR4-3200 got "8 t/s on short context. Longer context 3 t/s" on llama.cpp ROCm (#29149 comment). Not viable. - PLE table on SSD (any Flash-Next quant): 32 rows per token (#30772) of 90 B each (Strata
AMD_HIP.md), each on its own 4 KB page, so ≤128 KB of random IO per token. At 50 t/s that is a few thousand IOPS, trivial. Prefill touches ~32 pages per token, ~1.3 M random reads for 40k tokens. On NVMe at high queue depth that is a few seconds, but single-threadedmadvise(#30772) can make it much slower. Strata measured +22.5 % prefill with the table in RAM against direct reads (§6). Decode impact: low. Prefill: measure it.
6. Strata
Identity. github.com/Niko1221/Strata: "Qwen3.8-Flash-Next on any consumer hardware: one-click install for Windows / Linux. Strata inference engine, OpenAI/Anthropic API on localhost". C++, MIT (LICENSE). Its docs/HOW_IT_WORKS.md says it is "Built with parts of llama.cpp / ggml (MIT)". It supports Qwen3.8-Flash-Next (ISTA-DASLab GSQ-RCO quants recommended, Unsloth UD-IQ4_XS…Q8_0 supported), Qwen3.6-35B-A3B and DeepSeek V4.1-Flash. There is no dense Qwen3.8-27B support, so it cannot replace today's model.
How it tiers (docs/HOW_IT_WORKS.md, docs/MODELS.md):
- VRAM: attention/DeltaNet, routers, shared experts, output head, MTP, KV ("from 64K: only its most-read part, the rest streams from RAM"), plus an expert cache that fills the rest of VRAM with the most-used experts and adapts at runtime.
- RAM: "all 24,576 experts, pinned". The CPU computes cache misses in place, concurrently with the GPU (AVX-512/AVX2).
--pcie-fracsplits misses between PCIe fetch and CPU compute. On gfx12 it defaults to 0.15 after a measured +9.9 % to +25.6 % decode on an R9700 (docs/AMD_HIP.md). - SSD: the 28.8 GB n-gram table, "read a few rows per token through the OS cache". From 0.2.7 it keeps the table in RAM when there is room for it plus 8 GiB.
- Low-RAM mode: "setup maps them from the model files and keeps in RAM only what the card does not hold … With a small card most experts come from the SSD and it is much slower".
- Multi-GPU (
docs/MULTI_GPU.md): pipeline (layer) split, "not tensor parallelism". "No NVLink or peer-to-peer access is needed, cards on x4 or x1 slots work". Each card caches the experts of its own layers, so two cards hold about twice as many experts.docs/AMD_HIP.mddocuments HIP layer split for gfx1201 pairs and adds "A split is worth it when no single card holds the model's experts".
AMD/ROCm: the R9700 (gfx1201) is "Maintainer-tested", with Linux hip-rx9070 engine packages in every recent release (releases).
RAM requirement for the requested file: docs/MODELS.md lists UD-Q4_K_XL as "111 GB download, 72 GiB experts, RAM for all experts ~88 GB". With less RAM it "keeps as many as fit and reads the rest from the SSD (much slower)".
Claimed vs verifiable:
| Claim | Source | Host RAM | Verifiable? |
|---|---|---|---|
| UD-Q4_K_XL on one R9700: 63 t/s prose, 87 code, 32K prompt 1,110-1,320 t/s | docs/MODELS.md, "engines before 0.1.45.3" |
not stated there | Self-reported |
| IQ3_S on one R9700: 87.6-101.9 t/s across 1K-128K | community R9700 report | ~503 GiB, 2x EPYC 9334 | Has per-request JSON and hashes. "Single GPU still uses CPU experts, system RAM and PCIe; these rates do not establish desktop-CPU speed or minimum RAM" |
| R9700 + 9070 XT, 47 GB RAM, Coder IQ1_M: 45.5 / 48.3 t/s decode | docs/AMD_HIP.md |
47 GB | Self-reported |
| "125B+ MoE on a single 12GB GPU", "25-38 tok/s" | stratallm.org | n/a | The site calls itself "Unofficial Community Documentation". Its speed figures contradict each other and link no method. Ignore it |
None of these numbers were rerun for this doc. The project says its benchmark protocol "is still a draft" (docs/COMMUNITY_BENCHMARKS.md).
Maturity: repo created 2026-09-24. ~4,690 commits, ~3,594 of them by the one maintainer. ~36k stars, 895 open issues, a release roughly daily (0.1.40 → 0.2.7 in three weeks). Installation is curl … | sh, an AppImage, or Docker (docs/DOCKER.md, not read). It is young, single-maintainer and churning, and it would need its own container and gateway wiring here.
Verdict for this box: not worth it now. The 32 GB of RAM defeats its core design (all experts pinned in RAM). Low-RAM mode on 2x32 GB VRAM would still read ~20 GB of experts from NVMe for UD-Q4_K_XL. It cannot run the dense 27B that this stack is tuned around. Revisit if RAM goes to 96-128 GB (the board takes 128 GB). On a big-RAM host its self-reported R9700 Flash-Next numbers (60-100 t/s) are well above llama.cpp's 27B, so it would be worth a real A/B then.
7. Recommendation
Primary: keep Qwen3.8-27B-UD-Q4_K_XL + DFlash2 and run two single-GPU replicas, one per card.
Why:
- (1) Layer split gives a dense model no decode gain.
- (2) Tensor split is experimental on ROCm, measured at 1.31x, crashes with DFlash2, and would run over a PCIe 3.0 x4 chipset link.
- (3) Q8_K_XL roughly halves decode.
- (4) Flash-Next at a usable quant does not fit, and RAM offload pages from NVMe.
- (5) The workload is 2 parallel agents. The log already shows decode falling to p10 15 t/s while the other slot prefills (
qwen3.8-27b-throughput-tuning.md). Two replicas remove that interference and double each slot's context.
Per-instance flags (two services, identical except the device). Each is today's command with these changes:
--device ROCm0 # ROCm1 for the second service; confirm names with --list-devices
--n-gpu-layers 999
--ctx-size 262144 --parallel 1 # full 262144 per agent instead of 131072
--flash-attn on --cache-type-k q8_0 --cache-type-v q8_0
--cache-ram 6144 # per instance; both together must stay well under free RAM (§1)
--spec-type draft-dflash -md /models/Qwen3.8-27B-DFlash2-Q4_K_M.gguf --spec-draft-n-max 7
--jinja
GPU_MAX_HW_QUEUES=1 stays on both services (docs/architecture.md#gpu-access). Gateway: register both backends in OmniRoute and make sure a conversation stays on one backend (prompt-cache affinity). Otherwise every switch is a full re-prefill. Whether OmniRoute can balance two connections per conversation was not checked.
Independently of the second card: lower LLAMA_CACHE_RAM. 24 GiB on a 32 GB host with a 10 GB OmniRoute limit oversubscribes RAM.
Alternatives to A/B, in order:
- Single fast stream: Q4_K_XL with
-sm tensor -ts 1,1 -fa on --fit off --ctx-size <set by hand> --spec-type draft-mtp --spec-draft-n-max 2(the built-in MTP head; DFlash2 crashes withtensor). Estimate ~34 t/s no spec, ~50 with MTP if stable. Watch for #27122-style lockups. - Quality: Q8_K_XL (or UD-Q6_K_XL at 25.3 GB) with
-sm layer, DFlash2 placed with-devd, ~25 t/s estimated. Only if Q4 quality is shown to fail on your tasks. - Flash-Next experiment: UD-IQ3_XXS or UD-Q2_K_XL, all in VRAM:
-sm layer -ngl 999 --lazy-mode auto --load-mode none -fa on -c 131072, MTP via-md MTP/mtp-Qwen3.8-Flash-Next-shared-Q8_0.gguf --spec-type draft-mtponce #29811/#30860 are fixed. Judge it on your own agent tasks. The KLD at these sizes is 3.5-5x that of UD-Q4_K_XL.
8. What to benchmark once the card arrives
- Topology:
sudo lspci -vvLnkSta for both cards (expect 16 GT/s x16 and 8 GT/s x4). Runrocm-smi --showtopoandrocm-bandwidth-testfor host↔GPU and GPU↔GPU (does P2P work through the chipset?). Check the PSU and the case slot fit first. - Per-card baseline:
llama-bench -m Qwen3.8-27B-UD-Q4_K_XL.gguf -dev ROCm0and then-dev ROCm1,-fa 1 -ctk q8_0 -ctv q8_0 -p 512,8192 -n 128 -d 0,32768. Decode should match. Load time and prompt-cache restores are where the x4 link shows. - Split modes: the same bench with
-sm layervs-sm tensor -ts 1,1for Q4_K_XL and Q8_K_XL. Compare tg against #28863's 1.31x and pp against layer. - Server replay (fixed ~40k and ~2k prompts, production temperature, concurrency 1 and 2) for: (a) two replicas, (b) one
-sm layer --parallel 2, (c)-sm tensor+ MTP. Recordtimings.predicted_per_second,prompt_per_second, draft acceptance, and the p10 decode under concurrency. - Correctness: a needle test at ~6-10k and ~60-90k tokens on each config, because #30884 reproduces on 2x R9700 with
--tensor-split. Also check that DFlash2 output under-sm layermatches single-card output on a fixed prompt. - RAM:
free -handvmstat 5after an hour of agent traffic, before and after lowering--cache-ram. Swap-in should be ~0. - Optional Flash-Next: that UD-IQ3_XXS/UD-Q2_K_XL loads at all on HIP (#29149, try
--load-mode none), tg/pp at 4k/32k, and prefill time with the PLE table on NVMe.
Open questions
- Does OmniRoute support two backends with sticky routing per conversation?
- Does the
ghcr.io/ggml-org/llama.cpp:server-rocmimage build withGGML_HIP_RCCL? It only matters for-sm tensor. - Does ROCm P2P work between a CPU-attached slot and a chipset-attached slot on B550? It is unverified, and the answer probably does not matter outside
tensormode. - Is there a B550/X570 board with CPU x8/x8 bifurcation that fits this chassis? It would give both cards Gen4 x8. It is only worth it if step 3 shows
tensorscaling well. No specific board was checked.
Sources
- Hugging Face: unsloth/Qwen3.8-27B-GGUF tree, its config.json, UD-Q8_K_XL file, unsloth/Qwen3.8-Flash-Next-GGUF and its tree, UD-Q4_K_XL shard 1, z-lab DFlash2 GGUF, incoai Flash-Next DFlash2 GGUF. Tensor sizes come from parsing the GGUF headers (range requests) with a throwaway script.
- Unsloth: Qwen3.8-Flash-Next run guide (sizes, KLD table, MTP flags).
- llama.cpp master (fetched 2026-10-29):
docs/multi-gpu.md,common/arg.cpp,src/llama-arch.cpp,src/llama-context.cpp. PRs: #19378, #23792, #27342, #27742, #27825, #27858, #27861, #27969, #28569, #29761, #29887, #29963, #30772, #30815, #31243. Issues: #23567, #27122, #27829, #28582, #28777, #28863, #29149, #29811, #30860, #30884, #30887. - Hardware: ASRock B550M Pro4 manual (asrock.com's spec page returned no content to the fetcher), ComputerBase B550, Guru3D B550 PCIe, Puget R9700, Tom's Hardware R9700.
- Strata: repo (README, LICENSE,
docs/HOW_IT_WORKS.md,MODELS.md,MULTI_GPU.md,SECOND_GPU.md,AMD_HIP.md,COMMUNITY_BENCHMARKS.md, read via the GitHub API on 2026-10-29), releases, stratallm.org. - This repo:
docker-compose.yml,.env.example,docs/architecture.md,qwen3.8-27b-throughput-tuning.md,qwen3.8-27b-quant.md,500k-context-2-parallel-agents-gpu-roadmap.md.