Field Manual No.11 · 27 August 2026

Maximising Qwen3.8-27B on a 5090 Laptop

40.8 to 141.6 tokens per second on a laptop GPU. The build recipe, the sweeps, and the claims we had to correct along the way.

Every number here was measured on one machine running Windows. Follow the steps and you land on the same configuration. Runs that turned out to be invalid are kept and labelled rather than quietly dropped, because knowing which measurements were wrong is most of the value.

RTX 5090 Laptop 24GBsm_120 · driver 610.88 CUDA 12.8.93llama.cpp 192067bQwen3.8-27B UD-Q4_K_M 18-entry trap catalogue
§ 01 · Start here

The configuration

If you read one thing, read this. Every flag is justified by a measurement below.

llama-server -m Qwen3.8-27B-UD-Q4_K_M.gguf \
  -ngl 999 -fa on --jinja \
  -c 262144 -b 1024 -ub 1024 \
  --cache-type-k q4_0 --cache-type-v q4_0 \
  --spec-type draft-mtp --spec-draft-n-max 5 \
  --reasoning-effort low \
  --parallel 1 --no-mmap \
  --host 127.0.0.1 --port 8080
Why each flag is there — measured contribution, not folklore
FlagEffectEvidence
--spec-draft-n-max 5+137%Swept 2–12, sharp peak at 5. Community default of 2 costs 35 tok/s here.
--reasoning-effort low~4.2×Time-to-answer 231 s+ → 55 s on an identical prompt.
--spec-type draft-mtpenablesMTP heads ship inside the GGUF as blk.*.nextn.*; ignored without this.
-fa onrequiredSaves ~2.3 GB. Without it 256K will not fit.
--cache-type-k/v q4_0requiredLighter and faster than f16. 256K lands at 23,504 of 24,462 MB.
--parallel 1protectsSpeculative decode is single-stream; the gain vanishes above 1.
-c 262144−2%81.2 vs 82.9 tok/s at 32K. Nearly free to allocate.
--no-mmapneutralWeights live in VRAM; mmap only helps when paging.
Pick effort per task

off — 33 s, pure code, best draft acceptance. low — 55 s, still reasons, the general default. medium — 91 s, real deliberation. xhigh — the shipped default, and it did not finish inside 16,384 tokens.

n-max is workload dependent — do not use one value

Structured output wants a much wider draft window than prose or code. Set --spec-draft-n-max 10 when generating JSON, HTML, XML or any rigid format; 5 for code and prose. Using 5 on JSON costs 21% (141.6 → 112.1 tok/s).

WorkloadSet n-maxDecodevs baseline
JSON / structured10141.6+250%
HTML / markup10118.1+192%
Code593.7+131%
Prose558.4+45%
§ 02 · Rationale

Decisions & why

Each of these was a fork where the obvious choice was wrong.

DecisionChosenReasoning
CUDA version12.8.1The 13.x line crashes the Blackwell MMQ kernel. winget only offers 13.3, so this must come from NVIDIA's archive manually. 12.8+MMQ benchmarks ~6,500 t/s prefill against ~750 for 13.x+cuBLAS.
Driverkeep 610.88The 12.8.1 installer bundles 572.61. Installing it would be a downgrade, so Display.Driver is omitted from the subpackage list.
QuantizerUnslothTheir GGUFs preserve the blk.*.nextn.* MTP tensors. Not every quantizer keeps them, and without them the single largest speed lever is dead.
Quant levelUD-Q4_K_M15.32 GiB leaves ~8 GB for KV cache, which is what makes 256K context reachable. Q5/Q6 would trade context headroom for fidelity.
GeneratorNinjaMuch faster than MSBuild, but does not auto-discover MSVC — vcvars64.bat must be imported first.
ShellPowerShellGit Bash cannot load the built binaries at all (UCRT resolution failure).
Bench toolllama-clillama-bench has no speculative-decoding flags, so it cannot measure the thing that matters most.
§ 03 · Reproduce it

Install & build, in order

0

Verify prerequisites

MSVC is the usual blocker. Check before downloading 3 GB of toolkit.

winget --version
git --version
& "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" `
   -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 `
   -property installationPath

If VS Build Tools is absent, install it with the C++ workload — the bare package is not enough:

winget install --id Microsoft.VisualStudio.2022.BuildTools --exact `
  --override "--quiet --wait --norestart `
              --add Microsoft.VisualStudio.Workload.VCTools --includeRecommended"
winget install --id Kitware.CMake --exact
winget install --id Ninja-build.Ninja --exact
Note

If winget reports exit 43 (“no applicable upgrade”), the package is already installed — but your --override was not applied. Verify cl.exe exists rather than trusting the exit code.

1

Install CUDA 12.8 — not 13.x

curl.exe -L -o cuda_12.8.1.exe `
  https://developer.download.nvidia.com/compute/cuda/12.8.1/local_installers/cuda_12.8.1_572.61_windows.exe

# 3.14 GB. Verify the signature before running it.
(Get-AuthenticodeSignature .\cuda_12.8.1.exe).Status   # -> Valid

# Explicit subpackages. Display.Driver deliberately ABSENT.
.\cuda_12.8.1.exe -s -n `
  nvcc_12.8 cudart_12.8 thrust_12.8 `
  cublas_12.8 cublas_dev_12.8 `
  nvrtc_12.8 nvrtc_dev_12.8 `
  cuda_profiler_api_12.8 nvtx_12.8 nvml_dev_12.8 `
  cuobjdump_12.8 nvdisasm_12.8 nvprune_12.8 cuxxfilt_12.8 `
  nvfatbin_12.8 nvjitlink_12.8 `
  visual_studio_integration_12.8
verify:
nvcc --version          -> release 12.8, V12.8.93
nvidia-smi --query-gpu=driver_version --format=csv,noheader   -> 610.88 (unchanged)
Trap 01 · silent total failure

One invalid subpackage name aborts the entire install. Exit -522190823 = 0xE0E00019. The NVIDIA log ends normally at 2.101 s with no error text and nothing is installed. There is no cuda_cccl; it is nvrtc_12.8 and nvml_dev_12.8, not the cuda_-prefixed forms. Get names from NVIDIA's Windows install guide — never guess.

2

Add CUDA to PATH

The installer does not do this, and nothing tells you.

$cuda = 'C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.8\bin'
[Environment]::SetEnvironmentVariable('Path',
  [Environment]::GetEnvironmentVariable('Path','Machine') + ";$cuda", 'Machine')
Trap 02 · no error message

Without this, every llama.cpp binary exits -1073741515 (0xC0000135, STATUS_DLL_NOT_FOUND) producing no stdout and no stderr at all. --help returns nothing.

3

Build llama.cpp for sm_120

git clone --depth 1 https://github.com/ggml-org/llama.cpp
cd llama.cpp

# Ninja does not auto-discover MSVC; import vcvars into the session first.
$vc = 'C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvars64.bat'
cmd /c "`"$vc`" && set" | ForEach-Object {
  if ($_ -match '^([^=]+)=(.*)$') { Set-Item "env:$($matches[1])" $matches[2] }
}

# Delete build/ EVERY time. See Trap 03.
Remove-Item build -Recurse -Force -ErrorAction SilentlyContinue

cmake -B build -S . -G Ninja `
  -DCMAKE_BUILD_TYPE=Release `
  -DGGML_CUDA=ON `
  -DCMAKE_CUDA_ARCHITECTURES=120 `
  -DGGML_CUDA_FORCE_CUBLAS=OFF `
  -DGGML_CUDA_F16=ON `
  -DLLAMA_CURL=OFF
cmake --build build -j 24
# verify you are on the fast path, not silently on cuBLAS:
Select-String build\CMakeCache.txt -Pattern 'FORCE_CUBLAS|CUDA_ARCHITECTURES'
  GGML_CUDA_FORCE_CUBLAS:BOOL=OFF          <- must be OFF
  CMAKE_CUDA_ARCHITECTURES:UNINITIALIZED=120

# confirm the GPU is seen with the right compute capability:
.\build\bin\llama-bench.exe --list-devices
  Device 0: NVIDIA GeForce RTX 5090 Laptop GPU, compute capability 12.0, VRAM 24462 MiB
Trap 03 · the expensive one

GGML_CUDA_FORCE_CUBLAS persists in CMakeCache.txt across reconfigures. If it was ever ON, it stays ON, the MMQ path is disabled, and you lose 5–6× on prefill with no warning whatsoever. Delete build/ and grep the cache afterwards.

Non-issue

MSVC 14.44 compiles cleanly against CUDA 12.8 despite postdating it. Do not pre-emptively add -allowUnsupportedCompiler.

4

Fetch the model

curl.exe -L --fail --retry 3 -o Qwen3.8-27B-UD-Q4_K_M.gguf `
  https://huggingface.co/unsloth/Qwen3.8-27B-GGUF/resolve/main/Qwen3.8-27B-UD-Q4_K_M.gguf

# verify byte-exact against the remote Content-Length:
(Get-Item Qwen3.8-27B-UD-Q4_K_M.gguf).Length     -> 16464440224

Reported by llama.cpp as qwen35 27B Q4_K - Medium, 15.32 GiB, 27.32 B params.

5

Disable Sysmem Fallback

NVIDIA Control Panel → Manage 3D Settings → CUDA - Sysmem Fallback PolicyPrefer No Sysmem Fallback, scoped to llama-server.exe. Restart the process — the policy binds at CUDA context creation.

Trap 04 · 5–10× silent tax

Since driver 536.40, Windows does not OOM when you overfill VRAM. It spills into shared system memory over PCIe. No error, no crash, and llama.cpp still reports “using GPU”. At 256K context you hold 958 MB of headroom, so you want a loud failure rather than a quiet one.

§ 04 · Reference

Trap catalogue

Every failure encountered, with its exact symptom. Ordered by cost.

#TrapSymptomFix
Build & toolchain
01Bad CUDA subpackage nameexit -522190823Aborts whole install, no error text. Use documented names.
02CUDA bin not on PATHexit -1073741515STATUS_DLL_NOT_FOUND, zero output. Add to machine PATH.
03Stale FORCE_CUBLASsilent, 5–6× slowerDelete build/; grep CMakeCache.txt.
04CUDA 13.x on Blackwellsegfault / cuBLASUse 12.8. winget only has 13.3.
05Ninja can't find MSVCcompiler test failsImport vcvars64.bat before configure.
06Bundled driver downgrade610.88 → 572.61Omit Display.Driver from subpackages.
07winget exit 43override ignoredPackage present; workload may not be. Verify cl.exe.
Shell & flags
08Git Bash runs the exeapi-ms-win-crt-heapUse PowerShell. Failure line starts with the exe path, not error: — a naive grep reports success.
09-no-cnv removedinvalid argumentUse -st/--single-turn. Present in arg.cpp but unregistered — trust --help.
10llama-bench for MTPflags absentUse llama-cli/llama-server.
11PowerShell arg splattingmangled argsVerify the binary received what you sent before blaming the flag.
Model & measurement
12-effort minimalJinja ExceptionOnly low/medium/xhigh exist.
13-effort highsilently fakedFalls back to xhigh. Byte-identical output proves it.
14Token cap too lowtruncated mid-wordInvalidates time-to-answer. Verify output completes.
15Sysmem fallbacksilent 5–10×Set “Prefer No Sysmem Fallback”.
16Benchmarking at -np > 1MTP gain vanishesSpeculative decode is single-stream.
17-f plus empty -phangs foreverllama-cli waits on stdin; under a non-interactive shell it never returns. Model loads (VRAM occupied) then sits at idle power. Pass -f alone.
18$args in PowerShell--model is required$args is an automatic variable; assigning to it and splatting silently drops everything. Rename it.

Trap 13 in detail — how a fake flag was caught

--reasoning-effort high returns plausible numbers, so it looks like data. Stripping timing lines and hashing the output bodies proves all three are the same generation:

high.log     14190 bytes   64.0 t/s   md5 4df134687af5408a19b989dc4b5c8530
xhigh.log    14190 bytes   63.7 t/s   md5 4df134687af5408a19b989dc4b5c8530
default.log  14190 bytes   63.9 t/s   md5 4df134687af5408a19b989dc4b5c8530

The 0.3 tok/s spread is run noise on identical work. Generalisable lesson: when a flag has no documented value list, verify it changed the output before you trust the number.

§ 05 · Evidence

Complete raw data

Individual runs, not just means. --temp 0 and a fixed seed make speculative decoding deterministic run-to-run, but NOT byte-identical across draft widths — see the corrected methodology note below.

Experiment A — draft width, reasoning OFF. -n 1024, 2 runs each. Prompt t/s is short-prompt, not comparable to llama-bench pp512.
ConfigRun 1Run 2MeanSpreadvs basePrompt t/s
baseline40.940.640.80.3219 / 225
n-max 261.161.161.10.0+49.9%199 / 197
n-max 365.265.265.20.0+60.0%194 / 192
n-max 463.863.863.80.0+56.6%203 / 197
n-max 596.796.996.80.2+137.5%193 / 199
n-max 694.795.295.00.5+133.0%188 / 189
n-max 793.993.993.90.0+130.4%194 / 188
n-max 894.193.793.90.4+130.4%190 / 193
n-max 1089.289.689.40.4+119.4%193 / 184
n-max 1283.183.283.20.1+104.0%184 / 191
Experiment B — draft width, thinking ON. -n 512, 3 runs each. Same optimum, far smaller gain.
ConfigRun 1Run 2Run 3Meanvs base
baseline40.940.540.640.7
n-max 246.346.346.346.3+13.9%
n-max 343.343.343.443.3+6.6%
n-max 551.851.751.751.7+27.2%
n-max 647.147.047.347.1+15.9%
n-max 851.051.451.251.2+25.9%
draft-dspark40.340.3−1.0%
Experiment C — reasoning effort. Time to a complete answer, identical prompt, n-max 5.
EffortAnswer timeTokensDecodeStatusNote
off33.2 s~3,00890.6completeNo thinking block emitted.
low55.1 s~4,78386.8completeThinks, then answers.
medium91.4 s~8,43692.3completeExtended deliberation.
xhigh (default)231 s+>16,38470.3truncatedHit the cap still mid-thinking. 231 s is a floor.
high64.0Silently resolves to xhigh.
minimalerrorJinja exception; not a valid level.
Experiment D — context window cost. Reasoning off, n-max 5, window allocated but empty.
ContextDecodePromptPeak VRAM% of 24,462 MBHeadroom
32,76882.9111.317,262 MB70.6%7,200 MB
131,07281.3100.219,920 MB81.4%4,542 MB
262,14481.2102.123,504 MB96.1%958 MB
Experiment F — context actually FILLED. Real source text fed to depth, then 256 tokens generated. This is the number that matters.
DepthPrefillDecodeRetentionPeak VRAMWall
empty (ref)83.4100%17,262 MB
24K filled1357.563.876%17,272 MB37.9 s
100K filled807.357.569%19,930 MB155.9 s
200K filled522.236.143%23,514 MB466.1 s
The most important number in this guide

At 200K filled, decode is 36.1 tok/s — below the 40.8 tok/s unoptimised baseline we started from. Deep context costs more than MTP and reasoning-effort tuning gained combined. Every other speed figure in this guide is a shallow-depth figure.

Prefill degrades too: 1357 → 807 → 522 t/s with depth. At 200K that is roughly 6.4 minutes of ingest before the first token. Time-to-first-token, not decode rate, dominates a genuinely long session.

Peak VRAM at 200K filled (23,514 MB) matches the allocated-only figure (23,504 MB) almost exactly, confirming the KV cache is preallocated rather than grown. VRAM planning can use the empty-window numbers; throughput planning cannot. Note also that Experiment D's “Prompt t/s” column (~100–111) is overhead on a tiny prompt, not a real prefill rate — the true rates are in Experiment F.

Experiment G — output shape × draft width. Reasoning off, -n 1024. Baseline holds at 40.4–40.5 across all four, so content affects acceptance, never raw decode.
Shapebasen2n3n4n5n6n8n10n12n16n20Best
code40.560.362.661.293.789.893.1n5 +131%
prose40.448.545.838.658.453.849.0n5 +45%
json40.463.971.671.3112.1120.0130.7141.6137.1130.5122.2n10 +250%
markup40.464.370.769.3109.0111.8115.6118.1111.2101.196.0n10 +192%

Three things worth extracting. The optimum moves with output shape — n5 for code and prose, n10 for JSON and HTML. The gain varies by 5×, from +45% on prose to +250% on JSON; rigid formats are trivially predictable, so drafts land far more often. And n4 on prose is actively harmful (38.6 against a 40.4 baseline) — the only measured config anywhere in this guide that is slower than no speculation at all.

141.6 tok/s on JSON is the fastest figure measured on this box, well above the 96.8 headline, which was code-shaped. If your workload is structured extraction or API-shaped output, the ceiling here is considerably higher than the front page suggests.

Experiment E — power limit. llama-bench, pp512/tg128, 3 reps.
LimitDecode (tg128)Prefill (pp512)Verdict
95 W default40.86 ± 0.681664.14 ± 1.73
175 W enforced40.94 ± 0.581661.82 ± 9.09No effect. Difference is inside error bars in both directions.
§ 06 · Instrumentation

Power & thermal telemetry

Sampled at 2 Hz during Experiment B, with the limit raised to 175 W.

RunSamplesPeak WAvg WPeak SMAvg SMMem clkPeak °C
baseline41171.2109.3198716871410151
mtp-n240170.6100.0210015741410156
mtp-n341170.3103.7201716121410157
mtp-n441170.9101.820471602900158
mtp-n538169.995.0227215451410156
dspark42173.3108.5246015721410157

Three things fall out of this. Average draw is 95–109 W against a 170 W ceiling — the GPU is not power-starved even at the 95 W default, which is why raising the limit changed nothing. Peak temperature is 58 °C, so thermal throttling is simply not a factor on this chassis; the common advice to power-cap for sustained throughput does not apply here. The fastest config draws the least power — n-max 5 averages 95.0 W, the lowest of the set, while being the fastest. Speculative decoding replaces memory traffic with cheap parallel verification.

Anomaly

The mtp-n4 trace caught a memory clock of 9001 MHz against 14101 MHz elsewhere — a momentary downclock. n4 is also the one config that underperforms its neighbours (63.8 against 65.2 at n3). Not enough samples to call it causal; flagged for anyone re-running.

§ 07 · Interpretation

What the numbers mean

The n-max curve is discontinuous, not a curve

n4→n5 jumps 52% between adjacent integers (63.8→96.8), and n5 beats n6. With run-to-run spread of ±0.2 this is not noise. The same cliff appears with thinking on (43.9→51.7), just smaller. This is kernel tiling: certain draft widths produce batch shapes that map well onto the hardware.

Practical consequence: you cannot interpolate. A coarse sweep of 2/4/8 would have found 63.8, 63.8, 93.9 and concluded “bigger is better, use 8” — missing the peak entirely. Sweep every integer through the plausible range.

Derived draft acceptance

Not directly reported, but inferable. Speedup ≈ 1 + (mean accepted drafts per verify step):

ModeSpeedupImplied extra tokens/stepReading
reasoning off2.37×~1.37 of 5Code output drafts well.
thinking on1.27×~0.27 of 5Reasoning prose drafts badly.

Roughly 5× better acceptance on code than on reasoning prose, consistent with the commonly cited ~0.80 vs ~0.60 acceptance figures. Marked derived, not measured — llama.cpp did not print acceptance counters in these runs.

MTP costs prefill to buy decode

Rarely mentioned and worth knowing: enabling MTP reduces prompt throughput. Thinking-on runs show baseline 301.4 t/s prompt against 254–259 with MTP — roughly −15%. Reasoning-off runs show ~222 baseline against ~193 — about −12%.

Net strongly positive for generation-heavy work. But for a workload that ingests huge prompts and emits little — bulk classification, reranking — MTP may be a net loss. Measure your own shape.

Rate is the wrong metric for reasoning effort

Decode rate across effort levels spans only 70–92 tok/s. Token spend spans 3,008 to over 16,384 — more than 5×. Optimising tok/s would have picked medium (92.3, the highest of the completed runs) when off answers the same question in a third of the time. Measure time to a complete answer.

Why the laptop part beat its bandwidth ratio

The laptop 5090 has 896 GB/s against the desktop's 1792 — exactly half. A pure bandwidth model predicts ~30 tok/s baseline against the desktop's 61.4. It measured 40.8, or ~67% of desktop, not 50%. Decode is bandwidth-bound but not purely so. Predictions from bandwidth ratios alone will be pessimistic.

§ 08 · Don't repeat these

Negative results

TriedResultWhy
95 W → 175 W0%40.86→40.94 tok/s. GPU genuinely drew 171 W peak at 1987–2472 MHz, but averaged only 95–109 W — it was never power-starved. Decode is bandwidth-bound and GDDR7 clocks don't scale with power budget.
draft-dspark−1%40.3 vs 40.8 baseline. No benefit over no speculation at all.
n-max > 8falls offn10 89.4, n12 83.2. Wider drafts cost more than they recover.
-effort highByte-identical to xhigh. Plausible numbers that are just the default — worse than an error.
-effort minimalerrorJinja Exception: Unexpected reasoning effort minimal. Supported types are xhigh (default), medium, and low.
Thermal power-cappingn/aPeak 58 °C. Standard advice to cap at 75–80% for sustained throughput solves a problem this chassis does not have.
Git Bash as host shellcannot runUCRT resolution failure on every binary.
§ 09 · Method

Benchmark methodology

Two methodology errors were made and corrected during this work. Both are easy to repeat and neither announces itself.

RuleReason
Verify output completesAt -n 3072 the high-effort runs were cut off mid-token, so “time to answer” was really “time to hit the cap”. Even -n 16384 did not finish xhigh. Read the tail of the output, don't trust the timer.
Confirm the flag did something-effort high was benchmarked before anyone noticed it silently resolves to the default. Hash the output body against a known config.
--temp 0 + fixed seedCorrected. An earlier version of this guide claimed speculative decoding is lossless and therefore byte-identical across draft widths. That is false. Each config is deterministic run-to-run (rep1 == rep2, always), but configs diverge from each other by 98–117 lines. Cause: batched draft verification uses different matmul shapes than single-token decode; floating-point addition is not associative, an argmax flips, and divergence cascades. Lossless in theory, not bit-exact in practice. The fixed seed still gives reproducibility within a config, which is what makes the timings comparable.
Check quality across draft widthsBecause output genuinely differs by n-max, wider drafts could in principle trade quality for speed. Probed on structured output: JSON stayed valid with all 10 records and every required field at base, n5, n10 and n20. Reassuring, but one probe on one task type — not a general guarantee.
Verify the flag changed the outputHash the output body against a known-good config. Two separate settings in this guide (--reasoning-effort high, and thinking:{type:'disabled'} on the API side) produced plausible numbers that were measuring something other than what was intended.
--parallel 1Speculative decode is single-stream. Mixed parallelism produces incomparable numbers.
Generate ≥ 400 tokensShort generations pay more in speculation overhead than they recover.
Subtract model load~12.7 s constant on this box. Measure it once with -n 1 and subtract, or it swamps short runs.
Sample telemetry alongsidePower and clock traces are what proved the limit change was inert, rather than assuming it helped.
Retain invalid runsThe truncated and faked runs are the most instructive rows in the dataset.

Benchmark harness

$common = @('-m',$model,'-ngl','999','-fa','on',
            '-ctk','q4_0','-ctv','q4_0',
            '-c','32768','-n','1024','--seed','42','--temp','0',
            '-st','--no-warmup','--reasoning','off','-p',$prompt)

foreach ($n in 2,3,4,5,6,7,8,10,12) {
  for ($i=1; $i -le 2; $i++) {
    & $cli @common --spec-type draft-mtp --spec-draft-n-max $n *> "$out\n$n-$i.log"
  }
}
# llama-cli reports: [ Prompt: NNN t/s | Generation: NN.N t/s ]
# NOT the llama_perf_context_print / "eval time" format older guides parse.
§ 10 · Honest gaps

Untested & open questions

Stated so nobody mistakes absence of data for absence of a problem.

  • Filled-context throughput is unmeasured. Now measured — Experiment F. Decode retains 43% at 200K filled (36.1 tok/s). Better than the ~12 tok/s some published far-context figures suggest, but still below our unoptimised baseline.
  • Depth beyond 200K is still unmeasured. The 262,144 window was allocated but filled only to ~200K. The last 60K may degrade further.
  • Single-shot only. Each measurement is one prompt, one generation. A real multi-turn session with a growing cache may behave differently again.
  • Quality was never measured. No perplexity, no benchmark scoring. The claim that -effort low is a reasonable default rests on it completing sensibly, not on evidence it answers as well as xhigh.
  • Only one quant tested. Q5_K_M and Q6_K were never pulled. The speed cost of higher fidelity on this box is unknown.
  • One prompt shape. Now measured — Experiment G. The optimum does move: n5 for code and prose, n10 for JSON and markup. Translation and tool-calling remain untested.
  • Acceptance rates are derived, not measured. Inferred from speedup ratios.
  • The n4 memory downclock was seen once. Unexplained, possibly relevant to n4's underperformance.
  • Sysmem fallback was never actually disabled during these runs — it is a Control Panel setting. Results held anyway because 256K fits, but a filled window may behave differently.
§ 11 · Provenance

Exact versions

GPU
RTX 5090 Laptop, 24,463 MB
Compute cap
12.0 → sm_120
Driver
610.88
Bandwidth
896 GB/s, 256-bit
Mem clock
14,001 MHz (GDDR7)
Power
95 W default / 175 W max
PCIe
Gen 5 ×16
CPU
Core Ultra 9 290HX Plus, 24C/24T
RAM
63.4 GB
OS
Windows 11 Home SL
CUDA
12.8.93 (12.8.1)
MSVC
14.44.35207
CMake
4.4.2
Ninja
1.13.2
llama.cpp
192067b, 2026-08-26
Model repo
unsloth/Qwen3.8-27B-GGUF
Quant
UD-Q4_K_M, 15.32 GiB
Bytes
16,464,440,224
Arch / params
qwen35 / 27.32 B
Native context
262,144

The laptop 5090 is a different part from the desktop 5090 — 24 GB on a 256-bit bus at 896 GB/s, against 32 GB / 512-bit / 1792 GB/s. Roughly half the bandwidth and three-quarters the VRAM. Published desktop figures do not transfer, and neither do these in the other direction.

Build flags of record

-DCMAKE_BUILD_TYPE=Release
-DGGML_CUDA=ON
-DCMAKE_CUDA_ARCHITECTURES=120
-DGGML_CUDA_FORCE_CUBLAS=OFF
-DGGML_CUDA_F16=ON
-DLLAMA_CURL=OFF