Repository navigation
Elastic K/V (--kv-grow, opt-in): the K/V takes VRAM as the context grows, the expert cache holds the rest (+68% cache on a 16 GB card at 262K) - #378
Conversation
|
Pushed 1d17e32, fixes from an audit of this PR (each checked against the code):
IQ2_XS: the same served scenario (grow at a request and mid-decode, trim and refill, regrow, a parked conversation's restore) runs as before; 🤖 Generated with Claude Code |
|
Rebased on 0.1.32 (c499bd1). One conflict, in
Checked on 0.1.32: IQ2_XS (ISTA), RTX 5090, a fixed With
🤖 Generated with Claude Code |
|
Tested on Windows 11 (WDDM): RTX 4080 SUPER 32 GB (sm_89), Ryzen 7 5800X3D, 64 GB, PCIe 3.0, CUDA 13.3; IQ3_S at
At 8K the CPU computes 0.52 instead of 0.92 experts per layer (VRAM hits 98.3 instead of 96.8 %). For the 64K requests the K/V grew once to 65536 cells (330 slots given up, 12363 still hold experts); there the VRAM hit rate was 98.3 % without it already, and nothing changes.
I run with it now: |
|
@BlueKingMuch thank you for the thorough test, the conversation-cache run included. 🤖 Generated with Claude Code |
|
Rebased on 0.1.37, plus a fix from a code review (7e5b7e7). The bug: with
Now: such a run maps the whole window from new memory at start ("elastic K/V off for this run ... mapped up front"), or stops with a message if it does not fit. The same 32K prompt reads through: 32,036 tokens in 4 chunks. The growth's and the trim's residency-table uploads also wait for their own copy. A pageable Without 🤖 Generated with Claude Code |
|
We'd take |
|
I rebased this onto 0.1.38 (c053829). The only conflict was the source list in CMakeLists.txt ( Without the flag, compared with v0.1.38 built from the tag with the same toolchain:
Decode is in tokens/s, rounds 1 / 2. The one low cell, 168.8 on the 24-token chat, is run-to-run spread: the second round gives 172.6 against 172.8. With
|
… expert cache holds the rest At --max-context 262144 the K/V is allocated whole at start (3.35 GiB at int8 with the drafter's) and the expert cache is sized around it. With --kv-grow (or STRATA_KV_GROW=1) the K/V pools and the cache arena are CUDA VMM ranges (core/vmm.hpp; the driver calls through the runtime's entry-point query, nothing links the driver library). The K/V maps 2 MiB chunks for the cells the requests reach (16384 at start); when a request needs more, slots just below the prompt path's loan give theirs up, a hotter expert in them first moving into the slot of the coldest one in the loan region. A short request after a long one hands the chunks back and the slots refill from the profile. No address moves, so no graph is captured again, and --max-context cells always fit. - serve: grows at a request (its prompt, and a parked conversation before its restore) and in the decode rounds; trims once the outgoing session is parked - generate: grows once for the prompt and max_new, before the loan - one GPU with a profile, the whole K/V in VRAM (no --kv-resident), every expert in RAM (not the low-RAM tier); STRATA_KV_GROW_INIT / _STEP / _FLOOR for tests. Off by default: more slots move experts between GPU and CPU. - tests/core/vmm_test: map, a chunk moved between ranges keeps its bytes, release IQ2_XS, RTX 5090, 262K, fixed cache: first-token logits and 64 tokens bit-identical to 0.1.31 off, mapped whole, grown into new VRAM, and grown by giving up 266 slots. Cache auto: 15,119-15,207 -> 17,300-17,503 slots, short chat 15.06-15.33 -> 14.71-14.83 ms a round; an emulated 16 GB card: 3,396 -> 5,719 slots, short chat 100 -> 115 tok/s, a 32K prompt 7.44 -> 6.36 s and 24.6 -> 22.2 ms a round after it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VZy1yKaDDiA8a7svdwaHio
…ngine, the trim maps one run From an audit of this PR, each finding checked against the code: - VmmRange: a run whose cuMemSetAccess failed stayed counted as mapped (a later map_range skipped it, so the chunks were never made accessible); it is unmapped and freed again (commit_run). - a failed growth or trim could leave host_res, d_res and the slot range half-changed while serve went on to the next request; such a failure now stops the engine, as other CUDA failures do. The residency uploads are checked. - the trim maps the chunks it takes back as one run (one cuMemSetAccess instead of one per 2 MiB chunk) and refills the slots with async copies and one sync instead of a blocking cudaMemcpy per expert. IQ2_XS: the served scenario (grow at a request and mid-decode, trim and refill, regrow, a parked conversation's restore) runs as before; vmm_test passes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VZy1yKaDDiA8a7svdwaHio
… up front; its table uploads wait for the copy With --kv-grow the K/V is made elastic at session init and maps 16,384 cells. Growth needs more than init checks: a device residency table, cache slots and a loan above the floor. Without them (--no-pool, --no-token-graph, the dump modes, --expert-cache auto resolving to 0) kvg_start left the growth off, nothing mapped more, and a prompt past 16K wrote K/V into unmapped memory: an illegal address. That run now maps the whole window from new memory at start, or stops with a message when it does not fit. The growth's and the trim's residency-table uploads also wait for their own copy (a pageable cudaMemcpy may return before its DMA lands; the streams that read the table are non-blocking). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VZy1yKaDDiA8a7svdwaHio
|
Rebased onto 0.1.39 (d5ee0cc). 0.1.39's segmented expert cache (#533,
Without the flag, against v0.1.39 (IQ2_XS, fixed |
|
Reopened as #1040, the same branch and commit. GitHub closed this one when my fork was made private by mistake, which took it out of the fork network. |
|
In 0.1.40: --kv-grow is in (opt-in, off by default). It was ported over --vram-elastic: the cache arena tries the segmented backing first, then the VMM range, then cudaMalloc. It switches itself off with a warning under --batch, --vram-elastic and --peer-device. |
At
--max-context 262144the K/V of the whole context is allocated at start: 3.35 GiB at int8 with the drafter's. The expert cache is sized around it. On a 16 GB card that is a quarter of the VRAM, and most requests never reach those cells.With
--kv-grow(orSTRATA_KV_GROW=1), the K/V holds physical memory only for the cells the requests reach. The expert cache holds the rest. It is off by default: more slots move experts between the GPU and the CPU, which changes tokens, so it does not pass a byte-identical gate against main by design.How it works:
core/vmm.hpp). The K/V pools and the cache arena are CUDA VMM ranges: addresses reserved once, physical memory mapped in 2 MiB chunks. The driver calls go throughcudaGetDriverEntryPointByVersion, so nothing linkscuda.lib.--max-contextcells always fit: the window is never smaller.--max-new.--kv-resident), every expert in RAM (not the low-RAM tier). Otherwise the flag is ignored.STRATA_KV_GROW_INIT/_STEP/_FLOOR.tests/core/vmm_testmaps, moves a chunk between ranges keeping its bytes, and releases.One pitfall worth knowing: a device-to-device
cudaMemcpydoes not wait for the copy. The moves sync before their source chunks are unmapped. Without that, the copy faulted, and the error surfaced at the nextcudaMemsetas "no VRAM".Same output when nothing moves
Setup: IQ2_XS (ISTA), RTX 5090, a 32K prompt and 64 tokens, fixed
--expert-cache 14900. First-token logits were dumped with #276'sSTRATA_DUMP_FIRST_LOGITSin a test build.These are bit-identical to 0.1.31, logits and tokens:
--kv-growmapped whole (STRATA_KV_GROW_INIT=262144);--kv-growgrown from 4096 cells into new VRAM (_FLOORabove the cache).Grown by giving up 266 slots (171 experts moved): the same first token and the same 64 tokens here. In general a given-up expert is computed on the CPU, which rounds differently.
Speed
Setup: IQ2_XS (ISTA), RTX 5090, 9950X3D, 128 GB, Windows 11,
--max-context 262144 --kv int8 --prefill auto --spec 4, cache auto, 256 tokens. The runs alternated with and without the flag.--kv-grow--vram-reserve-mib 17884): expert cacheOn our NVFP4 fork (2.76 MB experts, 5090) the cache grows from ~7,060 to ~8,290 slots and a short chat from 111-115 to 126-133 tok/s.
Serve
A served run with
--conversation-cache-mib 8192behaved as expected:🤖 Generated with Claude Code
https://claude.ai/code/session_01VZy1yKaDDiA8a7svdwaHio