Skip to content

[AMD] [AGENTX] Minimax-M3 Perf Tuning - #2671

Open
ajith-sirra-amd wants to merge 3 commits into
mainfrom
amd/minimax-m3-agentic-perf-tuning
Open

[AMD] [AGENTX] Minimax-M3 Perf Tuning#2671
ajith-sirra-amd wants to merge 3 commits into
mainfrom
amd/minimax-m3-agentic-perf-tuning

Conversation

@ajith-sirra-amd

Copy link
Copy Markdown
Collaborator

Add MiniMax-M3 FP4 MI355X Agentic Support with MTP

Adds an updated single-node agentic-coding benchmark recipe for MiniMax-M3 (FP4) on MI355X using vLLM, with EAGLE3 MTP speculative decoding and DRAM-backed KV cache offloading (vllm-simple).

Changes

  • New launch script: starts a vLLM server with EAGLE3 speculative decoding, AITER MoE/fusion backends, INT4 quick-reduce all-reduce, FP8 KV cache, TP/EP parallel, and optional native KV offloading to host DRAM, then runs the agentic replay/eval harness.
  • New config entry minimaxm3-fp4-mi355x-vllm-agentic-mtp (image vllm-openai-rocm:nightly-cb8104839c141609d99f1254459ef3a4f1bd4263, tp=4, DRAM KV offloading via vllm-simple, spec-decoding: mtp).
  • Synthetic acceptance for the throughput replay: rejection_sample_method: synthetic with synthetic_acceptance_length: 3.35, the committed MiniMax-M3 EAGLE3 golden AL for thinking_on at num_speculative_tokens=5 (golden_al_distribution/minimaxm3_eagle3.yaml), per the AgentX fairness guidelines. EVAL_ONLY accuracy runs keep real target verification, since synthetic acceptance bypasses verification and corrupts the eval score.
  • Changelog entry documenting the addition (PR link TBD).

Status

Marked [WIP].

Signed-off-by: Sirra <asirra@amd.com>
@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 关于重新运行失败任务的文档

@ajith-sirra-amd ajith-sirra-amd added AMD agentx AgentX benchmarks, recipes, and infrastructure labels Aug 19, 2026
Comment on lines 126 to +129
export VLLM_ROCM_USE_AITER=1
export VLLM_ROCM_USE_AITER_MOE=1
export VLLM_ROCM_USE_AITER_FUSION_SHARED_EXPERTS=1
# The AITER page-16 sparse-attention path requires exactly one KV head per
# tensor-parallel rank. MiniMax-M3 has four KV heads, so TP4 uses that fast
# path while TP2 uses vLLM's supported Triton sparse-attention fallback.
if [ "$TP" -eq 4 ]; then
export VLLM_ROCM_SHUFFLE_KV_CACHE_LAYOUT=1
else
export VLLM_ROCM_SHUFFLE_KV_CACHE_LAYOUT=0
fi
export VLLM_ROCM_SHUFFLE_KV_CACHE_LAYOUT=1

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.

🔴 Removing the TP-conditional guard on VLLM_ROCM_SHUFFLE_KV_CACHE_LAYOUT and hard-coding it to 1 violates the AITER page-16 fast path's documented requirement of exactly one KV head per TP rank (MiniMax-M3 has 4 KV heads). configs/amd-master.yaml's minimaxm3-fp4-mi355x-vllm-agentic-mtp still has a live { tp: 2, ... } arm, which will now launch with 2 heads/rank under the shuffled layout — a topology the deleted comment explicitly says only supports the Triton fallback (shuffle=0). Restore the if [ "$TP" -eq 4 ] guard, or confirm the new v0.27.1 image no longer needs it and drop the TP2 arm instead.

Extended reasoning...

The bug: The previous version of this script conditioned VLLM_ROCM_SHUFFLE_KV_CACHE_LAYOUT on the tensor-parallel size, with a comment explaining why:

# The AITER page-16 sparse-attention path requires exactly one KV head per
# tensor-parallel rank. MiniMax-M3 has four KV heads, so TP4 uses that fast
# path while TP2 uses vLLM'\''s supported Triton sparse-attention fallback.
if [ "$TP" -eq 4 ]; then
    export VLLM_ROCM_SHUFFLE_KV_CACHE_LAYOUT=1
else
    export VLLM_ROCM_SHUFFLE_KV_CACHE_LAYOUT=0
fi

This PR deletes both the comment and the conditional, replacing them with a single unconditional line (benchmarks/single_node/agentic/minimaxm3_fp4_mi355x_mtp.sh:126-129):

export VLLM_ROCM_SHUFFLE_KV_CACHE_LAYOUT=1

Why this matters here specifically: MiniMax-M3 has 4 KV heads. The deleted comment states the AITER page-16 sparse-attention fast path (which this shuffle env var enables) requires exactly 1 KV head per TP rank. At TP=4, that's 4 heads / 4 ranks = 1 head/rank — valid. At TP=2, that's 4 heads / 2 ranks = 2 heads/rank — the documented invariant is violated, and the recipe author's own fallback (shuffle=0, Triton path) exists precisely to handle that case.

The trigger path: configs/amd-master.yaml (also touched by this PR, just above the diff hunk shown) still defines a live TP2 arm for this exact recipe:

minimaxm3-fp4-mi355x-vllm-agentic-mtp:
  ...
  search-space:
  - { tp: 4, kv-offloading: none, conc-list: [...], spec-decoding: mtp }
  - { tp: 2, kv-offloading: none, conc-list: [1, 2, 5], spec-decoding: mtp }
  - { tp: 4, kv-offloading: dram, kv-offload-backend: { name: lmcache, version: "0.5.3" }, conc-list: [32, 40], spec-decoding: mtp }

Every sweep point in the tp: 2 arm launches minimaxm3_fp4_mi355x_mtp.sh with TP=2, which after this change unconditionally sets VLLM_ROCM_SHUFFLE_KV_CACHE_LAYOUT=1. There is nothing else in the script that neutralizes this for TP2 — --attention-backend TRITON_ATTN was already present in the old script alongside the TP-conditional, so it does not make the shuffle var redundant; the shuffle var governs the KV-cache physical layout independently (relevant given the --hf-overrides '{"text_config": {"use_index_cache": true, "index_topk_freq": 4}}' MSA/index-cache path also present in this script).

Why nothing else in this PR prevents it: The PR is otherwise a broad refactor (bash strict-mode fixes, swapping LMCache for vLLM-native KV offload, dropping AIPerf metrics vars, etc.) and the image was bumped to vllm-openai-rocm:v0.27.1. Nothing in the diff or PR description states that the new image relaxes the page-16 fast path's one-head-per-rank requirement — this looks like an accidental drop of a still-necessary guard rather than an intentional simplification, since the guard is a hardware/algorithm invariant tied to MiniMax-M3'''s head count, not an image-version workaround.

Step-by-step proof:

  1. configs/amd-master.yaml schedules a sweep point with tp: 2 for minimaxm3-fp4-mi355x-vllm-agentic-mtp.
  2. The launcher sets TP=2 and invokes minimaxm3_fp4_mi355x_mtp.sh.
  3. The script unconditionally executes export VLLM_ROCM_SHUFFLE_KV_CACHE_LAYOUT=1 (line 129).
  4. vllm serve starts with --tensor-parallel-size 2 and the AITER page-16 shuffled KV-cache layout enabled.
  5. MiniMax-M3'''s 4 KV heads split across 2 ranks give 2 heads/rank, which the deleted comment states is unsupported by that fast path (only 1 head/rank is supported); the TP4 arm in the same config is the only one satisfying that invariant.
  6. Depending on how strictly AITER validates this at engine startup, this either fails to launch or produces a silently incorrect KV-cache layout for the TP2 sweep points — either way, those 3 concurrency points (1, 2, 5) in the search space are broken by this change.

The fix: Restore the TP-conditional (if [ "$TP" -eq 4 ]; then ... else ... fi) exactly as it was, or — if the v0.27.1 image genuinely no longer needs the one-head-per-rank restriction — state that explicitly and verify it, since dropping the check silently while keeping the TP2 arm risks a broken or corrupted sweep point either way.

Comment on lines 95 to 104
OFFLOAD_ARGS=(
--kv-transfer-config
"{\"kv_connector\":\"LMCacheMPConnector\",\"kv_connector_module_path\":\"lmcache.integration.vllm.lmcache_mp_connector\",\"kv_role\":\"kv_both\",\"kv_connector_extra_config\":{\"lmcache.mp.server_urls\":\"$LMCACHE_SERVER_URLS_CSV\",\"lmcache.mp.mq_timeout\":6000.0}}"
--kv_offloading_backend native
--kv_offloading_size "$TOTAL_CPU_DRAM_GB"
)
;;
*)
echo "Unsupported KV_OFFLOAD_BACKEND: $KV_OFFLOAD_BACKEND (expected empty or lmcache)" >&2
echo "Unsupported KV_OFFLOAD_BACKEND: ${KV_OFFLOAD_BACKEND:-}" >&2
exit 1
;;
esac

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 script's KV_OFFLOAD_BACKEND case statement only handles "" and "vllm-native" now (the lmcache branch was removed), but configs/amd-master.yaml's minimaxm3-fp4-mi355x-vllm-agentic-mtp entry still has a third search-space arm using kv-offload-backend: { name: lmcache, version: "0.5.3" } (conc-list [32, 40]). That arm will set KV_OFFLOAD_BACKEND=lmcache, hit the script's *) default branch, and exit 1 with "Unsupported KV_OFFLOAD_BACKEND: lmcache", deterministically failing those two sweep points.

Extended reasoning...

The bug

This PR rewrites benchmarks/single_node/agentic/minimaxm3_fp4_mi355x_mtp.sh to drop LMCache support entirely and replace it with a vllm-native backend (using --kv_offloading_backend native / --kv_offloading_size). The case "${KV_OFFLOAD_BACKEND:-}" statement now only has two real branches: "" (no offload) and vllm-native. Everything else — including the previously-supported lmcache value — falls through to the *) branch, which prints Unsupported KV_OFFLOAD_BACKEND: ... and exit 1.

The triggering config

configs/amd-master.yaml (unchanged by this PR, at line 1701) still has a third search-space arm for minimaxm3-fp4-mi355x-vllm-agentic-mtp:

- { tp: 4, kv-offloading: dram, kv-offload-backend: { name: lmcache, version: "0.5.3" }, conc-list: [32, 40], spec-decoding: mtp }

The code path that triggers it

  1. .github/workflows/run-sweep.yml sets the workflow input kv-offload-backend: ${{ matrix.config["kv-offload-backend"].name }}, which for this arm resolves to the literal string "lmcache".
  2. .github/workflows/benchmark-tmpl.yml exports this as KV_OFFLOAD_BACKEND=lmcache into the job environment.
  3. The rewritten script's case statement no longer has an lmcache) branch (it was entirely deleted in this diff, replaced by vllm-native)), so KV_OFFLOAD_BACKEND=lmcache falls into *).
  4. The script prints Unsupported KV_OFFLOAD_BACKEND: lmcache to stderr and calls exit 1.

Why nothing else catches this

There's no validation step between the YAML config and the shell script that cross-checks backend names against what the script actually supports — utils/matrix_logic only validates that kv-offload-backend.name is a non-empty string with an optional version (see test_kv_offload_backend_accepts_optional_version), so lmcache passes that check and reaches the runner, where it fails at execution time instead of at config-validation time.

Step-by-step proof

  1. Config arm: tp: 4, kv-offloading: dram, kv-offload-backend: { name: lmcache, version: "0.5.3" }, conc-list: [32, 40].
  2. Sweep matrix expansion produces two jobs (conc=32, conc=40) with KV_OFFLOAD_BACKEND=lmcache set in the environment.
  3. minimaxm3_fp4_mi355x_mtp.sh runs case "${KV_OFFLOAD_BACKEND:-}" in "") ... ;; vllm-native) ... ;; *) echo "Unsupported KV_OFFLOAD_BACKEND: lmcache" >&2; exit 1 ;; esac.
  4. Since lmcache matches neither "" nor vllm-native, the *) branch runs, and the script exits 1 before ever starting the vLLM server.
  5. Both sweep points (conc 32 and conc 40) for that arm fail deterministically on every run — this is not a flake.

Before this PR, the script had a working lmcache) branch, so this is a regression introduced specifically by this diff, not a pre-existing issue. The PR description itself only mentions "vllm-simple" DRAM offloading, so the author likely didn't intend to keep the config's lmcache arm active — that arm should either be updated to use vllm-native (matching the new script) or removed if lmcache support is being fully retired for this recipe.

Fix

Either:

  • Update the lmcache arm in configs/amd-master.yaml to use kv-offload-backend: { name: vllm-native } (dropping the version field, which is no longer needed), or
  • Restore an lmcache) branch in the script if LMCache support should be kept alongside the new vllm-native path.

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

Labels

agentx AgentX benchmarks, recipes, and infrastructure AMD

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants