The hardware
- GPU: AMD Radeon AI PRO R9700 (RDNA 4, gfx1201, 32 GB VRAM, 300 W)
- Second GPU: Intel Arc A750. It drives the monitor and runs the small helper jobs.
- CPU and RAM: Intel i7-13700K, 64 GB RAM
- OS: Kubuntu 26.10, ROCm 10 for the AMD card, Vulkan for the Arc
The software stack
| Job | Tool | Runs on |
|---|---|---|
| Chat UI | Open WebUI | |
| Main LLMs | vLLM, Strata, llama.cpp | R9700 |
| Images and video | ComfyUI (Z-Image Turbo, FLUX.3 Klein, LTX-2.5) | R9700 |
| Prompt improver for images | llama.cpp (Vulkan) | Arc A750 |
| Speech-to-text | whisper.cpp (large-v3) | Arc A750 |
| Text-to-speech | Piper (English), OmniVoice (Serbian) | CPU |
| Voice assistant | Home Assistant | |
| Local coding agent | Claude Code pointed at Strata | R9700 |
The models I compared
The hard part is fitting a good LLM and leaving room for image generation on one card. I tested three approaches:
- Swift 27B on vLLM. Swift 1.5, a fine-tune of Qwen3.8-27B, quantized to MXFP4, with a DFlash2 speculative-decoding drafter. vLLM gets 66% of the VRAM: 64k of context, and enough room left for ComfyUI.
- Flash-Next on Strata. Qwen3.8-Flash-Next is a mixture-of-experts model, here an IQ2_XS quant. Strata keeps the experts in system RAM and caches the hot ones in VRAM. I set it to always leave 11 GB of VRAM free for ComfyUI.
- Swift Flash-Next on Strata. The Swift fine-tune of Flash-Next, same quant and same settings.
For reference I also ran two models on plain llama.cpp.
How I benchmarked
A small script talked to each server’s OpenAI-compatible API, with streaming on and ComfyUI loaded on the same card the whole time:
- Single-user decode: four task types (chat, code, prose, JSON) at temperature 0 and 1, best of two runs, 300–700 output tokens each.
- Time to first token on a short prompt.
- Long-prompt processing at roughly 3k, 12k and 46k input tokens.
- Concurrency: 1, 4 and 8 simultaneous requests.
- Reasoning test: six classic trick questions with thinking on, checked for correctness.
Decode speed, one user
Tokens per second:
| Task | Swift 27B (vLLM) | Flash-Next (Strata) | Swift Flash-Next (Strata) |
|---|---|---|---|
| Code | 204–214 | 124–136 | 102–113 |
| JSON | 146–170 | 114–117 | 102–114 |
| Chat | 100–125 | 91–98 | 76–84 |
| Prose | 81–95 | 82–84 | 71–79 |
Swift on vLLM wins clearly on code and JSON, where the speculative drafter guesses well. On free-form prose the three are close.
Latency and long prompts
| Swift 27B (vLLM) | Flash-Next (Strata) | Swift Flash-Next (Strata) | |
|---|---|---|---|
| Time to first token (short prompt) | 0.08 s | 0.52 s | 0.77 s |
| 3k-token prompt | 0.8 s | 2.1 s | 2.4 s |
| 12k-token prompt | 3.3 s (~3,700 tok/s) | 5.1 s (~2,450 tok/s) | 6.4 s (~1,950 tok/s) |
| 46k-token prompt | 13.8 s (~3,350 tok/s) | 18.1 s (~2,550 tok/s) | 21.8–24.4 s (~2,000 tok/s) |
Several users at once
Total tokens per second across all requests:
| Users | Swift 27B (vLLM) | Flash-Next (Strata) | Swift Flash-Next (Strata) |
|---|---|---|---|
| 1 | 121 | 67 | 56–58 |
| 4 | 350 | 106 | 89–90 |
| 8 | 339 | 107 | 90–91 |
This is where vLLM really pulls ahead: it batches requests, so four users get almost three times the total throughput. Strata stays at about 106 tok/s however many users there are.
Tuning vLLM for several users
The Swift build shipped with settings for one user. I changed two:
- Speculative tokens: 15 to 7
- Max batched tokens: 2048 to 4096
Single-user code generation dropped by about 25%, and throughput with 4 to 8 users rose 53–84%. All the Swift numbers above are with the tuned settings.
Reasoning with thinking on
| Flash-Next | Swift Flash-Next | |
|---|---|---|
| Correct | 6/6 | 6/6 |
| Total output tokens | 707 | 722 |
| Total time | 16.2 s | 13.5 s |
The Swift fine-tune didn’t think noticeably less on easy questions, and both got everything right.
Why the Swift fine-tune was slower on Strata
Swift Flash-Next was 10–25% slower than the original everywhere. Strata’s expert-cache profile is built for the original model, so with the fine-tune the cache hit rate started at about 35% and only climbed to about 63%. More expert lookups fall through to system RAM.
Reference: llama.cpp
| Model | Decode |
|---|---|
| Qwen3.8-27B Q6 | ~22 tok/s |
| Qwen3.6-35B-A3B Q4 | ~87 tok/s |
How this compares with other R9700 owners
My numbers line up with what other single-R9700 users have published. The one gap is Swift with 8 users, where published runs reach about 480 tok/s against my 339. They give vLLM 98% of the VRAM; I give it 66% so image generation can run alongside.
Claude Code: Anthropic’s servers against my local model
Strata offers an Anthropic-compatible API, so I set up claude-local: the regular Claude Code app with its own config folder, pointed at Flash-Next on Strata. I gave it and regular Claude Code (Opus 5.5) the same tasks. Each task had hidden checks the agents never saw.
Round 1: five everyday tasks
| Task | Opus 5.5 | claude-local |
|---|---|---|
| 1. Fix 3 bugs so the tests pass, without touching the tests | Pass, 8 s, 3 steps | Pass, 21 s, 8 steps |
| 2. Build a duration parser and formatter from a spec (34 hidden checks, including tricky invalid inputs) | 34/34, 36 s | 34/34, 52 s |
| 3. Rename a function across 4 files, add a default value, CLI error handling (10 checks) | 10/10, 35 s, 11 steps | 10/10, 50 s, 24 steps |
| 4. Log-summary script with an exact output format, tested on a log it never saw | Exact, 22 s | Exact, 52 s |
| 5. Answer questions about how one of my scripts behaves | All correct, 25 s | All correct, 35 s |
| Total | 126 s, 27 steps | 210 s, 53 steps |
Task 4 had a trap: read the spec literally and the reason “llama.cpp is loaded…” gets cut to “llama”. Both got it right. The one difference in depth: on the code question, only Opus also spotted a subtler timing race in the script.
Round 2: one harder task
A broken repository with five separate problems:
- Python 2
printsyntax - a version mismatch
- a data file path that only worked from the repo folder
- a helper script that couldn’t import the package
- a test command that breaks on Python 3.14, to be fixed without editing the tests
| Opus 5.5 | claude-local | |
|---|---|---|
| Hidden checks | 10/10 | 10/10 |
| Time | 59 s | 164 s (2.8× longer) |
| Steps | 24 | 39 |
Both found all five problems. The difference was in how they fixed a circular import. Opus removed the unnecessary import and warned that outside code using it would break. The local model moved one line so the loop no longer fails, which works but is fragile. Tests don’t catch that kind of quality gap.
Takeaway
- Correctness: the local model matched Opus on every task.
- Speed: it was 1.7 to 2.8 times slower, and the gap grew on harder work.
- Cleanliness: its fixes were a little less clean, and it read code less deeply.
- Cost: free and fully private, against about $1.20 of usage for the Opus runs.
These were well-defined tasks with a clear “done” test. Long, open-ended system work, where nothing tells the agent it’s wrong, is where I’d still use Opus.
Images: the prompt improver makes the difference
ComfyUI runs Z-Image Turbo for text-to-image and FLUX.3 Klein for text-to-image and editing. Both are hooked into Open WebUI, so you can ask for an image in a chat.
The biggest quality jump came from rewriting the prompt first. A short request becomes a detailed scene description, and the results look clearly more realistic and lived-in. The rewriting runs in a chain:
- First choice: a small vision model on the Arc A750, using Black Forest Labs’ official prompt-upsampling instructions. About 38 tok/s: roughly 10 s for text-to-image and 14 s for edits.
- Fallback: the main LLM on the R9700.
- Last resort: the raw prompt.
Timings and fixes:
- Z-Image Turbo: about 15 s plain. About 40–44 s with the prompt improver plus a detail pass (1.25× upscale, a light second sampling pass at denoise 0.3, a touch of film grain).
- FLUX.3 resolution: running at about 2 megapixels (1440×1440) instead of 1 fixed most eye defects on small faces.
- Prompt rules: a few pose and clutter rules in the improver’s instructions stopped awkward half-kneeling legs and overly busy scenes.
Video, and a ROCm bug
LTX-2.5 (22B) does image-to-video but needs the whole card, so a video mode unloads the LLMs first. The LLM proxy refuses to restart Strata while a video renders. I learned that the hard way, when a chat request reloaded the LLM mid-render and the video job ran out of memory.
The nastiest problem was videos coming out as pure noise or black frames. It turned out to be a ROCm 10.0 bug with dynamic VRAM on gfx1201, fixed in ROCm 10.1. Upgrading ComfyUI’s PyTorch to the ROCm 10.1 build fixed it: three back-to-back video runs came out clean.
Voice assistant
Home Assistant uses the box for speech-to-text, the LLM and text-to-speech, in English and Serbian:
- Speech-to-text: whisper large-v3 on the Arc.
- LLM: Flash-Next on Strata with thinking off. It replies in 0.4–0.5 s when warm and about 1.5 s cold.
- Device control: 9 out of 9 commands correct in both languages.
- State questions (“is a light on?”): the model guessed at first. After an instruction to never guess and always check the live state, it got 12 out of 12.
Switching costs
| Action | Time |
|---|---|
| Strata cold start | ~37 s |
| Switching between the two Strata models | 35–45 s |
| Loading Swift 27B in vLLM | 2–6 min |
Which one do I use?
Flash-Next on Strata is my daily driver. It’s slower than Swift on vLLM, but it leaves 11 GB for image generation, has vision, reasons well, and runs the voice assistant and the local coding agent. Swift on vLLM stays installed for raw speed or many users at once.
Lessons
- 32 GB is enough for a full stack if you plan the VRAM. Reserve memory for image generation explicitly, and don’t let the LLM take it all.
- A cheap second GPU is worth it. Moving speech-to-text and the prompt improver to the Arc freed the main card.
- MoE with the experts in RAM is a real option. Slower than a dense model fully in VRAM, but a much bigger model in a fraction of the VRAM.
- Tune for how you’ll actually use it. Fewer speculative tokens cost a little single-user speed and gained a lot with several users.
- A local coding agent is genuinely useful. On well-defined tasks it matched a frontier model on correctness at about half the speed, for free.
- On a new GPU architecture, suspect the driver stack. The “corrupted cache” was a ROCm bug, and updating fixed what no workaround could.