Skip to content

perf: add GB200 Qwen3.5 FP4 SGLang MTP AgentX - #2568

Open
cquil11 wants to merge 5 commits into
mainfrom
agent/gb200-qwen35-sglang-mtp-agentx
Open

perf: add GB200 Qwen3.5 FP4 SGLang MTP AgentX#2568
cquil11 wants to merge 5 commits into
mainfrom
agent/gb200-qwen35-sglang-mtp-agentx

Conversation

@cquil11

@cquil11 cquil11 commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds a new GB200 Qwen3.5-397B-A17B NVFP4 SGLang MTP AgentX submission using stable SGLang v0.5.17-cu130 and NVIDIA srt-slurm v1.0.45.

The complete agentx-fast discovery matrix mirrored the useful B200/B300 single-node search space and extended beyond the expected knee. Its 30 throughput points all completed successfully after failed-only infrastructure retries.

  • TP4 without offload: c1, c4, c8, c12, c16, c20, c32, c40
  • TP2/EP2 without offload: c4, c8, c16, c20, c24
  • TP4 HiCache: c48, c56, c64, c72

These 17 measured Pareto points form the official full sweep. TP2 HiCache and the remaining sampled points were dominated and removed. HiCache uses the established bounded DRAM convention rather than an ad hoc memory override.

Broad fast run: https://github.com/SemiAnalysisAI/InferenceX/actions/runs/31563972895

Metrics correctness

  • SGLang launches with metrics and cache reporting enabled.
  • srt-slurm v1.0.45 supplies the logical aggregate worker metrics endpoint to the custom benchmark.
  • AIPERF_REQUIRED_SERVER_METRIC_PREFIX is sglang:, so missing or empty server metrics fail final validation.
  • The new SGLang synthetic-acceptance injector applies only to throughput; eval remains real MTP.

Validation

  • the three retained recipes parse and dry-run with NVIDIA srt-slurm v1.0.45
  • 15 targeted srt-slurm custom benchmark/logical endpoint tests pass
  • 231 InferenceX matrix-generator tests pass
  • generated official matrix contains exactly 17 throughput points
  • synthetic SGLang injection and EVAL_ONLY no-op behavior verified
  • shell/Python syntax and git diff checks pass

@github-actions

Copy link
Copy Markdown
Contributor

Thanks for the contribution! Please reach out to respective companies' CODEOWNER to fill in the latest PR_REVIEW_CHECKLIST.md before pinging core maintainer on Slack for review. In order for the signoff PR check bot to trigger, you must follow the PR_REVIEW_CHECKLIST.md template correctly, including the phrase As a PR reviewer and CODEOWNER, I have reviewed this and have.

For PR verification, add the full-sweep-fail-fast label (strongly recommended) to this PR — the benchmark sweep only runs on labeled PRs. Use full-sweep-enabled only if you need matrix jobs to keep running past a failure.

PR authors are responsible for ensuring that after merging, all GitHub Action jobs fully pass. A lot of the time, failures are just flakes and simply re-running the failed jobs will fix it. See GitHub's docs on re-running failed jobs


感谢你的贡献!请联系相应公司的 CODEOWNER 填写最新的 PR_REVIEW_CHECKLIST.md,然后再在 Slack 上联系核心维护者进行审阅。为了触发 signoff PR 检查机器人,你必须正确遵循 PR_REVIEW_CHECKLIST.md 模板,包括保留英文语句 As a PR reviewer and CODEOWNER, I have reviewed this and have

如需进行 PR 验证,请为此 PR 添加 full-sweep-fail-fast 标签(强烈推荐)— 基准测试 sweep 仅在带有标签的 PR 上运行。仅当需要矩阵任务在失败后继续运行时才使用 full-sweep-enabled

PR 作者有责任确保合并后所有 GitHub Action 任务完全通过。 很多时候失败只是偶发抖动(flake),重新运行失败的任务即可解决。参见 GitHub 关于重新运行失败任务的文档

@cquil11 cquil11 added the agentx-fast Run AgentX throughput with 1 warmup request per lane and a 20-minute profile; not reusable label Aug 11, 2026
@cquil11
cquil11 marked this pull request as ready for review August 11, 2026 21:58
@cquil11
cquil11 requested a review from a team August 11, 2026 21:58
@claude

claude Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Claude finished @cquil11's task in 7m 58s —— View job


Review of PR #2568

  • Gather context and read the full diff
  • Review recipe YAMLs (MTP flags, EP usage, formatting)
  • Validate configs/nvidia-master.yaml changes (image accessibility, model prefixes, perf-changelog pairing)
  • Verify perf-changelog.yaml entry is appended at the end
  • Review runners/launch_gb200-nv.sh (enroot import pattern)
  • Review synthetic injector changes
  • Post inline comments and summary

Summary

One 🟡 WARNING posted inline: all four new recipes omit AIPERF_HTTP_X_DYNAMO_SESSION_ID_FROM_CORRELATION_ID/AIPERF_USE_DYNAMO_CONV_AWARE_ROUTING, so benchmark_lib.sh's dynamo-* default will append --use-dynamo-conv-aware-routing (dynamo-frontend nvext.session_control payloads) against an SGLang frontend — every existing SGLang agentic recipe, including the GB300 qwen3.5 siblings these mirror, explicitly opts out.

Everything else checks out: perf-changelog.yaml entry is correctly appended at the end of the file; lmsysorg/sglang:v0.5.17-cu130 is a public Docker Hub image; SGLang recipes correctly use explicit expert-parallel-size; the srt-slurm v1.0.45 pin is verified against a hardcoded commit SHA; the new sglang.py injector's regex matches the recipes' aggregated_environment: blocks and its interface matches the driver's contract (including the spec_tokens_from_recipe(...) or 2 fallback); the master-config search space totals exactly 35 concurrency points as claimed; and the SGLANG_SIMULATE_ACC_* env var names (including TOKEN_MODE: real-draft-token) match the already-merged GB300 recipes.

One pre-existing observation, not caused by this PR: the inter-concurrency drain gate in benchmarks/multi_node/agentic_srt.sh:78-79 only polls vllm:num_requests_running/waiting and dynamo_frontend_active_requests, so for SGLang backends it always reads 0 and passes after 3 polls (~30s fixed delay) rather than actually verifying drain. This affects all existing SGLang agentic recipes equally.

Note: I could not externally verify that srt-slurm v1.0.45 ships install-torchao.sh (appended as --setup-script for all dynamo-sglang runs in launch_gb200-nv.sh:684) — the PR's dry-run validation claim covers this.

WEKA_LOADER_OVERRIDE: semianalysis_cc_traces_weka_062126_256k
AIPERF_DATASET_MMAP_CACHE_DIR: /aiperf_mmap_cache
HF_HUB_CACHE: /hf_hub_cache
AIPERF_REQUIRED_SERVER_METRIC_PREFIX: "sglang:"

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: Missing the AIPerf dynamo conv-aware-routing opt-out that every other SGLang agentic recipe sets (applies to all 4 new recipes in this PR).

Why it matters: With FRAMEWORK=dynamo-sglang and neither AIPERF_USE_DYNAMO_CONV_AWARE_ROUTING=0 nor AIPERF_HTTP_X_DYNAMO_SESSION_ID_FROM_CORRELATION_ID=true set, benchmarks/benchmark_lib.sh:1982-1991 defaults to appending --use-dynamo-conv-aware-routing, which makes AIPerf emit nvext.session_control bind/close actions in request bodies. That field targets the dynamo frontend's KV router — these recipes use frontend: { type: sglang }, which doesn't implement it (best case it's silently ignored payload noise; the same mechanism 400s on current dynamo builds per the comment in benchmark_lib.sh). Every existing SGLang agentic recipe (deepseek-v4, glm5.2, and the GB300 qwen3.5 siblings these recipes mirror, e.g. agg-gb300-tp2-c1-mtp-hicache-jid2530006.yaml:89-90) explicitly opts out.

Fix: Mirror the GB300 siblings in all four recipes:

Suggested change
AIPERF_REQUIRED_SERVER_METRIC_PREFIX: "sglang:"
AIPERF_REQUIRED_SERVER_METRIC_PREFIX: "sglang:"
AIPERF_HTTP_X_DYNAMO_SESSION_ID_FROM_CORRELATION_ID: "true"
AIPERF_USE_DYNAMO_CONV_AWARE_ROUTING: "0"

@claude claude Bot left a comment

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.

Looks good — the two findings are wording/log-convention nits (bare exception instead of sys.exit, and a stale vLLM-specific error message) in the new sglang synthetic-acceptance backend, not correctness issues.

Extended reasoning...

Overview

This PR adds a new GB200 Qwen3.5-397B-A17B NVFP4 SGLang MTP AgentX submission: four new recipe YAMLs, a new configs/nvidia-master.yaml search-space entry, a perf-changelog entry, a new branch in runners/launch_gb200-nv.sh that clones a pinned NVIDIA/srt-slurm v1.0.45 (with commit-hash verification), and a new runners/synthetic_injectors/sglang.py backend that rewrites *_environment: blocks to inject golden synthetic-acceptance env vars for throughput-only runs.

Security risks

None material. The new shell branch clones a pinned tag and verifies the resolved commit SHA before proceeding (matching the existing pattern used for other pinned srt-slurm/producer clones in this file), and the injector only operates on local recipe YAML text via regex substitution — no new user-controlled input or shell-injection surface.

Level of scrutiny

This follows the repo's well-established pattern for adding a new model+hardware AgentX recipe (mirrors prior Kimi K3 / GLM-5.2 / DSV4 additions in recent history), so moderate scrutiny is appropriate. The two reported issues are both nit-level: a bare ValueError on an unreachable double-injection guard (rather than the driver's sys.exit convention), and the shared driver's log wording still saying "speculative-config entries" for the sglang backend's environment-block injections. Neither affects correctness — the actual injection logic (regex matches on the 2-space aggregated_environment: blocks present in all four new recipes) is exercised correctly, and the explicit SYNTHETIC_ACCEPTANCE_LENGTH=3.39 setting means the AL auto-lookup / spec_tokens_from_recipe path (flagged and ruled out by the bug hunt as reading the wrong SGLang field) isn't even reached in this PR's usage.

Other factors

PR description reports recipe dry-runs, targeted srt-slurm/matrix-generator test passes, and verified synthetic-injection / EVAL_ONLY no-op behavior. The shell script changes follow the same commit-pinning and cleanup conventions already used elsewhere in launch_gb200-nv.sh.

Comment on lines +18 to +22
def rewrite(content, al, log):
"""Add throughput-only golden-acceptance variables to each worker role."""
if "SGLANG_SIMULATE_ACC_LEN" in content:
raise ValueError("recipe already contains SGLANG_SIMULATE_ACC_* variables")

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.

🟡 In runners/synthetic_injectors/sglang.py (lines 18-22), rewrite() raises a bare ValueError on the double-injection guard instead of following the driver's error convention. inject_synthetic_acceptance.py:153 calls injector.rewrite() with no try/except, and every other failure path in that driver (and the sibling vllm.py backend design) reports errors via sys.exit("ERROR: ...") for a clean one-line CI message — this is the one path that lets a raw Python traceback escape into CI logs instead.

Extended reasoning...

The bug: sglang.py's rewrite() raises raise ValueError("recipe already contains SGLANG_SIMULATE_ACC_* variables") (line 21) when the double-injection guard trips. This is the only error-signaling path in the new backend, and it is a bare Python exception rather than the driver's established error convention.

Code path: inject_synthetic_acceptance.py's inject() calls injector.rewrite(content, al, _log) at line 153 with no try/except around it, and main() never wraps the call to inject() either — the exception is free to propagate all the way up to sys.exit(main(sys.argv)) at the bottom of the module. Every other failure branch in this same driver — missing AL lookup table entry (line 78), unresolvable YAML/model key/spec-token count (lines 95, 108, 118), missing injector or zero rewrites (lines 137, 156) — instead calls sys.exit("ERROR: ..."), which argparse/Python prints as a single clean line plus a non-zero exit code, no traceback.

Why nothing prevents it: the guard exists specifically to catch a should-never-happen state (a recipe that has already been synthetically injected), so it's rarely exercised, and no test or caller currently wraps injector.rewrite() defensively. There's nothing in the driver or in runners/synthetic_injectors/__init__.py's backend contract that requires backends to signal errors via return value rather than exception, so the new sglang backend silently diverges from the pattern every sibling error path follows.

Impact: if this guard ever fires (e.g., a recipe accidentally gets the injection step run twice, or a bad rerun/retry reuses an already-patched CONFIG_FILE), the CI job dies with a raw multi-frame Python traceback instead of the driver's usual one-line ERROR: ... message, breaking the convention that makes these failures easy to grep out of CI logs. Functionally the run still fails (which is correct), so this is a log-cleanliness/consistency issue, not a correctness bug — hence nit severity, matching all three verifiers' independent assessment.

Step-by-step proof:

  1. A recipe's aggregated_environment: block already contains SGLANG_SIMULATE_ACC_LEN (e.g., because the injection step ran once and, for whatever reason, ran again on the same file).
  2. The CI/launcher script invokes python3 inject_synthetic_acceptance.py $CONFIG_PATH $FRAMEWORK.
  3. main() resolves the injector via get_injector("dynamo-sglang"), which returns the sglang module, then calls inject(...) which calls injector.rewrite(content, al, _log) at line 153.
  4. Inside rewrite(), the check if "SGLANG_SIMULATE_ACC_LEN" in content: at line 20 is true, so line 21 executes raise ValueError(...).
  5. Nothing catches it — inject() has no try/except around the call, main() has no try/except around inject() — so the exception propagates to the top-level sys.exit(main(sys.argv)), which Python renders as an uncaught exception: a full traceback is printed to stderr and the process exits with code 1, instead of the driver's clean ERROR: recipe already contains SGLANG_SIMULATE_ACC_* variables line that every other failure mode produces.

Fix: either catch the ValueError in inject() (or in main()) and re-raise via sys.exit(f"ERROR: {exc}"), or have rewrite() itself return (content, 0) and let the driver's existing "count == 0" sys.exit(...) branch (line 156) handle it uniformly — the latter is more consistent with how the sibling vllm.py backend never raises at all.

Comment on lines +17 to +30

def rewrite(content, al, log):
"""Add throughput-only golden-acceptance variables to each worker role."""
if "SGLANG_SIMULATE_ACC_LEN" in content:
raise ValueError("recipe already contains SGLANG_SIMULATE_ACC_* variables")

variables = (
f'\n SGLANG_SIMULATE_ACC_LEN: "{al:g}"'
'\n SGLANG_SIMULATE_ACC_METHOD: "match-expected"'
'\n SGLANG_SIMULATE_ACC_TOKEN_MODE: "real-draft-token"'
)
rewritten, count = _ENV_BLOCK_RE.subn(lambda match: match.group(1) + variables, content)
if count:
log(f"Added SGLANG_SIMULATE_ACC_* to {count} worker environment block(s)")

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.

🟡 The shared driver's diagnostics ('no speculative-config entries were found' / 'Modified {count} speculative-config entries' in runners/inject_synthetic_acceptance.py:155-163) hardcode vLLM-specific 'speculative-config' wording, but the new sglang backend (runners/synthetic_injectors/sglang.py) rewrites aggregated/prefill/decode_environment blocks — nothing to do with speculative-config JSON. This wording fires on every sglang injection, not just the count==0 error case, and could misdirect debugging. Consider generalizing to backend-neutral phrasing (e.g. 'injectable entries') in the shared driver.

Extended reasoning...

runners/inject_synthetic_acceptance.py is written as a framework-agnostic driver (per its own module docstring) that delegates the actual recipe rewrite to a backend registered in runners/synthetic_injectors/. However, its two diagnostic messages still bake in the original vLLM backend's terminology:

if count == 0:
    sys.exit(
        "ERROR: SYNTHETIC_ACCEPTANCE=true but no speculative-config "
        f"entries were found in {config_file}"
    )
...
_log(f"Modified {count} speculative-config entries")

This PR wires in the new sglang backend via runners/synthetic_injectors/__init__.py (from . import sglang, vllm), and sglang.py's rewrite() derives count from _ENV_BLOCK_RE.subn(...), where _ENV_BLOCK_RE = re.compile(r"(?m)^( (?:aggregated|prefill|decode)_environment:\s*)$"). That regex matches YAML environment-variable blocks, not speculative-config JSON entries (that term belongs solely to the vLLM backend's _SPEC_CONFIG_RE). So the driver's wording no longer describes what the sglang path actually modified.

Step-by-step proof:

  1. A GB200 Qwen3.5 SGLang recipe (e.g. agg-gb200-tp4-mtp.yaml added in this PR) sets SYNTHETIC_ACCEPTANCE=true and FRAMEWORK=dynamo-sglang via additional-settings in configs/nvidia-master.yaml.
  2. launch_gb200-nv.sh invokes inject_synthetic_acceptance.py "$CONFIG_PATH" "$FRAMEWORK".
  3. The driver resolves the sglang backend via get_injector("dynamo-sglang") and calls sglang.rewrite(content, al, log).
  4. rewrite() matches the recipe's aggregated_environment: block with _ENV_BLOCK_RE, injects SGLANG_SIMULATE_ACC_* env vars, and returns count=1.
  5. Back in the driver, since count != 0, it logs "Modified 1 speculative-config entries" — inaccurate on every successful run, since what was actually modified was an environment-variable block, not a speculative-config entry.
  6. If a future sglang recipe's env-block key or indentation ever drifted from the exact 2-space _ENV_BLOCK_RE pattern (e.g. a differently indented YAML style), count would be 0 and the driver would exit with "no speculative-config entries were found in {config_file}" — pointing whoever is debugging toward the wrong recipe section (speculative-config JSON, which doesn't exist for sglang) instead of the actual environment-block regex mismatch.

This is purely a diagnostic-text accuracy issue — I confirmed both messages are reachable (the success message on literally every current sglang injection since all four new recipes have a 2-space aggregated_environment: block that matches count≥1, and the error message only on the more speculative future-drift scenario). Nothing about the injection logic itself is wrong, and the error message does include {config_file}, so a developer isn't completely lost — but the phrase "speculative-config entries" actively points at the wrong artifact type for sglang, since sglang recipes have no speculative-config JSON section at all.

The fix is simple: since inject_synthetic_acceptance.py is explicitly designed to be framework-agnostic (per its own docstring), generalize the wording in both messages to something backend-neutral, e.g. "no injectable entries were found" and "Modified {count} injectable entries", so the message stays accurate regardless of which backend (vLLM speculative-config, sglang env blocks, or a future trtllm backend) produced the count.

One verifier argued the term is defensible because both backends inject "speculative-acceptance configuration," just in different locations, and that the count==0 trigger requires a hypothetical future indentation drift. That's a fair point for the error path, but it doesn't address the success-path message, which is inaccurate on every single sglang run today, not just a hypothetical drift scenario — so this is a real (if low-severity) wording bug in the current code, not purely speculative.

@github-actions

Copy link
Copy Markdown
Contributor

@github-actions

Copy link
Copy Markdown
Contributor

2 similar comments
@github-actions

Copy link
Copy Markdown
Contributor

@github-actions

Copy link
Copy Markdown
Contributor

@cquil11 cquil11 removed full-sweep-enabled agentx-fast Run AgentX throughput with 1 warmup request per lane and a 20-minute profile; not reusable labels Aug 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant