Conversation
The load_back_pending_id pin slot was single-valued, so two live
overlapping load-backs on one node hit the commit_load_back assertion
('node X pinned by load-back Y, new anchor X'). This happens under heavy
device-eviction<->load-back cycling on small VRAM: one request's Full-KV
chain pins an evicted ancestor while another request anchors at that
ancestor for its independently-evicted mamba state. Overlapping
transfers only read the shared host slots and write disjoint
destinations, so concurrent pins are safe; track them as a set and
release each anchor's pin at its ack.
A first load-back anchored at a descendant pins the evicted ancestor via its Full-KV chain; before its ack a second load-back anchors at that ancestor for its independently-evicted mamba state. On the previous single-valued pin this reproduced the production crash 'AssertionError: node X pinned by load-back Y, new anchor X' in 0.3s.
|
This looks like the right fix for concurrent ownership. One lifecycle edge case that may be worth making explicit: should a pin identify only the anchor ID, or the specific load-back generation/component as well? If an anchor/node ID can ever be reused while a delayed ack is still in flight, a generation-tagged pin could avoid an ABA-style release of a newer load-back. |
|
Good question — I checked this specifically. Anchor pins are keyed by node id, and |
|
can you confirm this is a fix for write back policy right? |
|
@ziang663 can you help take a look? |
|
I think the fix is correct. Retrieving the set allows the same node to be pinned by multiple requests, while there's an assumption that the components must be different, which I believe can be guaranteed. |
|
@ffahleraz pls fix the lint |
DEP8 at conc 384/512 dies mid-run on an assertion in unified_tree_core.py commit_load_back: load_back_pending_id holds a single anchor, but a node can legitimately be pinned by two concurrent H->D load-backs. Eight schedulers exit together and the surviving DP ranks block forever in the MLP-sync collective, which surfaces as a frozen in-flight count with zero errors. sgl-project/sglang#34975 fixes it (the pin becomes a set); it is still open, so this points at sglang-staging:dev-cu13-pr-35880 -- build commit 2fca6c4e35, which is dev-nightly-0820 (92eeed41d7) with #35880 cherry-picked on top. Same CUDA 13.0.3, same layer count. Swap to a released tag once it merges.
|
Does swa / mamba really need But for swa / mamba, 1. they won't be reclaim by dup, 2. their lock will protect full window/state, so the real bug maybe just that they should not participate in the full kv's Wonder if this simple change can fix the issue you see: e16de90 |
* [NVIDIA][AgentX] DeepSeek-V4 B300 SGLang: mega-MoE + prefill-decode-interval (+28%) Retunes the existing B300 SGLang AgentX recipe. The headline change is --prefill-decode-interval 20 (sgl-project/sglang#35017): under speculative decoding plus DP attention SGLang synchronises decode globally, so a rank with no prefill work runs an idle batch and the busiest rank sets the clock for all eight. Measured at conc 128 over 1800 s on 8xB300, that takes output throughput from 2,433-2,470 to 3,127-3,161 tok/s and closes the gap to the vLLM recipe from 1.32x to 1.03x. Every engine-side change is scoped to the DP-attention path; the TP-only path is left exactly as upstream had it, because all measurements here ran with DP attention enabled. Also restructures the search space from 47 points to 12 and raises the CPU tier to match the vLLM agentic lane on the same runner. * [NVIDIA][AgentX] Condense the DSv4 HiCache ratio comment No functional change; retriggers CI. * [NVIDIA][AgentX] Fix DEP4 startup: mem-fraction-static must clear the weights floor DEP4 shards the model over half the node, so per-rank weight memory roughly doubles and the weights-only floor rises above 0.9. The engine refuses to start with 'Loaded weights leave no GPU memory for the KV cache' and reports a minimum viable 0.9013. Keep upstream's 0.95 there; raise DEP8 to 0.93 (0.92 at the conc>=512 tail). * [NVIDIA][AgentX] Lower TP>=8 hicache ratio to 3 to fit host memory hicache capacity is a host/device token ratio, so host bytes scale with device KV and therefore with mem-fraction-static -- the two knobs multiply. ratio=4 at mem-fraction 0.93 left only 5.84 GB free on a 2,964 GB node and the V4 paged pool failed to allocate (requested 8.70 GB). ratio=3 keeps the tier near 2 TB with headroom. * [NVIDIA][AgentX] Scale DEP8 mem-fraction-static down with concurrency MegaMoE's transient workspace sits outside the static allocation and needs a single ~7 GB contiguous block, so required headroom grows with batch size. At conc 256, 0.93 (~16 GB free) and 0.95 (~11 GB free) both OOM one DP rank, which hangs the engine in the MLP-sync collective; 0.835 (~42 GB free) runs. Ladder: 0.93 at conc 64/128, 0.9 at 256, 0.89 at 384, 0.875 at 512/576. * [NVIDIA][AgentX] Size TP-only decode graphs for subagent fan-out AgentX concurrency counts session trees, not requests, and the recipe already sets max-running-requests to 2*CONC to allow fan-out. Capturing decode graphs only up to CONC therefore dropped every larger batch to eager decode on the conc 1/4/8 rows. Capture to 4*CONC (still capped at 64); the runtime clamps to the request-pool size, so it cannot over-capture. * [NVIDIA][AgentX] Point changelog entry at PR #2701 * [NVIDIA][AgentX] Lower DEP4 mem-fraction-static to 0.93 0.95 leaves only ~11 GB of GPU headroom -- the same margin that OOM'd a rank at DEP8 conc 256 and hung the engine. 0.93 gives ~16 GB, matching what DEP8 conc 128 runs with at an identical per-rank load (max-running/dp = 32). * [NVIDIA][AgentX] Use dev-nightly-0820 + HiCache load-back fix DEP8 at conc 384/512 dies mid-run on an assertion in unified_tree_core.py commit_load_back: load_back_pending_id holds a single anchor, but a node can legitimately be pinned by two concurrent H->D load-backs. Eight schedulers exit together and the surviving DP ranks block forever in the MLP-sync collective, which surfaces as a frozen in-flight count with zero errors. sgl-project/sglang#34975 fixes it (the pin becomes a set); it is still open, so this points at sglang-staging:dev-cu13-pr-35880 -- build commit 2fca6c4e35, which is dev-nightly-0820 (92eeed41d7) with #35880 cherry-picked on top. Same CUDA 13.0.3, same layer count. Swap to a released tag once it merges. * [NVIDIA][AgentX] Correct search-space point count in changelog The removed rows total 42 points (7 + 12 + 6 + 10 + 7), not 47. * [NVIDIA][AgentX] Lower DEP8 conc 512/576 mem-fraction-static to 0.86 * [NVIDIA][AgentX] Lower DEP8 conc 384 mem-fraction-static to 0.88 * [NVIDIA][AgentX] Drop DEP4 rows; extend TP-8 and DEP8 concurrency DEP4 cannot serve this trace. Sharding DeepSeek-V4 over half the node leaves ~627k KV tokens per rank at mem-fraction 0.93 (measured from the hicache host pool / ratio 8), against prompts reaching 950,812 tokens -- the longest requests cannot be held at all. Raising mem-fraction far enough to hold them leaves too little room for the mega-MoE workspace, so the two constraints barely overlap; every DEP4 point failed. Search space is now TP-8 no-offload at conc 1/4/8/16/32 and DEP8+hicache at conc 32/64/128/256/384/512/576 -- still 12 points. * [NVIDIA][AgentX] Enable router retries so one transient send failure cannot abort a run The recipe launched sglang_router with --disable-retries. A single transient router->engine send failure ("error sending request") then surfaces as a 500, and AgentX treats a failed root warmup request as fatal: "ProfileAborted: A root AgentX warmup request failed, so profiling was not started." The engine stays healthy throughout -- it keeps logging prefill batches and serving /metrics 200 OK -- so the run dies with no result and no crash to point at. Measured on 8xB300 / DeepSeek-V4-Pro at conc 512: three separate 2h15m arms were killed this way, each by exactly one failed request (2 ERROR lines in router.log). After enabling retries, one 3600s run logged 22 such transients spread over all 8 DP workers with zero client-visible 500s and errors=0 -- every one recovered. Retry is safe here: "error sending request" means the request was never delivered, so there is no partial state, and it cannot inflate throughput because it only fires on a failed send. Runs that never hit a transient are unaffected (the three completed baseline arms logged zero router errors either way). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Update perf-changelog.yaml --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: Cameron Quilici <cjquilici@gmail.com>
|
@weireweire you are right, thanks for #36317. Looking at main again, @hzh0425 @ziang663 sorry for going quiet on the lint. The same workload had a second crash, |
…新 DSV4 B300 SGLang AgentX 镜像和 HiCache 并发网格 (#2759) * chore(dsv4-b300): move AgentX HiCache MTP lane to a published nightly image Bump dsv4-fp4-b300-sglang-agentic-hicache-mtp from the one-off staging tag lmsysorg/sglang-staging:dev-cu13-pr-35880 to the published nightly lmsysorg/sglang:nightly-dev-cu13-20260827-20621aa1, so every point is reproducible from a public image. Image-only change: the write policy, search space, dram-utilization and all serving flags from #2701 are left untouched. --prefill-decode-interval is retained because sgl-project/sglang#35017 merged before this nightly's build commit. The HiCache load-back fix (sgl-project/sglang#34975 and its cherry-pick #35880) is still unmerged, so the DEP8 conc 384/512/576 crash is a known risk; this is recorded in perf-changelog.yaml. 将 dsv4-fp4-b300-sglang-agentic-hicache-mtp 的镜像从一次性构建的 staging 标签 lmsysorg/sglang-staging:dev-cu13-pr-35880 切换到已发布的 nightly lmsysorg/sglang:nightly-dev-cu13-20260827-20621aa1,使所有数据点均可基于公开 镜像复现。 本次仅改动镜像:#2701 引入的写策略、搜索空间、dram-utilization 及全部服务参数 均保持不变。由于 sgl-project/sglang#35017 已在该 nightly 的构建提交之前合并, --prefill-decode-interval 得以保留。HiCache load-back 修复(sgl-project/sglang#34975 及其 cherry-pick #35880)仍未合并,因此 DEP8 并发 384/512/576 存在已知的崩溃 风险,该风险已记录在 perf-changelog.yaml 中。 Co-Authored-By: Claude <noreply@anthropic.com> * chore(changelog): point the entry at PR #2759 Replace the placeholder pr-link now that the PR number exists. PR 号确定后,将 changelog 条目中的占位 pr-link 替换为实际链接。 Co-Authored-By: Claude <noreply@anthropic.com> * fix(dsv4-b300): drop DEP8 concurrency 64 and 128 from the hicache row Concurrency 128 failed on run 33051183882 with a CUDA OOM inside deep_gemm fp8_fp4_paged_mqa_logits (5.35 GiB requested, 4.55 GiB free) on DP ranks 3, 4 and 5, crashing scheduler_0 and aborting AIPerf during warmup. Concurrency 64 shares the same mem-fraction-static 0.93 tier and is dropped with it. The changelog entry also corrects the previous entry's prediction: DEP8 concurrency 384, 512 and 576 passed on this image, so the HiCache load-back failure did not reproduce. 并发 128 在 run 33051183882 上因 deep_gemm fp8_fp4_paged_mqa_logits 内的 CUDA OOM 失败(请求 5.35 GiB,仅剩 4.55 GiB),DP rank 3、4、5 同时报错,导致 scheduler_0 崩溃并使 AIPerf 在 warmup 阶段中止。并发 64 与其同属 mem-fraction-static 0.93 档位,一并移除。 changelog 同时修正了上一条目的预测:DEP8 并发 384、512、576 在该镜像上均通过, HiCache load-back 失败并未复现。 Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Cam Quilici <cjquilici@gmail.com>
Motivation
Under heavy device-eviction↔load-back cycling (small-VRAM GPUs with
--enable-hierarchical-cache, hybrid GDN/mamba models), the scheduler crashes with:at
UnifiedTreeCore.commit_load_back(unified_tree_core.py). Hit repeatedly servingnvidia/Qwen3.6-35B-A3B-NVFP4+ EAGLE + hicache ratio-12 on RTX 5090 (32GB) under a production-trace replay; 96GB GPUs never trip it (no deep evicted chains → no overlap).Root cause
UnifiedTreeNode.load_back_pending_idis single-valued, but two live load-backs can legitimately pin the same node:load_back_pending_id = A).mamba_component.build_hicache_transferspins[node.id]).new anchor== the node itself, matching the production message shape exactly).The overlap is semantically safe: both in-flight DMAs only read the shared host slots, and their writes target disjoint destinations (Full-KV pool vs mamba pool). Only the bookkeeping cannot represent it.
Modifications
load_back_pending_id: Optional[int]→load_back_pending_ids: set[int].commit_load_backadds the anchor id (idempotent for a node sitting in both the Full and an aux transfer list of one anchor).finish_load_backdiscards its anchor id along the root path; duplicate tracking updates once the last pin drains._is_settled_full_host_duplicate,_can_reclaim_full_host_duplicate) and the sanity check test set-emptiness; split inheritance copies the set.Test / repro
test/registered/unit/mem_cache/test_unified_loadback_multipin.py— a deterministic CPU repro built on the existingUnifiedRadixCacheSuitefixtures (FULL+MAMBA): backup → full evict → load-back anchored at the leaf (ack withheld) → load-back anchored at the pinned ancestor.sanity_checkclean.Checklist
CI States
Latest PR Test (Base): ❌ Run #31902335393
Latest PR Test (Extra): ❌ Run #31902335225