Guest User

Untitled

a guest
Oct 25th, 2026
101
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.74 KB | Software | 0 0
  1. Qwen3.8-27B on an RTX 3090: Inference Options and Benchmark Results
  2.  
  3. Oct 24, 2026 ยท @Someone
  4. Summary
  5.  
  6. HyperQwen (a patched vLLM in Docker) is the fastest way to run Qwen3.8-27B on an RTX 3090: 133 tokens/s single-stream with a 94 ms time to first token, 3.2x the 41 t/s of the stock llama.cpp router. Among llama.cpp builds, the llamAmpere fork with MTP speculative decoding at depth 2 and its turbo4 KV cache reaches 73 t/s (1.76x) and keeps the router, the GGUF models and the 64k context intact. Compiling stock llama.cpp specifically for sm_86 changes nothing, and on stock llama.cpp every speculative-decoding preset (MTP, DFlash2) is slower than plain decoding.
  7.  
  8. The work shipped as NixOS specialisations (llm-fa86, llm-llamampere), a benchmark tool (llm-bench), a corrected base preset set, and a Docker setup for HyperQwen outside Nix. Two decisions remain open: whether to make the llamAmpere configuration the router default, and whether HyperQwen earns a permanent place as an alternative engine.
  9. Setup and method
  10.  
  11. All numbers come from one machine, one model and one protocol, measured on 2026-10-24.
  12.  
  13. Item
  14.  
  15.  
  16. Value
  17.  
  18. Host
  19.  
  20.  
  21. NixOS unstable (nixpkgs 02c087f), kernel 6.18, 32 GB RAM
  22.  
  23. GPU
  24.  
  25.  
  26. NVIDIA GeForce RTX 3090, 24 GB, driver 595.104.02, compute capability 8.6
  27.  
  28. Model (llama.cpp)
  29.  
  30.  
  31. unsloth/Qwen3.8-27B-GGUF, UD-Q4_K_XL (17.6 GB), text only, context 65536, --fit on
  32.  
  33. Model (HyperQwen)
  34.  
  35.  
  36. Qwen3.8-27B W4A16 AutoRound with requantized heads, prepared by the HyperQwen prepare step
  37.  
  38. Serving stack
  39.  
  40.  
  41. services.llama-cpp router (--models-preset), port 48123, models load on demand and unload after 300 s idle
  42.  
  43. Benchmark
  44.  
  45.  
  46. llm-bench: one fixed coding prompt, 512 output tokens, 3 runs, temperature 1.0, seed 42, model unloaded after each alias
  47.  
  48. Metrics
  49.  
  50.  
  51. decode t/s from the server's timings (median of 3); TTFT = wall clock to the first streamed token with the model loaded; cold load = warm-up request after unload; draft acceptance = accepted / drafted tokens
  52.  
  53. Before each load
  54.  
  55.  
  56. VRAM in use was 1.6 to 1.8 GB (desktop, Sunshine, ComfyUI); a run with more than ~2 GB taken is not comparable
  57.  
  58. HyperQwen exposes no server-side timings, so a Python script with the same prompt and parameters measured it through its OpenAI-compatible streaming API: decode t/s = tokens after the first divided by the time from the first to the last chunk.
  59. Options tested
  60.  
  61. Four options were tested. Only engines that need different compiled CUDA kernels became NixOS specialisations; the vLLM-based engine runs in Docker outside Nix.
  62.  
  63. Option
  64.  
  65.  
  66. What changes
  67.  
  68.  
  69. How it is activated
  70.  
  71. Base config fix
  72.  
  73.  
  74. nixpkgs llama.cpp 0.6.0 as shipped. The three Qwen3.8 presets moved from KV q8_0/q4_0 to q8_0/q8_0, because the stock build has no FlashAttention kernel for the mixed combination and fell back to an f16 conversion on every step. New control preset qwen3.8-27b-base with no speculative decoding.
  75.  
  76.  
  77. sudo nixos-rebuild switch --flake .#
  78.  
  79. llm-fa86
  80.  
  81.  
  82. Same llama.cpp, rebuilt with CMAKE_CUDA_ARCHITECTURES=86 and GGML_CUDA_FA_ALL_QUANTS=ON (every KV quant combination gets a kernel, sm_86 only). Adds preset qwen3.8-27b-kv84 (MTP depth 2, KV q8_0/q4_0) to check whether the missing kernel was the whole problem.
  83.  
  84.  
  85. sudo nixos-rebuild switch --specialisation llm-fa86 --flake .#
  86.  
  87. llm-llamampere
  88.  
  89.  
  90. llamAmpere v0.5.5, a llama.cpp fork tuned for Ampere: verify matvec kernels, a q4_K/q5_K/q6_K routing table, compressed KV types turbo2 to turbo6. Built with the nixpkgs recipe and the same two CUDA flags. Adds presets ampere-none (true control, spec-type = none), ampere-mtp4, ampere-turbo4 (depth 4, KV turbo4) and ampere-turbo2 (depth 2, KV turbo4).
  91.  
  92.  
  93. sudo nixos-rebuild switch --specialisation llm-llamampere --flake .#
  94.  
  95. HyperQwen
  96.  
  97.  
  98. HyperQwen: vLLM 0.31 patched with int8 Marlin and int8 flash-attention kernels for sm_86, prebuilt image ghcr.io/syv-ai/hyperqwen:latest, its own W4A16 model. Tested in two modes: SPEC=dflash2 (the .env.example default) and SPEC=mtp (the launcher default).
  99.  
  100.  
  101. docker compose --profile single up -d; API on port 18020
  102.  
  103. Switching between the base and a specialisation restarts only llama-cpp.service; no reboot is needed and the model cache in /var/cache/llama-cpp is shared. The specialisations add their presets through a new services.llama-cpp.presets option that the base module renders to the router's INI file.
  104. Results
  105. HyperQwen decodes 133 t/s, 3.2x the stock llama.cpp baseQwen3.8-27B on an RTX 3090, median of 3 runs, 512 tokens; dashed line = stock baseHyperQwen dflash2 (ctx 40k)133.4HyperQwen mtp (ctx 40k)123.2llamAmpere turbo2: MTP d2, KV turbo472.9llamAmpere: MTP d2, KV q842.8llamAmpere turbo4: MTP d4, KV turbo442.5llamAmpere: no speculation41.6stock llama.cpp: no speculation41.3llm-fa86: no speculation41.2llamAmpere: auto (adaptive MTP)26.4llamAmpere: MTP d4, KV q822.7llm-fa86 kv84: MTP d2, KV q8/q422.0stock: MTP d2, KV q819.1stock Turbo-Coder: MTP d218.8llm-fa86: MTP d2, KV q818.7stock: DFlash2 d413.4llm-fa86: DFlash2 d413.0stock base 41.3 t/s
  106.  
  107. Everything above the dashed line beats plain decoding on the stock router; on stock llama.cpp no speculative preset does. The table carries the rest of each measurement. Depth = speculative draft tokens per step (spec-draft-n-max).
  108.  
  109. Configuration
  110.  
  111.  
  112. Engine
  113.  
  114.  
  115. Speculation
  116.  
  117.  
  118. KV k/v
  119.  
  120.  
  121. Draft acc
  122.  
  123.  
  124. TTFT ms
  125.  
  126.  
  127. Cold load s
  128.  
  129.  
  130. Context
  131.  
  132. HyperQwen dflash2
  133.  
  134.  
  135. vLLM 0.31 patched
  136.  
  137.  
  138. DFlash2 drafter
  139.  
  140.  
  141. int8
  142.  
  143.  
  144. n/a
  145.  
  146.  
  147. 94
  148.  
  149.  
  150. 260 (compile)
  151.  
  152.  
  153. 40k
  154.  
  155. HyperQwen mtp
  156.  
  157.  
  158. vLLM 0.31 patched
  159.  
  160.  
  161. MTP
  162.  
  163.  
  164. int8
  165.  
  166.  
  167. n/a
  168.  
  169.  
  170. 110
  171.  
  172.  
  173. 270 (compile)
  174.  
  175.  
  176. 40k
  177.  
  178. llamAmpere turbo2
  179.  
  180.  
  181. llamAmpere 0.5.5
  182.  
  183.  
  184. MTP depth 2
  185.  
  186.  
  187. turbo4/turbo4
  188.  
  189.  
  190. 0.69
  191.  
  192.  
  193. 226
  194.  
  195.  
  196. 8.3
  197.  
  198.  
  199. 64k
  200.  
  201. llamAmpere MTP d2
  202.  
  203.  
  204. llamAmpere 0.5.5
  205.  
  206.  
  207. MTP depth 2
  208.  
  209.  
  210. q8_0/q8_0
  211.  
  212.  
  213. 0.60
  214.  
  215.  
  216. 282
  217.  
  218.  
  219. 8.9
  220.  
  221.  
  222. 64k
  223.  
  224. llamAmpere turbo4
  225.  
  226.  
  227. llamAmpere 0.5.5
  228.  
  229.  
  230. MTP depth 4
  231.  
  232.  
  233. turbo4/turbo4
  234.  
  235.  
  236. 0.43
  237.  
  238.  
  239. 276
  240.  
  241.  
  242. 7.0
  243.  
  244.  
  245. 64k
  246.  
  247. llamAmpere none
  248.  
  249.  
  250. llamAmpere 0.5.5
  251.  
  252.  
  253. none
  254.  
  255.  
  256. q8_0/q8_0
  257.  
  258.  
  259. -
  260.  
  261.  
  262. 239
  263.  
  264.  
  265. 12.0
  266.  
  267.  
  268. 64k
  269.  
  270. stock base
  271.  
  272.  
  273. nixpkgs llama.cpp 0.6.0
  274.  
  275.  
  276. none
  277.  
  278.  
  279. q8_0/q8_0
  280.  
  281.  
  282. -
  283.  
  284.  
  285. 266
  286.  
  287.  
  288. 6.5
  289.  
  290.  
  291. 64k
  292.  
  293. llm-fa86 base
  294.  
  295.  
  296. llama.cpp 0.6.0 sm86
  297.  
  298.  
  299. none
  300.  
  301.  
  302. q8_0/q8_0
  303.  
  304.  
  305. -
  306.  
  307.  
  308. 264
  309.  
  310.  
  311. 10.6
  312.  
  313.  
  314. 64k
  315.  
  316. llamAmpere auto
  317.  
  318.  
  319. llamAmpere 0.5.5
  320.  
  321.  
  322. fork default: adaptive MTP
  323.  
  324.  
  325. q8_0/q8_0
  326.  
  327.  
  328. 0.48
  329.  
  330.  
  331. 424
  332.  
  333.  
  334. 9.4
  335.  
  336.  
  337. 64k
  338.  
  339. llamAmpere MTP d4
  340.  
  341.  
  342. llamAmpere 0.5.5
  343.  
  344.  
  345. MTP depth 4
  346.  
  347.  
  348. q8_0/q8_0
  349.  
  350.  
  351. 0.43
  352.  
  353.  
  354. 385
  355.  
  356.  
  357. 8.4
  358.  
  359.  
  360. 64k
  361.  
  362. llm-fa86 kv84
  363.  
  364.  
  365. llama.cpp 0.6.0 sm86
  366.  
  367.  
  368. MTP depth 2
  369.  
  370.  
  371. q8_0/q4_0
  372.  
  373.  
  374. 0.58
  375.  
  376.  
  377. 497
  378.  
  379.  
  380. 9.3
  381.  
  382.  
  383. 64k
  384.  
  385. stock MTP
  386.  
  387.  
  388. nixpkgs llama.cpp 0.6.0
  389.  
  390.  
  391. MTP depth 2
  392.  
  393.  
  394. q8_0/q8_0
  395.  
  396.  
  397. 0.50
  398.  
  399.  
  400. 466
  401.  
  402.  
  403. 21.5
  404.  
  405.  
  406. 64k
  407.  
  408. stock Turbo-Coder
  409.  
  410.  
  411. nixpkgs llama.cpp 0.6.0
  412.  
  413.  
  414. MTP depth 2 (DavidAU finetune)
  415.  
  416.  
  417. q8_0/q8_0
  418.  
  419.  
  420. 0.65
  421.  
  422.  
  423. 543
  424.  
  425.  
  426. 33.4
  427.  
  428.  
  429. 64k
  430.  
  431. llm-fa86 MTP
  432.  
  433.  
  434. llama.cpp 0.6.0 sm86
  435.  
  436.  
  437. MTP depth 2
  438.  
  439.  
  440. q8_0/q8_0
  441.  
  442.  
  443. 0.50
  444.  
  445.  
  446. 448
  447.  
  448.  
  449. 20.9
  450.  
  451.  
  452. 64k
  453.  
  454. stock DFlash2
  455.  
  456.  
  457. nixpkgs llama.cpp 0.6.0
  458.  
  459.  
  460. DFlash2 depth 4
  461.  
  462.  
  463. q8_0/q8_0
  464.  
  465.  
  466. 0.44
  467.  
  468.  
  469. 514
  470.  
  471.  
  472. 13.3
  473.  
  474.  
  475. 64k
  476.  
  477. llm-fa86 DFlash2
  478.  
  479.  
  480. llama.cpp 0.6.0 sm86
  481.  
  482.  
  483. DFlash2 depth 4
  484.  
  485.  
  486. q8_0/q8_0
  487.  
  488.  
  489. 0.48
  490.  
  491.  
  492. 763
  493.  
  494.  
  495. 15.6
  496.  
  497.  
  498. 64k
  499.  
  500. llamAmpere DFlash2
  501.  
  502.  
  503. llamAmpere 0.5.5
  504.  
  505.  
  506. DFlash2
  507.  
  508.  
  509. q8_0/q8_0
  510.  
  511.  
  512. fails to load (dflash requires ctx_other)
  513.  
  514.  
  515. -
  516.  
  517.  
  518. -
  519.  
  520.  
  521. -
  522. Findings
  523.  
  524. The missing FlashAttention kernel was not the whole story. The stock build logged no FlashAttention vector kernel compiled for K/V types q8_0-q4_0, converting K and V to f16 instead (slow) for every Qwen3.8 preset. Moving to q8_0/q8_0 removes the warning, yet every speculative preset on stock llama.cpp still loses to plain decoding: each MTP step costs more than twice a plain decode step, so a 50 to 65 percent draft acceptance cannot pay for it.
  525.  
  526. Compiling for sm_86 alone changes nothing. llm-fa86 matches the stock build within noise on every preset. The kv84 preset (q8_0/q4_0 with the kernel present) reaches 22 t/s, better than the 19 t/s of q8_0/q8_0 MTP but still half the plain base.
  527.  
  528. llamAmpere's plain decode equals upstream (41.6 vs 41.3 t/s). Its gain is entirely in the speculative path: MTP depth 2 goes from 18.7 to 42.8 t/s on the same q8_0 KV, and with its turbo4 KV cache to 72.9 t/s with 0.69 acceptance. Depth 4 is slower than depth 2 on every KV type, and the fork's automatic adaptive MTP (26.4 t/s) is worse than an explicit depth 2.
  529.  
  530. HyperQwen is in a different class: 133 t/s and 94 ms TTFT with the DFlash2 drafter, 123 t/s with MTP, with run-to-run spread under 0.5 t/s. It is the only option that exceeds 2x the base.
  531.  
  532. Speculation costs first-token latency on llama.cpp. Drafted presets add 200 to 300 ms of TTFT on stock llama.cpp; only the llamAmpere turbo presets bring TTFT below the plain base (226 vs 266 ms).
  533.  
  534. Cold start matters with a 300 s idle unload. The first request after idle pays 6 to 9 s on the fork, 21 to 33 s for the stock MTP presets (the Turbo-Coder finetune is the slowest to load), and 4 to 5 minutes for HyperQwen, which also holds 21 to 23 GB of VRAM while it runs.
  535.  
  536. Caveats and method notes
  537.  
  538. VRAM contention invalidates a run. An early pass read 15 t/s for the stock base because another process held VRAM when --fit sized the context, and layers went to the CPU without any warning in the log. llm-bench now prints VRAM in use before each load; the reported runs all started at 1.6 to 1.8 GB.
  539.  
  540. HyperQwen ran at 40k context, not 64k. Its launcher pins a 5.2 GiB KV pool assuming an empty card and ran out of memory during warm-up with the 1.7 GB the desktop, Sunshine and ComfyUI hold. The test used GPU_UTIL=0.90, KV_MEM=3972844748 and MAX_LEN=40960. With the card free the stock 64k should fit; this was not verified.
  541.  
  542. HyperQwen serves a different quantization. W4A16 AutoRound with requantized heads and its own drafters, not the UD-Q4_K_XL GGUF. Both are 4-bit, but output quality was not compared.
  543.  
  544. turbo4 compresses the KV cache to about 4 bits. Its effect on answer quality at long context was not measured; a real-use check is needed before making it the default.
  545.  
  546. Speculative decoding is not bit-exact with plain sampling by design; the benchmark measures speed only.
  547.  
  548. Docker on this host uses CDI, not the legacy NVIDIA runtime. A local docker-compose.override.yml replaces the stock driver: nvidia reservation with driver: cdi and nvidia.com/gpu=all.
  549.  
  550. The llamAmpere fork does not support draft-dflash, and its --spec-type defaults to auto, so a preset without spec-type is not a control there.
  551.  
  552. Short prompt (41 tokens for llama.cpp, 83 for vLLM's template), so prompt-processing numbers were not compared.
  553.  
  554. Recommendations and open decisions
  555.  
  556. For the router (keeps GGUF models, 64k context, multi-model on demand): adopt the llamAmpere build as the default services.llama-cpp.package and set the Qwen3.8-27B preset to MTP depth 2 with cache-type-k/v = turbo4. Expected: 73 t/s and 226 ms TTFT instead of 19 t/s and 466 ms today. Precondition: a quality check of turbo4 in real coding sessions.
  557.  
  558. For the fastest single-user Qwen3.8-27B: run HyperQwen in single mode with SPEC=dflash2. It needs the whole GPU, so it is an alternative to the router, not a companion; a small wrapper that unloads the router's models (or stops llama-cpp.service) before docker compose up would make the switch safe. Verify 64k context with the card free before relying on it.
  559.  
  560. Drop the DFlash2 preset from the stock router: it is the slowest option on every llama.cpp build and does not load on the fork.
  561.  
  562. Optional fine sweep on llamAmpere: KV turbo3, turbo5, turbo6, depths 1 and 3, and draft-mtp-adaptive with turbo4. About six presets, one specialisation switch.
  563.  
  564. Sources: llamAmpere; HyperQwen.
  565.  
Advertisement
Add Comment
Please, Sign In to add comment