Skip to content

Add GB200 MiniMax-M3 FP4 AgentX sweep - #2609

Open
cquil11 wants to merge 18 commits into
mainfrom
agent/gb200-minimaxm3-fp4-agentx
Open

Add GB200 MiniMax-M3 FP4 AgentX sweep#2609
cquil11 wants to merge 18 commits into
mainfrom
agent/gb200-minimaxm3-fp4-agentx

Conversation

@cquil11

@cquil11 cquil11 commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

What changed

  • Add a tuned 17-point GB200 MiniMax-M3 FP4 AgentX sweep with EAGLE3 and synthetic acceptance length 2.78.
  • Preserve the complete B200 TP4 baseline, including SimpleCPU offload.
  • Add measured GB200 Pareto candidates: aggregate DEP4, aggregate DEP8, and KV-routed 1P/1D DEP8-to-DEP4.

Correctness

  • Use stable vLLM v0.27.1, NVIDIA/srt-slurm v1.0.50, and Dynamo v1.3.1.
  • Route by KV state with correlation-ID session affinity.
  • Collect token/cache metrics from every logical vLLM endpoint, including both P/D roles.
  • Cap SimpleCPU offload at 512 GiB per GB200 node (128 GiB per rank).
  • Keep strict request validation with no topology-specific threshold exceptions.

Tuning result

Direct GB200 tests covered aggregate DEP4/DEP8, one to three prefill workers, DEP4/DEP8 P/D combinations, and resident/SimpleCPU variants. The final matrix retains the non-dominated points and removes multi-prefill, symmetric P8/D8, and high-concurrency offload points that were worse in both normalized throughput and P90 E2E interactivity.

Validation

  • Exactly 17 unique full-duration jobs.
  • 141 matrix/changelog tests pass.
  • Performance changelog validation passes against current main.
  • All six retained recipes pass NVIDIA/srt-slurm v1.0.50 dry-run validation.
  • Direct runs verified EAGLE3, KV/session routing, warm-cache behavior, SimpleCPU offload, and complete logical server-metrics URL construction.

@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 agentx-fast Run AgentX throughput with 1 warmup request per lane and a 20-minute profile; not reusable full-sweep-enabled labels Aug 14, 2026
@cquil11
cquil11 marked this pull request as ready for review August 14, 2026 22:09
@cquil11
cquil11 requested a review from a team August 14, 2026 22:09
@claude

claude Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

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


PR Review: GB200 MiniMax-M3 FP4 AgentX sweep

  • Gather context (diff vs origin/main, read changed files)
  • Validate configs/nvidia-master.yaml (model prefixes, images, changelog pairing)
  • Validate perf-changelog.yaml entry position (appended at end)
  • Review new srt-slurm recipe YAMLs
  • Review runners/launch_gb200-nv.sh and benchmarks/benchmark_lib.sh
  • Post inline comments for issues found
  • Post review summary

Summary

No blocking issues; two 🟡 warnings posted inline. (1) All 14 recipes set AIPERF_DYNAMO_SESSION_TIMEOUT_SECONDS: "14400", which is dead when AIPERF_HTTP_X_DYNAMO_SESSION_ID_FROM_CORRELATION_ID=true (its only consumer is the conv-aware-routing branch at benchmarks/benchmark_lib.sh:2055-2064, which that opt-in skips), and unlike every dsv4 header-routing recipe they omit router-session-affinity-ttl-secs, so high-concurrency sessions may lose KV affinity after the upstream 300s default TTL. (2) disagg-3p1d-dep4-dep4-agentic.yaml and its vllm-simple variant are not referenced by any CONFIG_FILE in the master config — the 60 sweep points map to the other 12 recipes, so these two are dead files.

Everything else checked out: perf-changelog entry is correctly appended at the end and paired with the master-config change; minimaxm3 prefix and dram-utilization/kv-offload-backend fields are established on main; vllm/vllm-openai:v0.27.1 is a public Docker Hub image; the launcher retains the enroot docker:// import path; sweep point counts match the stated 60 (35 agg + 25 disagg); SimpleCPU offload math is 512 GiB/node (128 GiB × 4 ranks/node) in every offload recipe, under the 550 GB allowance; and DEP/topology resource counts (agg_nodes/prefill_nodes/decode_nodes vs data-parallel-size, dp_launch_mode: per_node) are self-consistent across all recipes.

Comment on lines +30 to +34
args:
router-mode: "kv"
router-kv-events: true
router-temperature: "0"
kv-cache-block-size: 128

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: Session-affinity TTL is not configured, and AIPERF_DYNAMO_SESSION_TIMEOUT_SECONDS: "14400" (line 84) is dead on this routing path — this applies to all 14 new recipes.

Why it matters: These recipes opt into header-based session routing (AIPERF_HTTP_X_DYNAMO_SESSION_ID_FROM_CORRELATION_ID: "true" + AIPERF_USE_DYNAMO_CONV_AWARE_ROUTING: "0"). With that opt-in, benchmark_lib.sh skips the conv-aware-routing branch (benchmarks/benchmark_lib.sh:2055-2064) — the only consumer of AIPERF_DYNAMO_SESSION_TIMEOUT_SECONDS — so the 14400s value is never applied anywhere. Per the comment in that same block, the upstream router default affinity TTL is 300s, which is shorter than an overloaded high-concurrency agentic request. Every existing dsv4 recipe that uses this exact header-routing pattern sets the TTL router-side instead (e.g. disagg-gb200-1p1d-dep8-dep8-mtp-agentic.yaml:46 uses router-session-affinity-ttl-secs: 14400). Without it, sessions at c128–c256 can lose worker affinity mid-conversation, silently degrading the KV-aware routing this sweep is meant to measure.

Fix: Set the TTL on the frontend like the dsv4 recipes (and drop the dead AIPERF_DYNAMO_SESSION_TIMEOUT_SECONDS env entries):

Suggested change
args:
router-mode: "kv"
router-kv-events: true
router-temperature: "0"
kv-cache-block-size: 128
args:
router-mode: "kv"
router-kv-events: true
router-temperature: "0"
router-session-affinity-ttl-secs: 14400
kv-cache-block-size: 128

If srt-slurm v1.0.50 / Dynamo v1.3.1 applies a long affinity TTL by default for X-Dynamo-Session-ID sessions, please note that in the PR description instead. Fix this →

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 functional half of this is fixed: commit d707e11 ("perf(agentx): prune GB200 MiniMax tuning grid") added router-session-affinity-ttl-secs: 14400 to the frontend args block in this recipe (and the other 13), so session affinity is now actually enforced router-side instead of relying on the dead env var. However, the second part of the suggested fix — dropping the now-confirmed-dead AIPERF_DYNAMO_SESSION_TIMEOUT_SECONDS: "14400" entry from benchmark.env — was not done; it is still present at line 93 in this file (and in all the other 13 recipes). It remains inert cleanup at this point, not a functional issue, since benchmark_lib.sh skips the conv-aware-routing branch that would have consumed it.

@github-actions

Copy link
Copy Markdown
Contributor

Comment thread perf-changelog.yaml
Comment on lines 5980 to +5992
description:
- "Inject the committed synthetic MTP acceptance length only for GB300 AgentX throughput; keep eval-only jobs on real target verification."
pr-link: https://github.com/SemiAnalysisAI/InferenceX/pull/2571


- config-keys:
- minimaxm3-fp4-gb200-dynamo-vllm-agentic-agg-mtp
- minimaxm3-fp4-gb200-dynamo-vllm-agentic-disagg-mtp
scenario-type:
- agentic-coding
description:
- "Add GB200 MiniMax-M3 NVFP4 AgentX with EAGLE3-GQA, the B200 TP4 baseline, DEP4/DEP8, KV-routed P/D, and high-concurrency SimpleCPU offload."
- "Use vLLM v0.27.1, NVIDIA/srt-slurm v1.0.50, and Dynamo v1.3.1 with full 1M context, MiniMax parsing, KV-cache token metrics, and every logical vLLM metrics endpoint."
pr-link: https://github.com/SemiAnalysisAI/InferenceX/pull/2609

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.

🔴 This diff mutates a pre-existing byte in perf-changelog.yaml: the prior last line (two trailing spaces after the #2571 pr-link) is deleted and replaced with a bare empty line before the new #2609 entry is appended, instead of leaving it untouched. AGENTS.md explicitly requires the file to be append-only and byte-sensitive ('Preserve all existing bytes and separator whitespace, and append only at the tail'). Fix: restore the original (two-space) separator line and append the new entry after it.

Extended reasoning...

What the bug is

AGENTS.md states, verbatim, in its "Non-negotiable benchmark invariants" section (line 21): "Every change ... requires a new perf-changelog.yaml entry. The file is append-only and byte-sensitive. Preserve all existing bytes and separator whitespace, and append only at the tail."

The diff for this PR does not honor that. Looking at the hunk around line 5980-5992:

   pr-link: https://github.com/SemiAnalysisAI/InferenceX/pull/2571
-  
+
+- config-keys:
+    - minimaxm3-fp4-gb200-dynamo-vllm-agentic-agg-mtp:
+    ...

The pre-existing separator line after the #2571 entry — which was two trailing spaces (" ") followed by a newline — is deleted and replaced with a bare empty line, and only then is the new #2609 block appended. I confirmed this directly against the working tree with cat -A: the current file shows $ (empty line) where the base (pre-PR) content had $ (two spaces before EOL). That is a mutation of a historical byte, not a pure append.

Why it is not caught by the main CI gate

The PR description claims "Performance changelog validation passes against current main," which is true but incomplete. The primary check-changelog job runs validate_perf_changelog.py::main(), which calls validate_matrix_compatible_change -> validate_generated_config -> process_changelog.py. process_changelog.py::get_added_lines (lines ~41-42) explicitly treats whitespace-only/empty-line deletions as ignorable, so this diff sails through that check. That is exactly why a human reviewer skimming a unified diff, or the automated CI gate, would not flag it — and is precisely the failure mode the repo docs (docs/ci-procedures.md:158, docs/testing.md:100) warn about: whitespace-only historical deletions can slip past the diff reader, requiring a manual byte-diff gate instead.

Where it actually breaks something

utils/validate_perf_changelog.py::validate_raw_change() is a separate, stricter byte-level gate used by the changelog merge/ingest path (utils/prepare_perf_changelog_merge.py, invoked by merge_with_reuse.sh at lines 97/130/155, and also used by recover_failed_ingest.py). Its logic:

def validate_raw_change(base_raw, head_raw, additions, corrections):
    if additions:
        if not head_raw.startswith(base_raw):
            raise ChangelogValidationError(
                "appended entries changed historical perf-changelog.yaml bytes; "
                "restore the base file byte-for-byte and append at the end"
            )

Because the base file (merge-base) ends with ...pull/2571\n \n and this PR's head ends with ...pull/2571\n\n- config-keys:..., head_raw.startswith(base_raw) is False ("\n \n" != "\n\n"). validate_raw_change raises ChangelogValidationError("appended entries changed historical perf-changelog.yaml bytes; restore the base file byte-for-byte and append at the end") — so the standard reuse-merge path used to land this PR onto main would reject it.

Step-by-step proof

  1. Base (main, commit d136fa6, the #2571 entry's tail) ends: ...pull/2571\n + (two spaces) + \n.
  2. This PR's diff shows - (removing that two-space line) and + (adding a bare empty line), then appends the new #2609 block.
  3. Head file (this PR) now ends: ...pull/2571\n + `` (nothing) + \n- config-keys:\n - minimaxm3-fp4-gb200-dynamo-vllm-agentic-agg-mtp\n...
  4. base_raw = ...pull/2571\n \n, head_raw = ...pull/2571\n\n- config-keys:....
  5. head_raw.startswith(base_raw) compares byte-for-byte: at the position right after pull/2571\n, base_raw expects \n (space, space, newline) but head_raw has \n (newline) directly — mismatch, so startswith returns False.
  6. validate_raw_change raises ChangelogValidationError, blocking the merge-prep/ingest step even though the primary CI check-changelog job passed.

The fix

Restore the original two-space (" ") line exactly as it existed on main, and append the new #2609 config-keys block immediately after it (rather than replacing that line with an empty one first).

@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 14, 2026
@cquil11
cquil11 marked this pull request as draft August 14, 2026 22:31
@cquil11
cquil11 marked this pull request as ready for review August 15, 2026 02:53
@claude

claude Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Claude finished @cquil11's task in 5m 42s —— View job


Re-review: GB200 MiniMax-M3 FP4 AgentX sweep (pruned 60 → 17 points)

  • Gather context (diff vs origin/main, compare against prior review)
  • Verify prior issues (session-affinity TTL, orphaned 3P1D recipes, perf-changelog byte mutation)
  • Review pruned matrix in configs/nvidia-master.yaml + changelog pairing
  • Review recipe YAMLs, runners/launch_gb200-nv.sh, benchmarks/benchmark_lib.sh
  • Post review summary

Still open from the previous review — 🔴 BLOCKING

perf-changelog.yaml still mutates a historical byte. The earlier inline comment on perf-changelog.yaml (posted 2026-08-14, before the prune commit) remains applicable: the base file on main ends the #2571 entry with a two-space line ( + newline), and this diff deletes that line and replaces it with a bare empty line before appending the #2609 entry. I re-verified byte-for-byte against the current merge-base (35d731a9e): base tail is ...pull/2571\n \n, head tail is ...pull/2571\n\n- config-keys:.... utils/validate_perf_changelog.py::validate_raw_change (utils/validate_perf_changelog.py:219) requires the head file to start with the base bytes exactly, so the reuse-merge/ingest path (prepare_perf_changelog_merge.py via merge_with_reuse.sh) will reject this with "appended entries changed historical perf-changelog.yaml bytes" even though the lenient check-changelog CI job passes. Fix: restore the original two-space line after pr-link: .../pull/2571, then one empty line, then the new - config-keys: block — i.e. the tail must be ...pull/2571\n \n\n- config-keys:.... Fix this →

Fixed since the previous review

  • Session-affinity TTL: all 6 retained recipes now set router-session-affinity-ttl-secs: 14400 on the frontend (e.g. agg-tp4-agentic.yaml:37). The now-redundant AIPERF_DYNAMO_SESSION_TIMEOUT_SECONDS: "14400" env entry was kept in each recipe — it's dead on this routing path but harmless; feel free to drop it or leave it.
  • Orphaned recipes: the unwired 3P1D dep4-dep4 files are gone; every one of the 6 remaining recipes is referenced by exactly one CONFIG_FILE in the master config.

New/changed code since the previous review — checked, no new issues

  • Matrix prune: 17 unique points as stated — agg TP4 resident (8) + TP4 SimpleCPU (3) + DEP4 (2) + DEP4 SimpleCPU (2) + DEP8 (1) + disagg 1P1D DEP8→DEP4 (1). Changelog entry is paired with the master-config change and correctly references both new config keys.
  • benchmarks/benchmark_lib.sh:2042-2044: the new --extra-inputs append is gated on AIPERF_EXTRA_INPUTS, which only the 6 new recipes set (thinking:true, no whitespace, so the unquoted expansion is safe). No existing run is affected.
  • runners/launch_gb200-nv.sh:397-408: new v1.0.50 branch is scoped to minimaxm3 + fp4 + dynamo-vllm agentic only, pins the tag to commit e4019633 with a fail-fast check, and the existing enroot docker:// import path is unchanged. I could not independently verify the tag→commit mapping from this sandbox (no network), but a mismatch fails loudly at launch rather than silently.
  • Recipes: topology/resource counts are self-consistent (disagg prefill 2 nodes × 4 GPUs = DEP8, decode 1 node × 4 = DEP4, dp_launch_mode: per_node); SimpleCPU offload is 512 GiB/node = 128 GiB × 4 ranks as claimed; image vllm/vllm-openai:v0.27.1 is public Docker Hub; minimaxm3 prefix and dram-utilization are established on main.

Verdict: one blocking issue remains — the perf-changelog.yaml separator byte. Everything else from the prior review is resolved and the pruned sweep is internally consistent.

@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.

Beyond the inline finding, I also checked the disaggregated recipe's bare kv_events_config: true (vs. the sibling aggregate recipes' { aggregated: true }) for its DP-attention prefill/decode workers — this is a valid, distinct schema shape for disaggregated KV-events config, not a mistake, so it's not flagged as a bug.

Extended reasoning...

This run's bug-hunting pass found one new minor issue (posted inline: an unused dram-utilization field on the disagg-mtp scenario, since its only search-space arm uses kv-offloading: none). A finder also raised the disaggregated recipe's bare kv_events_config: true as a possible inconsistency versus the aggregate recipes' { aggregated: true } form; on inspection this is a legitimate alternate shape for the disaggregated P/D topology rather than an error, so it was ruled out and is not included as a bug.

disagg: true
scenarios:
agentic-coding:
- dram-utilization: 0.61

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 new minimaxm3-fp4-gb200-dynamo-vllm-agentic-disagg-mtp scenario sets dram-utilization: 0.61 (configs/nvidia-master.yaml:7346), but its only search-space entry uses kv-offloading: none — there's no dram arm to consume it. This is a pre-existing pattern issue in this PR's new config, not a runtime bug: it's dead and misleads a reader into thinking DRAM offload is exercised here, unlike the sibling agg-mtp scenario which legitimately uses two dram arms. Either drop the field or add the intended dram arm.

Extended reasoning...

dram-utilization is read by exactly two consumers in this repo, and both gate on the presence of a kv-offloading: dram search-space entry:

  1. validate_dram_offload_capacity in utils/matrix_logic/validation.py only requires/validates dram_utilization when a search-space entry has kv_offloading == 'dram'. It never reads or enforces the field for entries whose kv_offloading is none.
  2. agentic_dram_offload_gb in utils/matrix_logic/generate_sweep_configs.py (lines ~153-161) returns 0 immediately when kv_offloading != 'dram', before it ever reads Fields.DRAM_UTILIZATION.

The new minimaxm3-fp4-gb200-dynamo-vllm-agentic-disagg-mtp scenario (configs/nvidia-master.yaml:7346-7360) has exactly one search-space entry, and it is spec-decoding: mtp, kv-offloading: none, conc-list: [48]. There is no kv-offloading: dram arm anywhere in this scenario. That means dram-utilization: 0.61 set at the scenario level is never consumed by either code path — it's inert configuration.

This is easy to miss because the sibling scenario added in the same PR, minimaxm3-fp4-gb200-dynamo-vllm-agentic-agg-mtp (line ~7263), legitimately needs dram-utilization: 0.61 for its two kv-offloading: dram / kv-offload-backend: vllm-simple arms. It looks like the value was copied from the agg-mtp block into the disagg-mtp block without adjusting for the fact that the disagg scenario's search space doesn't (yet) include a dram-offload arm.

Impact is limited to code clarity, not runtime correctness: no validation error is raised, no job fails, and no sweep behavior changes, since the field is simply never read for a kv-offloading: none entry. But it's misleading for anyone reading the config later — they'd reasonably assume the disagg-mtp scenario measures DRAM KV offload the way the agg-mtp one does, when in fact it doesn't.

Step-by-step proof:

  1. Read configs/nvidia-master.yaml:7333-7360 — the minimaxm3-fp4-gb200-dynamo-vllm-agentic-disagg-mtp scenario has one agentic-coding entry: { dram-utilization: 0.61, search-space: [ { spec-decoding: mtp, kv-offloading: none, conc-list: [48], prefill: {...}, decode: {...} } ] }.
  2. Grep the search-space entry list for kv-offloading: dram — no match; only kv-offloading: none appears.
  3. Read utils/matrix_logic/generate_sweep_configs.py agentic_dram_offload_gb: if kv_offloading != 'dram': return 0 runs before agentic_config[Fields.DRAM_UTILIZATION] is ever accessed, so for this scenario the function always short-circuits to 0 without touching the config value.
  4. Read utils/matrix_logic/validation.py validate_dram_offload_capacity: the check that requires/uses dram_utilization is inside the branch keyed on entry.kv_offloading == 'dram'; since no entry in this scenario satisfies that, the validator never looks at dram_utilization here either.
  5. Therefore dram-utilization: 0.61 at line 7346 has zero effect on sweep generation, validation, or job configuration for the disagg-mtp scenario — confirmed dead by both of its would-be consumers.

Fix: remove dram-utilization: 0.61 from this scenario (cleanest, since no dram arm currently exists), or, if a DRAM-offloaded disagg arm was actually intended (the PR body's "SimpleCPU offload" language suggests DRAM offload was a goal), add a kv-offloading: dram search-space entry analogous to the ones in agg-mtp, wired to a disagg recipe that sets kv-transfer-config with SimpleCPUOffloadConnector.

@github-actions

Copy link
Copy Markdown
Contributor

@github-actions

Copy link
Copy Markdown
Contributor

@cquil11
cquil11 force-pushed the agent/gb200-minimaxm3-fp4-agentx branch from b7f1d38 to 1cf03b2 Compare August 15, 2026 05:49
@github-actions

Copy link
Copy Markdown
Contributor

@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