Skip to content

[AMD] [AgentX] Add MiniMax-M3 MXFP8 EAGLE3 on MI325X - #2579

Open
cquil11 wants to merge 4 commits into
mainfrom
agent/mi325x-minimaxm3-fp8-agentx
Open

[AMD] [AgentX] Add MiniMax-M3 MXFP8 EAGLE3 on MI325X#2579
cquil11 wants to merge 4 commits into
mainfrom
agent/mi325x-minimaxm3-fp8-agentx

Conversation

@cquil11

@cquil11 cquil11 commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • add a vLLM v0.27.1 MiniMax-M3 MXFP8 AgentX recipe for MI325X using the PoR EAGLE3-GQA draft and golden synthetic acceptance length 2.78
  • begin with an isolated TP8/EP8 c18 LMCache MP gate near the measured resident-memory knee
  • collect the vLLM metrics endpoint explicitly; router metrics are also explicit for later DP-attention rows

Mooncake is intentionally excluded because its AMD TCP store previously collapsed mid-replay in run 30517360090. Once the LMCache gate is functional, this PR will launch the complete broad resident fast matrix without fail-fast or premature pruning.

Validation

  • bash -n
  • YAML parsing
  • matrix generation: exactly one TP8/EP8 c18 LMCache gate with a convention-derived 599 GB DRAM limit
  • git diff --check

@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 关于重新运行失败任务的文档

1 similar comment
@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 AMD agentx AgentX benchmarks, recipes, and infrastructure agentx-fast Run AgentX throughput with 1 warmup request per lane and a 20-minute profile; not reusable sweep-enabled labels Aug 12, 2026
Comment on lines +68 to +96
lmcache)
require_agentic_kv_offload_backend lmcache
LMCACHE_VERSION="0.5.3"
LMCACHE_ROCM_INDEX="https://github.com/LMCache/LMCache/releases/expanded_assets/v${LMCACHE_VERSION}-rocm"
agentic_pip_install --quiet --no-cache-dir --no-deps \
"sortedcontainers==2.4.0" \
"opentelemetry-exporter-prometheus==0.61b0" \
"cupy-rocm-7-0==14.1.1" \
"lmcache==${LMCACHE_VERSION}" --find-links "$LMCACHE_ROCM_INDEX"
python3 -c \
"import cupy; import lmcache.integration.vllm.lmcache_mp_connector; import opentelemetry.exporter.prometheus" \
>/dev/null

LMCACHE_N_SERVERS="$TP"
LMCACHE_L1_SHARD_GB=$((TOTAL_CPU_DRAM_GB / LMCACHE_N_SERVERS))
if [ "$LMCACHE_L1_SHARD_GB" -lt 1 ]; then
echo "Error: LMCache DRAM budget is less than 1 GB per TP rank." >&2
exit 1
fi

LMCACHE_SERVER_URLS=()
LMCACHE_HTTP_PORTS=()
LMCACHE_LOGS=()
: > "$RESULT_DIR/lmcache_command.txt"
for shard in $(seq 0 $((LMCACHE_N_SERVERS - 1))); do
shard_port=$((5555 + shard))
shard_http_port=$((8080 + shard))
shard_log="${LMCACHE_LOG%.log}_${shard}.log"
LMCACHE_CMD=(

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.

🟡 sweep:supported-transfer-mode lmcache_driven — this ~30-line LMCache-MP spawn block (pinned pip installs, import probe, per-shard lmcache server loop, wait_for_ready loop, kv-transfer-config JSON) in minimaxm3_fp8_mi325x_mtp.sh:68-96 is copied verbatim from minimaxm3_fp4_mi355x_mtp.sh. Factor it into a benchmark_lib.sh helper parameterized by TP/DRAM budget via env vars per AGENTS.md, so the /dev/shm guard drift (flagged separately) can't recur.

Extended reasoning...

What/where: benchmarks/single_node/agentic/minimaxm3_fp8_mi325x_mtp.sh (lines 68-96, the lmcache) case branch) reproduces the LMCache-MP server-spawn logic near-verbatim from minimaxm3_fp4_mi355x_mtp.sh: the same pinned pip install set (sortedcontainers==2.4.0, opentelemetry-exporter-prometheus==0.61b0, cupy-rocm-7-0==14.1.1, lmcache==0.5.3 from the same ROCm find-links index), the same python3 -c "import cupy; import lmcache..." probe, the same per-TP-rank for shard in $(seq 0 ...) loop spawning lmcache server on ports 5555+shard / 8080+shard with identical flags (--l1-init-size-gb 10 --l1-read-ttl-seconds 7200 --chunk-size 256 --max-workers 2 --eviction-policy LRU --supported-transfer-mode lmcache_driven), the same wait_for_ready health-check loop, and a byte-identical kv-transfer-config JSON payload for LMCacheMPConnector. A third variant of the same spawn logic exists in dsv4_fp4_mi355x_vllm_mtp.sh.

Why this is the AGENTS.md convention, not a nitpick about style: AGENTS.md states verbatim: "Shared benchmark Bash behavior belongs in benchmark_lib.sh, with parameters passed through environment variables." The repo already follows this for wait_for_ready, append_command, write_command, stop_background_process_tree, and require_agentic_kv_offload_backend — all live in benchmark_lib.sh and are sourced by every recipe script. The LMCache-MP spin-up block is exactly the kind of behavior that convention targets: it is identical business logic (spawn N lmcache servers sized to a DRAM budget, wait for them, assemble the connector config) that only varies by TP-derived variable names across call sites, yet it was pasted into a third file instead of being lifted into a helper like start_lmcache_mp_servers "$TP" "$TOTAL_CPU_DRAM_GB".

Proof this isn't hypothetical — it has already caused drift: Compare the two copies directly. minimaxm3_fp4_mi355x_mtp.sh's copy of this block includes a /dev/shm capacity guard before spawning the LMCache servers (added later, per the sibling PR discussion referenced in the companion bug_001 finding) — a check that this new minimaxm3_fp8_mi325x_mtp.sh copy does not have. Concretely:

  1. A future engineer fixes a resident-memory OOM in the fp4 script by adding a /dev/shm size check ahead of the spawn loop.
  2. They test only that script, ship the fix, and move on — there is no single source location that would have forced them to touch the fp8 copy too.
  3. This fp8 script (and the dsv4 script) keep spawning lmcache server processes without that guard, so the same OOM class can resurface here even though it was "fixed" upstream.
  4. The next maintainer, unaware a fix already exists, either rediscovers the bug from scratch or re-implements a slightly different guard in a fourth location.

Why nothing today prevents this: there is no benchmark_lib.sh entry point for LMCache-MP spin-up (wait_for_ready is shared, but the surrounding spawn/probe/config-assembly logic is not), so each new MTP+LMCache recipe script is authored by copy-pasting the nearest sibling rather than calling a shared function. bash -n and the PR's own validation steps (YAML parsing, matrix generation) cannot catch behavioral drift between copies — only a shared helper can.

Suggested fix: extract a benchmark_lib.sh function, e.g. start_lmcache_mp_servers <n_servers> <total_dram_gb> <log_prefix>, that performs the pip install, import probe, per-shard spawn loop, wait_for_ready loop, and returns/exports the assembled kv-transfer-config JSON (plus any capacity guard). All LMCache-MP recipe scripts — this one, minimaxm3_fp4_mi355x_mtp.sh, and dsv4_fp4_mi355x_vllm_mtp.sh — would then call the same helper with TP/DRAM-budget parameters passed as arguments or env vars, matching the pattern already used for require_agentic_kv_offload_backend and friends. This is a code-quality/DRY finding, not a functional bug in this PR, so it doesn't block merge.

Comment on lines +81 to +86
LMCACHE_N_SERVERS="$TP"
LMCACHE_L1_SHARD_GB=$((TOTAL_CPU_DRAM_GB / LMCACHE_N_SERVERS))
if [ "$LMCACHE_L1_SHARD_GB" -lt 1 ]; then
echo "Error: LMCache DRAM budget is less than 1 GB per TP rank." >&2
exit 1
fi

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.

🟡 Missing /dev/shm capacity check before sizing LMCache L1 shards: LMCACHE_L1_SHARD_GB in minimaxm3_fp8_mi325x_mtp.sh (lines 81-86) is derived straight from TOTAL_CPU_DRAM_GB/LMCACHE_N_SERVERS with only a >=1GB-per-shard sanity check. The sibling script this was copied from, minimaxm3_fp4_mi355x_mtp.sh (lines 104-111), additionally probes free /dev/shm via df -BG --output=avail /dev/shm, caps the request at 90% of that, and exits early with a clear error — this new script drops that guard, so an oversized DRAM budget will instead surface as an unclear failure deep in lmcache/tmpfs allocation and risks exhausting shared host memory for co-located jobs.

Extended reasoning...

What the bug is. minimaxm3_fp8_mi325x_mtp.sh computes the per-shard LMCache L1 size directly from the operator-supplied TOTAL_CPU_DRAM_GB env var and the TP-derived LMCACHE_N_SERVERS, then only checks that the resulting per-shard value is at least 1 GB:

LMCACHE_N_SERVERS="$TP"
LMCACHE_L1_SHARD_GB=$((TOTAL_CPU_DRAM_GB / LMCACHE_N_SERVERS))
if [ "$LMCACHE_L1_SHARD_GB" -lt 1 ]; then
    echo "Error: LMCache DRAM budget is less than 1 GB per TP rank." >&2
    exit 1
fi

There is no check that the aggregate requested size (LMCACHE_L1_SHARD_GB * LMCACHE_N_SERVERS, effectively TOTAL_CPU_DRAM_GB) actually fits in the host's /dev/shm.

Where this diverges from the pattern it was copied from. The sibling recipe benchmarks/single_node/agentic/minimaxm3_fp4_mi355x_mtp.sh (same EAGLE3/LMCache MP setup, same lmcache_driven transfer mode) has exactly the missing guard at lines 104-111, run before sizing the shards:

SHM_FREE_GB=$(df -BG --output=avail /dev/shm 2>/dev/null | tail -1 | tr -dc '0-9')
if [ -n "$SHM_FREE_GB" ] && [ "$SHM_FREE_GB" -gt 0 ]; then
    SHM_CAP_GB=$((SHM_FREE_GB * 90 / 100))
    if [ "$LMCACHE_L1_SIZE_GB" -gt "$SHM_CAP_GB" ]; then
        echo "Error: LMCache L1 ${LMCACHE_L1_SIZE_GB} GB exceeds 90% of free /dev/shm (${SHM_CAP_GB} GB)." >&2
        exit 1
    fi
fi

I verified this block still exists in the sibling script at those exact lines. This PR's new script reuses the same lmcache server ... --supported-transfer-mode lmcache_driven construction, per-shard loop, and wait_for_ready polling as the sibling, but the /dev/shm probe was dropped when the LMCache block was ported over.

Why this matters. LMCache's lmcache_driven L1 backing store for the MP connector lives in tmpfs (/dev/shm), which is a resource shared with the OS and any co-located processes on the runner — it is not the same pool as TOTAL_CPU_DRAM_GB (regular process/page-cache DRAM). Without the cap, a TOTAL_CPU_DRAM_GB value that's large relative to the runner's actual /dev/shm size will pass the trivial >= 1GB per shard check and proceed to spawn lmcache server processes that attempt to allocate more tmpfs than is physically backed. This recipe's own PR description flags a convention-derived ~599 GB DRAM budget (from dram-utilization: 0.20), which is a plausible trigger if the mi325x-amds runner's /dev/shm is sized smaller than that.

Concrete walk-through. Suppose the runner has /dev/shm sized at 400 GB (smaller than the ~599 GB budget) and TP=8:

  1. LMCACHE_L1_SHARD_GB = 599 / 8 = 74 GB/shard — passes the >= 1 check trivially.
  2. Eight lmcache server ... --l1-size-gb 74 ... processes are launched in parallel, each attempting to reserve tmpfs backing.
  3. Aggregate requested tmpfs (~592 GB) exceeds the 400 GB actually available; some shard(s) fail deep inside lmcache's tmpfs allocation, or the host itself experiences memory pressure from over-committing shared memory.
  4. Failure is only detected later via wait_for_ready's health-check timeout / dead-PID check against LMCACHE_LOGS, producing an opaque timeout instead of the sibling script's immediate, actionable "Error: LMCache L1 ... exceeds 90% of free /dev/shm ..." message.
  5. Worse, if other jobs are co-located on the same host, the oversized tmpfs request can degrade or OOM those unrelated jobs rather than just this one.

How to fix. Port the sibling's /dev/shm probe verbatim into this script, before (or alongside) the existing per-shard-minimum check: query df -BG --output=avail /dev/shm, cap the requested aggregate at 90% of that, and exit with a clear error if the budget exceeds the cap.

Severity. This is a robustness/diagnostics regression relative to the sibling pattern, not a correctness bug in the benchmark data itself — when the guard would fire, the run fails either way, just with a much worse error message and added risk to co-located jobs. Given the recipe deliberately uses a conservative dram-utilization: 0.20, the guard is unlikely to trip in practice on the intended runner. Recommending nit.

@github-actions

Copy link
Copy Markdown
Contributor

@cquil11 cquil11 added non-canary-full-sweep-enabled Run the full sweep without the canary gate (full search space, no trim) and removed sweep-enabled labels Aug 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

@cquil11 cquil11 removed non-canary-full-sweep-enabled Run the full sweep without the canary gate (full search space, no trim) agentx-fast Run AgentX throughput with 1 warmup request per lane and a 20-minute profile; not reusable labels Aug 12, 2026
@cquil11
cquil11 force-pushed the agent/mi325x-minimaxm3-fp8-agentx branch from 024a4d4 to 7eeea89 Compare August 12, 2026 21:19
@cquil11 cquil11 added the non-canary-full-sweep-enabled Run the full sweep without the canary gate (full search space, no trim) label 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

Labels

agentx AgentX benchmarks, recipes, and infrastructure AMD non-canary-full-sweep-enabled Run the full sweep without the canary gate (full search space, no trim)

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant