Files
serverAppConfigs/serverAppConfigs-docs/LLM/dankserver-vram-growth.md
T
Frank 500ac2b871 docs(vault): consolidate the vLLM offload-knob claim to one owner (C2, C6)
The "`--swap-space` is removed; use `--kv-offloading-size`" claim was carried by
six pages, two of them with their own copy of the upstream source citation. It
now lives only in [[RAM CPU Offloading in VLLM]], which carries the 2026-10-19
verification against `vllm/config/cache.py` (no `swap_space`; `kv_offloading_size`
activates only with `kv_offloading_backend`):

- `VLLM Overview` (two argument tables), `VLLM Index` (parameter table),
  `DFlash DSpark Speculative Decoding`, `Shared Memory Architecture Dankclaw`,
  `Hybrid CPU GPU Processing Dankserver` -> cite the owner.
- `VLLM Index`'s example command used the dead flag; it now shows the live pair.
- `dankserver-vram-growth`: banner disambiguates the 2026-08 `frank-zealot`
  (llama.cpp on the 3090 when that card was in Dankserver) from today's endpoint
  of the same name on Dankclaw, so a retired crash signature does not read as a
  live fault on a same-card-class service.

Refs: vault de-dup campaign, clusters C2 and C6.
2026-10-19 02:01:14 -07:00

144 lines
6.6 KiB
Markdown

---
type: reference
title: Dankserver VRAM and RAM Growth Under Load
description: Investigation of the recurring cuBLAS crash on frank-zealot. What grows, what does not, and what to do next.
tags:
- dankserver
- llamacpp
- vram
- ram
- ctx-checkpoints
- crash
timestamp: 2026-09-03
---
> **SUPERSEDED 2026-08-17.** The Frank redo retired this topology.
> See [[frank-redo-2026-08-17]] and [[deployment]] for the active configuration.
> The `frank-zealot` below is the 2026-08 service: llama.cpp on the RTX 3090
> **while that card sat in Dankserver**. Today's [[frank-zealot]] is a different host
> (Dankclaw), payload and runtime build, though the same card class - this crash
> signature is not a live fault.
# Dankserver VRAM and RAM Growth Under Load
This page records the investigation of the recurring cuBLAS crash on
`frank-zealot` (llama.cpp, RTX 3090 24 GiB). The service aborts
with `CUDA error: an unsupported value or parameter was passed to the
function` in `cublasSgemm_v2` at `ggml-cuda.cu:1541` during
`update_slots` -> `decode` -> `process_ubatch`. It crashed on 2026-08-13
at 19:45 and 19:55 and on 2026-08-14 at 12:00:43.
## What Was Ruled Out
### ctx-checkpoints do not grow VRAM
`--ctx-checkpoints 4` stores context checkpoints in host RAM. The log
records every checkpoint at exactly 62.813 MiB. The size never grows.
The reasoning:
- `common_prompt_checkpoint` (common/common.h) stores the serialized KV
state in `std::vector<uint8_t>` fields. These live on the host heap.
- `update_tgt()` and `update_dft()` call `llama_state_seq_get_data_ext`
with `LLAMA_STATE_SEQ_FLAGS_PARTIAL_ONLY`. The serialized state moves
from VRAM to RAM for the snapshot and back on restore.
- With 4 slots and `--ctx-checkpoints 4`, the maximum checkpoint RAM is
16 x 62.813 MiB, about 1.0 GiB. That is fixed and small.
- `data_spec` (spec-decode state) is empty for ngram-mod. The ngram-mod
implementation does not override `get_state()`.
### ngram-mod does not grow VRAM
ngram-mod allocates one fixed 64 MiB CPU hash table
(`common_ngram_mod::entries`, 16M int32 entries). It never resizes. The
matcher keeps about 12 bytes per sequence. Draft generation runs on the
CPU. The speculative path allocates zero GPU memory.
### The CUDA graph cache has eviction in this build
`ggml_backend_cuda_context::cuda_graph()` sweeps every 5 seconds and
evicts graphs unused for 10 seconds. Upstream issue #25082 describes an
unbounded graph cache, but build b10108-13-g555881ebc includes the
eviction sweep. This build does not exhibit that leak.
## What Grows
### RAM growth is suspected, not yet confirmed
systemd reported MemoryPeak 46.9 GiB for the crashed process and 45.9
GiB for the replacement. RSS is flat at 32.3 GiB, which matches the
documented idle baseline of about 32 GiB. The difference is file-backed
page cache from the `--no-mmap` model read; cgroup memory.current counts
file pages attributed to the process. That gap is not proof of a leak by
itself.
The upstream pattern to watch is issue #26216: the prompt cache and
checkpoint accumulation exceed the `--cache-ram` cap and do not plateau.
The environment file does not set `--cache-ram`, so the llama.cpp
default of 8192 MiB applies. The monitor logs RSS, which separates
anonymous memory from file cache. If RSS climbs under load while VRAM is
flat, the RAM-side accumulation is real.
### VRAM suspects under load
VRAM is flat at idle: 21,268 MiB across every monitor sample. The crash
correlates with load density and slot context size (143K tokens on
slot 2), not with wall-clock time. The two mechanisms worth testing:
1. Flash-attention F16 K conversion workspace. The prefill kernel on
Ampere is `MMA_F16`. It converts the quantized K cache (q8_0) to F16
for the attention math. The workspace size scales with the attended
context length (`ggml_nelements(K) x 2`). It is allocated into the
graph buffer via `ggml_cuda_flash_attn_ext_get_alloc_size`.
2. The CUDA VMM pool grows monotonically. `ggml_cuda_pool_vmm::alloc`
only reserves more physical memory; `free` only releases the
reservation slot. The pool never shrinks during the process
lifetime. Any new peak allocation permanently expands VRAM usage.
Both match the observed pattern: growth under prefill bursts at deep
context, flat at idle, crash when a burst exceeds the ~2.8 GiB headroom.
## Monitoring
The monitor runs on Dankserver:
```
/home/dank/vram-monitor.sh
/home/dank/vram-log.csv
```
It samples GPU VRAM and process RAM every 10 seconds and appends a CSV
row. The columns are timestamp, uptime, GPU VRAM MiB, GPU utilization,
process RSS, process VSZ, and systemd MemoryPeak.
## Resolution: dual-f16 KV Cache (2026-08-14)
The fix chose the first mechanism over the second. Both K and V moved to f16 on 2026-08-14. When K is already f16, the flash-attention MMA_F16 prefill kernel needs no F16 conversion copy, so the context-proportional workspace disappears. The pool becomes static: 20,480 bytes per cell (10 full-attention layers x 2 kv heads x 256 dims x 2 bytes for K plus 2 bytes for V).
The pool at 800768 cells cost 15,640 MiB and did not fit. The load-time warmup graph (1472x1472 image) failed cudaMalloc by 248 MiB, and the service idled at 23,966 of 24,576 MiB. The context pool was refitted to 700416 cells (4 slots x 175104): KV pool 13,680 MiB, idle 22,110 MiB, about 2.0 GiB free after warmup. Parallel stays 4.
Verified 2026-08-14 after deploy: flash attention enabled, warmup clean, decode about 42-44 t/s, cached prompt tokens working. The vram monitor log shows the restart arc (23,966 -> 5,210 during load -> 22,110 idle).
## Mitigation Options
The documentation for the benchmark ([[frank-zealot]])
prescribes the first option for this crash class. None of these change
the model.
| Option | Effect | Cost |
| ------ | ------ | ---- |
| `GGML_CUDA_FORCE_MMQ=1` | Bypasses the cuBLAS path in the crashing function | Slight decode slowdown |
| `--cache-ram 2048` | Bounds the confirmed RAM growth | Cache misses on prompt reuse |
| `--ctx-checkpoints 2` | Cuts checkpoint RAM and save cost in half | More prefill work after eviction |
| Apply PR #24649 | Clears slot checkpoints before the prompt-cache save | Needs a llama.cpp rebuild |
Upstream references: issue #26216 (prompt cache growth), issue #25082
(graph cache, not applicable to this build), issue #23635 (quantized KV
with speculative decoding), PR #24649 (checkpoint-to-cache duplication),
PR #25649 (slot checkpoints freed on prompt clear, merged), issue #26119
(same cuBLAS signature, confirmed on V100 with speculative decoding).
## See Also
- [[frank-zealot]]
- [[Dankserver System Specs]]
- [[deployment]]