Repository navigation
Conversation
host_memory_budget_bytes() divides psutil's available host memory by the number of co-located ranks (sgl-project#35540). Each rank read it when it reached a pool's sizing guard, so a rank arriving after its peers had pinned their pools saw that memory gone from `available` while still dividing by the full rank count, and the guard rejected configurations that fit. With a sidecar pool (DSA indexer, MiniMax index-K, mamba) the same happened again on the second guard. Reduce the reading to the TP group-wide minimum first. The collective is a barrier, so no rank allocates before all of them have read, and the minimum is the latest reading, which already reflects every pool the group built earlier. TP ranks of one pipeline stage build the same host pools in the same order, so the collective is matched on every rank; the world group gives no such guarantee, which is why sgl-project#35540 avoided a collective there. Single-rank and non-distributed runs keep the local reading.
alilxxey
requested review from
Ying1123,
alphabetc1,
hanming-lu,
hnyls2002,
huangtingwei9988,
hzh0425,
ispobock,
merrymercy,
xiezhq-hermann and
yizhang2077
as code owners
September 5, 2026 20:46
5 tasks done
Open
2 tasks done
4 of 5 tasks
Venkat2811
added a commit
to datacrunch-research/sglang
that referenced
this pull request
Oct 3, 2026
… not headroom // ranks (upstream sgl-project#38157) `host_memory_budget_bytes()` outside a budget scope returned `free // ranks_per_host()`, where `free` is whatever host headroom is left at the moment the rank asks. On an 8-GPU pod the ranks arrive at different times, so a rank that checks after its peers have pinned their pools sees a smaller `free`, divides it by 8 again, and is refused although every pool fits. Seen on 2026-10-01 in the Kimi-K3 L3 gates (one rank refused with "Requesting 24.77 GB but only have 22.74 GB free"); the same recipe passed on other runs because it is a race. This commit returns the whole remaining headroom (clamped at 0) for the explicit-size guard and updates the two unit tests that pinned the split. The guard now rejects a single pool that cannot fit at all; it no longer tries to catch co-located pools that jointly exceed the host, which the recipes size explicitly. HiCache auto-sizing keeps its per-rank budget: the division moves to its call site in hicache_auto_size.py, so the default-ratio path and its tests are unchanged. Upstream fix: sgl-project#38157 (issue sgl-project#38156) reads host memory once per TP group before splitting the budget. It predates sgl-project#40135's rewrite of this file and does not apply to v0.5.21, so this layer carries the one-line form instead. Drop it when sgl-project#38157 or a successor merges. Upstream: sgl-project#38157 Issue: sgl-project#38156 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Venkat2811
added a commit
to datacrunch-research/sglang
that referenced
this pull request
Oct 3, 2026
… not headroom // ranks (upstream sgl-project#38157) `host_memory_budget_bytes()` outside a budget scope returned `free // ranks_per_host()`, where `free` is whatever host headroom is left at the moment the rank asks. On an 8-GPU pod the ranks arrive at different times, so a rank that checks after its peers have pinned their pools sees a smaller `free`, divides it by 8 again, and is refused although every pool fits. Seen on 2026-10-01 in the Kimi-K3 L3 gates (one rank refused with "Requesting 24.77 GB but only have 22.74 GB free"); the same recipe passed on other runs because it is a race. This commit returns the whole remaining headroom (clamped at 0) for the explicit-size guard and updates the two unit tests that pinned the split. The guard now rejects a single pool that cannot fit at all; it no longer tries to catch co-located pools that jointly exceed the host, which the recipes size explicitly. HiCache auto-sizing keeps its per-rank budget: the division moves to its call site in hicache_auto_size.py, so the default-ratio path and its tests are unchanged. Upstream fix: sgl-project#38157 (issue sgl-project#38156) reads host memory once per TP group before splitting the budget. It predates sgl-project#40135's rewrite of this file and does not apply to v0.5.21, so this layer carries the one-line form instead. Drop it when sgl-project#38157 or a successor merges. Upstream: sgl-project#38157 Issue: sgl-project#38156 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Venkat2811
added a commit
to datacrunch-research/sglang
that referenced
this pull request
Oct 3, 2026
… not headroom // ranks (upstream sgl-project#38157) `host_memory_budget_bytes()` outside a budget scope returned `free // ranks_per_host()`, where `free` is whatever host headroom is left at the moment the rank asks. On an 8-GPU pod the ranks arrive at different times, so a rank that checks after its peers have pinned their pools sees a smaller `free`, divides it by 8 again, and is refused although every pool fits. Seen on 2026-10-01 in the Kimi-K3 L3 gates (one rank refused with "Requesting 24.77 GB but only have 22.74 GB free"); the same recipe passed on other runs because it is a race. This commit returns the whole remaining headroom (clamped at 0) for the explicit-size guard and updates the two unit tests that pinned the split. The guard now rejects a single pool that cannot fit at all; it no longer tries to catch co-located pools that jointly exceed the host, which the recipes size explicitly. The two places that use the budget to *size* a pool rather than to check one keep this rank's share through the new host_memory_budget_bytes_per_rank(): HiCache auto-sizing (hicache_auto_size.py) and the unified page-envelope pool when both --hicache-size and --hicache-ratio are 0 (pool_host/unified.py). Their behaviour is unchanged. Upstream fix: sgl-project#38157 (issue sgl-project#38156) reads host memory once per TP group before splitting the budget. It predates sgl-project#40135's rewrite of this file and does not apply to v0.5.21, so this layer carries the one-line form instead. Drop it when sgl-project#38157 or a successor merges. Upstream: sgl-project#38157 Issue: sgl-project#38156 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Venkat2811
added a commit
to datacrunch-research/sglang
that referenced
this pull request
Oct 3, 2026
… not headroom // ranks (upstream sgl-project#38157) `host_memory_budget_bytes()` outside a budget scope returned `free // ranks_per_host()`, where `free` is whatever host headroom is left at the moment the rank asks. On an 8-GPU pod the ranks arrive at different times, so a rank that checks after its peers have pinned their pools sees a smaller `free`, divides it by 8 again, and is refused although every pool fits. Seen on 2026-10-01 in the Kimi-K3 L3 gates (one rank refused with "Requesting 24.77 GB but only have 22.74 GB free"); the same recipe passed on other runs because it is a race. This commit returns the whole remaining headroom (clamped at 0) for the explicit-size guard and updates the two unit tests that pinned the split. The guard now rejects a single pool that cannot fit at all; it no longer tries to catch co-located pools that jointly exceed the host, which the recipes size explicitly. The two places that use the budget to *size* a pool rather than to check one keep this rank's share through the new host_memory_budget_bytes_per_rank(): HiCache auto-sizing (hicache_auto_size.py) and the unified page-envelope pool when both --hicache-size and --hicache-ratio are 0 (pool_host/unified.py). Their behaviour is unchanged. Upstream fix: sgl-project#38157 (issue sgl-project#38156) reads host memory once per TP group before splitting the budget. It predates sgl-project#40135's rewrite of this file and does not apply to v0.5.21, so this layer carries the one-line form instead. Drop it when sgl-project#38157 or a successor merges. Upstream: sgl-project#38157 Issue: sgl-project#38156 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Venkat2811
added a commit
to datacrunch-research/sglang
that referenced
this pull request
Oct 3, 2026
… not headroom // ranks (upstream sgl-project#38157) `host_memory_budget_bytes()` outside a budget scope returned `free // ranks_per_host()`, where `free` is whatever host headroom is left at the moment the rank asks. On an 8-GPU pod the ranks arrive at different times, so a rank that checks after its peers have pinned their pools sees a smaller `free`, divides it by 8 again, and is refused although every pool fits. Seen on 2026-10-01 in the Kimi-K3 L3 gates (one rank refused with "Requesting 24.77 GB but only have 22.74 GB free"); the same recipe passed on other runs because it is a race. This commit returns the whole remaining headroom (clamped at 0) for the explicit-size guard and updates the two unit tests that pinned the split. The guard now rejects a single pool that cannot fit at all; it no longer tries to catch co-located pools that jointly exceed the host, which the recipes size explicitly. The two places that use the budget to *size* a pool rather than to check one keep this rank's share through the new host_memory_budget_bytes_per_rank(): HiCache auto-sizing (hicache_auto_size.py) and the unified page-envelope pool when both --hicache-size and --hicache-ratio are 0 (pool_host/unified.py). Their behaviour is unchanged. Upstream fix: sgl-project#38157 (issue sgl-project#38156) reads host memory once per TP group before splitting the budget. It predates sgl-project#40135's rewrite of this file and does not apply to v0.5.21, so this layer carries the one-line form instead. Drop it when sgl-project#38157 or a successor merges. Upstream: sgl-project#38157 Issue: sgl-project#38156 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Venkat2811
added a commit
to datacrunch-research/sglang
that referenced
this pull request
Oct 3, 2026
… not headroom // ranks (upstream sgl-project#38157) `host_memory_budget_bytes()` outside a budget scope returned `free // ranks_per_host()`, where `free` is whatever host headroom is left at the moment the rank asks. On an 8-GPU pod the ranks arrive at different times, so a rank that checks after its peers have pinned their pools sees a smaller `free`, divides it by 8 again, and is refused although every pool fits. Seen on 2026-10-01 in the Kimi-K3 L3 gates (one rank refused with "Requesting 24.77 GB but only have 22.74 GB free"); the same recipe passed on other runs because it is a race. This commit returns the whole remaining headroom (clamped at 0) for the explicit-size guard and updates the two unit tests that pinned the split. The guard now rejects a single pool that cannot fit at all; it no longer tries to catch co-located pools that jointly exceed the host, which the recipes size explicitly. The two places that use the budget to *size* a pool rather than to check one keep this rank's share through the new host_memory_budget_bytes_per_rank(): HiCache auto-sizing (hicache_auto_size.py) and the unified page-envelope pool when both --hicache-size and --hicache-ratio are 0 (pool_host/unified.py). Their behaviour is unchanged. Upstream fix: sgl-project#38157 (issue sgl-project#38156) reads host memory once per TP group before splitting the budget. It predates sgl-project#40135's rewrite of this file and does not apply to v0.5.21, so this layer carries the one-line form instead. Drop it when sgl-project#38157 or a successor merges. Upstream: sgl-project#38157 Issue: sgl-project#38156 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Venkat2811
added a commit
to datacrunch-research/sglang
that referenced
this pull request
Oct 3, 2026
… not headroom // ranks (upstream sgl-project#38157) `host_memory_budget_bytes()` outside a budget scope returned `free // ranks_per_host()`, where `free` is whatever host headroom is left at the moment the rank asks. On an 8-GPU pod the ranks arrive at different times, so a rank that checks after its peers have pinned their pools sees a smaller `free`, divides it by 8 again, and is refused although every pool fits. Seen on 2026-10-01 in the Kimi-K3 L3 gates (one rank refused with "Requesting 24.77 GB but only have 22.74 GB free"); the same recipe passed on other runs because it is a race. This commit returns the whole remaining headroom (clamped at 0) for the explicit-size guard and updates the two unit tests that pinned the split. The guard now rejects a single pool that cannot fit at all; it no longer tries to catch co-located pools that jointly exceed the host, which the recipes size explicitly. The two places that use the budget to *size* a pool rather than to check one keep this rank's share through the new host_memory_budget_bytes_per_rank(): HiCache auto-sizing (hicache_auto_size.py) and the unified page-envelope pool when both --hicache-size and --hicache-ratio are 0 (pool_host/unified.py). Their behaviour is unchanged. Upstream fix: sgl-project#38157 (issue sgl-project#38156) reads host memory once per TP group before splitting the budget. It predates sgl-project#40135's rewrite of this file and does not apply to v0.5.21, so this layer carries the one-line form instead. Drop it when sgl-project#38157 or a successor merges. Upstream: sgl-project#38157 Issue: sgl-project#38156 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Venkat2811
added a commit
to datacrunch-research/sglang
that referenced
this pull request
Oct 3, 2026
… not headroom // ranks (upstream sgl-project#38157) `host_memory_budget_bytes()` outside a budget scope returned `free // ranks_per_host()`, where `free` is whatever host headroom is left at the moment the rank asks. On an 8-GPU pod the ranks arrive at different times, so a rank that checks after its peers have pinned their pools sees a smaller `free`, divides it by 8 again, and is refused although every pool fits. Seen on 2026-10-01 in the Kimi-K3 L3 gates (one rank refused with "Requesting 24.77 GB but only have 22.74 GB free"); the same recipe passed on other runs because it is a race. This commit returns the whole remaining headroom (clamped at 0) for the explicit-size guard and updates the two unit tests that pinned the split. The guard now rejects a single pool that cannot fit at all; it no longer tries to catch co-located pools that jointly exceed the host, which the recipes size explicitly. The two places that use the budget to *size* a pool rather than to check one keep this rank's share through the new host_memory_budget_bytes_per_rank(): HiCache auto-sizing (hicache_auto_size.py) and the unified page-envelope pool when both --hicache-size and --hicache-ratio are 0 (pool_host/unified.py). Their behaviour is unchanged. Upstream fix: sgl-project#38157 (issue sgl-project#38156) reads host memory once per TP group before splitting the budget. It predates sgl-project#40135's rewrite of this file and does not apply to v0.5.21, so this layer carries the one-line form instead. Drop it when sgl-project#38157 or a successor merges. Upstream: sgl-project#38157 Issue: sgl-project#38156 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Venkat2811
added a commit
to datacrunch-research/sglang
that referenced
this pull request
Oct 3, 2026
… headroom // ranks (upstream sgl-project#38157) Squash of the kimi-k3 integration PR #13 (layer 5b). host_memory_budget_bytes() outside a budget scope returned free // ranks_per_host(), where free is whatever host headroom is left at the moment the rank asks. On an 8-GPU pod the ranks arrive at different times, so a rank that checks after its peers have pinned their pools divides a smaller headroom by 8 again and is refused although every pool fits (seen 2026-10-01 in the Kimi-K3 L3 gates: "Requesting 24.77 GB but only have 22.74 GB free"). The guard now checks a pool against the whole remaining headroom; the two unit tests that pinned the split are updated. The two callers that size a pool rather than check one, HiCache auto-sizing (hicache_auto_size.py) and the unified page-envelope pool when both --hicache-size and --hicache-ratio are 0 (pool_host/unified.py), keep this rank's share through the new host_memory_budget_bytes_per_rank(); their behaviour is unchanged. Upstream fix sgl-project#38157 (issue sgl-project#38156) predates sgl-project#40135's rewrite of this file and does not apply to v0.5.21. Retire this layer when sgl-project#38157 or a successor merges. Upstream: sgl-project#38157 Issue: sgl-project#38156 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
Fixes #38156.
host_memory_budget_bytes()(#35540) splitspsutil.virtual_memory().available - 10 GiBbyranks_per_host()so that co-located ranks do not oversubscribe the host. The split is only right for a reading taken before any co-located rank allocates. Each rank readsavailablewhen it reaches the guard, and TP ranks reach it seconds apart: pinning a large host pool is slow and nothing synchronizes the ranks between pools. A rank that arrives afterkpeers have pinned their pools seesavailablereduced byk × poolwhile still dividing by the full rank count, so the peers' pools are charged twice and the guard raisesValueError: Not enough host memory availableon a host with room for all of them. With a sidecar pool (DSA indexer, MiniMax index-K, mamba) this repeats on the second guard after the first pool is pinned everywhere. On a tp8 host with 1772 GiB and--hicache-size 176(8 × 164 GiB KV + 8 × 33 GiB indexer, 86 GiB to spare) the start is intermittent; see the issue for the arithmetic.Modifications
host_memory_sync_group(): the TPcpu_groupof a multi-rank run,Noneotherwise. TP ranks of one pipeline stage build the same host pools in the same order, so a collective issued once per pool is matched on every rank; the world group gives no such guarantee (pipeline stages own different layers and build different pools), which is why [HiCache] Split the host-memory budget across co-located ranks #35540 avoided a collective there.host_memory_budget_bytes(): reduce the reading to the group-wideMINover that group before subtracting the reserve and splitting. The collective is a barrier, so no rank allocates before all of them have read, and the minimum is the latest reading, which already reflects every pool the group built earlier. Same pattern assync_fixed_hicache_size()in the same constructor (PP-groupMIN) andget_available_gpu_memory(distributed=True). Single-rank and non-distributed behavior is unchanged; the six guards that call the helper are untouched.test_mem_pool_host.py,TestHostMemoryBudget): the budget is sized from the group-wide minimum withReduceOp.MINon anint64tensor over the sync group; no collective and the local reading without a group; the sync group is the TPcpu_groupfor multi-rank runs andNonefor single-rank runs, before distributed init, and before the TP group is built. Also exercised end to end on a real 3-process gloo group with divergent per-rank readings over two pool rounds: every rank gets the same minimum-derived budget, and a single-process run with sglang's real_TPat world size 1 never issues the collective.Known limits, unchanged by this PR: with several pipeline stages on one host the stages still race each other (the window shrinks to the stage); with a TP group spanning hosts the minimum may come from a peer host, which is conservative;
ranks_per_host()still counts only the ranks of this process group, so data-parallel replicas launched as separate groups are not accounted for.Accuracy Tests
Not applicable: this changes only the host-pool sizing guards, not model forward paths.
Checklist
CI States
Latest PR Test (Base): ❌ Run #33991104005
Latest PR Test (Extra): ❌ Run #33991103784
Latest PR Test (AMD ROCm 7.2): ❌ Run #33991103904