Pull requests / #2640
#2640 generate: --prompt-cache-policy log keeps checkpoints on a geometric grid (opt-in, default lru unchanged; #2431)
open · @qiuzan2001 · 0 comments · View on GitHub
AMD / HIPNVIDIA / CUDAModels & quantsDocumentationLinux
Description
For #2431 (TwoAnts' point there that the retention should thin out with distance rather than keep only the newest points).
## What it changes
A new engine argument, `--prompt-cache-policy lru|log`, default `lru`.
With more checkpoints than `--prompt-cache`, the current rule keeps the root and the most recently used points. A 256K conversation with the defaults (`--prompt-cache 6`, `--prompt-cache-every 16384`) therefore holds the root (~30K) and 192K–256K, and a request that branches anywhere between them (an agent compacting its history, an edited early message, a sub-agent that shares the first half) reads everything after the root again.
`log` picks the victim against a geometric grid behind the newest point instead: targets at `every × (2^k − 1)` behind it (0, 16K, 48K, 112K, …), the root pinned as before, the targets that would fall behind the root spent halving the widest remaining gap. The tail checkpoint (`--prompt-cache-tail`) and `pin=N` prefixes are treated exactly as before. The function is pure (`eviction_victim_log` in `include/strata/program/conv_cache.hpp`); the one call site in `src/program/generate.cpp` passes the chain's lengths to it only when the flag is on.
Simulated growth with the defaults (`conv_cache_test`): at 256K `lru` keeps 30/192/208/224/240/256K, `log` keeps 30/80/128/224/240/256K; the largest gap is 96K against 162K. At 1M it is 674K against 930K.
## What it leaves alone
- The default path calls the same `eviction_victim` with the same arguments as before; the new branch is only entered with `--prompt-cache-policy log` and `--prompt-cache-every > 0`. A chain with no more points than `--prompt-cache` is the same either way.
- With more than 64 checkpoints the `log` rule falls back to `lru` (its scratch arrays are fixed; the help text and the docs say so).
- `sycl/src/program/generate.cpp` is not changed. The header is shared and additive, so the SYCL build is unaffected, but the flag is an unknown argument there until that port is re-migrated.
## How it was checked
- `src/program/conv_cache_test.cpp`: 37/37 (16 new: the simulated 128K/256K/1M growth, root/newest/pinned never the victim, the tail rule first, `--prompt-cache-every 0` / no lengths / one slot / more than 64 items fall back to the plain rule, the root not the shortest, the root the longest).
- An independent review pass read the diff and fuzzed the policy with 300,000 random chains under ASan/UBSan: no out-of-bounds, never the root or a pinned item.
- CUDA build (RTX 5070 Ti, CUDA 13.1, arch 120). Not built: HIP (no AMD card here), SYCL (not wired, see above), Metal (no Mac).
- A/B on the machine below with `tools/checkpoint_branch_test.py` (included): four greedy requests against the server, 4 output tokens, thinking off. Three rounds per arm, alternating order, the engine restarted for every run. Every run of an arm reused the same token counts; the prompt time below is the median.
RTX 5070 Ti 16 GB, Ryzen 7 5800X3D, 76 GB RAM, Linux, CUDA 13.1; Swift 1.5 IQ3_S, 262,144 context, `--kv int8 --kv-resident 32768`, `--prompt-cache 6`, `--prompt-cache-every 16384`; engine built from this branch, 0.1.44.1 otherwise.
| request | prompt tokens | lru: reused / read / prompt time | log: reused / read / prompt time |
| --- | ---: | ---: | ---: |
| cold read | 178,823 | 0 / 178,823 / 62.5 s | 0 / 178,823 / 62.5 s |
| retry of the last turn | 178,823 | 165,035 / 13,788 / 5.7 s | 165,035 / 13,788 / 5.7 s |
| branch at 90,104 | 90,104 | 16,384 / 73,720 / 23.9 s | 66,731 / 23,373 / 8.2 s |
| return to the full conversation | 178,823 | 81,920 / 96,903 / 34.4 s | 83,115 / 95,708 / 34.1 s |
The branch request is the only one where the two differ. (In this run the root the engine kept was the periodic 16,384-token checkpoint, which lands before the ~30K system prompt ends; the gap the `log` rule closes is the same either way.)
Docs: one paragraph in `docs/DETAILS.md` ("Which checkpoints stay") with these numbers, and the flag in the engine options list.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Related on this site
Quick links to related pages.