Skip to content

perf(sglang): reduce admission checkpoint copies - #321

Merged
jthomson04 merged 2 commits into
mainfrom
jthomson04/sglang-admission-checkpoints
Sep 28, 2026
Merged

jthomson04 merged 2 commits into
mainfrom
jthomson04/sglang-admission-checkpoints

Conversation

@jthomson04

@jthomson04 jthomson04 commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Summary

The broader SGLang admission rollback checks from #158 copy resident radix edges and waiting leases before timing validation. Large AgentX traces spend substantial host CPU time copying unchanged cache data.

Delay the cache snapshot until the first mutation and share unchanged radix edge arrays. Lease checkpoints save page counts; shared-edge splits copy each half once, and partial eviction copies only the retained prefix. Unique edges retain their existing mutation path. When prefix caching is disabled, rejected allocations do not need a cache snapshot.

Review map

  • Risk: medium; admission rollback and physical KV ownership.
  • Start with the checkpoint boundary and lease restoration in sglang_backend.rs, then shared-edge mutations in radix_cache.rs.
  • With prefix caching enabled, snapshot before matching: matching can split, touch and lock nodes. Restore queue and lease state even when admission did not need a cache snapshot.
  • Debug checks require a checkpoint before buffering KV events and no events on rollback without a checkpoint. They do not detect every possible future mutation. The admission token has a private constructor.
  • No CLI, Python, serialized configuration, performance-model math or data changes. This remains a cache snapshot, not a journal; full-pool metadata copies and a transaction redesign are deferred.

Validation

Tested commit: 7b1f8bb04e365122add1adaee9c601f9f10bcaa7, based on main 2e01b14a543889f96639a4b0bb4c1d17523b545b.

  • cargo +1.96.1 test -p aisimulate-core --locked --lib engine::: 591 passed.
  • cargo +1.96.1 fmt --all -- --check and git diff --check: pass.
  • New caller-level partial-prefix failure/retry test verifies topology, owned key/page arrays, locks, timestamps, eviction order, capacity, queue/lease state and suppressed events after two failures. A successful retry admits and completes the request once.
  • Mutation check: moving the checkpoint below prefix matching makes that test fail. The correct code was restored and all 591 tests passed again.
  • Existing split and LRU/Belady eviction tests now exercise both unique arrays and live shared snapshots. Existing continuation rollback coverage checks lease restoration; a focused test covers rejected and successful allocation with prefix caching disabled.
  • The no-fit queue/hash rollback test remains. Expected values come from pre-pass state, exact page identities, eviction victims and request outcomes; no timing goldens changed.
  • Codex source inspection: 7b1f8bb04e365122add1adaee9c601f9f10bcaa7. Hosted checks and external reviews must be read at this same head; local results do not imply hosted CI success.

Additional review changes: matched benchmarks

Compared fff607b35f468928ca89f29daf2b6b9c9c0c5304 with 7b1f8bb04e365122add1adaee9c601f9f10bcaa7: three alternating pairs per workload, 18 timed runs total. Negative CPU change means less host CPU time.

Workload Previous PR CPU Revised CPU CPU change Paired 95% interval
AgentX: 8 sessions / 1,664 requests 9.8063 s 9.6666 s -1.42% -2.28% to +0.09%
AgentX: 32 sessions / 4,265 requests 23.7863 s 23.6554 s -0.55% -5.45% to +3.38%
Decode control: 32 chains / 256 requests 1.9098 s 1.9041 s -0.30% -1.42% to +0.65%

All three workloads have exact per-request and simulated-summary parity, excluding generated UUIDs and three host-timing fields. All 18 timed runs completed the expected request/token counts and matched simulated summaries. No runs were excluded or replaced; no overlapping builds, profiles or simulation jobs were detected.

The differences are small and all intervals include zero: this campaign does not establish a clear end-to-end speedup. No repeatable slowdown was observed.

Point estimates are ratios of CPU medians. Intervals are nominal paired Student-t intervals on log ratios (paired geometric means); three pairs have limited precision. These results measure the combined review changes, not the isolated benefit of any one optimization. Qualification runs are separate from timing estimates.

Original #158 regression evidence

Historical measurements on #158's exact merge (8772127ba718239cecf26d80f59d49c67bb9b058), with and without the original lazy/shared-edge patch:

AgentX workload #158 CPU Original fixed CPU CPU reduction Wall speedup
8 sessions, 1,664 requests 24.04 s 14.90 s 38.0% 1.61x
32 sessions, 4,265 requests 106.31 s 37.65 s 64.6% 2.82x

The 8-session row compares five original and three fixed timing runs. The 32-session row compares single capture-enabled runs and has lower precision. These older measurements are separate from the current-head comparison above. Session count is the trace subset, not fixed request concurrency. A separate three-pair comparison against pre-#158 retained +3.46% CPU for 8 sessions, +5.42% for 32 sessions and +0.71% for the decode control.

Modeling and measurement provenance

Host-cost measurements use the public aisimulate predict --stack engine CLI: Qwen/Qwen3.8-2.4T-A95B-FP8, GB300, SGLang 0.5.17, AIC op-level timing, prefix caching and trace arrival times. Each new comparison uses the same inputs, dependency locks and release flags: Python 3.12.12, Rust 1.96.1, x86-64-v3, thin LTO, one codegen unit and frame pointers. Intel Core Ultra 9 285K; replay on CPU 4, observer on CPU 0. Separate native extensions and their hashes were verified before running.

These results measure simulator CPU/wall time, not predicted GPU or serving performance. Related: #158.

@coderabbitai

coderabbitai Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Important

Review skipped

Review was skipped as selected files did not have any reviewable changes.

⚙️ Run configuration

Configuration used: Repository: ai-dynamo/aisimulate/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: f2e6ce16-aac0-40d9-8410-50749d05c2f9

📥 Commits

Reviewing files that changed from the base of the PR and between 3d32556 and 294e3a1.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: ai-dynamo/aisimulate/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: bdd304ff-0e7b-42b2-97db-e41d4506c1e3

📥 Commits

Reviewing files that changed from the base of the PR and between fff607b and 7b1f8bb.

📒 Files selected for processing (3)
  • crates/core/src/engine/cache/radix_cache.rs
  • crates/core/src/engine/kv_manager/sglang_backend.rs
  • crates/core/src/engine/scheduler/sglang/tests.rs
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • ai-dynamo/dynamo (manual)
  • ai-dynamo/aiconfigurator (manual)

Included review availability: Your plan provides up to 12 included reviews per hour; 9 remain after this review.

📜 Recent review details
⏰ Context from checks skipped due to timeout. (21)
  • GitHub Check: Prediction Regression / Collect prediction snapshot (old)
  • GitHub Check: Prediction Regression / Collect prediction snapshot (new)
  • GitHub Check: Platform Wheels / Build wheels (macosx_arm64)
  • GitHub Check: Platform Wheels / Build wheels (manylinux_2_28_x86_64)
  • GitHub Check: Platform Wheels / Build wheels (manylinux_2_28_aarch64)
  • GitHub Check: Collector Data / Perf data sanity (informational)
  • GitHub Check: Collector Data / Check collector data
  • GitHub Check: Engine Golden Regression
  • GitHub Check: Release Artifact Contract (amd64)
  • GitHub Check: Python 3.11 compatibility
  • GitHub Check: Public API Rust (amd64)
  • GitHub Check: Application Test Wheel (amd64)
  • GitHub Check: Python 3.13 compatibility
  • GitHub Check: Rust (arm64)
  • GitHub Check: Public API Rust (arm64)
  • GitHub Check: Application Test Wheel (arm64)
  • GitHub Check: Release Artifact Contract (arm64)
  • GitHub Check: Python Dependency Licenses
  • GitHub Check: Rust (amd64)
  • GitHub Check: Rust feature modes
  • GitHub Check: Forward Prediction Performance (advisory)
🧰 Additional context used
📓 Path-based instructions (3)
Review scheduler state transitions and event ordering, including cancel, preempt, drain, retry, duplicate, empty, and terminal paths.

⚙️ CodeRabbit configuration file

Files:

  • crates/core/src/engine/scheduler/sglang/tests.rs
  • crates/core/src/engine/cache/radix_cache.rs
  • crates/core/src/engine/kv_manager/sglang_backend.rs
Read REVIEW.md before commenting.

⚙️ CodeRabbit configuration file

Files:

  • crates/core/src/engine/scheduler/sglang/tests.rs
  • crates/core/src/engine/cache/radix_cache.rs
  • crates/core/src/engine/kv_manager/sglang_backend.rs
Before making any change under: `python/aisimulate/src/aiconfigurator/generator/**` MUST read: `python/aisimulate/.claude/rules/generator-development.md` Before making any change under `python/aisimulate/collector/**` MUST read: `python/ais...

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • crates/core/src/engine/scheduler/sglang/tests.rs
  • crates/core/src/engine/cache/radix_cache.rs
  • crates/core/src/engine/kv_manager/sglang_backend.rs
🔀 Multi-repo context ai-dynamo/dynamo, ai-dynamo/aiconfigurator

Linked repositories findings

ai-dynamo/dynamo

  • Replay converts every non-decode KvEvent into router events and applies them to the KV indexer (lib/mocker/src/replay/offline/extensions/kv_events/mod.rs:35-76, lib/mocker/src/replay/offline/extensions/kv_router/mod.rs:on_kv_events). Queued commit/rollback must preserve event visibility and ordering. [::ai-dynamo/dynamo::]
  • KvEventPublishers::publish_raw_batch explicitly treats batches as one source visibility boundary (lib/mocker/src/common/protocols.rs:121-146). The PR’s deferred event publication should retain that batching behavior. [::ai-dynamo/dynamo::]
  • No downstream consumers of the renamed admission/checkpoint types or TreeNode were found. [::ai-dynamo/dynamo::]

ai-dynamo/aiconfigurator

  • Both normal and benchmark SGLang rules enable prefix caching and the router when ModelConfig.prefix > 0 (src/aiconfigurator/generator/rule_plugin/sglang.rule:27-29, src/aiconfigurator/generator/rule_plugin/benchmark/sglang.rule:29-31). This directly exercises the PR’s checkpoint-before-prefix-matching path. [::ai-dynamo/aiconfigurator::]
  • No references to the changed Rust APIs or checkpoint types were found. [::ai-dynamo/aiconfigurator::]
🔇 Additional comments (5)
crates/core/src/engine/cache/radix_cache.rs (2)

213-236: LGTM!


1040-1040: LGTM!

Also applies to: 1049-1049

crates/core/src/engine/kv_manager/sglang_backend.rs (2)

394-396: LGTM!

Also applies to: 409-412


58-62: 🗄️ Data Integrity & Integration

The rollback mismatch is not reachable from these paths.

extend_cached_prefix runs after commit_admission, not before a possible rollback. activate_destination_lease runs from the command handler outside try_execute_pass_internal. During the admission window, allocation uses inactive leases and does not rewrite existing page IDs.

crates/core/src/engine/scheduler/sglang/tests.rs (1)

2742-2799: LGTM!


📝 Summary

Risk: Medium. Human review should focus on:

  1. Radix-cache copy-on-write: TreeNode.key and TreeNode.value change from Vec<_> to Arc<Vec<_>>. Check edge extension, splitting, and partial eviction when checkpoints share these arrays.
  2. Admission checkpoint timing and rollback: Check that the first checkpoint precedes every mutating path, especially prefix matching, and that rollback restores cache state, leases, queue state, and buffered KV events.
  3. Public contract compatibility: TreeNode.key and TreeNode.value are public fields with changed types. Check downstream callers that access or mutate them directly.

Behavior and contracts: Admission now starts without copying manager state. It checkpoints before mutation, shares unchanged radix-edge arrays, and records lease page counts rather than copying page IDs. Partial eviction retains only the necessary prefix. begin_admission, commit_admission, and rollback_admission now use the move-only SglangAdmission token.

Evidence supplied: The change adds rollback tests for failed validation and partial-prefix matching. The author reports 591 passing engine tests, successful formatting and diff checks, and exact per-request and simulated-summary parity. The author reports current-head CPU median differences of -1.42%, -0.55%, and -0.30%; all paired intervals include zero, so these results do not establish a clear speedup. Separate historical measurements against the exact #158 merge report 38.0% and 64.6% lower simulator CPU on two AgentX workloads. These measurements do not establish GPU or serving-performance improvements. The inspected source confirms the public field type changes and admission token signatures.

Evidence missing and readiness: Test and benchmark results have not been independently verified here. Current review findings and severity counts were not supplied. Technical review status and merge readiness remain undetermined. Bot review is not approval.

Walkthrough

Radix cache edges now share key and value vectors through Arc and use copy-on-write for mutation. SGLang admissions defer manager snapshots until operations may mutate state. Commit publishes queued events, and rollback restores checkpointed state. New tests cover cache snapshot isolation and scheduler admission retries.

Changes

SGLang admission rollback

Layer / File(s) Summary
Shared radix edge storage
crates/core/src/engine/cache/radix_cache.rs
Radix node keys and values use Arc<Vec<_>>. Edge extension, splitting, and partial eviction preserve shared contents through copy-on-write. Cache tests check saved-cache contents and independent eviction.
Admission checkpoint lifecycle
crates/core/src/engine/kv_manager/sglang_backend.rs, crates/core/src/engine/scheduler/sglang/tests.rs
Admissions begin without a snapshot. Allocation paths checkpoint before possible mutation, based on reservability and prefix-cache behavior. Commit publishes queued events; rollback restores checkpointed state. Tests cover capacity checks and repeated scheduler prefill-validation failures.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Merge Risk: ⚪ Minimal · up to 7b1f8

This change makes admission checkpoints cheaper by sharing radix-cache edge storage and taking the snapshot only when state may change. Rollback still restores the cache, leases, queue and events after a failed admission. No concrete correctness or data-integrity defect was established at the current head. The remaining notes are performance and robustness follow-ups, so the change looks ready to merge.

🚥 Pre-merge checks | ✅ 8
✅ Passed checks (8 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cross-Layer Contract ✅ Passed PASS. The PR changes only internal Rust implementation state in three files. engine::cache and engine::kv_manager are private modules, and the changed admission types and methods are pub(crate) …
Modeling And Data Evidence ✅ Passed The PR changes only Rust cache/admission code and regression tests; it does not refresh model formulas, golden data, or serialized outputs. The supplied validation includes reproducible before/after b…
Compatibility Boundaries ✅ Passed PASS. The PR changes only three internal Rust source files. engine::cache and engine::kv_manager remain private modules, so the changed TreeNode fields and SGLang admission types are not externa…
Review Evidence ✅ Passed The PR description names commands and results, including the Rust test command with 591 passing tests, formatting and diff checks, benchmark commands, and measured outcomes. It includes boundary and n…
Title check ✅ Passed The title identifies the behavioral change: reducing admission checkpoint copies. It is specific enough and matches the main changes to lazy snapshotting and shared radix-edge data.
Description check ✅ Passed The description explains the problem, implementation, rollback boundaries, risks, tests, benchmark evidence, provenance, and compatibility scope. It uses different headings from the template and omits…

Comment @coderabbitai help to get the list of available commands.

@jthomson04
jthomson04 marked this pull request as ready for review September 23, 2026 15:53
@jthomson04
jthomson04 requested review from a team as code owners September 23, 2026 15:53

@jthomson04 jthomson04 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No correctness bug found. Inside the admission window, only allocate_for_request_lease_capped and extend_allocation change the cache, and both call checkpoint_admission() before their first change. The late snapshot therefore restores the same state the old up-front one did. All 10 admission_validation_rollback tests pass on this head.

Inline comments cover one latent rollback risk, a test gap, a small API point and some remaining copy costs. Two items point at lines outside the diff:

  • RadixRequestLease::admission_checkpoint (sglang_backend.rs:48) still clones lease.pages for every waiting request on every pass. During admission, pages only grow: extend_allocation appends, and allocation fills an empty, inactive lease. Storing pages.len() and truncating on restore would be enough, as page_hashes_len already does. Otherwise a long chunked continuation at the head of the queue (for example 100k pages at page_size 1) is copied on every pass, including the "nothing fits" passes this PR targets.
  • Design: after this change the cost per admitting pass is still proportional to the cache size, not to the number of changes admission makes. Taking the snapshot late and making edges copy-on-write lower the cost, but under load most passes admit something. Two ways to go further:
    • An undo journal that records the few changes admission makes (split node ids, lock increments, evicted leaves/pages, allocated pages, event-id and page_to_block_hash writes) and replays them backwards on rollback.
    • Checking the planned (tokens_computed, prefix_tokens) before changing anything. get_new_batch_prefill already gets prefix lengths from the read-only prefix_match_hashes_len.

Comment thread crates/core/src/engine/kv_manager/sglang_backend.rs
Comment thread crates/core/src/engine/scheduler/sglang/tests.rs
Comment thread crates/core/src/engine/kv_manager/sglang_backend.rs Outdated
Comment thread crates/core/src/engine/kv_manager/sglang_backend.rs
Comment thread crates/core/src/engine/kv_manager/sglang_backend.rs Outdated
Comment thread crates/core/src/engine/cache/radix_cache.rs Outdated
Comment thread crates/core/src/engine/cache/radix_cache.rs Outdated
@jthomson04

Copy link
Copy Markdown
Contributor Author

Implemented the local changes in 7b1f8bb. All 591 engine tests pass; moving the snapshot after prefix matching makes the new rollback test fail as intended.

The lease-copy suggestion from the review body is also addressed: checkpoints save the page count, and rollback truncates appended pages. The existing continuation failure test covers restoration of existing page IDs, capacity, queue order and events.

The undo journal and validation-before-mutation proposals are deferred. Both would change transaction planning beyond the local copy reductions. Validation alone also does not cover later timing prediction or duration-conversion failures. We retain the rollback boundary and add the small debug checks rather than redesigning it.

Three alternating timing pairs per workload (18 runs) against the previous PR head show CPU median changes of -1.42% for AgentX with 8 sessions, -0.55% for 32 sessions, and -0.30% for the 32-chain decode control. All intervals include zero, so this does not establish a clear end-to-end speedup. No repeatable slowdown was observed. Every workload has exact per-request and summary parity. The full table, revisions and method are in the PR description.

The individual inline replies describe the fixes and the remaining full-cache snapshot limit. Review threads are left open for confirmation.

@Harrilee Harrilee left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed commit 7b1f8bb. Approve; no actionable correctness issues found.

The lazy checkpoint precedes prefix matching (including split/touch/lock), and all allocation/extension mutations inside admission are covered. Length-only lease rollback is valid because existing page IDs are not rewritten before commit. Shared radix edges preserve snapshot contents through splitting and LRU/Belady eviction; failure/retry tests exercise caller-visible rollback and event suppression.

Independent validation: cargo +1.96.1 test -p aisimulate-core --locked --lib engine:: — 591 passed, 0 failed; Rust formatting and diff checks passed. Full CI run 35889924285 succeeded on this exact SHA.

I did not rerun AgentX benchmarks. The historical 2.82x result is a single capture-enabled comparison, as disclosed; the current-head matched campaign measures additional review changes and does not establish a further speedup. Approval does not turn that historical figure into a new independently reproduced result.

@jthomson04
jthomson04 enabled auto-merge (squash) September 28, 2026 16:24
@jthomson04
jthomson04 force-pushed the jthomson04/sglang-admission-checkpoints branch from 7b1f8bb to 3d32556 Compare September 28, 2026 16:26
@copy-pr-bot

copy-pr-bot Bot commented Sep 28, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@jthomson04

Copy link
Copy Markdown
Contributor Author

/ok to test 3d32556

Signed-off-by: jthomson04 <jwillthomson19@gmail.com>
Signed-off-by: jthomson04 <jwillthomson19@gmail.com>
@jthomson04
jthomson04 force-pushed the jthomson04/sglang-admission-checkpoints branch from 3d32556 to 294e3a1 Compare September 28, 2026 18:20
@jthomson04

Copy link
Copy Markdown
Contributor Author

/ok to test 294e3a1

@jthomson04
jthomson04 merged commit 0ac7512 into main Sep 28, 2026
66 of 67 checks passed
@jthomson04
jthomson04 deleted the jthomson04/sglang-admission-checkpoints branch September 28, 2026 18:41
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.

2 participants