Optimal llama.cpp + Qwen3.8-Flash-Next settings on Strix Halo
updated on:Optimal configuration for running Qwen3.8-Flash-Next (176.94B MoE, qwen4exp arch) on a Strix Halo MiniPC with AMD Ryzen AI Max+ 395 (128 GiB LPDDR5x, Vulkan backend).
Requirements
| Component | Description |
|---|---|
| Hardware | FEVM FA-EX1 – Ryzen AI Max+ 395, Radeon 8060S (gfx1151), 128 GiB LPDDR5x-8500 (~215 GB/s bandwidth, ~112 GiB GTT on Linux) |
| llama.cpp | strix-halo-llamacpp v0.7.5 (source dff60048, Vulkan, bundles Mesa 26.3.0-devel) – required |
| Model | Qwen3.8-Flash-Next-UD-Q4_K_XL (~103.69 GiB, 4 shards, 176.94B params) |
| Model (smaller) | Qwen3.8-Flash-Next-UD-IQ4_XS (~87.25 GiB, 3 shards) – fits more context, slightly lower quality |
| Model (MTP) | mtp-Qwen3.8-Flash-Next-shared-Q8_0.gguf (~2.6 GiB) – shared MTP heads for speculative decoding |
| mmproj | mmproj-BF16.gguf (~0.85 GiB, multimodal, optional) |
| Template | chat_template.jinja v21.3 |
Model architecture (qwen4exp): 48 blocks, hybrid SSM (Gated DeltaNet) + full attention every 4th layer, QSA sparse-attention indexer (top_k=2048, compress_ratio=4 → 12 QSA layers), PLE layer (16 n-gram heads), MoE 512 experts / 10 active per token, 262144-token context.
The “why special build”
qwen4exp routes attention through a QSA sparse-attention indexer. In stock llama.cpp, on every decode step the indexer re-pools the entire block-key cache — O(n_kv) work per token, in every QSA layer. As context grows this dominates decode and throughput collapses.
The fix (upstream issue ggml-org/llama.cpp#28012, in strix-halo-llamacpp v0.7.5) caches the pooled block keys per block and recomputes only the tail. Measured on this box (Q4_K_XL, depth 32k, no MTP):
| Build | pp512 @ 32k | tg32 @ 32k |
|---|---|---|
stock pr28243 | 215.6 t/s | 20.3 t/s |
| strix-halo-llamacpp v0.7.5 | 321.8 t/s (+49%) | 23.6 t/s (+16%) |
The prebuilt tarball bundles a matched Mesa 26.3.0-devel RADV driver and a _run wrapper that wires it up — no system driver changes needed.
Getting the build
# Prebuilt (recommended) -- self-contained, bundles the Vulkan driver
curl -LO https://github.com/Nathanw1014/strix-halo-llamacpp/releases/download/v0.7.5/strix-halo-llamacpp-vulkan-portable.tar.gz
tar xzf strix-halo-llamacpp-vulkan-portable.tar.gz
# -> vulkan/bin/{llama-server,llama-cli,llama-bench}, vulkan/driver/, vulkan/_runThe _run wrapper (or the env below) sets up the bundled driver and the perf flags:
HERE=/path/to/strix-halo-llamacpp/vulkan
export VK_ICD_FILENAMES="$HERE/driver/radeon_icd.x86_64.json"
export VK_DRIVER_FILES="$VK_ICD_FILENAMES"
export LD_LIBRARY_PATH="$HERE/driver:$HERE/bin"
export GGML_VK_MMID_ROWLISTS=1 GGML_VK_MMID_SMALLN=1 GGML_VK_MMID_BM64=1
export GGML_VK_MMID_WAVE32=1 GGML_VK_MMID_F16B=1 GGML_VK_MMID_M128=1
export GGML_VK_FA_WAVE32=0Basic Launch (Vulkan)
#!/usr/bin/env bash
HERE=/path/to/strix-halo-llamacpp/vulkan
MODEL=/path/to/Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf # all 4 shards auto-loaded
TEMPLATE=/path/to/chat_template.jinja
# (driver env from "Getting the build" above, or just call $HERE/_run)
$HERE/bin/llama-server \
--host 0.0.0.0 \
--port 1234 \
-c 0 \
--n-gpu-layers 999 \
--batch-size 2048 \
--ubatch-size 1024 \
--flash-attn on \
--temp 1.0 \
--top-p 0.95 \
--top-k 20 \
--min-p 0.0 \
--prio 2 \
--cpu-strict 1 \
--threads 16 \
--threads-batch 32 \
--no-warmup \
--jinja \
--chat-template-file ${TEMPLATE} \
-ctk q8_0 \
-ctv q8_0 \
--parallel 1 \
--metrics \
--alias "qwen3.8-flash-next-q4_k_xl" \
-m ${MODEL}Parameters and Rationale
| Parameter | Value | Why |
|---|---|---|
-c (context) | 0 | Unbounded (model supports 262144). KV grows as needed; -ctk/-ctv q8_0 keeps it small |
--n-gpu-layers | 999 | Offload everything to the iGPU (UMA: “GPU memory” is system RAM) |
--batch-size | 2048 | Prompt-processing batch; bandwidth-bound, 2048 is fine |
--ubatch-size | 1024 | Micro-batch size. Tested 1024 vs 2048 – no difference (bandwidth-bound MoE). 1024 keeps peak RAM lower |
--flash-attn | on | FA on the bundled Mesa 26.3 is fast and stable for this model |
--temp / --top-p / --top-k | 1.0 / 0.95 / 20 | This is a reasoning model (thinking mode via the template); looser sampling is appropriate |
--min-p | 0.0 | Disabled – not needed when top-p/top-k are set |
--prio 2 --cpu-strict 1 | – | Elevated OS priority + strict thread pinning (PLE layer runs on CPU). --prio needs permission to set nice; harmless warning if denied |
--threads / --threads-batch | 16 / 32 | CPU threads for the PLE layer and batch work; matches the 16-CPU Strix Halo |
-ctk q8_0 -ctv q8_0 | q8_0 | Compresses the KV cache. Required – default (f16) drops prefill to ~70 t/s at depth. q4_0 gives no speed win and lower quality |
--parallel | 1 | Required. The QSA pool cache is single-slot; >1 desyncs the indexer (upstream bug). One slot per server |
--no-warmup | – | Skip the warmup pass (saves load time) |
Multi-model / on-demand: run a router instead of one fixed model:
llama-server --models-preset presets.ini --models-max 2 --host 0.0.0.0 --port 1234and put the flags above under a[qwen3.8-flash-next-q4_k_xl]section inpresets.ini. Models load on demand.
Multimodal Support (mmproj)
MM=/path/to/mmproj-BF16.gguf
# add to the launch command:
--mmproj ${MM} \Advanced Launch (MTP Speculative Decoding)
MTP uses a separate shared-heads draft (mtp-...-shared-Q8_0.gguf) to predict multiple tokens per step. On this box it delivers +33% decode (25.4 → 33.9 tok/s at ~4k depth) over no-MTP.
Add to the Basic Launch command:
MTP=/path/to/mtp-Qwen3.8-Flash-Next-shared-Q8_0.gguf
--spec-type draft-mtp \
--spec-draft-model ${MTP} \
--spec-draft-n-max 3 \MTP Parameters
| Parameter | Value | Why |
|---|---|---|
--spec-type | draft-mtp | MTP speculative decoding |
--spec-draft-model | shared Q8_0 | The shared MTP heads file (loaded as the drafter) |
--spec-draft-n-max | 3 | Up to 3 draft tokens/step. Tested 3 vs 5 – 5 is within noise (33.9 vs 34.7 tok/s) but adds checkpoint-restore overhead. 3 is the safe default |
v0.7.5 MTP uses context checkpoints (≈112 MB × 48 layers per rejected round). It’s a net win here, but if you ever see it go negative, drop
--spec-draft-n-maxto 1 or disable MTP.
Performance
Measured (strix-halo-llamacpp v0.7.5, bundled Mesa 26.3)
Depth 32768, Q4_K_XL, llama-bench (no MTP):
| Metric | Value |
|---|---|
| Prompt processing (pp512 @ 32k) | ~320 tok/s |
| Decode (tg32 @ 32k, no MTP) | ~23.5 tok/s |
| Decode (MTP n-max=3) | ~30–34 tok/s (depth-dependent) |
vs stock pr28243 (same flags): 15–17 tok/s decode, ~216–300 tok/s PP.
HIP / ROCm Backend (alternative)
The Vulkan build above is the safest choice, but a HIP/ROCm build is a strong alternative on this box. pwilkin/llama.cpp @ strix-halo (b0f31f58, 2026-09-16) is compiled against stock ROCm 10 with a custom retained-PM4 ROCr/HIP pair (pwilkin/rocm-systems @ ilintar-experiments) injected at runtime via LD_LIBRARY_PATH. It carries both the QSA pool-cache fix and the GPU TOP_K fix, and measures ~1.7× faster on prefill at decode parity (no MTP).
A/B (Q4_K_XL, depth 32k, identical flags)
| test | HIP (retained-PM4) | Vulkan (v0.7.5) | ratio |
|---|---|---|---|
| pp512 @ 32k | ~540 t/s | 326.97 t/s | ~1.7× |
| tg8 @ 32k (no MTP) | 22.07 t/s | 22.46 t/s | 0.98× |
With MTP (--spec-type draft-mtp, shared Q8_0 head) decode reaches ~43 tok/s (measured, ≤2k ctx) — vs ~34 tok/s for the Vulkan build at 4k depth; at 8k with real coding workloads the community reports ~47 tok/s (#27950). The GPU TOP_K fix (hipCUB / radix, #26592 / #27466) keeps the QSA indexer’s ggml_top_k on-GPU past ne=1024, where stock HIP falls back to CPU and decode collapses (the knee is at d1024).
Differences vs the Vulkan build
| aspect | Vulkan (v0.7.5) | HIP (strix-halo) |
|---|---|---|
| KV cache type | q8_0 (fast, small) | f16 required — q8_0 hits a GGML_ASSERT in qwen4exp |
| depth-collapse fix | QSA pool-cache #28012 | QSA pool-cache + GPU TOP_K (hipCUB/radix) |
| prefill @ 32k | ~327 t/s | ~540 t/s |
| runtime | bundled Mesa 26.3 RADV | retained-PM4 ROCr/HIP via LD_LIBRARY_PATH |
Building it (Docker, gfx1151)
A self-contained stack (Dockerfile + docker-compose.yaml + build.sh + fetch-sources.sh + .env) compiles the three components — ROCr → HIP (clr) → llama.cpp — for gfx1151, then serves/benches it with ROCm GPU passthrough. The key idea: llama.cpp compiles against stock /opt/rocm; the retained-PM4 ROCr/HIP pair is built separately and injected at runtime via LD_LIBRARY_PATH.
Dockerfile
ARG BASE_IMAGE=rocm/dev-ubuntu-24.04:10.0.0-full
FROM ${BASE_IMAGE}
# build deps — note the non-obvious ones: xxd (ROCk HSACO->ASCII),
# libgl-dev (clr OpenGL), libelf-dev (ROCk), CppHeaderParser (clr hipamd)
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential cmake ninja-build git pkg-config ccache python3 python3-pip \
xxd perl libdrm-dev libnuma-dev libelf-dev libdwarf-dev \
libgl-dev libegl-dev libglu1-mesa-dev libopenmpi-dev libssl-dev \
&& rm -rf /var/lib/apt/lists/*
RUN pip3 install --break-system-packages --no-cache-dir CppHeaderParser
WORKDIR /work
COPY fetch-sources.sh build.sh ./
RUN ./fetch-sources.sh # clone the two repos at pinned commits (see .env)
RUN ./build.sh # ~1h: ROCr -> HIP(clr) -> llama.cpp (GGML_HIP, gfx1151)
# custom ROCk/HIP (retained-PM4) FIRST so they shadow stock /opt/rocm at load
ENV LD_LIBRARY_PATH=/work/runtime/hip/lib:/work/runtime/rocr/lib:/opt/rocm/lib:/opt/rocm/lib64:/opt/rocm/lib/llvm/lib:/work/build/llama.cpp/bin \
HSA_OVERRIDE_GFX_VERSION=11.5.1 ENABLE_RETAINED_PM4=1 HSA_ENABLE_SDMA=0
ENV PATH=/work/build/llama.cpp/bin:${PATH}build.sh (the three components)
# 1/3 ROCk (retained-PM4 HSA runtime)
cmake -S src/rocm-systems/projects/rocr-runtime -B build/rocr -G Ninja \
-DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=$RUNTIME/rocr
cmake --build build/rocr -j$(nproc) && cmake --install build/rocr
# 2/3 HIP/clr (retained-PM4). HIP_COMMON_DIR must be the HIP *source* tree
# (has include/hip + cmake + *.cmake.in); stock /opt/rocm lacks the .in templates.
HIP_COMMON_DIR=$SRC/rocm-systems/projects/hip
cmake -S src/rocm-systems/projects/clr -B build/hip -G Ninja \
-DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DCLR_BUILD_HIP=ON -DCLR_BUILD_OCL=OFF \
-DROCM_PATH=$RUNTIME/rocr -DCMAKE_PREFIX_PATH=$RUNTIME/rocr;/opt/rocm \
-DHIP_COMMON_DIR=$HIP_COMMON_DIR -DHIP_PLATFORM=amd \
-DLLVM_BIN=/opt/rocm/lib/llvm/bin -DClang_BIN=/opt/rocm/lib/llvm/bin \
-DCMAKE_INSTALL_PREFIX=$RUNTIME/hip
cmake --build build/hip -j$(nproc) && cmake --install build/hip
# 3/3 llama.cpp (GGML_HIP, gfx1151) — against stock /opt/rocm
cmake -S src/llama.cpp -B build/llama.cpp -G Ninja \
-DCMAKE_BUILD_TYPE=Release -DGGML_HIP=ON -DGGML_HIP_NO_VMM=ON -DGGML_HIP_GRAPHS=ON \
-DGGML_HIP_MMQ_MFMA=ON -DGGML_CUDA_FA=ON -DGGML_NATIVE=ON -DGGML_OPENMP=ON \
-DGPU_TARGETS=gfx1151 -DCMAKE_HIP_COMPILER=/opt/rocm/lib/llvm/bin/clang++
cmake --build build/llama.cpp -j$(nproc)docker-compose.yaml (ROCm GPU passthrough + server)
x-rocm-gpu: &rocm-gpu
devices: ["/dev/kfd", "/dev/dri"] # ROCm = raw KFD + DRM nodes
security_opt: ["seccomp:unconfined"]
environment:
- HSA_OVERRIDE_GFX_VERSION=11.5.1
- ENABLE_RETAINED_PM4=1
- HSA_ENABLE_SDMA=0
volumes: ["/mnt/local/models:/mnt/local/models:ro"]
services:
server:
build: .
image: strix-pwilkin:gfx1151
<<: *rocm-gpu
ports: ["${SERVER_PORT:-1234}:1234"]
command: >
llama-server
-m $MODEL -md $MTP_DRAFT --mmproj $MM_PROJ
--spec-type draft-mtp --spec-draft-n-max 3
-ngl 999 -fa on -ctk f16 -ctv f16
-t 16 -tb 32 -b 2048 -ub 1024
-c ${CTX:-262144}
--cache-ram 24576 --cache-reuse 1024
--host 0.0.0.0 --port 1234 --jinja
restart: unless-stopped
bench:
image: strix-pwilkin:gfx1151
<<: *rocm-gpu
command: >
llama-bench -m $MODEL -ngl 999 -fa on -p 512 -n 8 -d 32768
-b 2048 -ub 1024 -ctk f16 -ctv f16 -t 16 -r 3
profiles: ["bench"].env (pinned commits + model + ctx)
LLAMA_GIT_SHA=b0f31f5876ef3856b55f5bb88072cc96e5effafe # pwilkin/llama.cpp@strix-halo
ROCM_SYSTEMS_GIT_SHA=7dda3ac6cfe6bbe0b7f08c23a67cfa118d8641a1 # pwilkin/rocm-systems@ilintar-experiments
GPU_TARGETS=gfx1151
CTX=262144
SERVER_PORT=1234
MM_PROJ=/mnt/local/models/.../Qwen3.8-Flash-Next-GGUF/mmproj-BF16.gguf # vision projectorRun it
cd strix-pwilkin/
docker compose build # ~1h; bakes /work/build/llama.cpp/bin + /work/runtime/{rocr,hip}
docker compose up -d server # serve: ctx 262144, MTP on, f16 KV, prompt cache, port 1234
docker compose run --rm bench # plain-decode A/B (llama-bench can't see draft flags)Launch flags are the Basic Launch set, except use -ctk f16 -ctv f16 (not q8_0). --cache-ram 24576 --cache-reuse 1024 enable the in-process prompt cache with KV shifting; note f16 KV holds ~half the tokens of q8_0 in the same RAM budget, so raise --cache-ram if prefix reuse is critical. --mmproj enables vision, but llama.cpp disables cache_reuse when a multimodal model is loaded (KV shifting is incompatible with image tokens) — so with --mmproj the prompt cache still works for repeated prefixes, but KV-shifting reuse is off.
For Other Tasks
The template enables thinking mode. Give it enough max_tokens to finish reasoning before the final answer.
Agentic / coding (deterministic)
--temp 0.3 --top-p 0.5 --top-k 5Creative text
--temp 1.0 --top-p 0.95 --top-k 40Known Issues
- Stock
llama.cppis unusable at depth – the QSA indexer re-pools the block-key cache O(n_kv)/token. Usestrix-halo-llamacppv0.7.5+ (or a build with the QSA pool-cache fix). This is the #1 gotcha. --parallel 1is mandatory – the QSA pool cache is single-slot; multiple slots desync the indexer. Run one server per model (or a router with one slot each).- KV cache type – default (f16) drops prefill to ~70 tok/s at growth. Always set
-ctk q8_0 -ctv q8_0. - Chat template is required – use chat_template.jinja v21.3; the default template can break tool calls / loops.
- Slot-save files are version-locked – v0.7.5’s indexer V-cache is 1-element wide; slot-save files from other builds won’t restore. In-process prompt cache is unaffected.
- Bundled driver – the build ships Mesa 26.3.0-devel. Use the
_runwrapper or setVK_ICD_FILENAMES/LD_LIBRARY_PATH(see “Getting the build”); the host driver (Mesa 26.1) is ~7% slower on prefill. --prio 2may printPermission deniedif the process can’t set nice values – harmless, the server runs at default priority.
Links
- strix-halo-llamacpp – the Vulkan build with the QSA pool-cache fix
- pwilkin/llama.cpp @ strix-halo – the HIP/ROCm build (retained-PM4, QSA pool-cache + GPU TOP_K fix)
- pwilkin/rocm-systems @ ilintar-experiments – the retained-PM4 ROCr/HIP pair injected at runtime
- Qwen3.8-Flash-Next GGUF (unsloth) – Q4_K_XL, IQ4_XS, MTP shared heads, mmproj
- upstream issue #28012 – QSA indexer re-pooling (the decode bottleneck)
- chat_template.jinja v21.3
- llama-bench
- Optimal llama.cpp + Qwen3.6 settings on Strix Halo – the dense 27B recipe (companion)