perf(LTX25-DECODE-THREADS): the video VAE decode had 20 cores and used one (#1009) - #1041
Merged
Conversation
… the seam it needs is already in the tree Lever 3 of the `LTX25-DECODE-SPEED` investigation (#1006, PR #1018). Issue #1009. `ParallelForRows` (`src/vt/cpu/cpu_threadpool.cpp:413`) is synchronous and used by 10+ CPU kernels in this tree. Zero of them are in the LTX-2.5 video VAE decode, whose 42 convolutions carry ~7.25 TFLOP at 448x256/25f and take 2681 s on one core of twenty. The spec commits to the axis before the code exists, because the axis is the whole risk. The sibling dtype row (#1008, `d1b0ea3a8`) had to change this convolution's summation ORDER to a blocked one to stay inside a 5e-06 tolerance, and parallelism is the second thing that can change a summation order. The partition taken is the output line `(oc, ti, hi)`: the `ci * kernel^3` reduction stays entirely inside one output element's body, so a worker executes exactly the serial instruction sequence for every element it owns, and the result does not depend on the worker count or on which worker stole which chunk. Splitting the reduction axis `ic` into per-thread partials is the alternative, and it is rejected in writing: it would make the summation order a function of the thread count. Two gate cases are declared rather than one, because a determinism A/B is green on a serial implementation too. The second case observes the dispatch itself through the pool's public work-stealing cursor, which is the case that is RED before the change. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…ParallelForRows (#1009) Lever 3 of the `LTX25-DECODE-SPEED` investigation (#1006, PR #1018). `ParallelForRows` (`src/vt/cpu/cpu_threadpool.cpp:413`) is synchronous and 10+ CPU kernels in this tree dispatch through it. Zero of them were in the LTX-2.5 conv video VAE decode, whose 42 convolutions carry ~7.25 TFLOP at 448x256/25f and ran on one core of twenty. Three sites now dispatch: `CausalConv3d`'s output nest, its padding gather, and `Linear3d`. The axis is the whole risk, so it is argued at the site. The partition is the output line `(oc, ti, hi)`, `out.w` contiguous elements: `Volume::At` makes row `r` exactly `[r*out.w, (r+1)*out.w)`, and the entire `ci * kernel^3` reduction stays inside one output element's body in the blocked order #1008 shipped. A worker therefore runs the serial arm's instruction sequence, in the serial arm's order, for every element it owns, and the result cannot depend on the worker count or on which worker stole which chunk — which matters, because `ParallelForRows` steals through an atomic cursor. Splitting the reduction axis `ic` into per-thread partials is the alternative and is rejected in the comment: it would make the summation order a function of the thread count. Two cases, because a thread-count A/B is green on a serial implementation too and would have measured nothing. "the decode DISPATCHES its convolutions to the CPU threadpool" reads the pool's public work-stealing cursor, which is 0 on a fresh pool and non-zero after a partitioned dispatch; it fails `CHECK( 0 > 0 )` before this change. "the decode is BIT-IDENTICAL across thread counts" decodes the same latent at 1, 2, 3, 5 and 8 workers and memcmps every arm against the 1-worker one, which short-circuits to the pre-change serial path. Both enter through `Ltx2VideoDecodeStreaming`, and both assert an analytically derived value of exactly 7 rather than a recorded one, so a stubbed decode's zeros cannot pass. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…34 golden margins that did not move The row's `## Outcome`, written after the measurements rather than before them. The risk this spec was built around did not bind. Both suites were rebuilt with `kLtx2GoldenTol` set to 0.0 before the change and again after, and all 34 recorded margins — 23 in `test_ltx2_vae`, 11 in `test_ltx2_tiling` — came back byte-for-byte identical, compared by diffing the two sorted lists rather than by eye. Nothing was within tolerance; nothing moved. Those before-values also match what `ltx25-decode-dtype.md` §8.1 recorded on its own host, so this box reproduces the sibling row rather than reporting a local artefact. CPU A/B, same binary, `VLLM_CPP_CPU_THREADS` the only variable, 14 runs per count across an ascending and a descending sweep: 1.93x at 2, 3.68x at 4, 6.78x at 8, 9.15x at 16, 9.14x at 20, and 9.67x at the checkpoint's real channel width of 128. Spread is under 7% at every count up to 8 and 21-23% at 16 and 20, on a box holding one non-agent process at ~1.07 cores throughout, at one-minute load 4.03 to 6.77. The flattening above 16 is NOT called a ceiling: the implied serial fraction is 6.3%, which is the right order for the elementwise passes this row deliberately left serial, and §7 owns them. The output checksum was bit-identical across all 84 A/B decodes at both shapes, which is a second determinism proof on pseudo-random weights beside the engineered fixture's memcmp. ThreadSanitizer is clean on all three LTX suites — after two instrument problems were settled first. The binaries would not start at all (`unexpected memory mapping`, exit 66, a verdict-shaped ASLR failure that `setarch -R` fixes), and a silent sanitizer is indistinguishable from an absent one, so a deliberate race was compiled into the same lane and produced 87 warnings before being reverted. Recorded honestly: reverting any ONE of the three dispatch sites is detected by nothing, because the single work-stealing cursor stays non-zero while the other two dispatch. T0 holds the conjunction; the per-site gap is written up and owed. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…nd the one that would have made a later agent weaken the test (#1044) Review of [PR #1041](#1041) returned PASS with five non-blocking findings. Each was re-verified before it was repaired, because a reviewer's finding is a hypothesis; none of the five was rejected. **F1 — the T1/T2/T3 gap now has an owner.** §8.6 measured that reverting any single one of the three dispatch sites is detected by nothing, and argued it in prose only. It is now [#1044](#1044), an entry in §7's `## Owed` table, and an index row owned by `LTX25-DECODE-THREADS`. The issue carries the closing test the reviewer supplied: a per-dispatch `Threadpool::RunCount()` and an EXACT expected count rather than `> 0`, plus a `res_x_y` fixture because `Linear3d` is unreachable with `decoder_blocks` empty. It is NOT implemented here — a new gate needs its own red-before evidence and its own review. The index row names the owning row rather than leaning on `## Owed`, and that is deliberate: `owed_issues()` in `scripts/check-agent-record.py` splits on a bare `\n## Owed`, this spec's heading is `## 7. Owed`, so nothing listed there is visible to the unowned ratchet. Measured: the unowned count is 33 before and after, against `UNOWNED_HIGH_WATER = 33`. **F2 — the public records carried three significant figures a 21-23% spread does not support.** `9.14x`/`9.67x` appeared bare in `FEATURES.md` and `USAGE.md` while the spec disclosed the load and the spread; the projection is what lost them. Both now carry `~9x at 16-20 workers` with the conditions, and the bare `9.67x at c=128` — n=3, no min/median/max, same contended box — is gone from `FEATURES.md` rather than restated. `MAX_CELL_CHARS = 220` binds and the `BENCHMARKS.md` LTX-2.5 cell sat at exactly 220: it is now 212, and the `FEATURES.md` cell 210 -> 204, both measured with the checker's own parser. **F3 — the #1009 index row was cited at `:275` on PR #1018.** #1018 is CLOSED and superseded by [#1038](#1038); the row lives at line **279** on `row/LTX25-DECODE-SPEED-R2`. The decision not to append a second #1009 row is correct and unchanged — under `merge=union` a duplicate reds `main` for every branch the moment #1038 lands. Only the citation moved, here and in the pull request body, along with the spec's two other pointers at the closed pull request. **F4 — the evidence SHA did not resolve.** §8 cited `dac85969c`, which is not an ancestor of the head (`git merge-base --is-ancestor` exits 1) and would not exist in a fresh clone. §8 now cites `d653f7319` and states why the measurement transfers: `dac85969c:src` and `d653f7319:src` are both `7444ffa171b0c2868c505b5b9ea1113fa39c5477`, both `:tests` are `f0e5eac268119e9fe94da478c50e2d668a2e64b3`, and the diff between them touches only the spec and three `docs/` files. **F5 — the determinism test's stated reason was arithmetically false, and that is the finding that mattered.** The comment said 3 and 5 were chosen because they "do not divide the row counts". Both conv row counts are 360 (`conv_in`, 24*3*5) and 15 (`conv_out`, 1*3*5), and 3 and 5 divide each of them. The choice works for a different reason: `nchunk` derives from `nth * 4`, so at `nr = 360` the stride is 45 at 2 workers, 30 at 3, 18 at 5 and 12 at 8 — four DIFFERENT partitions, which is the property the `memcmp` needs. Verified by replicating `cpu_threadpool.cpp:428-443` rather than by reading it. A later agent "fixing" the row counts to satisfy the stated rationale would have weakened the test while believing it was strengthening it, so the comment now says so outright. **Out of scope and untouched:** the parallelisation, the three dispatch sites, `kLtx2GoldenTol` (5e-06 on both arms), the new cases' assertions, and the `## Owed` ratchet beyond F1's entry. Nothing was re-measured: `dgx.casa` is down and the A/B harness is deliberately not in the tree, so every wall-clock figure stands as the implementer recorded it. Gate at this tree: `CONFIGURE_EXIT=0`, `BUILD_EXIT=0`, `: error:` count 0 on a full 1449-target build, `ctest -N` 492, `CTEST_EXIT=0`, **100% tests passed, 0 tests failed out of 492** in 165.47 s, the same two pre-existing skips. `No space left` 0 and `BFD`/internal-error 0 across both logs, each grep positive-controlled against a synthetic file carrying the real message forms (2, 1 and 2 hits there). One-minute load 10 to 26; free disk 40 GiB falling to 21 GiB. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…d by hand `fa3723b85` (#1038) landed the `LTX25-DECODE-SPEED` spec and appended 13 rows to `.agents/issue-index.md`, including the #1009 row this branch deliberately does not duplicate. That row is now on `main`, so the branch's omission is satisfied rather than merely deferred. The index was NOT taken from the union driver. `merge=union` reports a clean result and that result is wrong: it interleaves this branch's row among the 13 `main` appended, so `origin/main`'s file stops being a byte-identical prefix, which is the property append-only rests on. Resolved by taking `origin/main`'s file wholesale and re-appending only this branch's own row (#1044), then verifying three things -- main's 144213 bytes are a byte-identical prefix of the result, the re-appended row is byte-identical to this branch head's, and 271 rows carry 271 unique ids. No product code is touched by the merge; `main`'s side of it is records only. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…ed by key `d9441ef3e` (#1004, the MiniMax-Music3 partial device arm) landed while this row was in review. It touches `include/vllm.h`, `src/capi/vllm_c.cpp` and the speech engine, none of which this row touches, but it also edits three keyed public records that this row edits. `docs/BENCHMARKS.md` conflicted. The conflict is adjacent-hunk context, NOT a same-key edit: `main` never changed the `LTX-2.5 axes` row, which is verified rather than assumed -- that row is byte-identical between the merge base and `origin/main`. Resolved by taking `origin/main`'s file wholesale and reapplying this row's single key, then proving the result differs from `origin/main` in exactly ONE line, and that line is the `LTX-2.5 axes` row. The other 508 lines are byte-identical. `docs/FEATURES.md` and `docs/USAGE.md` auto-merged, and the result was verified by key rather than accepted: FEATURES gains exactly one key (`LTX-2.5 Conv VAE decode threading`) with 0 removed and 0 changed; USAGE has 0 keys added, removed or changed, its edits being prose. `.agents/issue-index.md` is unchanged by this merge -- `#1004` appended no rows -- and `origin/main`'s file remains a byte-identical prefix at 271 rows, 271 unique ids. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…rom main, not the driver Three commits landed while this row was being gated: `283c7e492` (#1051, the llama.cpp repin record), `e34d71379` (#1054, an AppleClang capture fix in qwen3.5) and `0f8580e26` (#1043, configurable MTP speculation depth). None touches `src/vllm/model_executor/models/ltx2_video_vae.cpp`, which is this row's only product file. `.agents/issue-index.md` overlapped, and its clean auto-merge is not trustworthy: `merge=union` silently interleaves, and on a sibling branch today it also reinstated a row `main` had just deleted. Taken from `origin/main` wholesale with this row's single `#1044` row re-appended, then verified -- `origin/main`'s file is a byte-identical prefix, 275 rows carry 275 unique ids, and `#1044` appears exactly once. The three keyed public records were verified by key rather than accepted: `docs/FEATURES.md` gains exactly one key (`LTX-2.5 Conv VAE decode threading`), `docs/BENCHMARKS.md` changes exactly one (`LTX-2.5 axes`), `docs/USAGE.md` changes none, and nothing is added or removed elsewhere in any of the three. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…m main `ef6669292` (#1057) landed, folding two Darwin prose paragraphs into keyed rows and taking `main` out of red on `check-public-doc-tables`. It is a records-only change to two markdown pages and one index row, so it cannot affect this row's build; the full gate at `291263166` -- which carried every other commit now on `main` -- was 494 of 495 with the single failure owned by #428. The forge reported a conflict and `git merge` reports none: GitHub computes mergeability without applying `.gitattributes` merge drivers, so a branch that appends an index row always looks conflicted there. The index was rebuilt from `origin/main` wholesale with this branch's own row re-appended, verified as a byte-identical prefix with unique ids. `check-public-doc-tables` passes on the merged tree, which matters because #1057 returns both pages to exactly their budget. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
localai-bot
pushed a commit
that referenced
this pull request
Aug 16, 2026
…threaded decode `ef6669292` (#1057, the doc-budget repair) and `ec0e410b5` (#1041, the LTX-2.5 video VAE decode threading) both landed while this row was in CI. #1041 is real product code in `src/vllm/model_executor/models/ltx2_video_vae.cpp`, so this merge was rebuilt and re-run rather than reasoned about: BUILD_EXIT 0 with 0 compile errors, `ctest -N` 495, **495 of 495 passed**, and the 12 `ltx2` suites green together at load 15. That combination is the one worth gating: #1041 parallelises the decode this row renders through, and this row adds the guided T2A denoiser. Neither had been run against the other before now. The index was rebuilt from `origin/main` wholesale with this branch's own eight rows re-appended (#1005, #1013, #1031, #1039, #1048, #1049, #1050, #1052), verified as a byte-identical prefix with 285 rows and 285 unique ids. The forge's conflict report is the union-driver artifact; `git merge` reports none. `check-public-doc-tables` passes on the merged tree, which matters because #1057 returns both pages to exactly their budget. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
This was referenced Aug 17, 2026
localai-bot
added a commit
that referenced
this pull request
Aug 17, 2026
…e the page said could not (#1088) (#1090) Closes #1088. `docs/USAGE.md` published 448x256 at 25 frames as "Measured NOT to complete", with the reason that its decode loses about 59 GB in 24 s. Two renders on `dgx.casa` on 16 to 17 August 2026 against `main` `0b0b8900f` completed that geometry in 3085 s and completed 704x448 at 25 frames in 4231 s. The page now records the newer envelope, and `docs/BENCHMARKS.md` no longer says the opposite of it in a cell a reader meets first. ## What was measured Container `vllmcpp-build:gb10`, `Release`, `VLLM_CPP_CUDA=ON`, arch `121a`, `TRITON=ON`, CUTLASS absent so FlashAttention-2 was not built, which is like for like with the earlier renders. `VLLM_CPP_CPU_THREADS=20`, NVFP4 transformer, no `--allow-unported`. `0b0b8900f` carries #1041 threaded decode, #1032 T2A and #1036 f32 decode accumulators. | Geometry | Result | Wall | |---|---|---| | 448x256 / 25 frames | completed | 3085 s | | 704x448 / 25 frames | completed | 4231 s | | 1024x576 / 25 frames | not attempted to completion, another session claimed the box | n/a | The ~59 GiB cliff did not recur under a 2 s memory guard that would have seen it: `MemAvailable` floors of 38.96 GiB over 1289 samples at 448x256 and 38.89 GiB over 1743 samples at 704x448, zero samples under 34 GiB on either, peak use of 80 of 119 GiB, and no reboot. The 704x448 artifact was verified rather than inferred from an exit code: 25/25 distinct frame md5s, 0 near-uniform and 0 near-black frames, adjacent-frame mean absolute difference 4.381 against a uniform-noise reference of 85.3 on the same shape, 0/24 zero-motion pairs, and audio at 48 kHz stereo, 1.010 s, -37.29 dBFS, 20/20 windows above threshold. The mp4 lives at `benchmarks/media/ltx25-704x448-25f-audio.mp4` on the render host and is gitignored by `.gitignore:35`, so it is not committed here. ## What is not claimed One run per geometry on a contended shared box with no oracle on either side. Two points establish no scaling law. 704x448 is not published as a ceiling: the next rung up stopped because another session claimed the box, not because of memory or an envelope. The page says all of this in its own words. ## The 59 GB is kept, not deleted It is the reason the old row gave, so deleting it would remove the evidence the newer result is measured against. It stays attributed to its own run, which is rung F1 in `.agents/benchmark-record.md`: a prompt-embeds render with no text tower that an armed watchdog ended at 13.77 GiB against an 18 GiB floor, rather than the engine failing. Attributing the fall is still #1014, and this change does not close it. ## The dominant cost moved off the decode `docs/USAGE.md` said most of a 320x192/25f render is spent in the host VAE decode. After #1041 threaded that decode, the dominant cost is a resolution-independent phase of about 1731 s, measured at 1731 s and 1732 s across two rungs whose voxel counts differ 2.75x, which is 57 to 66% of wall. That is #1087, which owns naming the phase. The sampler classified by CPU-time rate rather than by symbol, so what is measured is a duration and a scaling law and not a function, and the page says so. ## Files | Record | Edit | |---|---| | `docs/USAGE.md` | envelope table rows, the paragraph under it, the bounded-by paragraph, and the #1009 paragraph's stale "has not been re-measured" clause | | `docs/BENCHMARKS.md` | the `LTX-2.5 axes` row, edited in place as two table cells, 208 and 214 characters against `MAX_CELL_CHARS = 220`, so no prose paragraph is added to a page sitting at 35 of 35 | | `.agents/specs/ltx25-resolution-envelope.md` | new section 4.1 recording what superseded section 4, and the `## Owed` bullet that section 4 wrote | | `.agents/issue-index.md` | one row appended for #1088, zero rows edited, zero removed | ## Evidence Records only. No `src/`, `include/` or `tests/` change, so no build was run and none is claimed. Key-by-key proof, taking `HEAD`'s version of each file and reapplying the scoped edit: | Record | Keys in base | Keys now | Unrelated keys byte-identical | Changed | Added | Removed | |---|---|---|---|---|---|---| | `docs/USAGE.md` | 205 | 206 | 203 of 203 | `**Measured to complete on one GB10**` | `Largest size tried`, `Superseded, kept for the record` | `Measured NOT to complete` | | `docs/BENCHMARKS.md` | 190 | 190 | 189 of 189 | `LTX-2.5 axes` | none | none | Issue index, the three verifications the append-only rule needs: the base file is a byte-identical prefix of the new one, the addition is exactly one line whose sha256 is `65933626d961a41b…`, and the file has 290 rows against 290 unique issue ids. The union driver was never allowed to resolve anything: the file was rebuilt as base bytes plus the row. Checkers, each with a red control observed on the same tree before the green was believed: | Checker | Result | Armed control | |---|---|---| | `check-doc-checkpoint.py --staged` and `--commit cedb85e` | 0 | `--commit b5618b3` exits 1, "changed user_usage but did not update docs/USAGE.md" | | `check-public-doc-tables.py` | 0 | padding the new cell past 220 characters exits 1 at line 487, "table cell of 333 chars exceeds 220" | | `check-issue-index-append-only.py --base origin/main` | 0 | committing a deletion of the `#168` row exits 1, "this range removes or edits lines" | | `check-agent-record.py` | 0 | replacing the new row's owning row with a dash exits 1, "34 rows name no owner, above the recorded 33" | | `check-commit-style.py --range origin/main..HEAD` | 0 | an empty commit whose subject ends in a period exits 1 | | `check-commit-trailers.py --range origin/main..HEAD` | 0 | an empty commit with no trailer block exits 1 on three lines | | `check-pr-size.py --base origin/main --head HEAD` | 0 | n/a, no control run | Every tree mutation was restored and the restored file re-hashed to the pre-mutation sha256 before the next step. The key proof itself was seen red first, on an expectation that omitted the one key the change does edit in place, so its green is not a tautology. `scripts/agent-preflight.sh --staged` and `scripts/agent-ready.py` both report `All gates green` on `21544efd9`. `agent-ready` then exits 1 only on `expected exactly one live PR for row/LTX25-ENVELOPE-RECORD; found 0`, which this pull request is. `origin/main` advanced twice during this work, to `e9dfa6319` and then `9143196c7`. Both were merged in and every checker re-run afterwards; the second merge is the merge commit on this branch, and its message carries the trailer block because the range gate caught that it did not. ## What could not be verified The first `scripts/agent-preflight.sh` run exited 1 on `test_cpu_x86_llamacpp_floor`, on the unmodified tree before any edit in this branch. Its own output names the cause: `load=120.50`, so the harness discarded the contended leg and returned `NO_QUIET_WINDOW` (4) where the case expects `GIVING_UP` (2). That is #618. It passed on the later runs once the box quieted, so this branch has no evidence of that case being sound, only of it being load-dependent as #618 already says. The renders themselves were performed by another session and are reported here from its results. This branch did not run them, holds no GPU, and did not rebuild anything. `.agents/specs/ltx25-decode-speed.md` and `.agents/benchmark-record.md` also discuss the 448x256 rung. Neither is edited here: the decode-speed spec already records that the "inside the decode" half of the old sentence is unsupported, and the benchmark record is an append-only log of what each run observed, which stays true of the run it describes. Reconciling the investigation spec against the new rungs belongs to #1087, which owns the phase. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code] Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
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.
Lever 3 of the
LTX25-DECODE-SPEEDinvestigation (#1006, PR #1038 — #1018 wasthe earlier pull request and is now closed). Closes #1009.
ParallelForRows(src/vt/cpu/cpu_threadpool.cpp:413) is synchronous and 10+ CPUkernels in this tree dispatch through it. Zero of them were in the LTX-2.5 conv
video VAE decode, whose 42 convolutions carry ~7.25 TFLOP at 448x256/25f and ran
on one core of twenty. Three sites now dispatch:
CausalConv3d's output nest, itspadding gather, and
Linear3d.The axis is the whole risk, so it is argued at the site
The sibling dtype row (#1008,
d1b0ea3a8) had to change this convolution'ssummation order to a blocked one to stay inside a 5e-06 tolerance. Parallelism
is the second thing that can change a summation order.
The partition is the output line
(oc, ti, hi),out.wcontiguous elements.Volume::At(oc, ti, hi, wi)is((oc*t + ti)*h + hi)*w + wi, so rowris exactly[r*out.w, (r+1)*out.w)ofout.data— no element is written twice — and theentire
ci * kernel^3reduction stays inside one output element's body in theblocked order #1008 shipped. A worker therefore executes the serial arm's
instruction sequence, in the serial arm's order, on the serial arm's values, for
every element it owns. The result cannot depend on the worker count or on which
worker stole which chunk, and the second half matters:
ParallelForRowsstealsthrough an atomic cursor, so the row-to-thread assignment is genuinely
non-deterministic run to run.
Splitting the reduction axis
icinto per-thread partials would also be a legalconvolution. It is rejected in the comment at the site, because it would make the
summation order a function of the thread count.
The numerics did not move at all
Both suites were rebuilt with
kLtx2GoldenTolset to0.0before the change andagain after, so every golden reports its
max|diff|rather than its verdict.All 34 recorded margins — 23 in
test_ltx2_vae, 11 intest_ltx2_tiling—came back byte-for-byte identical, compared by diffing the two sorted lists
rather than by eye:
VAE_MARGINS_IDENTICAL (23 values),TILING_MARGINS_IDENTICAL (11 values).Those before-values are also the ones
ltx25-decode-dtype.md§8.1 recorded on itsown host, which is an independent check that this box reproduces the sibling row.
No tolerance was touched.
That table is itself a threading gate: the suite runs on the global pool,
hardware_concurrencywide, so every LTX-2.5 video golden after this changeexecutes on 20 workers, and the "Conv video decoder" fixture carries a
res_x_yblock so
Linear3dandconv_shortcutare on that path too.Two cases, because one of them measures nothing on its own
A thread-count A/B is green on a serial implementation. Shipping only that would
have been a test that passes while measuring nothing.
pool's public work-stealing cursor through
ChunkAdd(0), which is anon-mutating read. A fresh pool reads 0; a pool that has run a partitioned
dispatch reads at least
nth. Asserting 0 before the decode is theinstrument's own positive control. Before this change it fails
CHECK( 0 > 0 ).at 1, 2, 3, 5 and 8 workers and
memcmps every arm against the 1-worker one,which short-circuits to the pre-change serial path. 3 and 5 are there because
nchunkderives fromnth * 4, not fromnth— 45/30/18/12 are the chunkstrides at this fixture's 360
conv_inoutput lines, four different partitionsof the same output. It is NOT that 3 and 5 fail to divide the row counts: 360
and 15 are both divisible by each. That was the comment's original claim and it
was false; see the repairs below.
Both enter through
Ltx2VideoDecodeStreaming— what the render path calls atsrc/vllm/multimodal/ltx2_video.cpp:3258— and both assert an analyticallyderived value of exactly 7, not a recorded one, because #1008 recorded that a
zero-filled stub satisfies an expectation of zero.
The CPU A/B
Same binary,
VLLM_CPP_CPU_THREADSthe only variable, one decode throughLtx2VideoDecodeStreaming, 14 runs per count across an ascending and a descendingsweep so an ordering drift shows as spread rather than hiding in a mean.
A second shape at the checkpoint's real
base_channelsof 128: 5.1015 s at onethread against 0.5276 s at twenty, 9.67x. That is the weakest number here —
n = 3against the table's 14, no min/median/max, same contended box — so itcorroborates the table's shape at a second channel width and is not independently
a three-significant-figure result. The public records carry
~9x at 16-20 workerswith the conditions rather than either decimal.The load it was taken at. One-minute load average 4.03 to 6.77 on a box whose
one-minute average had been between 2 and 94 the same day, with one non-agent
process holding ~1.07 cores throughout. That process is part of why 16 and 20
spread 21-23% where everything at or below 8 spreads under 7%.
No ceiling is declared. The implied serial fraction at 9.14x on 20 workers is
6.3%, which is the right order for
PixelNorm,Silu,ApplyAdaLn, the residualadd and
expand— every one still serial, every one listed under## Owed.Memory bandwidth is the second candidate and is not separated here.
Determinism, proven a second time: the output checksum was bit-identical across
all 84 A/B decodes — six worker counts, two sweep directions, two shapes — on
pseudo-random weights rather than the engineered fixture.
What is NOT claimed
No end-to-end render speedup, no ratio against any oracle, no composition figure
with #1008. There is no GPU here,
dgx.casawas unreachable throughout, andltx_coreis not installed. The harness shape is synthetic and says so; whatgeneralises from it is the scaling, not the absolute wall.
ThreadSanitizer, with the instrument controlled first
RelWithDebInfo+VLLM_CPP_SANITIZE=thread:test_ltx2_vae42/42,test_ltx2_tiling10/10,test_ltx2_video57/57, allEXIT=0, zeroWARNING: ThreadSanitizer.Two instrument problems had to be settled before that meant anything. The binaries
would not start at all —
FATAL: ThreadSanitizer: unexpected memory mapping,EXIT=66, an ASLR-against-shadow-layout failure that a&&chain would have readas a race;
setarch x86_64 -Rfixes it. And a sanitizer that reports nothing isindistinguishable from one that is not instrumenting, so a deliberate unsynchronised
write was compiled into
CausalConv3d's parallel body in the same lane: 87WARNING: ThreadSanitizer: data race,EXIT=66, then reverted, rebuilt, and backto 0 and
EXIT=0.Mutations, three facts each
CHECK( 0 > 0 )CausalConv3d's output loop aloneLinear3dalonememcmp, on all four non-base armsREQUIRET1's failed first attempt is in the table on purpose. One unbalanced brace
closed the anonymous namespace early and produced 45
-Werrorerrors that read asunrelated
unused-functioncomplaints hundreds of lines away. The runner refusedto draw a verdict rather than running a stale binary and printing a plausible 42/42.
T1, T2 and T3 are an honest gap and it is owed. One work-stealing cursor is
shared, so reverting any single site leaves the other two dispatching and the case
reads non-zero. It gates "at least one of the three sites dispatches", and T0 is
what holds the conjunction. T3 additionally cannot be seen by that fixture at all,
since
Linear3dis only reached through ares_x_yblock. What does bound eachsite is the golden table above — the "Conv video decoder" arm reaches all three at
20 workers and did not move — and the wall-clock, which is what a serial
convolution would actually cost.
D1 is beside D2 because it is the weaker of the two. D1 perturbs the first row
of every chunk including the first, so the 1-worker arm moves too and the case
fails on its value assertion before reaching the
memcmp. D2 perturbs only chunksthat do not start at row 0, which is invisible at one worker, so the
memcmpacross worker counts is the only thing that can report it. It does, on all four.
Gate
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DVLLM_CPP_CUDA=OFF,-j6,ctest -j4.Run twice, the second at this branch head so a green gate chains to the push.
CONFIGURE_EXIT=0,BUILD_EXIT=0,: error:count 0,ctest -N492,CTEST_EXIT=0, 100% tests passed, 0 tests failed out of 492 in both runs(308.99 s and 316.87 s). Two pre-existing skips,
test_modelopt_mixed_precision_checkpointandtest_voxtral_e2e.No space left0 andBFDinternal-error/assertion 0 across every log,both greps positive-controlled against a synthetic file carrying the real message
forms — 1 and 2 hits there, 0 in the real logs.
check-doc-checkpoint --commitgreen on each of the three commits and armed (
b5618b305exits 1).Load average 32-52 on the first run and 82-94 on the head run, on a shared
20-core box; none of the load-dependent suites flaked in either. Free disk
21-30 GiB of 447 GB; the 834 MiB sanitizer tree was removed after use.
One deliberate omission
No
.agents/issue-index.mdrow is appended for #1009. That row already existsat
.agents/issue-index.md:279on PR #1038, branchrow/LTX25-DECODE-SPEED-R2,which filed the issue and is unmerged. #1018 was the pull request that carried it
first; #1018 is closed and #1038 supersedes it.
.gitattributessetsmerge=unionon that file andscripts/check-agent-record.pyrefuses a duplicate issue number, so a second copyhere would turn
mainred for every branch the moment #1038 merges — which is whata duplicate #995 row did on 2026-08-16. The sibling dtype row made the same call
for #1008. The link lives in the spec and in this body; the index link arrives with
#1038. One index row IS appended by this branch, for the new issue #1044 below,
and #1044 is not among the ids #1038 appends.
The review, and the five findings repaired after it
A fresh reviewer returned PASS with no blocking findings: the gate reran at
492/492, reduction safety was verified from
Volume::At's index arithmetic,determinism was proven by a mutation caught at all four worker counts,
ThreadSanitizer was clean against an 84-race positive control, the keyed records
were proven key by key, all nine anchors landed exactly, and a correctly-shaped
zero-filled buffer fails both new cases, so the zero-stub trap is closed.
Five non-blocking findings followed. Each was re-verified before it was
repaired, because a finding is a hypothesis; none of the five was rejected.
## OwedentryParallelForRowsseeds ONE pool cursor (cpu_threadpool.cpp:438, advanced:455), so two surviving sites keep it non-zero## Owedand to the issue index, owned byLTX25-DECODE-THREADSFEATURES.mdandUSAGE.mdcarried9.14x/9.67xbare, against a 21-23% spreadcheck-public-doc-tables.py's own parser~9x at 16-20 workerswith the conditions; the bare9.67x at c=128is gone fromFEATURES.md:275on PR #1018row/LTX25-DECODE-SPEED-R2(PR #1038)dac85969c, which does not resolve on the branchgit merge-base --is-ancestor dac85969c HEADexits 1d653f7319and states why the measurement transfersF1 is owed, not implemented. #1044 carries the closing test the reviewer
supplied — a per-dispatch
Threadpool::RunCount()bumped inRun()and anEXACT expected count rather than
> 0, plus a fixture carrying ares_x_yblock because
Linear3dis unreachable withdecoder_blocksempty. A new gateneeds its own red-before evidence and its own fresh review, so it is a row.
The index row names its owner rather than leaning on
## Owed, deliberately.owed_issues()inscripts/check-agent-record.pysplits on a bare\n## Owedand this spec's heading is
## 7. Owed, so nothing listed there is visible tothe unowned ratchet. Measured: the unowned count is 33 before and after,
against
UNOWNED_HIGH_WATER = 33. Four other specs have the same numberedheading (
ltx25-decode-dtype,ltx25-token-append,nemotron-h-a2q1-fp8-mamba,nemotron-h-a2q2-nvfp4-moe-lmhead); that is a record observation this row doesnot repair.
Cell widths, since
MAX_CELL_CHARS = 220binds and theBENCHMARKS.mdcellsat at exactly 220.
BENCHMARKS.mdLTX-2.5 axes 220 to 212;FEATURES.mddecode-threading 210 to 204. Both measured with the checker'sown
_table_rows, andcheck-public-doc-tables.pyexits 0.Keyed records, proven key by key against the merge base.
BENCHMARKS.md:179 unrelated keys byte-identical, only
LTX-2.5 axeschanged, none added orremoved.
FEATURES.md: 194 unrelated keys byte-identical.USAGE.md: 200unrelated keys byte-identical, no table row touched.
.agents/issue-index.md:origin/main's version is a byte-identical prefix, exactly one appendedline, and it is #1044.
Nothing was re-measured.
dgx.casais down and the A/B harness isdeliberately not in the tree, so every wall-clock figure above stands as the
implementer recorded it. No end-to-end render speedup is claimed here either.
Gate after the repairs.
CONFIGURE_EXIT=0,BUILD_EXIT=0,: error:count0 on a full 1449-target build,
ctest -N492,CTEST_EXIT=0, 100%tests passed, 0 tests failed out of 492 in 165.47 s, the same two pre-existing
skips.
No space left0 andBFD/internal-error 0 across both logs,each grep positive-controlled against a synthetic file carrying the real message
forms (2, 1 and 2 hits there, 0 in the real logs).
check-doc-checkpoint --commitgreen on all four branch commits and armed:b5618b305exits 1.One-minute load 10 to 26; free disk 40 GiB falling to 21 GiB.
Pushed with
--no-verify, and whyThe
pre-pushhook refuses this branch oncheck-public-doc-tables.py:This branch did not cause it. Matched-arm check:
origin/mainalone, in adetached worktree with no branch content, fails with the identical numbers.
Bisected to
e34d71379(#1054, an AppleClang capture fix that also added +5lines to BENCHMARKS and +4 to FEATURES);
283c7e492immediately before it exits0. Filed as #1055.
AGENTS.md: "Hooks are bypassable convenience, not evidence", and "a commit that
needs an exception argues for it in its own message". This is that argument. The
same checker run against this branch's own edits is clean -- it changes exactly
one key in BENCHMARKS (
LTX-2.5 axes) and adds exactly one in FEATURES(
LTX-2.5 Conv VAE decode threading), with every unrelated key provenbyte-identical to
origin/main.Gate at the merged tree
CONFIGURE_EXIT=0,BUILD_EXIT=0,: error:0, 493 targets linked,ctest -N495, 494 of 495 passed. The one failure is
test_serve_low_tools, which is#428 ("the concurrency-cap assertion races the server-side counter and reads
3 under load") -- it passes 3/3 when re-run alone, and this branch touches no
serve or tools file. Attribution verified against the issue that names the test,
not assumed from a family.
FOLLOWING_AGENTS_PROTOCOL
Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]