Repository navigation
Add H100 MiniMax-M3 NVMe and tiered AgentX sweep #2775
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 13 commits
5d79351
44d1af3
923f60d
3ebc733
0e41fd1
ef3c36e
366d271
0545bda
8de9e47
ae673c5
d83dd9d
f436e23
e631356
fc9433a
48389ac
1a0793b
3cb3641
893cd1f
8a00c0a
a4c2a6c
b52c639
cb0fec2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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" | ||
|
|
||
|
|
@@ -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 | ||
| exit "$exit_code" | ||
| } | ||
| trap cleanup_services EXIT | ||
|
|
@@ -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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 WARNING: All TP ranks are pointed at the same Why it matters: The per-rank capacity math implies the connector is instantiated once per TP worker (consistent with the existing Fix: Confirm the disk backend appends a rank identifier to
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Confirmed in the patched vLLM backend: |
||
| 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 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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" | ||
| } | ||
|
cursor[bot] marked this conversation as resolved.
|
||
| trap cleanup_allocation EXIT INT TERM | ||
|
Comment on lines
+301
to
+307
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 Fix: Delete the trailing # cleanup_allocation (EXIT trap) removes the NVMe dir, then scancels.i.e. remove line 345 entirely — the EXIT trap already performs |
||
|
|
||
| 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 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cancelled jobs leak NVMe cacheMedium Severity Each NVMe job creates a host directory Additional Locations (1)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 | ||
|
|
@@ -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 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Offload files persist after allocation endsHigh Severity Allocation teardown now only runs Reviewed by Cursor Bugbot for commit e631356. Configure here.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The launcher-side cleanup |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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"]: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📊 Line Count Report
|
||
| return 0 | ||
|
|
||
| available_mib = min( | ||
|
|
@@ -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( | ||
|
|
@@ -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( | ||
|
|
@@ -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: ( | ||
|
|
@@ -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( | ||
|
|
@@ -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: ( | ||
|
|
||


There was a problem hiding this comment.
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.Reviewed by Cursor Bugbot for commit e631356. Configure here.
There was a problem hiding this comment.
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 -deletenow 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 cleanupsrun.