Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
#!/usr/bin/env bash
set -eo pipefail

# Qwen3.8-27B (bf16, dense hybrid attention: 48 linear-attention and 16 full
# attention layers) on one H200, served by vLLM with the RadixArk DSpark drafter
# (Doopeworld's vLLM-loadable copy) drafting seven tokens per step.
# https://recipes.vllm.ai/Qwen/Qwen3.8-27B
# https://huggingface.co/Doopeworld/Qwen3.8-27B-DSpark-vLLM
source "$(dirname "$0")/../../benchmark_lib.sh"

check_env_vars MODEL TP CONC ISL OSL RANDOM_RANGE_RATIO RESULT_FILENAME

if [[ -n "${SLURM_JOB_ID:-}" ]]; then
echo "JOB $SLURM_JOB_ID running on $SLURMD_NODENAME"
fi

if [[ "$TP" -ne 1 ]]; then
echo "This recipe serves Qwen3.8-27B on a single GPU; got TP=$TP" >&2
exit 1
fi

DRAFT_MODEL="Doopeworld/Qwen3.8-27B-DSpark-vLLM"
# The drafter's trained block size. Its card measured k=4 and k=6 slower than
# k=7 despite the late positions' low acceptance: per-step overhead dominates
# the per-drafted-token cost.
NUM_SPEC_TOKENS=7

nvidia-smi

# Complete/resume partial downloads instead of trusting nonempty directories.
if [[ "$MODEL" != /* ]]; then hf download "$MODEL"; fi
hf download "$DRAFT_MODEL"

SERVER_LOG=/workspace/server.log

# Serve the matrix context (isl + osl + slack), not the checkpoint's 262K;
# accuracy evals use the eval context benchmark_lib derives.
MODEL_LEN="${MAX_MODEL_LEN:-$((ISL + OSL + 256))}"

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) This script silently defaults MAX_MODEL_LEN instead of validating it, violating the mandatory "no fallback defaults for caller-supplied config" rule in AGENTS.md. MODEL_LEN="${MAX_MODEL_LEN:-$((ISL + OSL + 256))}" uses the forbidden ${VAR:-default} pattern on MAX_MODEL_LEN, which is not in the check_env_vars list at line 9, even though the matrix generator (infx/matrix/generate.py:836,847) always forwards MAX_MODEL_LEN as a required field for every fixed-seq-len entry. Fix: add MAX_MODEL_LEN to check_env_vars and consume $MAX_MODEL_LEN directly (as qwen3.5_fp4_b200_trt_mtp.sh does), rather than silently recomputing a default when it is missing/unset, so a caller misconfiguration fails loudly instead of masking it.

Extended reasoning...

AGENTS.md's Bash conventions explicitly forbid ${VAR:-default} for caller-supplied configuration and require every required input be validated with check_env_vars before use. infx/matrix/generate.py always sets max-model-len: isl + osl + 256 (lines 836 and 847) for every fixed-seq-len row (both single-node and multi-node), and infx/matrix/validation.py:181,274 marks max_model_len as a required Pydantic field, so MAX_MODEL_LEN is genuinely caller-supplied, not optional. The new script never validates it and instead falls back silently at line 38: MODEL_LEN="${MAX_MODEL_LEN:-$((ISL + OSL + 256))}". Compare to the established pattern in qwen3.5_fp4_b200_trt_mtp.sh:5-11, which lists MAX_MODEL_LEN in check_env_vars and then uses/bounds it directly (MAX_MODEL_LEN=$(( MAX_MODEL_LEN > 8192 ? MAX_MODEL_LEN : 8192 ))), never defaulting it. If the caller ever fails to forward MAX_MODEL_LEN (a launcher bug, a manual invocation, a future refactor of the multinode/single-node code path), this script masks that misconfiguration instead of failing clearly like check_env_vars would, silently…

Verification: nit. The candidate is factually correct that this deviates from AGENTS.md's mandatory Bash conventions, but the severity is nit, not normal — no runtime failure is reachable. Line 38 of the new script uses the forbidden fallback pattern on a caller-supplied field: MODEL_LEN="${MAX_MODEL_LEN:-$((ISL + OSL + 256))}", and check_env_vars at line 9 (MODEL TP CONC ISL OSL RANDOM_RANGE_RATIO…

if [[ "${EVAL_ONLY:-false}" == true ]]; then
setup_eval_context
MODEL_LEN="$EVAL_MAX_MODEL_LEN"
fi

# vLLM's default max-num-seqs (1024) exceeds the GDN/Mamba cache blocks that fit
# next to the bf16 weights (472 on an 80 GB H100, run 35357364404) and engine
# start aborts before graph capture. Size the scheduler batch to the sweep point
# instead; the accuracy eval serves up to 256 concurrent requests.
MAX_NUM_SEQS=$(( CONC > 16 ? CONC : 16 ))
if [[ "${EVAL_ONLY:-false}" == true ]]; then
MAX_NUM_SEQS=256
fi

# Pyxis shares the host network; port 8888 can already belong to a host service.
select_available_server_port

# Probabilistic draft sampling measured ~23% faster than greedy on the drafter's
# card. Adaptive verification is rejected by vLLM's GDN attention backend for
# this hybrid architecture, so it stays at its default (off).
SPEC_CONFIG=$(printf '{"method":"dspark","model":"%s","num_speculative_tokens":%d,"draft_sample_method":"probabilistic"}' "$DRAFT_MODEL" "$NUM_SPEC_TOKENS")

start_gpu_monitor

VLLM_CMD=(
vllm serve "$MODEL" --served-model-name "$MODEL"
--host 0.0.0.0 --port "$PORT"
--tensor-parallel-size 1
# Text-only serving: skip the vision tower of Qwen3_5ForConditionalGeneration.
--language-model-only
--trust-remote-code
--kv-cache-dtype fp8
--max-model-len "$MODEL_LEN"
--max-num-seqs "$MAX_NUM_SEQS"
# Every 1k1k request prefills its full random prompt; no prefix-cache hits.
--no-enable-prefix-caching
--reasoning-parser qwen3
--enable-auto-tool-choice --tool-call-parser qwen3_xml
--speculative-config "$SPEC_CONFIG"
--disable-uvicorn-access-log
)
printf '%q ' "${VLLM_CMD[@]}" | tee /workspace/vllm_command.txt
printf '\n' | tee -a /workspace/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:-false}" == true ]]; then
run_eval --framework lm-eval --port "$PORT"
else
pip install -q datasets pandas
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" \
--result-filename "$RESULT_FILENAME" \
--result-dir /workspace/ \
`# Chat-templated prompts: raw random tokens tank draft acceptance.` \
--use-chat-template \
--server-pid "$SERVER_PID"
if [[ "${RUN_EVAL:-false}" == true ]]; then
run_eval --framework lm-eval --port "$PORT"
append_lm_eval_summary
fi
fi

stop_gpu_monitor
15 changes: 15 additions & 0 deletions configs/nvidia-master.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8150,3 +8150,18 @@ dsv41flash-fp4-h200-vllm-agentic-dspark:
# 8x141 GB holds the 511 GB checkpoint minus the UVA-offloaded Engram
# tables, so the KV cache stays GPU-resident across the full range.
- { tp: 8, kv-offloading: none, spec-decoding: mtp, conc-list: [1, 2, 4, 8, 16, 32, 64, 128] }

qwen3.827b-bf16-h200-vllm-mtp:
image: vllm/vllm-openai:nightly-cd10ed6f9f6b37a8ace9cf380007e66fe12ec0c3
model: Qwen/Qwen3.8-27B
model-prefix: qwen3.827b
runner: cluster:h200-dgxc
precision: bf16
framework: vllm
multinode: false
scenarios:
fixed-seq-len:
- isl: 1024
osl: 1024
search-space:
- { tp: 1, spec-decoding: mtp, conc-list: [1, 2, 4, 8, 16, 32, 64, 128] }
14 changes: 14 additions & 0 deletions perf-changelog.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8082,3 +8082,17 @@
- "Capture only full decode CUDA graphs (VLLM_USE_BREAKABLE_CUDAGRAPH=0 with cudagraph_mode FULL_DECODE_ONLY, as the MiniMax-M3 gfx942 arm does) and restore --moe-backend aiter: on gfx942 every worker segfaulted during piecewise graph capture with both the Triton W4A16 MoE kernel (run 35305045778) and the auto-selected unfused Triton kernel (run 35306398350), so the capture mode rather than the MoE kernel is the failing piece; prefill runs eagerly"
- "仅捕获完整的 decode CUDA graph(VLLM_USE_BREAKABLE_CUDAGRAPH=0 并设置 cudagraph_mode FULL_DECODE_ONLY,与 MiniMax-M3 gfx942 配方一致)并恢复 --moe-backend aiter:在 gfx942 上,无论使用 Triton W4A16 MoE 内核(运行 35305045778)还是自动选择的未融合 Triton 内核(运行 35306398350),所有 worker 都在 piecewise graph 捕获期间段错误,说明问题在于捕获模式而非 MoE 内核;prefill 以 eager 方式运行"
pr-link: https://github.com/SemiAnalysisAI/InferenceX/pull/3247

- config-keys:
- qwen3.827b-bf16-h200-vllm-mtp
description:
- "Add the H200 Qwen3.8-27B bf16 recipe: vLLM nightly-cd10ed6f9f6b37a8ace9cf380007e66fe12ec0c3 on one GPU (TP1), 1k1k only, with the RadixArk DSpark drafter Doopeworld/Qwen3.8-27B-DSpark-vLLM (a config-only copy vLLM loads as Qwen3DSparkModel) drafting seven tokens with probabilistic draft sampling, fp8 KV cache, the matrix context as --max-model-len, --language-model-only, and chat-templated prompts; concurrency 1-128"
- "新增 H200 Qwen3.8-27B bf16 配方:vLLM nightly-cd10ed6f9f6b37a8ace9cf380007e66fe12ec0c3 单卡(TP1),仅 1k1k,使用 RadixArk 的 DSpark 草稿模型 Doopeworld/Qwen3.8-27B-DSpark-vLLM(仅修改配置以便 vLLM 按 Qwen3DSparkModel 加载)预测 7 个 token 并采用 probabilistic 草稿采样,fp8 KV cache,--max-model-len 使用矩阵上下文,--language-model-only,提示词经 chat template 处理;并发 1-128"
pr-link: https://github.com/SemiAnalysisAI/InferenceX/pull/3261

- config-keys:
- qwen3.827b-bf16-h200-vllm-mtp
description:
- "Set --max-num-seqs to the sweep point (floor 16; 256 for accuracy evals) and disable prefix caching (--no-enable-prefix-caching): in run 35357364404 the H100 canary died at engine start because vLLM's default max-num-seqs of 1024 exceeded the 472 GDN/Mamba cache blocks that fit next to the bf16 weights on the 80 GB card (each decode sequence needs one block), and prefix caching is off so every 1k1k request prefills its full random prompt"
- "将 --max-num-seqs 设为并发点(下限 16;精度评测为 256)并关闭 prefix caching(--no-enable-prefix-caching):运行 35357364404 中 H100 canary 在引擎启动时失败,因为 vLLM 默认的 max-num-seqs 1024 超过了 80 GB 显卡上 bf16 权重旁仅剩的 472 个 GDN/Mamba cache block(每个 decode 序列需要一个 block);关闭 prefix caching 使每个 1k1k 请求完整 prefill 其随机提示词"
pr-link: https://github.com/SemiAnalysisAI/InferenceX/pull/3261
Loading