-
Notifications
You must be signed in to change notification settings - Fork 243
[Klaud Cold] qwen3.5-fp8-mi325x-sglang-agentic-mtp: first Qwen3.5 MI325X AgentX recipe, MTP with golden-AL simulated acceptance / MI325X 首个 Qwen3.5 AgentX 配方(MTP + 黄金 AL 模拟接受) #2430
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
5
commits into
main
Choose a base branch
from
feat/qwen3.5-fp8-mi325x-agentic-mtp
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
5 commits
Select commit
Hold shift + click to select a range
9168f71
feat(qwen3.5-mi325x-agentic-mtp): add first Qwen3.5 MI325X AgentX rec…
functionstackx 56cb999
chore: fill perf-changelog pr-link for #2430
functionstackx 29e2f86
fix: AgentX MTP conc grid — steps >=2, hard stop at conc 16
functionstackx e9c5721
fix(qwen3.5-mi325x-agentic-mtp): use the sglang-rocm image — the plai…
functionstackx 66359d2
Merge remote-tracking branch 'origin/main' into feat/qwen3.5-fp8-mi32…
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
188 changes: 188 additions & 0 deletions
188
benchmarks/single_node/agentic/qwen3.5_fp8_mi325x_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,188 @@ | ||
| #!/usr/bin/env bash | ||
| set -euo pipefail | ||
| set -x | ||
|
|
||
| # Agentic trace replay benchmark for Qwen3.5 FP8 on MI325X (gfx942) using SGLang | ||
| # with MTP speculative decoding. First Qwen3.5 AgentX recipe on MI325X; it is | ||
| # spec-decode only, per the AgentX policy that new agentic arms ship with | ||
| # speculative decoding enabled rather than as an STP/MTP A/B (MODELS.md). | ||
| # | ||
| # Structure follows the proven agentic/qwen3.5_fp8_mi355x_sglang.sh replay path, | ||
| # including its ROCm HiCache settings: Qwen3.5's hybrid Mamba path runs SGLang's | ||
| # no_buffer scheduler, which requires page_size=1, and the kernel/page_first | ||
| # HiCache transfer path faults on first prefill in that mode on ROCm, so the | ||
| # safer direct/layer_first copy path is kept. Attention backend is triton, as on | ||
| # the MI355X agentic recipe -- the fixed-seq-len gfx942 recipes use aiter, but | ||
| # the agentic replay path is validated on triton. --mem-fraction-static is 0.75, | ||
| # matching fixed_seq_len/qwen3.5_fp8_mi325x.sh; MI325X's 256 GB per GPU buys a | ||
| # larger KV pool at the same static fraction than MI300X's 192 GB. | ||
| # | ||
| # Speculative decoding mirrors fixed_seq_len/qwen3.5_fp8_mi325x_mtp.sh, the | ||
| # fixed-seq-len recipe on this same SKU: --speculative-algorithm EAGLE with 3 | ||
| # steps, eagle-topk 1 and 4 draft tokens, i.e. 3 speculative tokens per step. | ||
| # | ||
| # Throughput runs pin acceptance to the committed golden AL through SGLang's | ||
| # simulated-acceptance path; the EVAL_ONLY accuracy run leaves it off and keeps | ||
| # real verification. See the SGLANG_SIMULATE_ACC_* block. | ||
| # | ||
| # Required env vars: | ||
| # MODEL, TP, CONC, KV_OFFLOADING, TOTAL_CPU_DRAM_GB, RESULT_DIR | ||
| # | ||
| # KV_OFFLOADING=dram requires KV_OFFLOAD_BACKEND=hicache. | ||
|
|
||
| source "$(dirname "$0")/../../benchmark_lib.sh" | ||
|
|
||
| check_env_vars MODEL TP CONC KV_OFFLOADING TOTAL_CPU_DRAM_GB RESULT_DIR DURATION EP_SIZE | ||
|
|
||
| SCHEDULER_RECV_INTERVAL=${SCHEDULER_RECV_INTERVAL:-30} | ||
|
|
||
| if [[ -n "${SLURM_JOB_ID:-}" ]]; then | ||
| echo "JOB $SLURM_JOB_ID running on ${SLURMD_NODENAME:-unknown}" | ||
| fi | ||
|
|
||
| # `hf download` creates the target dir if missing and is itself idempotent. | ||
| # When MODEL_PATH is unset (stand-alone runs), fall back to the HF_HUB_CACHE | ||
| # Either way, MODEL_PATH is what the server is launched with. | ||
| if [[ -n "${MODEL_PATH:-}" ]]; then | ||
| if [[ ! -d "$MODEL_PATH" || -z "$(ls -A "$MODEL_PATH" 2>/dev/null)" ]]; then | ||
| hf download "$MODEL" --local-dir "$MODEL_PATH" | ||
| fi | ||
| else | ||
| hf download "$MODEL" | ||
| export MODEL_PATH="$MODEL" | ||
| fi | ||
| rocm-smi || true | ||
| amd-smi || true | ||
|
|
||
| # ---- Resolve traces and install deps ---------------------------------------- | ||
| resolve_trace_source | ||
| install_agentic_deps | ||
|
|
||
| # ---- Server config ---------------------------------------------------------- | ||
| SERVER_LOG="$RESULT_DIR/server.log" | ||
| mkdir -p "$RESULT_DIR" | ||
|
|
||
| CACHE_ARGS=() | ||
| WARMUP_ARGS=() | ||
| CUDA_GRAPH_MAX_BS="$CONC" | ||
| if require_agentic_kv_offload_backend hicache; then | ||
| # Qwen3.5 allocates one KV and one Mamba host pool per TP rank. | ||
| REQUESTED_HICACHE_TOTAL_GB="${HICACHE_TOTAL_CPU_DRAM_GB:-$TOTAL_CPU_DRAM_GB}" | ||
| if [ "$REQUESTED_HICACHE_TOTAL_GB" -gt "$TOTAL_CPU_DRAM_GB" ]; then | ||
| echo "Error: requested HiCache pool ${REQUESTED_HICACHE_TOTAL_GB} GB exceeds configured capacity ${TOTAL_CPU_DRAM_GB} GB" >&2 | ||
| exit 1 | ||
| fi | ||
| TOTAL_CPU_DRAM_GB="$REQUESTED_HICACHE_TOTAL_GB" | ||
| HICACHE_HOST_POOL_COUNT="${HICACHE_HOST_POOL_COUNT:-2}" | ||
| HICACHE_MAX_SIZE_GB_PER_RANK_POOL="${HICACHE_MAX_SIZE_GB_PER_RANK_POOL:-${HICACHE_MAX_SIZE_GB_PER_RANK:-180}}" | ||
| HICACHE_WRITE_POLICY="${HICACHE_WRITE_POLICY:-write_through_selective}" | ||
| # Qwen3.5's hybrid Mamba path runs SGLang's no_buffer scheduler on MI355X, | ||
| # which requires page_size=1. The kernel/page_first HiCache transfer path | ||
| # faults on first prefill in this mode on ROCm, so keep the default on the | ||
| # safer direct/layer_first copy path. These remain env-overridable. | ||
| HICACHE_PAGE_SIZE="${HICACHE_PAGE_SIZE:-1}" | ||
| HICACHE_IO_BACKEND="${HICACHE_IO_BACKEND:-direct}" | ||
| HICACHE_MEM_LAYOUT="${HICACHE_MEM_LAYOUT:-layer_first}" | ||
| # SGLang --hicache-size is per rank per host pool, while the workflow | ||
| # input is a node-total DRAM budget. Divide by TP and the number of | ||
| # host pools unless HICACHE_SIZE_GB is set directly for one-off tuning. | ||
| MAX_HICACHE_SIZE_GB=$((TOTAL_CPU_DRAM_GB / TP / HICACHE_HOST_POOL_COUNT)) | ||
| HICACHE_SIZE_GB="${HICACHE_SIZE_GB:-$MAX_HICACHE_SIZE_GB}" | ||
| if [ "$HICACHE_SIZE_GB" -gt "$MAX_HICACHE_SIZE_GB" ]; then | ||
| echo "Error: HICACHE_SIZE_GB=$HICACHE_SIZE_GB exceeds configured per-pool limit $MAX_HICACHE_SIZE_GB" >&2 | ||
| exit 1 | ||
| fi | ||
| if [ "$HICACHE_SIZE_GB" -gt "$HICACHE_MAX_SIZE_GB_PER_RANK_POOL" ]; then | ||
| HICACHE_SIZE_GB="$HICACHE_MAX_SIZE_GB_PER_RANK_POOL" | ||
| fi | ||
| if [ "$HICACHE_SIZE_GB" -lt 1 ]; then | ||
| echo "Error: computed HICACHE_SIZE_GB=$HICACHE_SIZE_GB from TOTAL_CPU_DRAM_GB=$TOTAL_CPU_DRAM_GB, TP=$TP, HICACHE_HOST_POOL_COUNT=$HICACHE_HOST_POOL_COUNT" >&2 | ||
| exit 1 | ||
| fi | ||
| echo "HiCache CPU pool: ${HICACHE_SIZE_GB} GB per rank per host pool across TP=${TP}, host_pool_count=${HICACHE_HOST_POOL_COUNT}" | ||
| CACHE_ARGS=( | ||
| --page-size "$HICACHE_PAGE_SIZE" | ||
| --enable-hierarchical-cache | ||
| --hicache-size "$HICACHE_SIZE_GB" | ||
| --hicache-io-backend "$HICACHE_IO_BACKEND" | ||
| --hicache-mem-layout "$HICACHE_MEM_LAYOUT" | ||
| --hicache-write-policy "$HICACHE_WRITE_POLICY" | ||
| ) | ||
| # HiCache startup reaches API readiness, but SGLang's internal warmup | ||
| # request has timed out after 600s on this Qwen MI355X path. Let aiperf | ||
| # own benchmark traffic instead of blocking server readiness on it. | ||
| WARMUP_ARGS=(--skip-server-warmup) | ||
| # Keep request concurrency as the swept variable, but do not force HiCache | ||
| # runs to capture ROCm graphs at every high concurrency point. | ||
| HICACHE_CUDA_GRAPH_MAX_BS="${HICACHE_CUDA_GRAPH_MAX_BS:-16}" | ||
| if [ "$HICACHE_CUDA_GRAPH_MAX_BS" -lt "$CUDA_GRAPH_MAX_BS" ]; then | ||
| CUDA_GRAPH_MAX_BS="$HICACHE_CUDA_GRAPH_MAX_BS" | ||
| fi | ||
| fi | ||
|
|
||
| echo "Starting SGLang server..." | ||
| export PYTHONNOUSERSITE=1 | ||
| export SGLANG_ENABLE_SPEC_V2=1 | ||
|
|
||
| # 3 speculative tokens per step (num-steps 3, eagle-topk 1, 4 draft tokens), | ||
| # the same MTP shape as fixed_seq_len/qwen3.5_fp8_mi325x_mtp.sh. | ||
| SPEC_ARGS=( | ||
| --speculative-algorithm EAGLE | ||
| --speculative-num-steps 3 | ||
| --speculative-eagle-topk 1 | ||
| --speculative-num-draft-tokens 4 | ||
| ) | ||
|
|
||
| # AgentX pins acceptance to the committed golden AL so submissions are compared | ||
| # on system performance at a fixed acceptance target rather than on draft-head | ||
| # quality (golden_al_distribution/README.md). 3.39 is the Qwen3.5 MTP curve at | ||
| # num_speculative_tokens=3, thinking_on (golden_al_distribution/qwen3.5_mtp.yaml) | ||
| # -- the same value the GB300 Qwen3.5 AgentX srt-slurm recipes pin. | ||
| # SGLANG_SIMULATE_ACC_TOKEN_MODE landed in SGLang v0.5.16, which is the image | ||
| # this recipe pins. | ||
| # | ||
| # EVAL_ONLY leaves simulated acceptance off: it commits drafted tokens | ||
| # regardless of the target logits, so generated text is wrong and the eval would | ||
| # score ~0. | ||
| if [ "${EVAL_ONLY:-false}" != "true" ]; then | ||
| export SGLANG_SIMULATE_ACC_LEN=3.39 | ||
| export SGLANG_SIMULATE_ACC_METHOD=match-expected | ||
| export SGLANG_SIMULATE_ACC_TOKEN_MODE=real-draft-token | ||
| fi | ||
|
|
||
| { set +x; } 2>/dev/null | ||
| SGLANG_CMD=( | ||
| python3 -m sglang.launch_server | ||
| --attention-backend triton | ||
| --model-path "$MODEL_PATH" --served-model-name "$MODEL" | ||
| --host=0.0.0.0 | ||
| --port "$PORT" | ||
| --tensor-parallel-size "$TP" | ||
| --ep-size "$EP_SIZE" | ||
| --trust-remote-code | ||
| --tokenizer-worker-num 6 | ||
| --enable-aiter-allreduce-fusion | ||
| --cuda-graph-max-bs "$CUDA_GRAPH_MAX_BS" | ||
| --max-running-requests "$CONC" | ||
| --max-prefill-tokens 32768 | ||
| --scheduler-recv-interval "$SCHEDULER_RECV_INTERVAL" | ||
| --mem-fraction-static 0.75 | ||
| --enable-metrics | ||
| "${SPEC_ARGS[@]}" | ||
| "${CACHE_ARGS[@]}" | ||
| "${WARMUP_ARGS[@]}" | ||
| ) | ||
| printf '%q ' "${SGLANG_CMD[@]}" | tee "$RESULT_DIR/sglang_command.txt" | ||
| printf '\n' | tee -a "$RESULT_DIR/sglang_command.txt" | ||
| "${SGLANG_CMD[@]}" > "$SERVER_LOG" 2>&1 & | ||
| SERVER_PID=$! | ||
| echo "Server PID: $SERVER_PID" | ||
|
|
||
| wait_for_server_ready --port "$PORT" --server-log "$SERVER_LOG" --server-pid "$SERVER_PID" | ||
|
|
||
| if [ "${EVAL_ONLY}" = "true" ]; then | ||
| run_eval --port "$PORT" | ||
| else | ||
| build_replay_cmd "$RESULT_DIR" | ||
| run_agentic_replay_and_write_outputs "$RESULT_DIR" | ||
| 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
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.
🟡 The sizing rationale ("one band wider than the MI300X sibling") repeated in the script header, the
amd-master.yamlcomment, and the perf-changelog entry references a Qwen3.5 agentic/AgentX recipe on MI300X that doesn't exist — only the fixed-seq-lenqwen3.5-bf16-mi300x-sglangandqwen3.5-fp8-mi300x-sglangare present. If the MI355X hicache sibling (qwen3.5-fp8-mi355x-sglang-agentic-hicache) was actually meant, that recipe sweeps a wider band (GPU-resident 1-32, HiCache 16-64) than this MI325X entry (1-24 / 16-48) — the opposite of what's claimed. This is a documentation-only issue and doesn't affect the sweep configuration itself.Extended reasoning...
The bug: All three places that justify this PR's concurrency search space — the script header comment in
benchmarks/single_node/agentic/qwen3.5_fp8_mi325x_mtp.sh, the comment directly above the newqwen3.5-fp8-mi325x-sglang-agentic-mtpentry inconfigs/amd-master.yaml(lines 1498-1520), and the new entry inperf-changelog.yaml— state that the chosen bands (GPU-resident conc 1-24, HiCache conc 16-48) are "one band wider than the MI300X sibling" / "Conc bands run past the MI300X entry's." This implies there is an existing Qwen3.5 agentic-coding recipe on MI300X that was used as the sizing baseline.Why the baseline doesn't exist: Grepping
configs/amd-master.yamlfor MI300X Qwen3.5 recipes turns up exactly two:qwen3.5-bf16-mi300x-sglangandqwen3.5-fp8-mi300x-sglang. Both use thefixed-seq-lenscenario withconc-start: 4, conc-end: 64— they are not agentic-coding recipes at all, and neither has a conc-list/band structure that could be described as narrower or wider in the sense this PR claims. There is no MI300X entry anywhere in the file with anagentic-codingscenario for Qwen3.5. The only Qwen3.5 agentic recipes in the whole config are on MI355X (qwen3.5-fp8-mi355x-sglang-agentic,qwen3.5-fp8-mi355x-sglang-agentic-hicache) plus this new MI325X entry and several NVIDIA (B200/B300/H100/GB300) recipes.Step-by-step proof:
configs/amd-master.yamlforqwen3.5entries withrunner: mi300xorrunner: cluster:mi300x*→ onlyqwen3.5-bf16-mi300x-sglang(fixed-seq-len, conc 4-64) andqwen3.5-fp8-mi300x-sglang(fixed-seq-len, conc 4-64).agentic-codingscenario paired with a Qwen3.5 model-prefix and an MI300X runner → zero matches.qwen3.5-fp8-mi355x-sglang-agentic-hicachesweeps GPU-residentconc-list: [1, 2, 4, 8, 16, 32](max 32) and HiCacheconc-list: [16, 32, 48, 64](max 64).conc-list: [1, 2, 4, 8, 12, 16, 24](max 24) and HiCacheconc-list: [16, 24, 32, 48](max 48).Why nothing else catches this: The comment/rationale text isn't validated against the config file's contents by any tooling —
bash -n, YAML parsing, andgenerate_sweep_configs.py test-config(all cited in the PR's own validation section) only check that the sweep configs are syntactically well-formed and generate the expected 11 jobs; none of them cross-check prose claims about sibling recipes. So a comment that references a nonexistent baseline sails through review checks cleanly, and a future engineer reading this recipe to understand why these specific bounds (24/48) were chosen has no way to verify or reproduce the reasoning — the actual justification ("MI325X has more per-GPU memory than MI300X") is directionally reasonable, but the specific numeric comparison it's anchored to doesn't exist.Impact and fix: This is purely a documentation/comment defect — the
conc-listvalues themselves are valid and the 11-job sweep will run and parse correctly regardless of the misleading comment. The fix is simply to correct the rationale text in all three locations (script header, yaml comment, changelog) to either drop the sibling comparison entirely and justify the bands purely from MI325X's own memory budget, or point to a real, currently-narrower baseline if one exists elsewhere in the repo.