Skip to content

Validate DeepSeek-V4 sliding-window cache sources on B200 - #2931

Open
cquil11 wants to merge 13 commits into
mainfrom
codex-dsv4-swa-cache-sources
Open

cquil11 wants to merge 13 commits into
mainfrom
codex-dsv4-swa-cache-sources

Conversation

@cquil11

@cquil11 cquil11 commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Summary

Validate the sliding-window/hybrid attribution update in cquil11/vllm#2, using the existing DeepSeek-V4-Pro B200 TP8/MTP AgentX recipe.

Point Connector Concurrency Host cache
DRAM Native OffloadingConnector 8 128 GB
NVMe SimpleCPUOffloadConnector, disk backend 14 Staging buffers only; 1 TB aggregate disk capacity
DRAM + NVMe Native TieringOffloadingSpec + filesystem tier 14 128 GB

GPU memory utilization is 0.85. This image loads ~105 GiB of weights per GPU, so 0.30–0.50 cannot fit this recipe. Canonical AgentX warmup/duration remain unchanged. B300 is excluded.

Uses full-sweep-enabled with no-evals: true on the new changelog entries,
supported by merged #2935.
Only the three throughput points run; this does not provide model-quality eval
evidence. CPU threading remains at the runtime default.

Frontend exception to the historical recipe: use the Python frontend
(VLLM_USE_RUST_FRONTEND=0) where the fork implements Prometheus source metrics.
The Rust frontend has a separate metrics implementation and currently ignores
the added attribution field. This validates the same engine/SWA/offloading path,
but is not a direct frontend-performance comparison with the historical run.

Implementation

  • Reuse the tested TP8 recipe from successful sweep 32526153588, including c8. That historical fleet was B200 DGXC; the current configuration routes to B200 Nscale.
  • Support kv-offloading: nvme and [dram, nvme], with explicit runtime backend validation. Other recipes continue accepting only their supported modes.
  • Mount a job-specific node-local NVMe directory, delete that exact directory before releasing the allocation, and bound cleanup. Do not inventory cluster NVMe in runners.yaml or delete other jobs' files.
  • Correct the B200 compatibility launcher to select the original DeepSeek-V4-Pro checkpoint for vLLM instead of its separate NVFP4 conversion, matching the proven recipe and native launcher.
  • Use the public Docker Hub image, pinned to digest sha256:3e5daa6e289046ca99e690a8a91a46a9ffb5a3c213dac6deb9afb06a6ef22188. Exact upstream wheel e41011129bd54c9e8e12b645ea9b964027d2363f plus fork PR head b0dc209c14451e9a4e63f1db148ed492d881b765, merged upstream #55712 SWA coverage fix, and open upstream #50014 unread-promotion protection. Exact validation source is separate from the unchanged metrics PR. No CUDA rebuild. FlashInfer Python, cubin, and CUDA 13 JIT-cache packages all use 0.6.18.
  • Promotion protection also makes pinned blocks unavailable for eviction by new stores. This can delay stores and affects performance; the derivative validates backend recovery and metrics, not performance neutrality of the metrics patch.

Validation

  • Schema/generator tests: 310 passed.
  • Bash syntax, backend rejection checks, and git diff --check: passed.
  • Exact-key generation produces only the three rows above.
  • Built-image versions: vLLM 0.28.1rc1.dev369+ge41011129, torch 2.13.0+cu130, FlashInfer 0.6.18.
  • The original image's 18 changed production files matched the fork PR head by SHA256. The derivative adds upstream SWA coverage and promotion protection; source-attribution behavior and the five canonical labels are unchanged. The diagnostic container's scheduler, tiering manager, and promotion policy hashes match the validation branch.
  • Default-thread native DRAM startup/replay passed on B200 diagnostic job 13298 with 128 GB host cache. The earlier 649 GB budget failed CUDA host registration. These probes did not exercise host cache hits and are not final metric-validation evidence.
  • Previous official run 34395212219 exercised native host/disk source hits and Simple disk hits; sampled counters reconciled with legacy and total cached-token counters. It is not accepted final evidence: DRAM failed startup and native tiered crashed with a missing-block assertion before source accounting. The replacement increases tiered host capacity from 64 to 128 GB.
  • Run 34400931163 again failed tiered loading with a missing-block assertion at 128 GB. DRAM and Simple NVMe entered the measured profile with verified host/disk hits and reconciled totals; the run was canceled for the patched-image sweep. It is not final validation evidence.
  • Upstream SWA coverage regressions against the original image sources: 6 expected failures, 10 passes. Backported offloading-scheduler suite: 300 passed, including our source-attribution checks. Changed-file pre-commit hooks passed, including mypy. The only adaptation retains each group's KV cache spec, which the newer upstream structure already exposed.
  • Official run 34404334784: DRAM and Simple NVMe jobs succeeded. Their AIPerf JSON/CSV artifacts reconcile every source/legacy counter. Buckets wholly inside the measured profile show 177,696 host tokens and 16,275,200 disk tokens respectively, with no unexpected sources. Artifacts: DRAM, NVMe. Full exported aggregates include a boundary bucket containing warmup-era counts; do not call those aggregates profile-only.
  • Native tiered in that run stalled with one deferred request, idle GPUs, and repeated disk reads. An independent reproduction stalled too. Backporting #50014 allowed B200 diagnostic13354 to finish the same full warmup and a 120-second replay, with reconciled device/host/disk JSON/CSV metrics. Replay exited0; the diagnostic wrapper subsequently failed its missing RESULT_FILENAME check. This short diagnostic is not official full-duration evidence. Targeted tests499passed; full tiering suite384passed/11skipped (overlapping sets), changed-file hooks passed.
  • Official run 34412940233 completed all three full benchmark profiles. DRAM and Simple NVMe jobs succeeded; native tiered failed only when its 60-second NVMe cleanup deadline interrupted deletion. Tiered AIPerf artifact: 2,064 valid measured requests, zero request errors, reconciled JSON/CSV counters; complete profiling buckets show 2,188,052 host and 15,720,608 disk tokens. Mixed required cache dependencies may legitimately produce external (105,020 tokens in those buckets). Whole-export totals include a warmup boundary bucket.
  • Commit 6a822f912 extends the bounded cleanup deadline to five minutes. Shell syntax and 116 focused changelog tests passed. Replacement official run 34420875287 is all green, retaining the image, three points, and canonical duration. DRAM: 1,117 valid requests; Simple NVMe: 2,075; tiered: 2,067. Zero request errors; one drain-deadline cancellation each for NVMe/tiered. TTFT/ITL coverage gates passed. All three AIPerf exports have exactly five sources, conserved legacy/source totals, and agreement across all nine JSON/CSV counter series. Complete profiling buckets show 190,624 host tokens (DRAM), 15,882,752 disk tokens (Simple), and 2,213,744 host plus 14,092,624 disk tokens (tiered). Raw artifacts: DRAM, NVMe, tiered. Slow cold checkpoint loading was accelerated with read-only file prefetch before profiling; server commands and thread settings were unchanged. P2P validation remains tracked separately in Validate current vLLM P2P cache sources on GB300 1P/1D #2938.
  • Current-code P2P validation runs separately in #2938, reusing the proven GB300 1P/1D AgentX topology.

Prepared with AI assistance; this is a validation PR, not a claim of completed GPU verification.


Note

Medium Risk
Touches Slurm launcher teardown, new matrix offload modes, and benchmark recipe/runtime flags; scope is mostly B200 validation but shared validation helpers affect other agentic sweeps.

Overview
Adds NVMe and tiered DRAM+NVMe agentic KV offload modes end-to-end: matrix validation/generation accepts nvme and ['dram','nvme'], maps tier lists to runtime values like dram+nvme, and keeps DRAM budgeting for DRAM/tiered (not pure NVMe). Shared benchmark_lib.sh checks offload mode against per-recipe allowed backends.

The DeepSeek-V4 B200 vLLM MTP recipe is retargeted to a cache-source validation sweep: pinned custom image, Python frontend (VLLM_USE_RUST_FRONTEND=0), lower GPU memory utilization, and three points—native DRAM, Simple disk NVMe, and native tiered DRAM+NVMe—via OffloadingConnector / SimpleCPUOffloadConnector configs. Nscale B200 launcher mounts a job-scoped NVMe path, extends cleanup to 5 minutes before releasing Slurm, fixes vLLM DSV4 checkpoint selection, and hardens allocation/import failures.

Docs note NVMe cleanup timeouts; perf-changelog.yaml records the validation iterations (image, host cache size, upstream backports).

Reviewed by Cursor Bugbot for commit 6a822f9. Bugbot is set up for automated code reviews on this repo. Configure here.

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Thanks for the contribution! Please reach out to respective companies' CODEOWNER to fill in the latest PR_REVIEW_CHECKLIST.md before pinging core maintainer on Slack for review. In order for the signoff PR check bot to trigger, you must follow the PR_REVIEW_CHECKLIST.md template correctly, including the phrase As a PR reviewer and CODEOWNER, I have reviewed this and have.

For PR verification, add the full-sweep-fail-fast label (strongly recommended) to this PR — the benchmark sweep only runs on labeled PRs. Use full-sweep-enabled only if you need matrix jobs to keep running past a failure.

PR authors are responsible for ensuring that after merging, all GitHub Action jobs fully pass. A lot of the time, failures are just flakes and simply re-running the failed jobs will fix it. See GitHub's docs on re-running failed jobs


感谢你的贡献!请联系相应公司的 CODEOWNER 填写最新的 PR_REVIEW_CHECKLIST.md,然后再在 Slack 上联系核心维护者进行审阅。为了触发 signoff PR 检查机器人,你必须正确遵循 PR_REVIEW_CHECKLIST.md 模板,包括保留英文语句 As a PR reviewer and CODEOWNER, I have reviewed this and have

如需进行 PR 验证,请为此 PR 添加 full-sweep-fail-fast 标签(强烈推荐)— 基准测试 sweep 仅在带有标签的 PR 上运行。仅当需要矩阵任务在失败后继续运行时才使用 full-sweep-enabled

PR 作者有责任确保合并后所有 GitHub Action 任务完全通过。 很多时候失败只是偶发抖动(flake),重新运行失败的任务即可解决。参见 GitHub 关于重新运行失败任务的文档

@cquil11
cquil11 marked this pull request as ready for review September 9, 2026 19:16
@cquil11
cquil11 requested a review from a team September 9, 2026 19:16
@claude

claude Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Claude finished @cquil11's task in 11m 49s —— View job


PR Review — 1 blocking, 1 warning

  • Gather context (diff vs origin/main, AGENTS.md, docs)
  • Review matrix generation/validation changes (infx/matrix/*, tests)
  • Review benchmark script + benchmark_lib.sh changes
  • Review launcher (runners/launch_b200-nscale-compat.sh)
  • Validate configs/nvidia-master.yaml + perf-changelog.yaml
  • Line count report for infx/matrix/generate.py
  • Post inline comments + summary

Summary: 🔴 The launcher's vLLM checkpoint override (launch_b200-nscale-compat.sh:31-33) is not scoped to the agentic recipe — every single-node b200-nscale job routes through this compat launcher, so the existing production dsv4-fp4-b200-vllm fixed-seq-len config silently switches from the NVFP4 conversion (which its script's --moe-backend flashinfer_cutedsl choice explicitly assumes) to the original checkpoint, with no perf-changelog.yaml entry for that key; gate it on SCENARIO_TYPE/IS_AGENTIC (see inline suggestion). 🟡 The NVMe cleanup trap can't run once the allocation is dead (time-limit kill, preemption, node failure), permanently stranding up to ~1 TB per run on node-local NVMe with nothing ever reclaiming it — see inline suggestion for a squeue-guarded stale sweep. Everything else checked out: changelog entries are correctly appended at the tail, the digest-pinned Docker Hub image is public and the docker://${IMAGE/@/:} form is semantically consistent with the proven enroot_uri_for_image digest handling in launch_gb200-nv.sh, the schema/generator changes and backend-mode narrowing are backward compatible for other recipes, and git diff --check is clean.

Two notes for the author: (1) merging intentionally replaces the existing dsv4 B200 vLLM AgentX production curve (TP8 conc 1–14 + DEP8 conc 32–196) with three validation points on a personal fork image — flagging so maintainers confirm that's the intent; (2) the Simple NVMe config passes the same disk_path ($NVME_OFFLOAD_DIR/cache.bin) to all 8 TP ranks — I couldn't inspect the fork connector (the vllm source MCP server failed to connect this session), so please confirm it rank-suffixes the file, since disk_capacity_bytes is divided per rank. I could not run pytest in this environment (command approval not granted), so the claimed 310-test pass was not independently re-verified.

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

@cquil11
cquil11 force-pushed the codex-dsv4-swa-cache-sources branch from 956b289 to fd2239f Compare September 9, 2026 19:25
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Comment on lines +31 to +33
if [[ "$FRAMEWORK" == "vllm" ]]; then
export MODEL_PATH="/scratch/models/DeepSeek-V4-Pro"
fi

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.

🔴 BLOCKING: This checkpoint swap is not scoped to the agentic validation recipe — it changes the weights for every dsv4-fp4 vLLM job on this launcher, including the existing production dsv4-fp4-b200-vllm fixed-seq-len config (configs/nvidia-master.yaml:947), which also has framework: vllm, runner: cluster:b200-nscale, and routes through this compat launcher for all single-node jobs (launch_b200-nscale-slurm.sh:39-41).

Why it matters: benchmarks/single_node/fixed_seq_len/dsv4_fp4_b200_vllm.sh:43-45 explicitly assumes the pinned checkpoint is the ModelOpt NVFP4 conversion ("The pinned checkpoint uses ModelOpt NVFP4 expert weights ... use FlashInfer CuTeDSL instead"). Silently serving /scratch/models/DeepSeek-V4-Pro to that recipe will break or change its existing benchmark curve, and there is no perf-changelog.yaml entry for dsv4-fp4-b200-vllm — this violates the repo invariant that every benchmark-affecting change be covered by a changelog entry for the affected config keys.

Fix: Scope the override to the agentic recipe. SCENARIO_TYPE (and IS_AGENTIC) are exported by benchmark-tmpl.yml and empty for fixed-seq-len jobs:

Suggested change
if [[ "$FRAMEWORK" == "vllm" ]]; then
export MODEL_PATH="/scratch/models/DeepSeek-V4-Pro"
fi
if [[ "$FRAMEWORK" == "vllm" && "${SCENARIO_TYPE:-}" == "agentic-coding" ]]; then
export MODEL_PATH="/scratch/models/DeepSeek-V4-Pro"
fi

(Alternatively, keep the unscoped change and add a perf-changelog.yaml entry for dsv4-fp4-b200-vllm plus fix its MoE-backend assumption — but that widens this validation PR considerably.)

Comment on lines +543 to +552
local rc=$?
if [[ -n "$NVME_HOST_DIR" ]]; then
timeout --kill-after=10s 60s srun --jobid="$JOB_ID" \
rm -rf -- "$NVME_HOST_DIR" || {
echo "NVMe cleanup failed: $NVME_HOST_DIR on job $JOB_ID" >&2
rc=1
}
fi
scancel "$JOB_ID" || true
exit "$rc"

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.

🟡 WARNING: Cleanup depends on the allocation still being alive. If the job dies before the EXIT trap's srun --jobid runs — SALLOC_TIME_LIMIT expiry (a hung benchmark at 480 min), preemption, or a node failure — srun fails against the dead job ID and /scratch/inferencex-kv-$JOB_ID is stranded permanently on node-local NVMe. Nothing ever reclaims it: each retry uses a new job ID and this PR deliberately deletes only the current job's directory, so repeated timeouts can accumulate up to ~1 TB of cache files per stranded run on a compute node the login node can't reach.

Fix: Keep the per-job delete, but before creating the new directory, sweep stale siblings whose owning job no longer exists (preserves the "never touch a live job's files" property):

    if [[ "${KV_OFFLOADING:-none}" == *nvme* ]]; then
        NVME_HOST_DIR="/scratch/inferencex-kv-$JOB_ID"
        srun --jobid="$JOB_ID" bash -c '
            for d in /scratch/inferencex-kv-*; do
                [[ -d "$d" ]] || continue
                jid="${d##*-}"
                squeue -h -j "$jid" > /dev/null 2>&1 || rm -rf -- "$d"
            done
        ' || true
        srun --jobid="$JOB_ID" mkdir -m 700 "$NVME_HOST_DIR" || exit 1

Comment thread infx/matrix/generate.py
"""
kv_offloading = benchmark.get(Fields.KV_OFFLOADING.value, "none")
if kv_offloading != "dram":
if kv_offloading not in ("dram", ["dram", "nvme"]):

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.

📊 Line Count Report

  • Total Lines: 1573
  • Base Lines: 1570
  • Change: 📈 +3 lines

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

@claude claude Bot 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.

Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.

Comment on lines +542 to +553
cleanup_offload_cache() {
local rc=$?
if [[ -n "$NVME_HOST_DIR" ]]; then
timeout --kill-after=10s 60s srun --jobid="$JOB_ID" \
rm -rf -- "$NVME_HOST_DIR" || {
echo "NVMe cleanup failed: $NVME_HOST_DIR on job $JOB_ID" >&2
rc=1
}
fi
scancel "$JOB_ID" || true
exit "$rc"
}

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.

🟡 (optional) The new EXIT-trap cleanup_offload_cache() overwrites rc=1 whenever the bounded NVMe rm -rf fails/times out, so a benchmark that already succeeded (rc=0) gets reported as a failed CI step just because scratch cleanup hit a transient Slurm/filesystem hiccup. Fix: isolate cleanup failures from the run's own exit status — log the cleanup failure but exit with the original $? captured before cleanup, only setting a nonzero code if the run itself failed.

Extended reasoning...

Main bench srun (line 585-591) finishes with rc=0 -> script falls off the end -> EXIT trap fires, local rc=$? captures 0 first. If the subsequent timeout --kill-after=10s 60s srun --jobid="$JOB_ID" rm -rf -- "$NVME_HOST_DIR" (545-549) fails for any reason (Slurm control-plane hiccup, node already draining, srun step rejected) it unconditionally sets rc=1, and exit "$rc" at line 552 then reports failure to the CI workflow even though the benchmark itself succeeded and produced valid results. This path is entirely new (no cleanup/trap existed on this branch before the diff), so it is not pre-existing; before the diff nothing could turn a successful run into a failed step this way.

Verification: normal. In the new EXIT trap at runners/launch_b200-nscale-compat.sh (added this diff), cleanup_offload_cache() does local rc=$? (capturing the main bench srun's exit code), then when NVME_HOST_DIR is set runs timeout --kill-after=10s 60s srun --jobid="$JOB_ID" rm -rf -- "$NVME_HOST_DIR" and on any nonzero result unconditionally sets rc=1, followed by exit "$rc". So a benchmark…

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

@github-actions

Copy link
Copy Markdown
Contributor

@github-actions

Copy link
Copy Markdown
Contributor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant