Fix/output reorder index space - #27705
Open
thc1006 wants to merge 3 commits into
Open
Conversation
thc1006
requested review from
a team,
JohannesGaessler and
ggerganov
as code owners
August 25, 2026 15:01
thc1006
force-pushed
the
fix/output-reorder-index-space
branch
from
August 25, 2026 15:04
345f883 to
75068ca
Compare
|
Hi @thc1006, thanks for your contribution! Per our contribution guidelines, the automated PR checker found the following issue(s) that need your attention:
Please note that maintainers reserve the right to make final decisions on PRs. If you believe there is a mistake, please comment below. |
thc1006
marked this pull request as ready for review
August 25, 2026 15:10
thc1006
force-pushed
the
fix/output-reorder-index-space
branch
from
August 26, 2026 11:26
75068ca to
d3a3869
Compare
Previously, `output_reorder()` applied `output_swaps` (an output-row permutation) to all enabled output buffers. However, `embd_layer_inp` and unmasked `embd_nextn` are token-indexed rather than output-indexed. Applying an output permutation to them corrupts the row-to-token association whenever `n_outputs < n_tokens` and outputs are out of order. We cannot simply disable the swap for these buffers. Their extraction paths write in `ubatch` order, but downstream consumers read by `batch` index. These orders diverge when a batch spans multiple ubatches or interleaves sequences, so removing the swap entirely worsens the ordering. This commit introduces `token_swaps` to correctly reorder token-indexed buffers into batch order: - Tracks all token indices (`tok_ids`) in the allocator. - Builds a `token_swaps` permutation in `decode()` alongside `output_swaps`. - Applies `token_swaps` to token-indexed rows and `output_swaps` to output-indexed rows. - Reuses cleared vectors to avoid per-decode allocations. Also adds `test_output_reorder_token_rows` to `test-llama-archs.cpp` to verify bit-exact row ordering across ubatch splits. Found while investigating ggml-org#27572. Assisted-by: Claude Opus Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com>
`cparams.embeddings_nextn_masked` dictates the layout for the NEXT
evaluation. If a caller toggles this flag after a decode but before
reading the buffer, `output_reorder` and `get_embeddings_nextn_ith`
apply the new mode's indexing logic and row swaps to the old mode's
allocated data.
This commit fixes the mismatch by snapshotting the layout state:
- Adds `embd_nextn_masked_output` to `llama_context`, recorded inside
`output_reserve` where the buffer size is physically decided.
- Updates the reorder loops and getters to use this snapshot so the
row domain always matches the allocation.
Also adds two safety assertions:
- Bounds checks in the token-row swap loop. Overrunning `embd_nextn`
silently corrupts neighboring regions within the monolithic
`buf_output` allocation, which ASan cannot detect.
- Width check in `extract_layer_inputs` ensuring `row_floats == n_embd`,
making explicit the coupling that `output_reorder` swaps exactly
`n_embd` floats at a time.
Adds extensive regression tests in `test-llama-archs.cpp`:
- Tests explicit mode flips ("masked decode then set unmasked", etc.)
using nearest-neighbor row matching.
- Sweeps batch shapes to prove `output_swaps` and `token_swaps` are
independent (e.g. when selected outputs are already sorted, making
`output_swaps` empty while `token_swaps` still needs to regroup).
- Adds `six_token_case` to assert correctness on longer permutation cycles.
Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com>
thc1006
force-pushed
the
fix/output-reorder-index-space
branch
from
August 26, 2026 19:59
d3a3869 to
a758945
Compare
`output_swaps` (and now `token_swaps`) applies a previous decode's permutation to the current outputs. A caller executing an `encode()` could previously inherit a stale permutation from a prior `decode()`. Because `buf_output` is a single allocation and `encode` outputs may be fewer than `decode` outputs, applying stale row swaps can index past the current logits and silently overwrite neighboring regions. This commit clears both permutations in `encode()` (symmetric to `decode()`), closing their lifecycle. Also fixes six testing issues in `tests/test-llama-archs.cpp`: - `probe_shapes` now counts and asserts failures rather than skipping silently. - `six_token_case` now returns failure correctly when a context fails. - The nearest-neighbor row matcher no longer accepts NaN as row 0. - `test_output_reorder_nextn_rows` uses `llama_model_n_embd_out` (matching production) instead of `llama_model_n_embd`. - Tests now skip explicitly via `SKIPPED` without a CPU backend. - The masked nextn test now validates it compared the expected number of rows rather than passing if all rows came back empty. Additionally, comments were rewritten to conform to project style (concise, one line per invariant). Assisted-by: Claude
thc1006
force-pushed
the
fix/output-reorder-index-space
branch
from
August 26, 2026 20:07
a758945 to
1cf3c1b
Compare
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.
Overview
This PR fixes two related indexing issues in
output_reorder()and closes a lifecycle bug for output permutations that could lead to silent memory corruption.embd_layer_inpand unmaskedembd_nextnare token-indexed, but upstream appliedoutput_swaps(an output-row permutation) to them. This scrambles hidden states whenn_outputs < n_tokens. This PR introducestoken_swapsto correctly align ubatch-written rows into batch order.cparamsdescribes the layout for the next evaluation. If a caller togglescparams.embeddings_nextn_maskedafter a decode but before reading the buffer, the getters and reorder loops apply the new mode's logic to the old layout. This PR snapshots the layout state intollama_contextto guarantee the row domain always matches the allocation.encode()did not clearoutput_swaps. If anencode()followed adecode()that regrouped tokens,llama_get_logits_ithwould apply a stale permutation to the newly generated logits. Becausebuf_outputis a single shared allocation (logits | embd | embd_nextn | ...), this stale swap could index past the logical bounds of the new logits and blindly overwrite neighboring regions within the same physical allocation. Bothoutput_swapsandtoken_swapsare now explicitly cleared duringencode().Additional information
1. Layout,
token_swaps, and Allocator UpdatesThe extraction paths for token-indexed buffers write in
ubatchorder, while downstream consumers read bybatchindex. These orders diverge when the splitter regroups.token_swapscorrectly aligns the rows.embd_nextnoutput_swaps(unchanged)embd_nextntoken_swaps(new)embd_layer_inptoken_swaps(new)embd, samplingoutput_swaps(unchanged)To support
token_swaps,src/llama-batch.{cpp,h}is updated.llama_batch_allocrnow tracks and exposestok_ids(the batch indices for every token, alongside the existingout_idsfor outputs). This givesoutput_reorderthe necessary index mapping to correctly permute the token rows.2. Layout State Snapshot & Assertions
embd_nextn_masked_outputtollama_context. It is recorded insideoutput_reserveand read at the three sites that previously relied oncparams. This prevents silent data corruption if the mode is flipped between decode and read.extract_layer_inputs: Assertsrow_floats == model.hparams.n_embdto explicitly enforce the coupling thatoutput_reorderswaps exactlyn_embdfloats at a time.3. Regression Tests & Test Suite Cleanups
Added ~500 lines of tests in
tests/test-llama-archs.cpp. The core row-domain and mode-flip tests fail on the base commit and pass with these changes:test_output_reorder_token_rows: Tests partial-output and all-output token row alignments using pure embeddings for bit-exact comparisons.test_output_reorder_nextn_rows: Tests explicit mode flips using nearest-neighbor row matching.probe_shapes: Sweeps 160 batch shapes to cover cases where selected outputs are already sorted (makingoutput_swapsempty), but token rows still need regrouping. Re-gating the existing swap cannot fix this.n_embd_outwidth correctly, etc.) and condensed verbose comments to adhere strictly to the project's 1-2 line descriptive standard.Note on
CONTRIBUTING.md:35(Missing test forencode()): Theencode()lifecycle fix (Overview point 3) lacks a fail-before test. Writing one requires an architecture with an encoder that populates these specific token-indexed buffers, which currently does not exist in the test suite. T5 was tested and passes, confirming the fix does not break existing encoder paths, but a strict fail-before test cannot be structurally constructed here.4. Performance
Measurements via
-p 512 -n 128(a single sequence with no regrouping, sotoken_swapsis empty) show that when the feature is practically inactive, the unconditional cost (onetok_ids.push_back()per token) falls cleanly within measurement noise [-1.62%, +1.77%], causing no measurable slowdown.5. Scope Boundary
This indexing mismatch was found while investigating #27572. While this PR strictly fixes the row-domain corruption and mode-flip bugs, it does not resolve the separate concurrent acceptance collapse reported there. That collapse was traced directly to graph-level write-after-read races when the ring buffer is disabled (addressed independently in #27311), not the indexing issues fixed here. This PR does not claim to fix #27572.
Requirements