Skip to content
Closed
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
5d79351
feat(agentx): add H100 NVMe KV offload sweep
cquil11 Aug 28, 2026
44d1af3
feat(agentx): add tiered DRAM and NVMe sweep
cquil11 Aug 28, 2026
923f60d
test(agentx): cover tiered KV offload matrices
cquil11 Aug 28, 2026
3ebc733
refactor(agentx): represent tiered offload as tier list
cquil11 Aug 28, 2026
0e41fd1
perf: schedule H100 MiniMax-M3 offload sweep
cquil11 Aug 28, 2026
ef3c36e
fix(agentx): extend H100 allocation time
cquil11 Aug 28, 2026
366d271
fix(h100): clean NVMe before releasing allocation
cquil11 Aug 28, 2026
0545bda
fix(agentx): reject multinode NVMe offload
cquil11 Aug 28, 2026
8de9e47
fix(h100): exclude node with broken NVLink P2P
cquil11 Aug 28, 2026
ae673c5
fix(h100): clean stale vLLM offload memory
cquil11 Aug 28, 2026
d83dd9d
fix(h100): purge stale NVMe offload directories
cquil11 Aug 28, 2026
f436e23
fix(h100): bound offload cleanup steps
cquil11 Aug 28, 2026
e631356
simplify H100 offload lifecycle
cquil11 Aug 28, 2026
fc9433a
test updated cache-source image
cquil11 Aug 28, 2026
48389ac
Merge remote-tracking branch 'origin/main' into codex/h100-minimaxm3-…
cquil11 Aug 28, 2026
1a0793b
bound in-container NVMe cleanup
cquil11 Aug 28, 2026
3cb3641
simplify H100 offload launcher
cquil11 Aug 28, 2026
893cd1f
test tiering timeout image
cquil11 Aug 28, 2026
8a00c0a
clean stale vLLM offload mmap files
cquil11 Aug 31, 2026
a4c2a6c
document stale offload cleanup
cquil11 Aug 31, 2026
b52c639
Merge remote-tracking branch 'origin/main' into codex/h100-minimaxm3-…
cquil11 Aug 31, 2026
cb0fec2
Narrow H100 cache-source validation sweep
cquil11 Sep 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions benchmarks/benchmark_lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -49,19 +49,19 @@ require_agentic_kv_offload_backend() {
fi
return 1
;;
dram)
dram|nvme|dram+nvme)
if [[ "${KV_OFFLOAD_BACKEND:-}" != "$expected_backend" ]]; then
echo "Error: expected KV_OFFLOAD_BACKEND=$expected_backend when KV_OFFLOADING=dram, got '${KV_OFFLOAD_BACKEND:-}'" >&2
echo "Error: expected KV_OFFLOAD_BACKEND=$expected_backend when KV_OFFLOADING=$KV_OFFLOADING, got '${KV_OFFLOAD_BACKEND:-}'" >&2
exit 1
fi
if [[ ! "${TOTAL_CPU_DRAM_GB:-}" =~ ^[1-9][0-9]*$ ]]; then
echo "Error: DRAM KV offloading requires a positive TOTAL_CPU_DRAM_GB capacity" >&2
if [[ "$KV_OFFLOADING" != "nvme" && ! "${TOTAL_CPU_DRAM_GB:-}" =~ ^[1-9][0-9]*$ ]]; then
echo "Error: $KV_OFFLOADING KV offloading requires a positive TOTAL_CPU_DRAM_GB capacity" >&2
exit 1
fi
return 0
;;
*)
echo "Error: unsupported KV_OFFLOADING value '$KV_OFFLOADING' (expected one of: none, dram)" >&2
echo "Error: unsupported KV_OFFLOADING value '$KV_OFFLOADING' (expected one of: none, dram, nvme, dram+nvme)" >&2
exit 1
;;
esac
Expand All @@ -86,18 +86,18 @@ if [[ "$_benchmark_caller" == */agentic/* ||
exit 1
fi
;;
dram)
dram|nvme|dram+nvme)
if [[ -z "${KV_OFFLOAD_BACKEND:-}" || "${KV_OFFLOAD_BACKEND:-}" == "none" ]]; then
echo "Error: KV_OFFLOAD_BACKEND is required when KV_OFFLOADING=dram" >&2
echo "Error: KV_OFFLOAD_BACKEND is required when KV_OFFLOADING=$KV_OFFLOADING" >&2
exit 1
fi
if [[ ! "${TOTAL_CPU_DRAM_GB:-}" =~ ^[1-9][0-9]*$ ]]; then
echo "Error: DRAM KV offloading requires a positive configured TOTAL_CPU_DRAM_GB capacity" >&2
if [[ "$KV_OFFLOADING" != "nvme" && ! "${TOTAL_CPU_DRAM_GB:-}" =~ ^[1-9][0-9]*$ ]]; then
echo "Error: $KV_OFFLOADING KV offloading requires a positive configured TOTAL_CPU_DRAM_GB capacity" >&2
exit 1
fi
;;
*)
echo "Error: unsupported KV_OFFLOADING value '$KV_OFFLOADING' (expected one of: none, dram)" >&2
echo "Error: unsupported KV_OFFLOADING value '$KV_OFFLOADING' (expected one of: none, dram, nvme, dram+nvme)" >&2
exit 1
;;
esac
Expand Down
29 changes: 28 additions & 1 deletion benchmarks/single_node/agentic/minimaxm3_fp8_h100_mtp.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
set -eo pipefail
set -x

# H100 MiniMax-M3 MXFP8 AgentX with EAGLE3 and optional Mooncake DRAM KV offload.
# H100 MiniMax-M3 MXFP8 AgentX with EAGLE3 and optional DRAM or NVMe KV offload.

source "$(dirname "$0")/../../benchmark_lib.sh"

Expand Down Expand Up @@ -47,6 +47,9 @@ cleanup_services() {
set +e
stop_background_process_tree "$SERVER_PID" "vLLM server" 60
stop_background_process_tree "$MOONCAKE_MASTER_PID" "Mooncake master" 30
if [[ -n "${NVME_OFFLOAD_DIR:-}" ]]; then
find "$NVME_OFFLOAD_DIR" -mindepth 1 -delete
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

NVMe teardown delete can stall jobs

High Severity

The benchmark EXIT trap now deletes NVMe offload contents with an unbounded find -delete. After a successful NVMe or tiered run, that teardown can keep the GitHub job stuck in Launch job script even though result files are already written, delaying or blocking artifact upload.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit e631356. Configure here.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Addressed in 1a0793b: the in-container find -delete now has a 120-second timeout plus a 5-second forced-termination bound. It runs only after the existing shutdown trap has stopped vLLM, inside the original Slurm/container step, so it cannot wait on a competing cleanup srun.

exit "$exit_code"
}
trap cleanup_services EXIT
Expand Down Expand Up @@ -97,6 +100,30 @@ EOF
--kv-transfer-config
'{"kv_connector":"MooncakeStoreConnector","kv_role":"kv_both","kv_connector_extra_config":{"load_async":true}}'
)
elif [ "$KV_OFFLOADING" = "nvme" ]; then
require_agentic_kv_offload_backend vllm-simple
: "${NVME_OFFLOAD_DIR:?NVME_OFFLOAD_DIR must be mounted by the H100 launcher}"
NVME_OFFLOAD_TOTAL_BYTES=8000000000000
NVME_OFFLOAD_PER_RANK_BYTES=$((NVME_OFFLOAD_TOTAL_BYTES / TP))
# vLLM appends .rank_<CUDA device index> to give each TP rank its own file.
OFFLOAD_ARGS=(
--kv-transfer-config
"{\"kv_connector\":\"SimpleCPUOffloadConnector\",\"kv_role\":\"kv_both\",\"kv_connector_extra_config\":{\"kv_offload_backend\":\"disk\",\"disk_path\":\"$NVME_OFFLOAD_DIR/cache.bin\",\"disk_capacity_bytes\":$NVME_OFFLOAD_PER_RANK_BYTES,\"disk_buffer_slots\":4,\"lazy_offload\":false}}"
)
Comment on lines +103 to +109

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: All TP ranks are pointed at the same disk_path ($NVME_OFFLOAD_DIR/cache.bin) while disk_capacity_bytes is computed per rank (8 TB / TP).

Why it matters: The per-rank capacity math implies the connector is instantiated once per TP worker (consistent with the existing vllm-simple usage in dsv4_fp4_b200_vllm_mtp.sh, which passes cpu_bytes_to_use_per_rank). If the fork's disk backend does not internally shard/suffix the path per rank, 8 workers will open and write the same 1 TB-capacity file and silently corrupt each other's cached blocks — producing wrong "cache hit" data rather than a crash. I couldn't verify the fork's SimpleCPUOffloadConnector disk backend (source unavailable in this review), and the PR's one-off validation covered the tiered OffloadingConnector path, not this NVMe-only path.

Fix: Confirm the disk backend appends a rank identifier to disk_path (and note it in a comment), or make it explicit in the config, e.g. a per-rank template/directory instead of a single cache.bin. If it does shard internally, feel free to resolve this.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Confirmed in the patched vLLM backend: SimpleCPUOffloadConnector rewrites the configured path as f"{disk_path}.rank_{device.index or 0}" (vllm/v1/simple_kv_offload/worker.py), so TP8 creates cache.bin.rank_0 through cache.bin.rank_7, each with the configured per-rank capacity. Added an inline comment beside the config to make this non-obvious behavior reviewable.

elif [ "$KV_OFFLOADING" = "dram+nvme" ]; then
require_agentic_kv_offload_backend vllm-native
: "${NVME_OFFLOAD_DIR:?NVME_OFFLOAD_DIR must be mounted by the H100 launcher}"
TOTAL_CPU_DRAM_GIB=$((TOTAL_CPU_DRAM_GB * 1000000000 / 1073741824))
PER_RANK_GIB=$(((TOTAL_CPU_DRAM_GIB - MODEL_CHECKPOINT_PAGE_CACHE_GIB) / TP - MODEL_CPU_OFFLOAD_GB - MOONCAKE_LOCAL_BUFFER_GIB))
if (( PER_RANK_GIB <= 0 )); then
echo "Error: CPU DRAM budget is too small for checkpoint cache, model, and DRAM+NVMe KV offload" >&2
exit 1
fi
CPU_OFFLOAD_TOTAL_BYTES=$((PER_RANK_GIB * TP * 1073741824))
OFFLOAD_ARGS=(
--kv-transfer-config
"{\"kv_connector\":\"OffloadingConnector\",\"kv_role\":\"kv_both\",\"kv_connector_extra_config\":{\"spec_name\":\"TieringOffloadingSpec\",\"cpu_bytes_to_use\":$CPU_OFFLOAD_TOTAL_BYTES,\"eviction_policy\":\"lru\",\"secondary_tiers\":[{\"type\":\"fs\",\"root_dir\":\"$NVME_OFFLOAD_DIR\",\"n_read_threads\":32,\"n_write_threads\":16,\"locality\":\"LOCAL\"}]}}"
)
else
echo "Error: unsupported KV_OFFLOADING='$KV_OFFLOADING'" >&2
exit 1
Expand Down
8 changes: 5 additions & 3 deletions configs/nvidia-master.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7341,7 +7341,7 @@ qwen3.5-fp4-b200-trt-mtp:
- { tp: 8, ep: 8, dp-attn: true, spec-decoding: "mtp", conc-list: [128, 256, 1024] }

minimaxm3-fp8-h100-vllm-agentic-mtp:
image: vllm/vllm-openai:v0.27.1
image: ttl.sh/cquil11-vllm-tier-dfb25dbef4-20260828:24h
Comment thread
cquil11 marked this conversation as resolved.
Outdated
Comment thread
cquil11 marked this conversation as resolved.
Outdated
model: MiniMaxAI/MiniMax-M3-MXFP8
model-prefix: minimaxm3
runner: cluster:h100-dgxc
Expand All @@ -7353,8 +7353,10 @@ minimaxm3-fp8-h100-vllm-agentic-mtp:
# The fast sweep places the resident HBM cliff between c5 and c6.
- dram-utilization: 0.80
search-space:
- { tp: 8, spec-decoding: mtp, kv-offloading: none, conc-list: [1, 2, 3, 4, 5] }
- { tp: 8, spec-decoding: mtp, kv-offloading: dram, kv-offload-backend: { name: mooncake, version: "0.3.11.post1" }, conc-list: [6, 8] }
- { tp: 8, spec-decoding: mtp, kv-offloading: none, conc-list: [1, 2, 3, 4, 5, 6, 7, 8] }
- { tp: 8, spec-decoding: mtp, kv-offloading: dram, kv-offload-backend: { name: mooncake, version: "0.3.11.post1" }, conc-list: [5, 6, 7, 8, 9, 10] }
- { tp: 8, spec-decoding: mtp, kv-offloading: nvme, kv-offload-backend: { name: vllm-simple }, conc-list: [7, 8, 9, 10, 11, 12, 13, 14] }
- { tp: 8, spec-decoding: mtp, kv-offloading: [dram, nvme], kv-offload-backend: { name: vllm-native }, conc-list: [7, 8, 9, 10, 11, 12, 13, 14] }

minimaxm3-fp8-h200-vllm-agentic-mtp:
image: vllm/vllm-openai:v0.27.1
Expand Down
13 changes: 13 additions & 0 deletions docs/recovery-results-procedures.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,19 @@ After pushing, never rebase, locally squash, amend, or force-push this carrier c

Canonical source: [complete failed-ingest recovery command](https://github.com/SemiAnalysisAI/InferenceX/blob/0c28706b33d4a796b82f6f9c3594c19c46365575/.claude/commands/recover-failed-ingest.md).

## H100 offload cleanup stalls

An H100 AgentX benchmark can finish and write all result files while the GitHub job remains in `Launch job script`. Check the launcher process tree on the login node. A long-running child matching either of these commands means teardown, rather than the benchmark, is stalled:

```text
srun --jobid=<id> bash -c find /dev/shm ... vllm_offload_*.mmap ... -delete
srun --jobid=<id> bash -c rm -rf -- /mnt/numa0/.../inferencex-kv-<id>
```

Preserve and verify the result files before intervening. The H100 launcher bounds each pre-run and post-run offload cleanup step with `H100_OFFLOAD_CLEANUP_TIMEOUT_S` (120 seconds by default), followed by a 15-second forced-termination window. A post-run timeout is best-effort: the launcher cancels the allocation and preserves the benchmark exit status so artifact uploads can proceed. A pre-run timeout fails closed because stale shared memory or NVMe state can invalidate the next benchmark.

For an already-running job created before this guard existed, terminate only the stuck login-node `srun` client after confirming the benchmark outputs are complete. The launcher's cleanup trap then continues to `scancel` the allocation. Do not cancel the GitHub job or delete its workspace; doing so can discard uploadable evidence.

## AMD root-owned workspace prevention and recovery

### Prevent recurrence
Expand Down
13 changes: 13 additions & 0 deletions docs/recovery-results-procedures_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,19 @@ git diff --check origin/main...HEAD

权威来源:[完整失败摄取恢复命令](https://github.com/SemiAnalysisAI/InferenceX/blob/0c28706b33d4a796b82f6f9c3594c19c46365575/.claude/commands/recover-failed-ingest.md)。

## H100 offload 清理停滞

H100 AgentX 基准可能已经完成并写出所有结果文件,但 GitHub 任务仍停留在 `Launch job script`。请在登录节点检查 launcher 进程树。若以下任一命令的子进程长时间运行,说明停滞发生在 teardown,而不是基准本身:

```text
srun --jobid=<id> bash -c find /dev/shm ... vllm_offload_*.mmap ... -delete
srun --jobid=<id> bash -c rm -rf -- /mnt/numa0/.../inferencex-kv-<id>
```

干预前必须保留并验证结果文件。H100 launcher 使用 `H100_OFFLOAD_CLEANUP_TIMEOUT_S` 限制每个运行前和运行后的 offload 清理步骤;默认超时为 120 秒,随后保留 15 秒强制终止窗口。运行后超时按 best-effort 处理:launcher 会取消 allocation,并保留基准退出状态,使 artifact 上传可以继续。运行前超时则会 fail closed,因为残留的共享内存或 NVMe 状态可能使下一次基准结果无效。

对于在该保护逻辑加入前已经启动的任务,只有在确认基准输出完整后,才能终止登录节点上卡住的 `srun` client。随后 launcher 的 cleanup trap 会继续执行 `scancel` 来释放 allocation。不要取消 GitHub 任务,也不要删除其工作区,否则可能丢失仍可上传的证据。

## AMD root-owned 工作区的预防与恢复

### 防止复发
Expand Down
12 changes: 12 additions & 0 deletions perf-changelog.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6576,3 +6576,15 @@
- "Recipes sourced from srt-slurm (recipes/trtllm/qwen3.5-fp4/inferencex/gb300/{mtp,stp})."
- "Runner: launch_gb300-nv.sh bumped from NVIDIA/srt-slurm@v1.0.29 to v1.0.72 for the dynamo-trt+qwen3.5+fp4 path."
pr-link: https://github.com/SemiAnalysisAI/InferenceX/pull/2730

- config-keys:
- minimaxm3-fp8-h100-vllm-agentic-mtp
scenario-type:
- agentic-coding
description:
- "Run the official H100 MiniMax-M3 AgentX sweep on the patched vLLM image that exports cached prompt tokens by physical source tier."
- "Retain the no-offload TP8 points at concurrency 1-8 and the Mooncake DRAM points at concurrency 5-10; add NVMe-only points at concurrency 7-14 using SimpleCPUOffloadConnector's disk backend with 8 TB aggregate capacity."
- "Add the same concurrency 7-14 range with declarative kv-offloading [dram, nvme], mapped by the vLLM recipe to OffloadingConnector's TieringOffloadingSpec with an LRU DRAM primary tier and node-local filesystem secondary tier."
- "Mount a job-scoped directory from the H100 node's native NVMe filesystem into the Pyxis container and remove it during allocation cleanup."
- "Collect the vLLM Prometheus endpoint through AIPerf so artifacts include vllm:prompt_tokens_cached_by_source alongside the native KV-offload tiering counters."
pr-link: https://github.com/SemiAnalysisAI/InferenceX/pull/2775
38 changes: 33 additions & 5 deletions runners/launch_h100-dgxc-slurm.sh
Original file line number Diff line number Diff line change
Expand Up @@ -289,13 +289,43 @@ else

export GPU_COUNT="${GPU_COUNT:-${TP:?TP must be set}}"

salloc --partition=$SLURM_PARTITION --account=$SLURM_ACCOUNT --gres=gpu:$GPU_COUNT --exclusive --time=180 --no-shell --job-name="$RUNNER_NAME"
SALLOC_TIME_LIMIT="${SALLOC_TIME_LIMIT:-300}"
SALLOC_ARGS=(
--partition="$SLURM_PARTITION"
--account="$SLURM_ACCOUNT"
--gres="gpu:$GPU_COUNT"
--exclusive
--time="$SALLOC_TIME_LIMIT"
--no-shell
--job-name="$RUNNER_NAME"
)
salloc "${SALLOC_ARGS[@]}"
JOB_ID=$(squeue --name="$RUNNER_NAME" -u "$USER" -h -o %A | head -n1)
if [[ -z "$JOB_ID" ]]; then
echo "ERROR: failed to resolve H100 Slurm allocation" >&2
exit 1
fi
trap 'rc=$?; scancel "$JOB_ID" 2>/dev/null || true; exit "$rc"' EXIT
cleanup_allocation() {
local rc=$?
trap - EXIT INT TERM
scancel "$JOB_ID" 2>/dev/null || true
exit "$rc"
}
Comment thread
cursor[bot] marked this conversation as resolved.
trap cleanup_allocation EXIT INT TERM
Comment on lines +301 to +307

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: The NVMe KV directory leaks on every successful run.

Why it matters: On the success path, the pre-existing scancel $JOB_ID at the end of the else branch (line 345) cancels the allocation before the script exits and the EXIT trap fires. cleanup_allocation then runs srun --jobid="$JOB_ID" rm -rf … against an already-cancelled job, which fails and is swallowed by 2>/dev/null || true. So the up-to-8 TB inferencex-kv-<jobid> directory is only removed on failure paths (where set -e exits while the allocation is still alive). With 16 NVMe-backed points per sweep, successful runs will steadily fill the node-local /mnt/numa0 array.

Fix: Delete the trailing scancel $JOB_ID on line 345 and let the trap do rm→scancel in order, e.g.:

    # cleanup_allocation (EXIT trap) removes the NVMe dir, then scancels.

i.e. remove line 345 entirely — the EXIT trap already performs scancel after the rm -rf while the allocation is still alive.

Fix this →


NVME_CONTAINER_MOUNT=""
if [[ "${KV_OFFLOADING:-none}" == "nvme" || "${KV_OFFLOADING:-none}" == "dram+nvme" ]]; then
NVME_HOST_ROOT="/mnt/numa0/enroot/cache/group-$(id -g)"
NVME_HOST_DIR="$NVME_HOST_ROOT/inferencex-kv-$JOB_ID"
srun --jobid="$JOB_ID" bash -c "
set -e
test -w '$NVME_HOST_ROOT'
mkdir -m 700 '$NVME_HOST_DIR'
findmnt -T '$NVME_HOST_DIR'
"
NVME_CONTAINER_MOUNT=",$NVME_HOST_DIR:/kv-offload"
export NVME_OFFLOAD_DIR=/kv-offload
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cancelled jobs leak NVMe cache

Medium Severity

Each NVMe job creates a host directory inferencex-kv-$JOB_ID and never removes old ones. Contents are cleared only inside the container after a graceful vLLM stop, so a cancel or timeout leaves up to 8 TB behind. The next allocation uses a new job id and can fail with no space on /mnt/numa0.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b52c639. Configure here.


# flock-serialize the enroot import so concurrent sweep jobs on the same
# shared NFS path don't race each other into 'File already exists' (race
Expand All @@ -317,12 +347,10 @@ else

srun --jobid=$JOB_ID \
--container-image=$SQUASH_FILE \
--container-mounts=$GITHUB_WORKSPACE:/workspace/,$HF_HUB_CACHE_MOUNT:$HF_HUB_CACHE,$AIPERF_MMAP_CACHE_HOST_PATH:/aiperf_mmap_cache \
--container-mounts=$GITHUB_WORKSPACE:/workspace/,$HF_HUB_CACHE_MOUNT:$HF_HUB_CACHE,$AIPERF_MMAP_CACHE_HOST_PATH:/aiperf_mmap_cache$NVME_CONTAINER_MOUNT \
--no-container-mount-home \
--container-workdir=/workspace/ \
--no-container-entrypoint --export=ALL,PORT=8888,AIPERF_DATASET_MMAP_CACHE_DIR=/aiperf_mmap_cache \
bash benchmarks/single_node/${SCENARIO_SUBDIR}${EXP_NAME%%_*}_${PRECISION}_h100${SPEC_SUFFIX}.sh

scancel $JOB_ID

fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Offload files persist after allocation ends

High Severity

Allocation teardown now only runs scancel, so host NVMe directories under /mnt/numa0 and vLLM DRAM-tier vllm_offload_*.mmap files in /dev/shm are never removed. Cancelled or timed-out jobs also skip the in-container delete, and leftover ~1 TB mmap files can hang the next exclusive job on that node under memory pressure.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit e631356. Configure here.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The launcher-side cleanup sruns and /dev/shm sweep were removed. On graceful shutdown vLLM unlinks its own mmap, and the benchmark clears only its bind-mounted job-scoped NVMe contents with the bounded in-container command from 1a0793b. The allocation trap itself now does only scancel, so cancellation cannot deadlock behind a second Slurm step.

22 changes: 15 additions & 7 deletions utils/matrix_logic/generate_sweep_configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ def agentic_dram_offload_gb(
budgeted separately if it ever gains its own pool).
"""
kv_offloading = benchmark.get(Fields.KV_OFFLOADING.value, "none")
if kv_offloading != "dram":
if kv_offloading != "dram" and kv_offloading != ["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: 1650
  • Base Lines: 1642
  • Change: 📈 +8 lines

return 0

available_mib = min(
Expand Down Expand Up @@ -356,13 +356,21 @@ def agentic_dram_offload_gb(


def agentic_kv_offload_suffix(
kv_offloading: str,
kv_offloading: str | list[str],
kv_offload_backend: dict | None,
) -> str:
"""Return a compact exp-name suffix for agentic KV offload settings."""
if kv_offloading == "none":
return "kvnone"
return f"kv{kv_offloading}-{kv_offload_backend['name']}"
mode = "+".join(kv_offloading) if isinstance(kv_offloading, list) else kv_offloading
return f"kv{mode}-{kv_offload_backend['name']}"


def agentic_kv_offload_runtime_value(kv_offloading: str | list[str]) -> str:
"""Convert declarative tier lists into the workflow's string input."""
if isinstance(kv_offloading, list):
return "+".join(kv_offloading)
return kv_offloading


def multinode_agentic_exp_name(
Expand Down Expand Up @@ -1029,7 +1037,7 @@ def generate_full_sweep(args, all_config_data, runner_data):
Fields.PREFILL.value: prefill,
Fields.DECODE.value: decode,
Fields.CONC.value: conc_batch,
Fields.KV_OFFLOADING.value: kv_offloading,
Fields.KV_OFFLOADING.value: agentic_kv_offload_runtime_value(kv_offloading),
Fields.TOTAL_CPU_DRAM_GB.value: total_cpu_dram_gb,
Fields.DURATION.value: duration,
Fields.EXP_NAME.value: multinode_agentic_exp_name(
Expand Down Expand Up @@ -1066,7 +1074,7 @@ def generate_full_sweep(args, all_config_data, runner_data):
Fields.DP_ATTN.value: dp_attn if dp_attn is not None else False,
Fields.SPEC_DECODING.value: spec_decoding,
Fields.CONC.value: conc,
Fields.KV_OFFLOADING.value: kv_offloading,
Fields.KV_OFFLOADING.value: agentic_kv_offload_runtime_value(kv_offloading),
Fields.TOTAL_CPU_DRAM_GB.value: total_cpu_dram_gb,
Fields.DURATION.value: duration,
Fields.EXP_NAME.value: (
Expand Down Expand Up @@ -1328,7 +1336,7 @@ def generate_test_config_sweep(args, all_config_data, runner_data=None):
Fields.PREFILL.value: prefill,
Fields.DECODE.value: decode,
Fields.CONC.value: conc_batch,
Fields.KV_OFFLOADING.value: kv_offloading,
Fields.KV_OFFLOADING.value: agentic_kv_offload_runtime_value(kv_offloading),
Fields.TOTAL_CPU_DRAM_GB.value: total_cpu_dram_gb,
Fields.DURATION.value: duration,
Fields.EXP_NAME.value: multinode_agentic_exp_name(
Expand Down Expand Up @@ -1364,7 +1372,7 @@ def generate_test_config_sweep(args, all_config_data, runner_data=None):
Fields.DP_ATTN.value: dp_attn if dp_attn is not None else False,
Fields.SPEC_DECODING.value: spec_decoding,
Fields.CONC.value: conc,
Fields.KV_OFFLOADING.value: kv_offloading,
Fields.KV_OFFLOADING.value: agentic_kv_offload_runtime_value(kv_offloading),
Fields.TOTAL_CPU_DRAM_GB.value: total_cpu_dram_gb,
Fields.DURATION.value: duration,
Fields.EXP_NAME.value: (
Expand Down
50 changes: 50 additions & 0 deletions utils/matrix_logic/test_generate_sweep_configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2385,6 +2385,56 @@ def test_agentic_node_dram_uses_explicit_gpu_count(self, sample_runner_config):
}
assert all(entry["duration"] == 3600 for entry in result)

def test_multi_tier_agentic_uses_dram_budget_and_distinct_name(
self, sample_runner_config
):
config = {
"dsv4-b300-agentic": {
"image": "vllm/vllm-openai:v0.23.0",
"model": "deepseek-ai/DeepSeek-V4-Pro",
"model-prefix": "dsv4",
"precision": "fp4",
"framework": "vllm",
"runner": "cluster:b300-nv",
"multinode": False,
"scenarios": {
"agentic-coding": [{
"dram-utilization": 0.80,
"search-space": [
{
"tp": 8,
"kv-offloading": "nvme",
"kv-offload-backend": {"name": "vllm-simple"},
"conc-list": [7],
},
{
"tp": 8,
"kv-offloading": ["dram", "nvme"],
"kv-offload-backend": {"name": "vllm-native"},
"conc-list": [7],
},
],
}],
},
},
}
args = argparse.Namespace(
config_keys=["dsv4-b300-agentic"],
seq_lens=None,
conc=None,
scenario_type=["agentic-coding"],
runner_node_filter=None,
)

result = generate_test_config_sweep(args, config, sample_runner_config)

assert [entry["kv-offloading"] for entry in result] == ["nvme", "dram+nvme"]
assert [entry["total-cpu-dram-gb"] for entry in result] == [0, 2399]
assert [entry["exp-name"] for entry in result] == [
"dsv4_tp8_conc7_kvnvme-vllm-simple",
"dsv4_tp8_conc7_kvdram+nvme-vllm-native",
]

def test_agentic_node_dram_rejects_tp_above_runner_gpus(self, sample_runner_config):
config = {
"dsv4-b300-agentic": {
Expand Down
Loading