server : evict checkpoints within min-step of each other - #25472
Conversation
|
Hmm, turns out the near end checkpoint isn't being created if within I also opened #25420 to address prefill issues. I can roll these fixes into a single PR if desired. |
Think we just have to allow diff --git a/tools/server/server-context.cpp b/tools/server/server-context.cpp
index 17941d9e9..f477d7a3a 100644
--- a/tools/server/server-context.cpp
+++ b/tools/server/server-context.cpp
@@ -3506,7 +3506,10 @@ private:
do_checkpoint = do_checkpoint && !has_mtmd;
// no need to create checkpoints that are too close together, unless it's the last user message
- do_checkpoint = do_checkpoint && (slot.prompt.checkpoints.empty() || is_last_user_message || n_tokens_start > slot.prompt.checkpoints.back().n_tokens + params_base.checkpoint_min_step);
+ do_checkpoint = do_checkpoint && (
+ slot.prompt.checkpoints.empty() ||
+ is_last_user_message || near_prompt_end ||
+ n_tokens_start > slot.prompt.checkpoints.back().n_tokens + params_base.checkpoint_min_step);
SLT_DBG(slot, "main/do_checkpoint = %s, pos_min = %d, pos_max = %d\n", do_checkpoint ? "yes" : "no", pos_min, pos_max);
// note: we create the checkpoint before calling llama_decode(), so the current batch is notDoing some testing. |
5add0ff to
33ac581
Compare
|
@ggerganov @aldehir thanks for the work on this. I wanted to share some benchmark data that might help decide between the eviction approach and prevention at creation time. I tested a prevention-based approach on a 29-request agent workload (branching conversations, sleep/wake, MTP). The numbers vs pre-bug baseline:
The approach combines the diff --git a/tools/server/server-context.cpp b/tools/server/server-context.cpp
--- a/tools/server/server-context.cpp
+++ b/tools/server/server-context.cpp
@@ -3522,12 +3522,15 @@ private:
// do not checkpoint after mtmd chunks
do_checkpoint = do_checkpoint && !has_mtmd;
- // no need to create checkpoints that are too close together, unless it's the last user message
- // apply a relaxed floor for is_last_user_message to prevent pathological clustering (#25023)
+ // no need to create checkpoints that are too close together, unless it's the last user
+ // message or we are near the end of the prompt (#25023)
+ // apply a relaxed floor for is_last_user_message to prevent pathological clustering;
+ // near_prompt_end fires unconditionally to ensure the critical end-of-prompt checkpoint exists
const int32_t checkpoint_floor = slot.prompt.checkpoints.empty()
? 0
: slot.prompt.checkpoints.back().n_tokens + params_base.checkpoint_min_step / 2;
do_checkpoint = do_checkpoint && (slot.prompt.checkpoints.empty()
+ || near_prompt_end
|| (is_last_user_message && n_tokens_start > checkpoint_floor)
|| n_tokens_start > slot.prompt.checkpoints.back().n_tokens + params_base.checkpoint_min_step);
SLT_DBG(slot, "main/do_checkpoint = %s, pos_min = %d, pos_max = %d\n", do_checkpoint ? "yes" : "no", pos_min, pos_max);Full analysis : post-enhanced-fix-analyse.md |
|
@ali0une I'm merging this in now and will continue to look for improvements. To do that, however, I need to find a way to benchmark this. Regarding your |
|
@aldehir many thanks for your answer from an architectural purity standpoint and for the time spent to analyze my relaxed floor for is_last_user_message approach. i'll keep on analysing logs from the upstream vanilla llama.cpp and my local repository with the relaxed floor for is_last_user_message and let you know if i find anything usefull. |
…, OpenCL Q6_K/Adreno, CORS, checkpoint min-step, prompt cache refactor, MoE expert API stays) Upstream highlights since 6be7459: - model: DFlash speculative with KV rotation (ggml-org#25823) - model: Hy3 (hy_v3) with MTP speculative decoding (ggml-org#25395) - model: DeepseekV4 with fused hyper-connection ops (ggml-org#25585) - ggml: 0.17.0, LIGHTNING_INDEXER, out_prod, f16 set_rows - vulkan: Q2_0 support, native e2m1/e4m3 conversions, transfer-queue race fix - CUDA: MMQ kernel config refactor (ggml-org#24127), tighter MMQ src1 buffer for fp4 (ggml-org#25613), CUDA graphs on Volta/Turing, MoE gate/up dedup, CUDA Virtual Devices - ROCm: hexagon L2 cache rework, native fp4, FP16/INT8 coopmat on AMD - SYCL: Battlemage flash attention via oneDNN XMX, XIELU op, fp16 conv2d_dw - OpenCL: Q6_K GEMM/GEMV fix, ragged-tile MoE prefill FP16, Adreno vectorized LD/ST, A7x optimizations, ABS op - kleidiai: SME2 f32 kernel, SME vs SME2 dispatch - server: refactor prompt cache state ownership (ggml-org#25649) - new server_prompt_cache_state separates prompt metadata from KV data - server: evict checkpoints within min-step (ggml-org#25472) - server: text-only slot save/restore with mtmd (ggml-org#25076) - server: --cors-* options (ggml-org#25655) - server: refactored server_stream (ggml-org#25541) - server: respect min-step when splitting prompt batches (ggml-org#25420) - server: move chat-template thinking probe inside init try/catch (ggml-org#24093) - common: auto-download dflash/eagle3 HF sidecars (ggml-org#25811), drop --stdin mutual-exclusion, align tokenize usage - conversion: BitNetForCausalLM, dflash tokenizer fix, split MTP export for HY V3 - llama-quant: exclude i32 ffn_gate_tid2eid routing table, allow manual tensor types with --pure - llama-batch: fix allowed decreasing pos in a seq (ggml-org#25449), n_keep_tail in split_equal for recurrent - llama: refactor fused ops (ggml-org#24646), TP fix for Phi3/Bert/Plamo2/3/ChatGLM - ui: agentic content UX, reasoning effort on mobile add sheet, MCP panel fixes, thinking menu fix - vendor: BoringSSL 0.20250713.0 - tests: actually exercise test-recurrent-state-rollback, ds_v4_hc sentinel init, export-graph-ops graceful exit CachyLLama preservation work (conflict resolution): 1. tools/server/server-task.h: Accept upstream's server_prompt refactor (no data member, clear() method). Move our t_last_used field from server_prompt to server_prompt_cache_state (where it now lives after the refactor). server_prompt_cache_state already has the size() method, so our old size() on server_prompt is no longer needed. 2. tools/server/server-context.cpp (create_checkpoint): Take upstream's min-step eviction pre-filter as the FIRST pass, then keep our existing highest-pos_min eviction as the capacity overflow fallback. These are complementary: min-step removes redundant checkpoints from the same task; highest-pos_min keeps the rec-window-friendly checkpoints when at cap. 3. tools/server/server-context.cpp (handle_completions_impl): Keep our std::vector<server_task> tasks batching for multi-prompt requests and per-user concurrency check, AND take upstream's res->set_req(&req) for spipe ownership transfer. 4. tools/server/server-task.cpp: Fix references to entry.tokens -> entry.prompt.tokens, entry.checkpoints -> entry.prompt.checkpoints, entry.n_tokens() -> entry.prompt.n_tokens(). Update find_eviction_candidate return type from list<server_prompt>::iterator to list<server_prompt_cache_state>::iterator. 5. ggml/src/ggml-cuda/mmq.cuh + new mmq-config-rdna3_5.cuh: Upstream's massive MMQ refactor moved per-architecture config into separate files but did NOT add RDNA3.5 (gfx1150/1/2/3, Strix Halo). Create mmq-config-rdna3_5.cuh (231 CASE entries) derived from rdna2 with nthreads=128 (4 warps) and I=48 (smaller X tile) matching our original Strix Halo tuning. Wire into both host and device dispatch paths before the RDNA4 / RDNA2 fallback. 6. README.md and AGENTS.md: Keep CachyLLama-specific links and project context where upstream added parallel content. Verified: - cmake --build builds clean (Release, CPU-only) - llama-server starts, --help shows all CachyLLama flags preserved: --cache-ssd-hot-ram, --cache-ssd-warm-ram, --cache-ssd-system-prompts, --cache-ssd-system-max-days, --cache-ssd-no-fsync, --cache-ssd-max-conversations, --max-concurrent-per-user - /expert-stats and /expert-tracking endpoints preserved - 55/58 tests pass; 3 failures unrelated to merge: - test-tokenizers-ggml-vocabs: missing model downloads - test-jinja-py: missing jinja2 Python module - test-quant-type-selection: snapshot mismatch on upstream's new MXFP4_MOE heuristic Custom CachyLLama files untouched (no upstream conflicts): - common/kv-ssd-cache.{cpp,h}, common/kv-ssd-posix.h, common/kv-ssd-system-cache.{cpp,h} - common/kv_page_manager.{cpp,h} - tools/server/server-context-page-manager.{cpp,h} - tools/server/server-context-ssd-cache.{cpp,h} - test_kv_page_manager.cpp, tests/test-ssd-cache-caps.cpp - STRIX_HALO_NOTES.md, docs/development/user-isolation-design.md - .github/workflows/build-cpu.yml, build-cuda-windows.yml, build-vulkan.yml
Upstream PR ggml-org#25472 always fires a checkpoint within the last ubatch of every prompt regardless of --checkpoint-min-step. On dense + sliding-window models (gemma-4) and dense MoE (Qwen3.6-35B) this doubles per-request SSD write volume and warm restore I/O - warm TTFT for gemma-4 large went 0.65s -> 2.12s after the merge. Default stays off (matching pre-ggml-org#25472 behavior). Add explicit CLI flag so profiles that want the upstream behavior can opt in.
Upstream PR ggml-org#25472 always fires a checkpoint within the last ubatch of every prompt regardless of --checkpoint-min-step. On dense + sliding-window models (gemma-4) and dense MoE (Qwen3.6-35B) this doubles per-request SSD write volume and warm restore I/O - warm TTFT for gemma-4 large went 0.65s -> 2.12s after the merge. Default stays off (matching pre-ggml-org#25472 behavior). Add explicit CLI flag so profiles that want the upstream behavior can opt in.
Upstream PR ggml-org#25472 always fires a checkpoint within the last ubatch of every prompt regardless of --checkpoint-min-step. On dense + sliding-window models (gemma-4) and dense MoE (Qwen3.6-35B) this doubles per-request SSD write volume and warm restore I/O - warm TTFT for gemma-4 large went 0.65s -> 2.12s after the merge. Default stays off (matching pre-ggml-org#25472 behavior). Add explicit CLI flag so profiles that want the upstream behavior can opt in.
Upstream PR ggml-org#25472 always fires a checkpoint within the last ubatch of every prompt regardless of --checkpoint-min-step. On dense + sliding-window models (gemma-4) and dense MoE (Qwen3.6-35B) this doubles per-request SSD write volume and warm restore I/O - warm TTFT for gemma-4 large went 0.65s -> 2.12s after the merge. Default stays off (matching pre-ggml-org#25472 behavior). Add explicit CLI flag so profiles that want the upstream behavior can opt in.
Overview
Continuation of #24176 (review)
When creating a new checkpoint, evict any checkpoints that are within min-step of an earlier one.
Since we create two checkpoints towards the end of the prompt, it is possible that the last checkpoint evicts the penultimate checkpoint. To avoid this, I keep track of the task id in the checkpoint and only evict checkpoints created from prior tasks. Let me know if there is a better approach.
fixes #25023
Requirements