Skip to content

fix(dsv4): honor prefill limits and derive SWA capacity - #116

Open
jpezzulli wants to merge 1 commit into
FlashML-org:mainfrom
jpezzulli:fix/prefill-swa-capacity
Open

fix(dsv4): honor prefill limits and derive SWA capacity#116
jpezzulli wants to merge 1 commit into
FlashML-org:mainfrom
jpezzulli:fix/prefill-swa-capacity

Conversation

@jpezzulli

Copy link
Copy Markdown

Closes #115.

Summary

This fixes two DSV4 prefill correctness defects and makes --max-prefill-length the normal single sizing knob for DSV4:

  • preserve the configured chunked-prefill limit instead of replacing it with the model's full sequence length;
  • read prefill_chunk_budget from the live KV pool so an in-place SWA rebuild cannot leave the scheduler with a stale startup budget;
  • derive the minimum DSV4 SWA capacity from the requested prefill, model window page, concurrency, and radix-retained working set;
  • add --swa-num-tokens as a validated expert absolute-capacity override;
  • report requested/effective prefill, pool cap, derived-versus-explicit SWA, and limiting reason consistently;
  • make destructive live-rebuild rollback restore both the previous geometry and its capacity-source metadata.

The locally validated --max-prefill-length 24576 configuration is not introduced as a universal default. The existing default remains unchanged; 24,576 is only the value validated on the hardware/checkpoint below.

Root causes

  1. DSV4 model adjustment unconditionally overwrote max_extend_tokens after CLI/config resolution.
  2. CacheManager copied the initial pool's prefill_chunk_budget; DSV4 rebuilds reinitialize pool geometry, so the copy became stale.
  3. Ratio-only DSV4 startup sizing did not express the capacity contract required by an absolute prefill chunk ceiling. A later absolute allocation could also diverge from the geometry used by startup/MoE budgeting.
  4. Absolute SWA pins could be silently capped by an incompatible full-history anchor, and rollback restored pages without restoring derived/explicit source state.
  5. Startup status and early rebuild exits used different reporting paths, so requested/pool/effective/source/reason were incomplete or inconsistent.

Behavior and precedence

For DSV4, startup now computes the minimum usable SWA pages as the concurrent/radix reserve plus twice ceil(max_prefill / window_page_size), matching the pool's live chunk-budget inverse. That derived absolute window is used consistently by the cost model, page solver, and allocation.

--swa-num-tokens remains an expert override. It must be positive, aligned to the resolved SWA page size, no smaller than the capacity required by --max-prefill-length, and no larger than the usable full-history anchor. Generic radix-SWA models retain their existing ratio sizing unless an explicit absolute override is supplied.

Live rebuilds recompute the pool budget and the scheduler rereads it. Rebuild replies and all rejection/timeout/preflight paths expose:

  • requested_prefill_tokens
  • pool_prefill_cap_tokens
  • effective_prefill_tokens
  • swa_capacity_source (derived, explicit, or none)
  • prefill_limiting_reason

The displayed SWA/full ratio is derived from allocated live geometry when an absolute window supersedes the fallback ratio.

Tests

Focused regressions:

env PYTHONPATH=/opt/freetoken/src/python \
  /opt/freetoken/.venv/bin/python -m pytest -q \
  tests/engine/test_cache_budget.py \
  tests/engine/test_attention_backend_matrix.py \
  tests/kvcache/test_pool_sizing_surface.py \
  tests/kvcache/test_cache_unit_bytes.py \
  tests/scheduler/test_cache_rebuild.py \
  tests/server/test_cache_args.py \
  tests/server/test_rebuild_maintenance.py

Result: 132 passed.

Complete non-slow suite with GPU 0 available:

env CUDA_VISIBLE_DEVICES=0 PYTHONPATH=/opt/freetoken/src/python \
  /opt/freetoken/.venv/bin/python -m pytest -q -m "not slow"

Result: 1,377 passed, 8 skipped, 11 deselected.

Explicitly not run

  • The 11 tests marked slow (large kernel sweeps / real-checkpoint reads) were deliberately deselected.
  • The automated needs_weights AIME E2E suite was not enabled.
  • The automated small-checkpoint tests/e2e/test_cache_rebuild.py server test was not run because its required small-model environment was not configured.
  • No multi-GPU or cross-checkpoint E2E matrix was run.

A real-checkpoint DSV4 serving workload and live rebuild were run manually as described below; these do not replace the omitted automated E2E suites.

Live validation

Environment:

  • FreeToken 0.1.2, branch based on 184a4f114d00b7805274841488f2906233b5a961
  • deepseek-ai/DeepSeek-V4-Flash-0731
  • NVIDIA RTX PRO 6000 Blackwell Workstation Edition, 97,887 MiB (RTX A4000 also installed but not used by the service)
  • NVIDIA driver 610.57.04
  • 2x Intel Xeon Platinum 8358, 219 GiB RAM, Fedora Linux 44

Configuration: 524,288 full-history tokens, --max-prefill-length 24576, 4,602 MoE cache slots, --memory-ratio 0.90, no explicit SWA flag.

Startup resolved to:

  • full history: 4,096 x 128 = 524,288 tokens;
  • derived SWA: 407 x 128 = 52,096 usable tokens;
  • requested / pool cap / effective prefill: 24,576 / 24,576 / 24,576;
  • source derived, reason requested_and_swa_pool;
  • effective allocated SWA/full ratio: 407/4096 = 0.099365234375.

Representative workload: 65,525 input tokens and 1,023 decoded tokens completed without OOM, using prefill chunks 24,576 + 24,576 + 16,373. TTFT was 25.631 s, aggregate prefill throughput 2,556.43 tok/s, decode throughput 40.58 tok/s, end-to-end time 50.817 s, and peak GPU memory was 90,071 MiB.

A live expert resize to 41,344 SWA tokens reported requested 24,576, pool/effective 19,200, source explicit, reason swa_pool; restart restored the derived 52,096-token geometry. An invalid ratio preflight returned HTTP 422 with all five observability fields and did not alter cache state.

No main-versus-branch performance A/B is claimed: this is a correctness/configuration change, and main cannot express the same DSV4 one-knob geometry without post-start mutation. The performance numbers above are branch validation only.

Preserve configured DSV4 chunked-prefill limits and keep pool-provided chunk budgets live across in-place cache rebuilds.

Derive the minimum DSV4 SWA pool from max prefill, validate expert absolute overrides, price pinned windows consistently, reject truncating geometries, and expose requested/effective/pool-cap/source/reason across startup and every rebuild outcome.
avlp12 pushed a commit to avlp12/FreeToken that referenced this pull request Aug 25, 2026
Cherry-picked from upstream PR FlashML-org#116 (jpezzulli, FlashML-org/FreeToken@466dee7).
Genuinely missing fix, not covered by our FlashML-org#105 (ca94148) or the FlashML-org#101-based
df6c340: those two already stopped forcing max_extend_tokens up to
max_seq_len, but left _dsv4_window_floor_pages() hard-capped at
min(prefill_reach_pages, 8) pages (1024 tokens) regardless of the configured
--max-prefill-length. The DSV4 SWA/window pool therefore stayed undersized
for any real chunk size, so DSV4PagedKVCache.prefill_chunk_budget silently
truncated prefill chunks far below what --max-prefill-length requested,
turning one large prompt into many tiny chunks (per-chunk kernel-launch and
compressor-carry overhead) -- the likely root cause of our 171-400 tok/s
prefill ceiling.

This commit adds dsv4_required_swa_pages()/dsv4_prefill_chunk_budget() to
kvcache/dsv4_cost_model.py, derives swa_num_pages_override from
max_extend_tokens/max_running_req in engine._adjust_config (unless an
explicit --swa-num-tokens override is given and is large enough), makes
CacheManager.prefill_chunk_budget a live delegation to the pool instead of a
construction-time snapshot (stale after in-place cache rebuilds), and adds
requested/effective/pool-cap/source/reason reporting through
cache_status.py, scheduler.py, api_server.py, cache_report.py, and
control_cli.py.

Conflicts resolved against our own additions in the same functions:
engine/config.py (kept our distributed_timeout=1800.0 alongside the new
swa_num_token_override/swa_capacity_source fields), engine/engine.py (kept
df6c340's comment explaining why max_extend_tokens is honored), and
scheduler/cache.py (kept our host-RAM KV tier attach_host_tier /
maybe_flush_host_tier / _host_restore machinery alongside the new live
prefill_chunk_budget property).

tests/dsv4 + tests/kvcache: 529 passed, 1 skipped.
tests/engine/test_cache_budget.py, tests/engine/test_attention_backend_matrix.py,
tests/scheduler/test_cache_rebuild.py, tests/server/test_cache_args.py,
tests/server/test_rebuild_maintenance.py: 98 passed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DSV4 ignores configured prefill limit and retains stale budget after SWA rebuild

1 participant