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,104 @@
name: "minimax-m3-vllm-disagg-gb200-1p2d-dep4-tep8-fp8-8k1k-eagle3"

model:
path: "minimax-m3-mxfp8"
container: "vllm/vllm-openai:nightly-6f91edf96d3f3272945809c04702380053bff4de"
precision: "fp8"

dynamo:
install: true
version: 1.3.0.dev20260710

health_check:
max_attempts: 720
interval_seconds: 10

sbatch_directives:
mem: "0"
cpus-per-task: "72"

srun_options:
mem: "0"

resources:
gpu_type: "gb200"
gpus_per_node: 4
prefill_nodes: 1
decode_nodes: 4
prefill_workers: 1
decode_workers: 2
gpus_per_prefill: 4
gpus_per_decode: 8

frontend:
type: dynamo
enable_multiple_frontends: false

backend:
type: vllm
connector: null

prefill_environment:
VLLM_ENGINE_READY_TIMEOUT_S: "3600"
VLLM_FLOAT32_MATMUL_PRECISION: "high"
VLLM_FLASHINFER_ALLREDUCE_BACKEND: "mnnvl"
UCX_CUDA_IPC_ENABLE_MNNVL: "y"
NCCL_CUMEM_ENABLE: "1"
NCCL_MNNVL_ENABLE: "1"
NCCL_NVLS_ENABLE: "1"

decode_environment:
VLLM_ENGINE_READY_TIMEOUT_S: "3600"
VLLM_FLOAT32_MATMUL_PRECISION: "high"
VLLM_FLASHINFER_ALLREDUCE_BACKEND: "mnnvl"
UCX_CUDA_IPC_ENABLE_MNNVL: "y"
NCCL_CUMEM_ENABLE: "1"
NCCL_MNNVL_ENABLE: "1"
NCCL_NVLS_ENABLE: "1"

vllm_config:
prefill:
kv-transfer-config: '{"kv_connector": "NixlConnector", "kv_role": "kv_both"}'
attention-config: '{"backend": "FLASHINFER", "use_trtllm_attention": true, "indexer_kv_dtype": "fp8"}'
tensor-parallel-size: 2
data-parallel-size: 2
data-parallel-rpc-port: 13345
enable-expert-parallel: true
trust-remote-code: true
no-enable-prefix-caching: true
block-size: 128
gpu-memory-utilization: 0.90
max-model-len: 9472
language-model-only: true
kv-cache-dtype: fp8
speculative-config: '{"method":"eagle3","model":"Inferact/MiniMax-M3-EAGLE3-GQA","num_speculative_tokens":3,"attention_backend":"FLASH_ATTN"}'

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 the two new EAGLE3 MTP recipes, the prefill speculative-config sets num_speculative_tokens: 3 (same as decode), whereas all 6 sibling MiniMax-M3 EAGLE3 MTP recipes on B300 — which this PR explicitly adapts from (PR #1863) — use prefill=1/decode=3. Both new GB200 files (1p2d-dep4-tep8-eagle3-8k1k.yaml:74 and 2p1d-dep4-dep16-eagle3-8k1k.yaml:74) are affected; worth a quick confirmation this was intentional rather than a copy-paste of the decode block.

Extended reasoning...

Both new GB200 MTP recipes set num_speculative_tokens: 3 in the prefill speculative-config block, exactly matching the decode block's value. Checking every existing MiniMax-M3 EAGLE3 MTP recipe on B300 (benchmarks/multi_node/srt-slurm-recipes/vllm/minimax-m3/b300-fp4/8k1k/mtp/*.yaml, 6 files: 1p1d, 1p2d, 1p4d, 1p6d, 2p1d, 2p3d) shows a uniform pattern of prefill=1, decode=3 — no exception. The configs/nvidia-master.yaml header comment for this exact sweep says it was "adapted from NV B300 PR #1863," making the B300 recipes the direct template these new GB200 files are supposed to mirror.

The affected locations are 1p2d-dep4-tep8-eagle3-8k1k.yaml:74 and 2p1d-dep4-dep16-eagle3-8k1k.yaml:74 — both set prefill num_speculative_tokens to 3 instead of the established 1.

Step-by-step check:

  1. grep -n num_speculative_tokens across the 6 B300 sibling files shows every prefill line reads "num_speculative_tokens":1 and every decode line reads "num_speculative_tokens":3 — 6/6 consistent.
  2. The new PR's 1p2d-dep4-tep8-eagle3-8k1k.yaml line 74 (prefill block) reads "num_speculative_tokens":3 — identical to its own decode block's value at the corresponding line further down.
  3. Same pattern repeats in 2p1d-dep4-dep16-eagle3-8k1k.yaml line 74.
  4. This is the exact shape you'd expect from copy-pasting the decode speculative-config string into the prefill block during recipe authoring.

That said, this is not a clear-cut bug and shouldn't block merge. A counter-precedent exists elsewhere in the repo: DeepSeek-V4 disaggregated MTP recipes (e.g. deepseek-v4/8k1k/disagg-gb200-low-latency-mtp2.yaml) set the same num_speculative_tokens on both prefill and decode (both 2), so "prefill == decode" is itself a legitimate, precedented convention for spec-decoding recipes in this repo — just not the one MiniMax-M3 has followed so far. Functionally, in disaggregated P/D serving the prefill worker performs a single prefill pass and hands off KV; it does not run the iterative multi-token speculative-decode loop the way the decode worker does, so this prefill setting mostly governs draft-model resource/cudagraph allocation rather than output correctness. There's no indication merging as-is would crash or corrupt results — worst case is a benign resource-allocation difference that the sweep (labeled full-sweep-fail-fast) will exercise empirically.

Suggested fix: if the intent was to mirror the B300 template, change prefill num_speculative_tokens to 1 in both new files at line 74. If prefill==decode was a deliberate choice (following the DeepSeek-V4 pattern instead), no change is needed — just worth a one-line confirmation from the author.

stream-interval: 32
max-cudagraph-capture-size: 2048
max-num-batched-tokens: 16384

decode:
kv-transfer-config: '{"kv_connector": "NixlConnector", "kv_role": "kv_both"}'
attention-config: '{"backend": "FLASHINFER", "use_trtllm_attention": true, "indexer_kv_dtype": "fp8"}'
tensor-parallel-size: 8
enable-expert-parallel: true
trust-remote-code: true
no-enable-prefix-caching: true
block-size: 128
gpu-memory-utilization: 0.90
max-model-len: 9472
language-model-only: true
kv-cache-dtype: fp8
speculative-config: '{"method":"eagle3","model":"Inferact/MiniMax-M3-EAGLE3-GQA","num_speculative_tokens":3,"attention_backend":"FLASH_ATTN"}'
stream-interval: 32
max-num-seqs: 1024
max-num-batched-tokens: 16384
max-cudagraph-capture-size: 4096

benchmark:
type: "sa-bench"
isl: 8192
osl: 1024
concurrencies: "16x64x128"
req_rate: "inf"
random_range_ratio: 0.8
use_chat_template: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
name: "minimax-m3-vllm-disagg-gb200-2p1d-dep4-dep16-fp8-8k1k-eagle3"

model:
path: "minimax-m3-mxfp8"
container: "vllm/vllm-openai:nightly-6f91edf96d3f3272945809c04702380053bff4de"
precision: "fp8"

dynamo:
install: true
version: 1.3.0.dev20260710

health_check:
max_attempts: 720
interval_seconds: 10

sbatch_directives:
mem: "0"
cpus-per-task: "72"

srun_options:
mem: "0"

resources:
gpu_type: "gb200"
gpus_per_node: 4
prefill_nodes: 2
decode_nodes: 4
prefill_workers: 2
decode_workers: 1
gpus_per_prefill: 4
gpus_per_decode: 16

frontend:
type: dynamo
enable_multiple_frontends: false

backend:
type: vllm
connector: null

prefill_environment:
VLLM_ENGINE_READY_TIMEOUT_S: "3600"
VLLM_FLOAT32_MATMUL_PRECISION: "high"
VLLM_FLASHINFER_ALLREDUCE_BACKEND: "mnnvl"
UCX_CUDA_IPC_ENABLE_MNNVL: "y"
NCCL_CUMEM_ENABLE: "1"
NCCL_MNNVL_ENABLE: "1"
NCCL_NVLS_ENABLE: "1"

decode_environment:
VLLM_ENGINE_READY_TIMEOUT_S: "3600"
VLLM_FLOAT32_MATMUL_PRECISION: "high"
VLLM_FLASHINFER_ALLREDUCE_BACKEND: "mnnvl"
UCX_CUDA_IPC_ENABLE_MNNVL: "y"
NCCL_CUMEM_ENABLE: "1"
NCCL_MNNVL_ENABLE: "1"
NCCL_NVLS_ENABLE: "1"

vllm_config:
prefill:
kv-transfer-config: '{"kv_connector": "NixlConnector", "kv_role": "kv_both"}'
attention-config: '{"backend": "FLASHINFER", "use_trtllm_attention": true, "indexer_kv_dtype": "fp8"}'
tensor-parallel-size: 2
data-parallel-size: 2
data-parallel-rpc-port: 13345
enable-expert-parallel: true
trust-remote-code: true
no-enable-prefix-caching: true
block-size: 128
gpu-memory-utilization: 0.90
max-model-len: 9472
language-model-only: true
kv-cache-dtype: fp8
speculative-config: '{"method":"eagle3","model":"Inferact/MiniMax-M3-EAGLE3-GQA","num_speculative_tokens":3,"attention_backend":"FLASH_ATTN"}'
stream-interval: 100
max-cudagraph-capture-size: 2048
max-num-batched-tokens: 16384

decode:
kv-transfer-config: '{"kv_connector": "NixlConnector", "kv_role": "kv_both"}'
attention-config: '{"backend": "FLASHINFER", "use_trtllm_attention": true, "indexer_kv_dtype": "fp8"}'
tensor-parallel-size: 1
data-parallel-size: 16
data-parallel-rpc-port: 13345
enable-expert-parallel: true
trust-remote-code: true
no-enable-prefix-caching: true
block-size: 128
gpu-memory-utilization: 0.90
max-model-len: 9472
language-model-only: true
kv-cache-dtype: fp8
speculative-config: '{"method":"eagle3","model":"Inferact/MiniMax-M3-EAGLE3-GQA","num_speculative_tokens":3,"attention_backend":"FLASH_ATTN"}'
stream-interval: 100
max-num-seqs: 1024
max-num-batched-tokens: 16384
max-cudagraph-capture-size: 2048

benchmark:
type: "sa-bench"
isl: 8192
osl: 1024
concurrencies: "512x1024"
req_rate: "inf"
random_range_ratio: 0.8
use_chat_template: true
59 changes: 53 additions & 6 deletions configs/nvidia-master.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8060,7 +8060,7 @@ qwen3.5-fp4-b200-trt:
- { tp: 8, ep: 8, dp-attn: true, conc-list: [256, 512, 1024] }

# MiniMax-M3 GB200 disagg sweep — adapted from NV B300 PR #1863.
# All prefill DEP4 (TP1 DP4 EP, 4 GPU/worker). Decode: Marlin, TEP8, DEP8, TEP4.
# All prefill DEP4 (TP1 DP4 EP, 4 GPU/worker). Decode: TEP8, DEP16, TEP4.
# 4 GPU/node (GB200 NVL72). FLASHINFER attention with FP8 KV cache.
minimaxm3-fp8-gb200-dynamo-vllm:
image: vllm/vllm-openai:nightly-4080263bb2c5d10deac17aaeb88e0823bc35bca9
Expand All @@ -8069,7 +8069,7 @@ minimaxm3-fp8-gb200-dynamo-vllm:
runner: gb200
precision: fp8
framework: dynamo-vllm
router: { name: dynamo-router, version: "1.3.0.dev20260614" }
router: { name: dynamo-router, version: "1.3.0.dev20260710" }
kv-p2p-transfer: nixl
multinode: true
disagg: true
Expand All @@ -8078,7 +8078,7 @@ minimaxm3-fp8-gb200-dynamo-vllm:
- isl: 8192
osl: 1024
search-space:
# 1p2d DEP4+DEP8, 5n: conc 512
# 1p1d DEP4+DEP16, 5n: conc 512

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 PR corrects the stale "Xp2d DEP8" comment wording to "Xp1d DEP16" in configs/nvidia-master.yaml, but the sibling SLURM recipe files it references (1p1d, 2p1d, 3p1d, 5p1d-dep4-dep16-8k1k.yaml under benchmarks/multi_node/srt-slurm-recipes/vllm/minimax-m3-gb200-fp8/8k1k/) still carry the identical stale header comment on line 3. It's comment-only and doesn't affect behavior, but since these files are what CONFIG_FILE points at, fixing them alongside the master-config comments would keep the same bug class from lingering.

Extended reasoning...

This PR explicitly sets out to fix a class of stale comments in configs/nvidia-master.yaml where search-space entries were mislabeled Xp2d DEP8 (implying 2 decode workers each running DEP8) when the actual resources block shows a single DEP16 decode worker (decode_workers: 1, gpus_per_decode: 16). The PR correctly rewrites these comments to Xp1d DEP16 at lines 8081, 8126, 8141, and 8156 of configs/nvidia-master.yaml.

However, the four sibling SLURM recipe YAML files that these minimaxm3-fp8-gb200-dynamo-vllm search-space entries reference via CONFIG_FILE=recipes/vllm/minimax-m3-gb200-fp8/8k1k/{1p1d,2p1d,3p1d,5p1d}-dep4-dep16-8k1k.yaml still contain the exact same stale wording in their own header comments. For example, 1p1d-dep4-dep16-8k1k.yaml line 3 reads:

# 1P DEP4 prefill (TP1 DP4 EP, 4 GPU/worker) + 2D DEP8 decode (TP1 DP8 EP, 8 GPU/worker = 2 nodes each).

yet the file's own resources block a few lines down shows decode_workers: 1 and gpus_per_decode: 16, and the vllm_config.decode section shows tensor-parallel-size: 1, data-parallel-size: 16 — i.e. one worker running DEP16, not two workers each running DEP8 across 2 nodes. The same mismatch (stale "2D DEP8 / 2 nodes each" header vs. the real single DEP16 worker config) appears identically in 2p1d-dep4-dep16-8k1k.yaml, 3p1d-dep4-dep16-8k1k.yaml, and 5p1d-dep4-dep16-8k1k.yaml.

Why this wasn't caught by existing validation: nothing in the config loader or CI cross-checks free-text YAML comments against the structured resources/vllm_config fields they describe, so a comment can drift arbitrarily from the config it documents without any test failing — which is exactly how the original bug in nvidia-master.yaml was introduced and now persists here in the recipe files.

Proof/walkthrough: Open benchmarks/multi_node/srt-slurm-recipes/vllm/minimax-m3-gb200-fp8/8k1k/1p1d-dep4-dep16-8k1k.yaml. Line 3 says "2D DEP8 decode ... 8 GPU/worker = 2 nodes each" — describing 2 decode workers, 8 GPUs each. But scroll to the resources: block: decode_workers: 1, gpus_per_decode: 16 — 1 worker, 16 GPUs. Scroll further to backend.vllm_config.decode: tensor-parallel-size: 1, data-parallel-size: 16 — confirms DEP16 with a single worker, matching the corrected # 1p1d DEP4+DEP16 comment this PR now writes into nvidia-master.yaml for the referencing search-space entry. The same check applies to the 2p1d/3p1d/5p1d variants.

Impact: Purely cosmetic/documentation — none of these fields feed into runtime behavior, so nothing breaks. But it undermines the very consistency this PR is trying to restore, since a reader following the CONFIG_FILE reference from the now-correct nvidia-master.yaml comment will land on a recipe file whose own header still describes the old, wrong topology.

Fix: Update line 3 of each of the four sibling files to match the corrected wording, e.g. # 1P DEP4 prefill (TP1 DP4 EP, 4 GPU/worker) + 1D DEP16 decode (TP1 DP16 EP, 16 GPU/worker).

- conc-list: [512]
prefill:
num-worker: 1
Expand Down Expand Up @@ -8123,7 +8123,7 @@ minimaxm3-fp8-gb200-dynamo-vllm:
ep: 8
dp-attn: false

# 2p2d DEP4+DEP8, 6n: conc 512,1024
# 2p1d DEP4+DEP16, 6n: conc 512,1024
- conc-list: [512, 1024]
prefill:
num-worker: 2
Expand All @@ -8138,7 +8138,7 @@ minimaxm3-fp8-gb200-dynamo-vllm:
ep: 16
dp-attn: true

# 3p2d DEP4+DEP8, 7n: conc 4096
# 3p1d DEP4+DEP16, 7n: conc 4096
- conc-list: [4096]
prefill:
num-worker: 3
Expand All @@ -8153,7 +8153,7 @@ minimaxm3-fp8-gb200-dynamo-vllm:
ep: 16
dp-attn: true

# 5p2d DEP4+DEP8, 9n: conc 4096
# 5p1d DEP4+DEP16, 9n: conc 4096
- conc-list: [4096]
prefill:
num-worker: 5
Expand All @@ -8168,6 +8168,53 @@ minimaxm3-fp8-gb200-dynamo-vllm:
ep: 16
dp-attn: true

minimaxm3-fp8-gb200-dynamo-vllm-mtp:
image: vllm/vllm-openai:nightly-6f91edf96d3f3272945809c04702380053bff4de
model: MiniMaxAI/MiniMax-M3-MXFP8
model-prefix: minimaxm3
runner: gb200
precision: fp8
framework: dynamo-vllm
router: { name: dynamo-router, version: "1.3.0.dev20260710" }
kv-p2p-transfer: nixl
multinode: true
disagg: true
scenarios:
fixed-seq-len:
- isl: 8192
osl: 1024
search-space:
# 1p2d TP2DP2+TEP8, 5n: conc 16,64,128 MTP
- spec-decoding: "mtp"
conc-list: [16, 64, 128]
prefill:
num-worker: 1
tp: 2
ep: 2
dp-attn: true
additional-settings:
- "CONFIG_FILE=recipes/vllm/minimax-m3-gb200-fp8/8k1k/mtp/1p2d-dep4-tep8-eagle3-8k1k.yaml"
decode:
num-worker: 2
tp: 8
ep: 8
dp-attn: false
# 2p1d TP2DP2+DEP16, 6n: conc 512,1024 MTP
- spec-decoding: "mtp"
conc-list: [512, 1024]
prefill:
num-worker: 2
tp: 2
ep: 2
dp-attn: true
additional-settings:
- "CONFIG_FILE=recipes/vllm/minimax-m3-gb200-fp8/8k1k/mtp/2p1d-dep4-dep16-eagle3-8k1k.yaml"
decode:
num-worker: 1
tp: 16
ep: 16
dp-attn: true

qwen3.5-fp4-b200-trt-mtp:
image: nvcr.io#nvidia/tensorrt-llm/release:1.3.0rc18
model: nvidia/Qwen3.5-397B-A17B-NVFP4
Expand Down
7 changes: 7 additions & 0 deletions perf-changelog.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5355,3 +5355,10 @@
- "Apply the accuracy-gated Kimi-K2.5 MXFP4 settings: tuned AITER MXFP4 MoE, fused shared experts, FP8 KV cache, block size 16, 16384 batched tokens, 512 sequences, async scheduling, gpu-memory-utilization 0.85 (headroom for CUDA-graph capture on MI355X), and the AITER BF16 GEMM path"
- "Extend the TP4 and TP8 8k1k concurrency sweep from 64 to 128 (1k1k deprecated per #2263)"
pr-link: https://github.com/SemiAnalysisAI/InferenceX/pull/2213

- config-keys:
- minimaxm3-fp8-gb200-dynamo-vllm-mtp
description:
- "Add MiniMax M3 MXFP8 GB200 Dynamo-vLLM disaggregated EAGLE3 MTP recipes"
- "Fix comment errors in minimaxm3-fp8-gb200-dynamo-vllm (Xp2d DEP8 -> Xp1d DEP16, remove phantom Marlin entry, bump router to 1.3.0.dev20260710)"
pr-link: https://github.com/SemiAnalysisAI/InferenceX/pull/2432
Comment on lines +5362 to +5364

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 appended changelog entry uses pr-link: TBD, but the changelog tooling only recognizes XXX or the canonical .../pull/<n> URL as valid pr-link values (see utils/validate_perf_changelog.py and utils/prepare_perf_changelog_merge.py). This will raise ChangelogValidationError when merge_with_reuse.sh runs prepare_perf_changelog_merge.py canonicalize at merge time, blocking the standard merge path until manually fixed. Please change TBD to XXX (or the real PR URL).

Extended reasoning...

The newly appended entry at the end of perf-changelog.yaml (for minimaxm3-fp8-gb200-dynamo-vllm-mtp) sets:

pr-link: TBD

The changelog tooling only accepts two forms for an unmerged/appended entry's pr-link:

  1. A placeholder from PR_LINK_PLACEHOLDERS in utils/validate_perf_changelog.py (lines ~24-27), which is {"XXX", "https://github.com/SemiAnalysisAI/InferenceX/pull/XXX"}.
  2. The canonical https://github.com/SemiAnalysisAI/InferenceX/pull/<pr_number> URL.

"TBD" matches neither. Note that the PR diff as displayed by GitHub/CI tooling shows the link already canonicalized to .../pull/2432, but that's an artifact of display-time canonicalization in this review pipeline — the actual committed content on this branch (confirmed via git show HEAD:perf-changelog.yaml) is pr-link: TBD.

Why this isn't caught before merge: The PR-time CI gate (validate_perf_changelog.py invoked from the changelog-check workflow) only calls validate_matrix_compatible_change, which drives matrix generation via process_changelog.py. It never calls validate_added_pr_link/compare_entries, so an invalid placeholder like TBD sails through CI silently.

Where it actually breaks: The standard merge path, merge_with_reuse.sh <pr>, unconditionally runs prepare_perf_changelog_merge.py canonicalize. That script's canonicalize_appended_links function (lines ~109-114) checks each appended entry's pr-link against PR_LINK_PLACEHOLDERS and the expected canonical URL; since TBD matches neither, it raises:

ChangelogValidationError: appended entry N has unexpected pr-link 'TBD'

Step-by-step proof:

  1. Author appends the changelog entry with pr-link: TBD (perf-changelog.yaml:5356).
  2. PR CI's changelog check only validates matrix compatibility, not the pr-link format — the PR shows green.
  3. Someone runs utils/merge_with_reuse.sh 2432 to merge, which internally invokes python utils/prepare_perf_changelog_merge.py canonicalize ....
  4. canonicalize_appended_links iterates appended entries, reaches this one, sees pr-link == "TBD", which is not in PR_LINK_PLACEHOLDERS = {"XXX", ".../pull/XXX"} and does not equal the expected https://github.com/SemiAnalysisAI/InferenceX/pull/2432.
  5. The function raises ChangelogValidationError, aborting the merge script before it can canonicalize the link and complete the merge.
  6. A maintainer has to manually edit perf-changelog.yaml to replace TBD with XXX (or the real URL) before retrying the merge.

This is also confirmed as an outlier: a grep of the entire 5356-line file shows only one non-empty, non-URL pr-link value (TBD, this entry) — every other unmerged entry in the file consistently uses XXX.

Fix: change pr-link: TBD to pr-link: XXX (the standard placeholder used everywhere else in the file), which merge_with_reuse.sh will then correctly canonicalize to the real PR URL at merge time.

Loading