Skip to content

fix(launch): request explicit Slurm memory for single-node jobs and fit host pools to measured DRAM - #3804

Merged
adibarra merged 29 commits into
mainfrom
fix/single-node-dram-mem
Oct 16, 2026
Merged

adibarra merged 29 commits into
mainfrom
fix/single-node-dram-mem

Conversation

@adibarra

@adibarra adibarra commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Replaces #3615 (Cam). Single-node srt-slurm jobs now make an explicit Slurm memory request from runners.yaml, available-cpu-dram-mib records what Slurm can actually allocate, and the AgentX host KV pools fit their DRAM budgets.

Launcher

  • Whole-node clusters (slurm.srt-slurm.single-node-exclusive: true, the default; every cluster except h100-cw): the job requests --mem=0, all the memory Slurm has on the node. Steps get no memory cap.
  • Shared clusters (single-node-exclusive: false; today only h100-cw): the job and its srun step request the serving GPUs' share, available-cpu-dram-mib * GPU_COUNT / gpus-per-node MiB, so a job no longer takes the whole node's memory.
  • No figure: a cluster without available-cpu-dram-mib gets no memory request, as before. Every cluster in runners.yaml has one.

The code change is gpu_share_mib and srun_options in infx/launch/drivers/srt/config.py, the mem sbatch directive, and the call in run_single_node.

Cluster DRAM figures

available-cpu-dram-mib is the lower of Slurm's RealMemory - MemSpecLimit and MemTotal less 16 GiB for the OS, over the cluster's GPU nodes (documented in configs/CONFIGS.md). Measured on 2026-10-15:

Cluster runners.yaml Measured Changed here
b200-nscale 2_047_536 2_047_536 2_048_000 → 2_047_536
mi300x-amd 1_531_436 1_531_436 1_547_820 → 1_531_436
mi325x-amd 2_305_165 2_305_165 2_321_549 → 2_305_165
mi355x-amds 3_079_397 3_079_398 3_095_781 → 3_079_397
b300-dsxe 3_976_192 3_976_192 no
h200-dgxc 1_471_356 1_471_356 no
h100-dsxe 1_915_904 1_915_904 no

Not measured: gb200-nv and gb300-nv (no single-node DRAM points here) and h100-cw (no login access; Slurm accepted its share on two cluster runs, see Verification).

Recipe and budget changes

These change what 11 config keys run. Pools now equal or fit their reported total-cpu-dram-gb:

Config Change
minimaxm3-fp4-b200-vllm-agentic-mtp Budget and SimpleCPU pool 1082 → 1073 GB (b200-nscale figure).
minimaxm3-fp4-b200-trtllm-agentic-mtp TP4 dram-utilization 0.8 → 1.0; per-rank host_cache_size 250 GiB → 268.25 GB, so the reported budget (858 → 1073 GB) matches the 1073 GB pool. It previously reported 865 GB for a 1074 GB pool. TP8 keeps 200 GiB per rank; budget 1731 → 1717 GB.
minimaxm3-fp4-b300-trtllm-agentic-mtp dram-utilization 0.8 → 1.0; per-rank host cache 388.6 → 374.5/374.75 GB, equal to the TP2/TP4 budgets (749/1499 GB) under the 3 TB policy cap. Previously the pools exceeded their reported budgets.
qwen3.5-fp8-mi355x-sglang-agentic-mtp dram-utilization 0.8 → 1.0; TP4 c64-80 hicache-size 400 → 374 GB per rank, within the 1500 GB TP4 share of the 3 TB policy. The 400 GB pools did not fit and were OOM-killed.
minimaxm3-fp8-mi300x-vllm-agentic-mtp LMCache L1 1296 → 1280 GB, equal to its budget (dram-utilization 0.7972).
qwen3.5-fp{4,8}-b200-sglang-agentic-mtp, dsv4-fp4-b200-vllm-agentic-mtp, dsv4-fp4-b200-sglang-agentic-hicache-mtp, glm5.2-fp{4,8}-b200-sglang-agentic-mtp TOTAL_CPU_DRAM_GB follows the b200-nscale figure (for example 1731 → 1717, 865 → 858); derived pools are recomputed by each recipe's own rule (for example qwen hicache-size 104 → 103 GB).

Main currently cannot build recipes for 8 of these keys: 82fc21bd6 lowered b200-nscale from 2_063_920 to 2_048_000 MiB, so their recipes' TOTAL_CPU_DRAM_GB no longer matches the matrix and launch fails with "Expected exactly one matching single-node SRT recipe". This PR updates them. Still broken on main and untouched here: qwen3.5-fp8-h100-sglang-agentic, qwen3.5-fp8-h100-sglang-agentic-mtp, minimaxm3-fp8-h100-vllm-agentic-mtp (sized for the retired h100-dgxc) and qwen3.8next-fp4-gb300-sglang-agentic-mtp.

On whole-node clusters the pools take their full budget; there is no per-GPU step cap to leave headroom under.

The 31-line header comment above minimaxm3-fp4-b200-vllm-agentic-mtp is removed under the current comment rules.

There is no perf-changelog.yaml entry; one will be added when results for these keys should be collected.

Verification

  • test_single_node_jobs_cap_dram_only_on_shared_nodes (fake Slurm, 8000 MiB, 8 GPUs per node, GPU_COUNT=4): whole-node gives job --mem=0 and no step cap; shared gives job and step 4000M; no figure gives no request.
  • Cluster runs: h100-cw (shared path, --mem=1998848M on job and step) passed twice; h200-dgxc (whole-node, #SBATCH --exclusive, #SBATCH --mem=0) passed with 40/40 requests (run 38746233127); mi355x and b200-nscale single-node AgentX passed on the integration branch.
  • Every changed config key generates its recipes, and each pool was checked against its reported budget.
  • CI suite: 2559 passed, 1 skipped. Ruff clean.

adibarra and others added 2 commits October 7, 2026 17:06
…DRAM

Port of #3615 onto the current srt driver. Every single-node srun step gets
--mem = available-cpu-dram-mib / gpus-per-node * GPU_COUNT through
SRT_SRUN_OPTIONS, so a point using half the node can no longer take more than
half its host DRAM.

The sbatch job requests the whole available-cpu-dram-mib only when
slurm.srt-slurm.single-node-exclusive is true; on shared nodes it requests the
same GPU share, so it leaves the rest of the node to other jobs. Clusters
without available-cpu-dram-mib keep their previous behavior.

Co-authored-by: Cam Quilici <cjquilici@gmail.com>
Three single-node AgentX DRAM arms pinned host KV pools at or within a few
GiB of their TP4 srun step's new --mem share of node DRAM:

- minimaxm3-fp4-b200-vllm-agentic-mtp TP4 SimpleCPU: dram-utilization
  1.0 -> 0.96, cpu_bytes_to_use and TOTAL_CPU_DRAM_GB 1082 -> 1038 GB
  (headroom 0.08 -> 41.1 GiB under 1,031,960 MiB).
- minimaxm3-fp4-b200-trtllm-agentic-mtp TP4: per-rank host_cache_size
  250 -> 240 GiB (headroom 7.8 -> 47.8 GiB under 1,031,960 MiB).
- qwen3.5-fp8-mi355x-sglang-agentic-mtp TP4 conc 64-80: per-rank
  hicache-size 400 -> 395 GB (headroom 21.5 -> 40.1 GiB under
  1,547,890 MiB).

Also trims the MiniMax-M3 B200 vLLM master-config comment to its rationale.
@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Thanks for the contribution!

  • Review: If this PR changes files owned by someone other than a repository admin or @SemiAnalysisAI/core, ask one eligible CODEOWNER to complete the latest PR_REVIEW_CHECKLIST.md before contacting a core maintainer on Slack. Follow the template exactly, including As a PR reviewer and CODEOWNER, I have reviewed this and have, so sign-off verification triggers.
  • PR verification: Sweeps run only when the PR appends an inferencex-e2e/perf-changelog.yaml entry and carries exactly one primary label: full-sweep-fail-fast (strongly recommended; canary plus per-matrix fail-fast), full-sweep-enabled (canary; matrix jobs continue after a failure), or non-canary-full-sweep-enabled (no canary or fail-fast). The modifiers all-evals, evals-only, and agentx-fast require a primary label. On fork PRs, a maintainer applies the label. See sweep labels and reuse.
  • After merging: PR authors must ensure all GitHub Actions jobs pass. Transient failures often pass on rerun; see how to rerun failed jobs.
中文

感谢你的贡献!

  • **审阅:**如果 PR 修改的文件归属于仓库管理员及 @SemiAnalysisAI/core 之外的 CODEOWNER,请先联系一位有资格的 CODEOWNER 填写最新的 PR_REVIEW_CHECKLIST.md,再通过 Slack 联系核心维护者。必须严格遵循模板,并保留 As a PR reviewer and CODEOWNER, I have reviewed this and have,才能触发签核验证。
  • **PR 验证:**只有当 PR 在 inferencex-e2e/perf-changelog.yaml 末尾追加了条目,并且恰好带有一个主标签时,才会运行扫描:full-sweep-fail-fast(强烈推荐;canary 加逐矩阵 fail-fast)、full-sweep-enabled(有 canary;矩阵任务在失败后继续运行)或 non-canary-full-sweep-enabled(无 canary,也无 fail-fast)。修饰标签 all-evals、evals-only 和 agentx-fast 必须与主标签一起使用。fork PR 的标签由维护者添加。参见扫描标签与复用。
  • **合并后:**PR 作者必须确保所有 GitHub Actions 任务通过。临时性失败通常可以通过重新运行恢复;参见重新运行失败任务的说明。

Slurm's RealMemory on b200-nscale and b300-dsxe is below available-cpu-dram-mib, so sbatch rejected the whole-node --mem. Exclusive jobs now take all the memory Slurm has, and a step that spans the whole node uses the job's memory instead of a share cap.
Measured on each cluster's GPU nodes as the lower of RealMemory - MemSpecLimit and MemTotal less 16 GiB: h100-dgxc and b300-dsxe reserve 128 GiB through MemSpecLimit, b200-nscale configures RealMemory 15.5 GiB under MemTotal, and the AMD clusters set RealMemory to MemTotal.
The lower available-cpu-dram-mib on h100-dgxc, b200-nscale and mi300x-amd
lowers the matrix total-cpu-dram-gb of their single-node DRAM offload points,
and the single-node binder rejected every recipe variant still naming the old
TOTAL_CPU_DRAM_GB. Each variant now names the new budget, and host pools sized
from it follow their recipe's rule:

- DSv4 B200 vLLM: SimpleCPU bytes per rank = budget / 8, 243.375 -> 241.5 GB.
- MiniMax-M3 B200 vLLM: cpu_bytes_to_use = budget, 1038 -> 1030 GB.
- MiniMax-M3 H100 vLLM: Mooncake segment = (budget GiB - 414) / 8 - 30,
  119 -> 105 GB.
- MiniMax-M3 MI300X vLLM: LMCache L1 = 8 x budget / 8, 1296 -> 1280 GB.
- Qwen3.5 B200 SGLang: hicache-size = (budget - TP) * 15 / TP / 31, 104 -> 103.
- Qwen3.5 H100 SGLang: hicache-size = budget / TP8 / 2 pools, 108 -> 100.

GLM-5.2 B200's 169 GB/rank pool (~1,662 GB with the DSA indexer) and the
MiniMax-M3 B200 TRT-LLM per-rank pools, which are not derived from the budget,
still fit. Every pool stays 32+ GiB under its srun step's share of node DRAM.
Validation OOM-killed the Qwen3.5 MI355X FP8 TP4 CONC 64 HiCache point
inside its srun step cap: 4 x 395 GB per-rank pools also exceeded the 3 TB
policy share of four GPUs (1500 GB). The MiniMax-M3 B300 TRT-LLM TP2/TP4
host caches (388.6 GB per rank) exceeded theirs too (750/1500 GB).

- Qwen3.5 MI355X FP8: dram-utilization 0.80 -> 1.0, hicache-size at
  CONC 64-80 395 -> 374 GB per rank (budget 1199 -> 1499 GB).
- MiniMax-M3 B300 TRT-LLM: dram-utilization 0.8 -> 1.0, TP2/TP4
  host_cache_size 388554555392 -> 374500000000/374750000000 bytes;
  TOTAL_CPU_DRAM_GB 599/1199/2399 -> 749/1499/2999.

The budgets of two formula-sized pools now name the pool itself, as the
refactor stack's '@dram' references expect: MiniMax-M3 MI300X 0.80 -> 0.7972
(TOTAL_CPU_DRAM_GB 1284 -> 1280, the LMCache L1) and MiniMax-M3 H100
0.80 -> 0.449 (1607 -> 902, eight 105 GiB Mooncake segments).
A step capped at its GPUs' share of node DRAM leaves no room for the
engine and page cache once its host pools fill the share:
qwen3.5-fp8-mi355x-sglang-agentic-mtp TP4 HiCache failed inside the step
cgroup ("Requesting 150.27 GB but only have 15.04 GB free") with its
pools at the DRAM policy limit.

Exclusive single-node jobs keep requesting --mem=0 and no longer cap
their srun steps, so pools must fit the node. On shared nodes the job and
its step still get the serving GPUs' share, which pools must fit.
…nother cluster

8c9da40 took h100-dgxc's 1,915,904 MiB from login-h100, which fronts the
DSXE H100 cluster (partitions batch_1/batch_all, /data), not the DGX Cloud
cluster runners.yaml calls h100-dgxc (partition hpc-gpu-1, /mnt/nfs). The
128 GiB MemSpecLimit that commit's message attributes to h100-dgxc is the
DSXE cluster's; h100-dgxc was not measured.

Restore 2,063,837 MiB and undo the H100 values derived from the wrong one:
- Qwen3.5 H100 SGLang: hicache-size 100 -> 108 GB, TOTAL_CPU_DRAM_GB
  1607 -> 1731.
- MiniMax-M3 H100 vLLM: dram-utilization 0.449 -> 0.80, Mooncake segment
  105 -> 119 GB, TOTAL_CPU_DRAM_GB 902 -> 1731.
Every h100-dgxc and h100 matrix row and H100 recipe again matches main.

CONFIGS.md gives the Slurm rule as how to measure the field, not as what
every recorded value is. runners.yaml h100-dgxc has no registered runners;
the DSXE H100 runners (cluster:h100-dsxe) have no runners.yaml entry.
adibarra added a commit that referenced this pull request Oct 9, 2026
…alidate/refactor-stack

#3804 restores h100-dgxc to 2,063,837 MiB and main's H100 recipe values.
Keep the stack's '@dram' H100 recipes and its MiniMax-M3 H100
dram-utilization 0.4725 (the ~119 GiB Mooncake segment), so every H100
bound recipe matches refactor/recipe-bundles again.
Condense the driver's DRAM cap comment, the available-cpu-dram-mib doc and
the MiniMax-M3 B200 dram-utilization comment; drop the test comment that
repeated the driver's and name the test for all three of its cases; fix the
MI300X LMCache recipe comment, whose L1 is now the whole 1280 GB budget
rather than shares of 1284 GB.

精简驱动中的 DRAM 上限注释、available-cpu-dram-mib 文档和 MiniMax-M3 B200
的 dram-utilization 注释;删除重复驱动注释的测试注释,并按三种情形重命名该
测试;修正 MI300X LMCache 配方注释:其 L1 现为完整的 1280 GB 预算,而非
1284 GB 的分摊份额。
@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

inferencex-e2e/infx diff: +48 -6

Generally most InferenceX submissions don't need to add changes to the infx package. On the off chance they do, generally the diff is small. Please prompt your agent to clean your PR to keep your diff to only necessary & minimal changes too. We have an increased code quality bar for changes to the infx package.

中文

大多数 InferenceX 提交通常不需要修改 infx 包。即使确实需要,改动通常也很小。请让你的 agent 清理 PR,使改动只包含必要且最小的内容。infx 包的改动有更高的代码质量要求。

@claude

claude Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @adibarra's task in 1m 36s —— View job


Review of PR #3804

  • Gather context
  • Review launch driver changes (srt/__init__.py, srt/config.py, test)
  • Review runners.yaml, master configs and recipes
  • Post findings

Summary: The driver change looks right. Exclusive jobs get --mem=0 with no step cap, and shared jobs get --mem=<share>M on both the job and the step. A cluster without available-cpu-dram-mib gets no memory request, and srun_options still returns None when there is nothing to pass. The recipe TOTAL_CPU_DRAM_GB values I checked match the new matrix arithmetic: b200-nscale TP4 0.96 → 1030, b300 TRT TP2/TP4/TP8 → 749/1499/2999, and mi300x 0.7972 → 1280. I left 2 inline comments:

  • 🔴 BLOCKING: amd-master.yaml and nvidia-master.yaml changed, but nothing was added to perf-changelog.yaml. Without it no sweep runs, and the changes to results (dram-utilization and pool sizes) go unvalidated.
  • 🟡 WARNING: The only clusters that now get a non-zero --mem are h100-cw and h200-cw (both 1_998_848), and neither value was re-measured in this PR. If either is above RealMemory - MemSpecLimit, every TP8 point there fails at sbatch. That is the same failure your Risk section describes.

Note: The PR body's headroom table says the minimaxm3 b200 vLLM pool goes to 1038 GB. The recipe actually sets 1030 GB, which is what 2,047,536 MiB × 0.96 × 4/8 works out to. The recipe is right; only the body is out of date.

I couldn't run the tests: running pytest through uv run needs approval in this environment.

agentic-coding:
- dram-utilization: 0.80
# CONC 64-80 HiCache pools take the 3 TB policy share per GPU.
- dram-utilization: 1.0

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.

🔴 BLOCKING: The master config files changed, but inferencex-e2e/perf-changelog.yaml was not updated. When you change inferencex-e2e/configs/amd-master.yaml or inferencex-e2e/configs/nvidia-master.yaml, you need to add an entry to inferencex-e2e/perf-changelog.yaml describing the change.

Why it matters: Without the entry, no sweep runs for this PR. These edits change results: dram-utilization changes on qwen3.5-mi355x, minimaxm3-mi300x, minimaxm3-b300-trtllm and minimaxm3-b200-vllm, plus the recipe pool sizes. The new --mem request is also never tested on a cluster. The PR body says this was deliberate, but merging these changes would leave the published curves out of sync with the configs that produced them.

Fix: Add an entry at the end of perf-changelog.yaml that lists the affected config keys and links #3804. Then apply full-sweep-fail-fast.

Comment on lines +54 to +56
step_mib = job_mib = config.gpu_share_mib(run.cluster, request.gpu_count)
if run.srt.single_node_exclusive and job_mib is not None:
step_mib, job_mib = None, 0

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: This change sends a real non-zero --mem only to the shared clusters (single-node-exclusive: false): h100-cw and h200-cw, both at available-cpu-dram-mib: 1_998_848. Those two values were not re-measured here. The PR re-measured b200-nscale, b300-dsxe and mi3xx, which all take --mem=0 now, so the new values never reach sbatch there.

Why it matters: A TP8 point on h100-cw or h200-cw now asks for --mem=1998848M on both the job and the srun step. Your Risk section already describes what happens if that is above RealMemory - MemSpecLimit: sbatch fails with "Memory specification can not be satisfied", and every TP8 point on those clusters fails before it starts. TP4 requests half that, which leaves more margin.

Fix: Run scontrol show node on a GPU node in each cluster and check that RealMemory - MemSpecLimit ≥ 1_998_848. If it isn't, re-measure both values the same way you did for the other clusters. Alternatively, run one TP8 smoke on h100-cw or h200-cw before merging.

@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 findings (none reported this run), I also checked the gpu_share_mib floor-to-zero scenario flagged as a candidate issue in config.py:179 — it would require available-cpu-dram-mib/gpus-per-node*GPU_COUNT to floor to 0, but every cluster's available-cpu-dram-mib in runners.yaml is in the hundreds of thousands to millions of MiB against gpus-per-node of 4 or 8, so this cannot occur with current config data.

Extended reasoning...

The diff adds a gpu_share_mib() DRAM-capping calculation and wires it into srun/sbatch mem options for single-node srt-slurm jobs, with matching test coverage (test_single_node_jobs_cap_dram_only_on_shared_nodes) that exercises launch() and asserts on emitted SRT_SRUN_OPTIONS and sbatch directives per the repo's test-quality conventions. No security-sensitive surface (auth, crypto, permissions) is touched; it is cluster-config and memory-accounting logic plus config/doc value updates matching the PR's documented headroom table.

# Conflicts:
#	inferencex-e2e/configs/nvidia-master.yaml
Main now keeps infx free of explanatory comments and bans header comments in configs. Remove the driver's DRAM cap comment, which CONFIGS.md already documents, and the MiniMax-M3 B200 vLLM master-config header.
# Conflicts:
#	inferencex-e2e/infx/launch/drivers/srt/config.py
Main's native single-node power telemetry (#3781) stages the cluster's GPU exporter for every non-eval single-node point and stops preparation when the cluster record has none, so the DRAM-cap driver test's ad hoc cluster now needs default_gpu_exporter to reach submission.
# Conflicts:
#	inferencex-e2e/configs/runners.yaml
… budget

b200-nscale runs single-node jobs whole-node (--mem=0), so the TP4 pools no
longer need headroom under a per-GPU step cap. The vLLM SimpleCPU pool and the
TRT-LLM per-rank host cache now take the full TP4 budget (dram-utilization 1.0,
1073 GB), and the TRT TP4 rows report the budget their pool actually uses
instead of a nominal 0.8 one.
@adibarra adibarra changed the title fix(launch): cap single-node srun steps at their GPUs' share of node DRAM fix(launch): request explicit Slurm memory for single-node jobs and fit host pools to measured DRAM Oct 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant