fix(core): preserve workspace layout state after allocation overflow - #297
Open
DuncanBetts wants to merge 1 commit into
Open
DuncanBetts wants to merge 1 commit into
DuncanBetts wants to merge 1 commit into
Conversation
DuncanBetts
marked this pull request as ready for review
September 20, 2026 13:07
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.
fix(core): preserve workspace layout state after allocation overflow
Problem and scope
Related Issue: none; this draft is submitted for scope review before runtime verification.
WorkspaceLayoutBuilder::allocandalloc_bytescurrently commit the aligned cursor beforechecking whether the requested allocation end is representable:
If
checked_addthrows, the builder is left partially modified. Starting from cursor 3, a failedSIZE_MAX-byte allocation aligned to 8 leaves the cursor at 8. If the exception is caught, thenext one-byte allocation ends at 9 rather than 4.
This change gives workspace allocation the same commit-after-validation behavior already used by
LayoutBuilder::add. Its scope is Core's dry-run workspace bookkeeping. Successful workspaceestimates, real arena allocation, CUDA work, model behavior, and the public Engine API are
unchanged.
Implementation
WorkspaceLayoutBuildernow contains aLayoutBuilderand delegates nonempty allocationaccounting, temporary scopes, and final peak alignment to it.
LayoutBuilder::addcomputes thealigned offset locally and updates its cursor only after the checked end calculation succeeds.
A failed allocation therefore leaves both cursor and peak unchanged.
The workspace-specific interface remains intact:
allocreturns a null-backed Tensor with the requested dtype and shape;alloc_bytesreturns a null-backed span;WorkspaceLayoutBuilder::Scopebecomes an alias forLayoutBuilder::Scope, removing the secondmovable scope-guard implementation. This is composition inside Core; it adds no inheritance,
virtual dispatch, callback, heap allocation, or new allocator abstraction.
The production diff removes 30 net lines (37 deletions, 7 additions). The focused test adds
96 lines and its registration adds three. Total repository LOC grows because the failure and
scope contracts receive direct behavioral coverage; the production rule itself has one owner.
For valid allocation sequences, the computed peak is intended to remain byte-for-byte identical.
Expected VRAM saving is 0 bytes, expected RAM saving is 0 bytes, and no GPU work changes. Host-side
delegation may compile away or may have a very small cost; it has not been benchmarked, so this PR
makes no CPU, binary-size, memory, or latency claim.
Verification
Performed locally with GCC 15.2.0:
All three checks passed.
The new
ninfer_layout_testcovers the overflow reproduction and verifies that the nextallocation ends at byte 4. It also covers empty and aligned peaks, Tensor shape and null backing,
nested and moved scope restoration, exception unwinding, zero-byte scratch, invalid alignment,
maximum representable size, and allocation-end/cursor/final-alignment overflow.
I also verified the change in a container based on
nvidia/cuda:13.1.2-devel-ubuntu24.04. The container configured a Releasesm_120abuild withtests enabled, applications and benchmarks disabled, and then built:
It ran the focused tests with:
ctest --test-dir /build --output-on-failure \ -R '^(ninfer_layout_test|ninfer_qwen3_5_runtime_mechanisms_test)$'Both selected tests passed (2/2). They are host-side tests, and the container ran without GPU
passthrough.
ninfer_layout_testcovers the overflow recovery and layout/scope boundaries;ninfer_qwen3_5_runtime_mechanisms_testchecks the existing model-side workspace-planning behavior.Notes for reviewers
implementations perform the same
align_up(cursor, alignment), checked addition, peak update,and final peak alignment.
path that catches this workspace overflow and continues with the same estimator. In ordinary
execution the exception therefore aborts planning and the partially advanced cursor dies with
the builder. The fix establishes a strong failure-state guarantee for future recovery/reuse; it
is not presented as a currently observed inference failure.
calculation, before a workspace allocation or device access, so it does not directly corrupt
memory.
aligned offset while the peak remains unchanged. A later successful allocation can incorporate
that phantom padding into the peak. It cannot move the cursor backward or produce an undersized
workspace estimate through this failure mode.
alignment - 1bytes forthe failed request when alignment itself succeeds: normally at most 255 bytes for the default
256-byte workspace alignment, though callers can request another power-of-two alignment.
builder, the inflated peak could allocate a little more VRAM than necessary, trigger a false
capacity/OOM decision at a tight boundary, or contribute to a later arithmetic overflow. These
are plausible consequences of future caught reuse, not failures observed in current inference.
workspace allocation methods would fix this reproduction, but it would retain the duplicate
scope, cursor, peak, alignment, and overflow implementation that allowed the rules to diverge.
Composition reuses the already-correct Core implementation without broadening ownership.
LayoutBuilder::add. That method rejects emptyregions; workspace scratch treats zero bytes as a no-op and must continue ignoring its otherwise
unused alignment.
allocconstructs its dry-run Tensor and computesits byte size before calling
LayoutBuilder::add. If both Tensor sizing and alignment areinvalid, the Tensor-size error may be observed first. This does not affect valid planning.
LayoutBuilder::Scope. Repository search found no consumer spellingWorkspaceLayoutBuilder::Scopeoutside its own method definition; callers useauto.NInfer has no installed/exported C++ SDK.
size_tcounters;its
LayoutBuildermember stores the same two counters. No heap or device allocation is added.LayoutBuilder::addreturns offset/size/alignmentmetadata that this adapter does not need. Optimized-code effects were not inspected or measured.
WorkspaceArenaandDeviceArenaretain their currentcapacity, pointer, and CUDA lifetime behavior.
LayoutBuilder::add; check thezero-byte early return; then read
test_workspace_boundariesbefore the broader scope tests.Not verified / limitations
origin/masterandorigin/devwere fetched immediately before submission.mastermatched thecandidate baseline, and commits ahead on
devdid not touch the affected files.