-
Notifications
You must be signed in to change notification settings - Fork 303
Add B200 TP2 DSv4.1 Flash CPU offload / 新增 B200 TP2 DSv4.1 Flash CPU 卸载 #3216
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
Open
functionstackx
wants to merge
16
commits into
main
Choose a base branch
from
feat/dsv41flash-b200-tp2-cpu-offload
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
1066234
feat: add B200 TP2 DSv4.1 Flash CPU-offload sweeps
functionstackx a256eed
docs: link B200 TP2 sweep to PR 3216
functionstackx 61cff68
Merge origin/main into feat/dsv41flash-b200-tp2-cpu-offload
functionstackx 64c57ed
dsv41flash agentic: TP2 memory caps for the B200 CPU-offload arm
functionstackx b5fe437
dsv41flash_fp4_b200_vllm_mtp.sh (8k1k): write the result JSON to the …
functionstackx b029a53
test_dsv41flash_b200: expect the fixed-seq result dir at the containe…
functionstackx f6bbff6
dsv41flash_fp4_b200_vllm_mtp.sh (8k1k): cap TP2 graph capture at 256
functionstackx 3785b00
dsv41flash_fp4_b200_vllm_mtp.sh (8k1k): serve the matrix context, not 1M
functionstackx 348ce7b
dsv41flash b200 tp2: floor --max-num-seqs at 16 and derive the eval c…
functionstackx 557dba6
Merge remote-tracking branch 'origin/main' into feat/dsv41flash-b200-…
functionstackx 7fd771d
dsv41flash b200 tp2: give the accuracy evals memory to start
functionstackx c4a6cf4
Merge remote-tracking branch 'origin/main' into feat/dsv41flash-b200-…
functionstackx d8ef916
dsv41flash b200 tp2: stage the accuracy-eval artifacts into the works…
functionstackx a8163cc
Merge origin/main
functionstackx 07e4515
dsv41flash b200 tp2: keep the accuracy eval inside the CUDA-graph tier
functionstackx dd1589e
perf-changelog: use the dsv41flash-fp4-b200-vllm-dspark key for the T…
functionstackx File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
132 changes: 132 additions & 0 deletions
132
benchmarks/single_node/fixed_seq_len/dsv41flash_fp4_b200_vllm_mtp.sh
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,132 @@ | ||
| #!/usr/bin/env bash | ||
| set -eo pipefail | ||
|
|
||
| # B200 fixed-sequence counterpart of the native DSpark AgentX recipe. | ||
| # https://recipes.vllm.ai/deepseek-ai/DeepSeek-V4.1-Flash | ||
| source "$(dirname "$0")/../../benchmark_lib.sh" | ||
| check_env_vars MODEL TP CONC ISL OSL RANDOM_RANGE_RATIO RESULT_FILENAME RESULT_DIR MAX_MODEL_LEN | ||
| check_env_vars INFMAX_CONTAINER_WORKSPACE | ||
| check_env_vars DSV41_MIN_CUDAGRAPH_CAPTURE_SIZE EVAL_ONLY VLLM_ENGINE_READY_TIMEOUT_S | ||
| export GPU_COUNT="$TP" | ||
|
|
||
| if [[ -n "${MODEL_PATH:-}" && "$MODEL_PATH" != "$MODEL" ]]; then | ||
| hf download "$MODEL" --local-dir "$MODEL_PATH" | ||
| else | ||
| hf download "$MODEL" | ||
| export MODEL_PATH="$MODEL" | ||
| fi | ||
|
|
||
| nvidia-smi | ||
| mkdir -p "$RESULT_DIR" | ||
| SERVER_LOG="$RESULT_DIR/server.log" | ||
| export VLLM_USE_RUST_FRONTEND=1 | ||
| export PYTHONUNBUFFERED=1 | ||
|
|
||
| LOAD_ARGS=() | ||
| if [[ -n "${VLLM_SAFETENSORS_LOAD_STRATEGY:-}" ]]; then | ||
| LOAD_ARGS=(--safetensors-load-strategy "$VLLM_SAFETENSORS_LOAD_STRATEGY") | ||
| fi | ||
|
|
||
| NUM_SPEC_TOKENS=5 | ||
| CAPTURE_SIZE="${DSV41_MIN_CUDAGRAPH_CAPTURE_SIZE}" | ||
| while (( CAPTURE_SIZE < CONC * (1 + NUM_SPEC_TOKENS) && CAPTURE_SIZE < 2048 )); do | ||
| CAPTURE_SIZE=$((CAPTURE_SIZE * 2)) | ||
| done | ||
|
|
||
| # TP2 leaves ~145 GiB of weights on each 180 GB B200 even with the Engram | ||
| # tables offloaded, and vLLM's memory profiling counts the captured graphs | ||
| # against the KV budget: c1-c32 served, but c64 (capture 512) ended with | ||
| # -2.65 GiB and c128 (capture 1024) with -10.8 GiB of KV memory in run | ||
| # 35316389982, while --max-model-len was still the 1M context. With the | ||
| # matrix-supplied context below, stop capturing above 512 tokens on TP2; | ||
| # larger DSpark verify batches decode eagerly. TP4 keeps the default. | ||
| if (( TP == 2 && CAPTURE_SIZE > 512 )); then | ||
| CAPTURE_SIZE=512 | ||
| fi | ||
| # Real DSpark verification (block rejection with adaptive verification) profiles | ||
| # roughly 10 GiB more than the synthetic-acceptance throughput points: on TP2 the | ||
| # c128 throughput point still had 8.13 GiB for KV at capture 512, while the | ||
| # c64 and c128 evals ended at -2.22 GiB and never started (run 35355746550). | ||
| # Halve the graph tier and let the eval use 0.95 of HBM; GSM8K prompts are short. | ||
| EVAL_MEM_ARGS=() | ||
| if [[ "${EVAL_ONLY}" == true ]] && (( TP == 2 )); then | ||
| CAPTURE_SIZE=256 | ||
| EVAL_MEM_ARGS=(--gpu-memory-utilization 0.95) | ||
| # 256 captured tokens cover 42 requests at 1 + 5 draft tokens each. The c128 | ||
| # eval kept ~125 GSM8K requests running, so every DSpark verify step ran | ||
| # above the graph tier through the untuned eager path (FlashInfer logged | ||
| # "No tuned config covers mxfp8_gemm" for each odd draft-head shape) and | ||
| # the draft lm_head GEMM died with cudaErrorIllegalAddress 70 s into the | ||
| # eval (run 35399984613). The TP4 eval at capture 1024 and the TP2 c1-c32 | ||
| # throughput points, all inside their graph tiers, were clean. Keep the | ||
| # eval's request concurrency inside the tier; GSM8K is short enough that | ||
| # 32 concurrent requests finish the 1319 prompts within the job budget. | ||
| EVAL_CONC_CAP=32 | ||
| if ! [[ "${EVAL_CONCURRENT_REQUESTS:-}" =~ ^[0-9]+$ ]] || (( EVAL_CONCURRENT_REQUESTS > EVAL_CONC_CAP )); then | ||
| export EVAL_CONCURRENT_REQUESTS="$EVAL_CONC_CAP" | ||
| fi | ||
| fi | ||
| select_available_server_port | ||
|
|
||
| # Match AgentX's golden AL 3.51; accuracy evals use real target verification. | ||
| if [[ "${EVAL_ONLY}" == true ]]; then | ||
| SPEC_CONFIG='{"method":"dspark","num_speculative_tokens":5,"draft_sample_method":"probabilistic","rejection_sample_method":"block","enable_adaptive_verification":true}' | ||
| else | ||
| SPEC_CONFIG='{"method":"dspark","num_speculative_tokens":5,"draft_sample_method":"probabilistic","rejection_sample_method":"synthetic","synthetic_acceptance_length":3.51,"enable_adaptive_verification":false}' | ||
| fi | ||
|
|
||
| start_gpu_monitor | ||
| # Fixed-sequence runs serve the matrix-supplied context (isl + osl + slack), | ||
| # not the checkpoint's 1M: the sparse-attention indexer allocates a | ||
| # [batched-tokens, max-model-len] fp8 buffer and the profiler reserves KV for | ||
| # one full-context request, which at 1M left 0.97 GiB of KV at TP2 c32 in run | ||
| # 35316389982. Accuracy evals use the eval context instead. | ||
| MODEL_LEN="$MAX_MODEL_LEN" | ||
| if [[ "${EVAL_ONLY}" == true ]]; then | ||
| # benchmark_lib derives EVAL_MAX_MODEL_LEN (isl + osl + 256, capped at the | ||
| # checkpoint's context) as the other fixed-seq arms do; the workflow does | ||
| # not export it (run 35320655804: both eval jobs exited at check_env_vars). | ||
| setup_eval_context | ||
| MODEL_LEN="$EVAL_MAX_MODEL_LEN" | ||
| fi | ||
| VLLM_CMD=( | ||
| vllm serve "$MODEL_PATH" --served-model-name "$MODEL" | ||
| --host 0.0.0.0 --port "$PORT" --tensor-parallel-size "$TP" | ||
| --language-model-only | ||
| --tokenizer-mode deepseek_v41 | ||
| --tool-call-parser deepseek_v41 --enable-auto-tool-choice | ||
| --reasoning-parser deepseek_v41 | ||
| --engram-config '{"cpu_offload":true}' | ||
| --speculative-config "$SPEC_CONFIG" | ||
| --max-model-len "$MODEL_LEN" | ||
| --max-cudagraph-capture-size "$CAPTURE_SIZE" | ||
| "${EVAL_MEM_ARGS[@]}" | ||
| --disable-uvicorn-access-log | ||
| "${LOAD_ARGS[@]}" | ||
| ) | ||
| printf '%q ' "${VLLM_CMD[@]}" | tee "$RESULT_DIR/vllm_command.txt" | ||
| printf '\n' | tee -a "$RESULT_DIR/vllm_command.txt" | ||
| "${VLLM_CMD[@]}" > "$SERVER_LOG" 2>&1 & | ||
| SERVER_PID=$! | ||
| wait_for_server_ready --port "$PORT" --server-log "$SERVER_LOG" --server-pid "$SERVER_PID" | ||
|
|
||
| if [[ "${EVAL_ONLY}" == true ]]; then | ||
| run_eval --framework lm-eval --port "$PORT" | ||
| # run_eval leaves lm-eval output in its temp directory for non-agentic | ||
| # scenarios; staging into the workspace root, where the workflow looks for | ||
| # results*.json, is the recipe's job (run 35387151990: GSM8K scored 0.97 and | ||
| # the job still failed with "no results*.json files found"). | ||
| append_lm_eval_summary | ||
| else | ||
| run_benchmark_serving \ | ||
| --model "$MODEL" --port "$PORT" --backend vllm \ | ||
| --input-len "$ISL" --output-len "$OSL" \ | ||
| --random-range-ratio "$RANDOM_RANGE_RATIO" \ | ||
| --num-prompts "$((CONC * 10))" --max-concurrency "$CONC" \ | ||
| `# The workflow reads $RESULT_FILENAME.json from the repository root, which the` \ | ||
| `# dsv41flash launchers mount at /ix rather than /workspace (run 35314631817` \ | ||
| `# wrote it under RESULT_DIR and the canary reported the result missing).` \ | ||
| --result-filename "$RESULT_FILENAME" --result-dir "$INFMAX_CONTAINER_WORKSPACE/" \ | ||
| --use-chat-template --tokenizer-mode deepseek_v41 \ | ||
| --server-pid "$SERVER_PID" | ||
| fi | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
🔴 New B200 TP2 fixed-seq-len recipe calls start_gpu_monitor at line 43 but never calls stop_gpu_monitor, unlike every other script in benchmarks/single_node/fixed_seq_len/ which pairs the two. The background nvidia-smi sampler (GPU_MONITOR_PID) is left running when the script exits normally, so it is never killed via the normal path, its CSV never gets the final one-shot sample or the truncated-tail repair that stop_gpu_monitor performs. Fix: add a stop_gpu_monitor call after the benchmark/eval step (matching run_benchmark_serving/run_eval calls above) so the monitor process is reaped and gpu_metrics.csv is finalized for every conc-list run in this new recipe.
Extended reasoning...
benchmark_lib.sh's start_gpu_monitor (line 321) forks 'nvidia-smi ... -l $interval > $output &' and only stop_gpu_monitor (line 369) kills GPU_MONITOR_PID, appends the final boundary sample needed for window interpolation, and repairs a truncated trailing CSV row. The new script at benchmarks/single_node/fixed_seq_len/dsv41flash_fp4_b200_vllm_mtp.sh calls start_gpu_monitor (line 43) then goes straight to wait_for_server_ready/run_benchmark_serving/run_eval and exits; no stop_gpu_monitor. Backgrounded child processes in bash are not killed when the parent script exits normally (no huponexit), so the nvidia-smi sampler keeps running and appending to gpu_metrics.csv indefinitely across every concurrency step in the [1,2,4,8,16,32,64,128] sweep for this recipe, and the CSV never receives the final sample or truncation repair, corrupting downstream power/energy analysis for this recipe's runs.
Verification: normal. The new recipe benchmarks/single_node/fixed_seq_len/dsv41flash_fp4_b200_vllm_mtp.sh calls start_gpu_monitor at line 43 and then goes straight to wait_for_server_ready (line 62), run_eval (line 65) or run_benchmark_serving (lines 67-74), and exits — with no stop_gpu_monitor anywhere. Grep count over benchmarks/single_node/fixed_seq_len confirms it is the ONLY script (active or deprecated)…