Is the 170 MiB of shared memory at 704K a spill? No: it matches a host-side attention mask buffer to the MiB, and the first real spill starts at 720K
Every 4B run at 704K showed 170 MiB of shared (system) GPU memory, against 146 at 512K, and I could not say whether that was the model overflowing the 10 GB card. I loaded the server at 11 context sizes and 3 ubatch sizes and read per-process memory counters. Shared memory sat at 82 MiB plus exactly context x ubatch x 2 bytes at every point up to 704K, which is the size of a half-precision attention mask held in host memory. At 720K it jumped 120 MiB past that, the first real overflow.
| Experiment | E21Q |
| Date | 2026-10-29 |
| Benchmark | Memory measurement only, no accuracy test. llama-server loaded at each setting, then per-process Dedicated and Shared Usage read from the Windows GPU Process Memory counters (max of 5 samples), plus nvidia-smi, before and after a short 2K-token prompt. |
| Model | Qwen3.5-4B, Q4_K_M GGUF, K and V cache at q4_0, flash attention on, YaRN context extension above 256K |
| Hardware | One of my dedicated test rigs: RTX 3080 10 GB |
| Verdict | BASELINE |
What I tested
Context 4K, 256K, 384K, 512K, 576K, 640K, 672K, 704K, 720K at ubatch 64, plus 704K at ubatch 32 and 16, and a 704K repeat. llama.cpp built with upstream PR 29827.
Pass bar, set before the run
None (supporting measurement). Reading rule set before the run: shared flat until dedicated memory nears 10 GB and then stepping up means spill; a constant amount at every size means overhead.
Result
| Context | Ubatch | Shared (measured) | 82 + ctx x ub x 2 bytes | Dedicated | nvidia-smi used |
|---|---|---|---|---|---|
| 4K | 64 | 82 MiB | 82 MiB | 2,969 MiB | 3,396 MiB |
| 256K | 64 | 114 MiB | 114 MiB | 5,315 MiB | 5,742 MiB |
| 384K | 64 | 130 MiB | 130 MiB | 6,484 MiB | 6,910 MiB |
| 512K | 64 | 146 MiB | 146 MiB | 7,652 MiB | 8,078 MiB |
| 576K | 64 | 154 MiB | 154 MiB | 8,236 MiB | 8,662 MiB |
| 640K | 64 | 162 MiB | 162 MiB | 8,820 MiB | 9,246 MiB |
| 672K | 64 | 166 MiB | 166 MiB | 9,112 MiB | 9,538 MiB |
| 704K | 64 | 170 MiB (repeat 170) | 170 MiB | 9,404 MiB | 9,830 MiB |
| 704K | 32 | 124 MiB | 126 MiB | 9,358 MiB | 9,784 MiB |
| 704K | 16 | 102 MiB | 104 MiB | 9,333 MiB | 9,760 MiB |
| 720K | 64 | 292 MiB | 172 MiB | 9,430 MiB | 9,856 MiB |
The ctx x ub x 2 bytes term is the size of a half-precision attention mask for one ubatch. That attribution comes from the exact size match; I did not capture llama.cpp's buffer-size log lines in this run, so it is not yet confirmed from the allocator itself.
The 82 MiB floor at 4K is not yet attributed.
A 2K-token prompt did not change shared memory at any point.
Reading: the earlier 704K runs (E21h, E21o, E21p) had no VRAM overflow. Their 170 MiB is the fixed floor plus an 88 MiB host buffer. 720K is the first point that actually overflows.
Smaller ubatch shrinks this buffer (ubatch 16 at 704K: 102 MiB), which may let a slightly larger context stay resident. E21r tests that next.
What happened
Shared memory grew smoothly with context from the smallest size, long before the card was full, so it was not overflow in the usual sense. The extra amount over the 4K level matched context times ubatch times 2 bytes at every setting, to within 2 MiB, and it scaled with ubatch exactly as that formula says. Dedicated memory at 704K was 9,404 MiB. One step further, at 720K, shared memory jumped by 120 MiB more than the formula predicts, which is the real spill point for this build and settings. The memory saving that makes these sizes fit is upstream llama.cpp PR 29827, so credit goes to its authors.
How to rerun
powershell -ExecutionPolicy Bypass -File e21q_chain.ps1
Code
All code for E21Q: e21q_chain.ps1, results.jsonl.
Prior work
No novelty claim. This is a memory accounting measurement of standard llama.cpp behavior.
Written by Andrew Rose with help from AI tools. Every number comes from the run logs.
Questions or ideas? me@andrewroseit.com or @AndrewRoseIT.