Conversation
The streaming path submitted the batch in fixed 64-chunk waves, so Silesia was ~51 serial submissions. Issue #29 measured the steady-state wall at ~230 ms against a ~12 ms device-resident decode and a ~4 ms compressed H2D and attributed the ~213 ms residual to those submissions by exclusion. This takes that lever. kWaveChunks is gone. The wave is the largest number of chunks whose staging fits a 384 MiB budget, capped at 4096 chunks, and the budget is charged against the LARGEST chunk in the call rather than the mean - so peak staging stays inside it for any distribution, a batch of tiny chunks with one huge one included. The chunk ceiling is not redundant with the budget: a batch of zero-length chunks divides the budget by one and would otherwise ask for a wave of kMaxBatchChunks metadata entries. A single chunk larger than the whole budget still decodes, alone in its wave, because the budget sizes a wave and is not a capacity limit on the ABI. Silesia becomes one submission on the device-output path and two on the host-output path. Measured 2026-08-09 on the RTX 3080 in the pinned container, both binaries built once and then alternated, 3 warmup + 30 runs per number: device out 235.4 -> 27.7 ms median, 8.5x, won 5 of 5 passes host out 377.5 -> 155.4 ms median, 2.4x, won 3 of 3 passes 27.7 ms against the ~16 ms floor the issue named. Host output does not reach the same place and the wave is not why: its readback targets pageable caller memory one chunk at a time, which is 3239 synchronous copies at any wave size, and that is #133 and #135. The price is peak staging, from ~4 MB per wave to at most the budget - ~102 MB pinned and ~102 MB device for Silesia on the device path. It is in docs/BENCHMARKS.md beside the numbers rather than in the small print. The suite stays green, stream_twin included: the same input decoded on a reused context is bit-identical to a fresh-context decode.
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.
Closes #33.
The lever, taken
kWaveChunks = 64submitted Silesia's 3239 chunks in ~51 serial waves. #29measured the steady-state wall at ~230 ms against a ~12 ms device-resident
decode and a ~4 ms compressed H2D and attributed the ~213 ms residual to the
submission count by exclusion. The wave is now sized by the staging it costs.
Three properties, and each of the two bounds is load-bearing:
the mean, so peak staging holds for any distribution. A batch of tiny chunks
with one huge one cannot talk the sizer into a wave that allocates far past
the budget, which a mean would allow.
a batch of zero-length chunks divides the budget by one and asks for a wave
of
kMaxBatchChunksentries.alone in its wave. The budget sizes a wave; it is not a capacity limit on
the ABI, and turning it into one would be a new refusal nobody asked for.
Silesia becomes one submission on the device-output path (source staging only)
and two on the host-output path (source plus the destination arena).
The measurement
RTX 3080, driver 560.94, pinned container
(
nvidia/cuda:12.6.2-devel-ubuntu24.04), 2026-08-09. Both binaries builtonce up front and then alternated, baseline and patched, pass after pass -
the protocol perf pass 4 established after an A/B/A that rebuilt between arms
read a drift as an effect. 3 warmup + 30 runs per number,
--gpu-stream-ctxover the hash-pinned Silesia corpus.
Device output 8.5x (0.90 -> 7.65 GB/s), won 5 of 5. Host output 2.4x
(0.56 -> 1.36 GB/s), won 3 of 3. There is no overlap between the two arms'
ranges on either path, so nothing here rests on a median alone.
The acceptance asked for a number approaching the ~16 ms floor. 27.7 ms leaves
a ~12 ms residual rather than the ~213 ms one. That residual is not
isolated and no claim is made about what it is.
What the change does not fix
Host output stays ~5x the device path, and the wave is not the reason: its D2H
targets pageable caller memory one chunk at a time, so it is 3239 synchronous
copies at any wave size. Removing the submission cost around them leaves them.
That is issues #133 and #135, and this PR touches neither.
The price
Peak staging rises from ~4 MB per wave to at most the budget: device memory
(compressed source plus, for host output, the destination arena) within
384 MiB by construction, pinned host memory within the same bound for the
source. For Silesia that is ~102 MB pinned and ~102 MB device on the
device-output path. Cold (first decode on a fresh context) moves the other
way and is reported rather than dropped: 241.5 -> 152.5 ms on the device path,
so the larger allocation costs more to make and still finishes ahead of the
baseline's submission count.
Both figures go into
docs/BENCHMARKS.mdbeside the tables, anddocs/BENCHMARK-METHODOLOGY.md's streaming rows are updated with the date andthe numbers they replace named.
Correctness
stream_twinis the property that matters here and it is green: the sameinput decoded on a reused context - after any number of prior decodes,
including one that grew the staging - is bit-identical to a fresh-context
decode. Chunk results do not depend on which wave carried them, so the wave
size is not an axis of the output; that is by construction rather than by a
test, and it is stated as such.
Host-only build unchanged:
Formatting:
Not covered
No Compute Sanitizer sweep.
src/stream.cppis host-side stream choreographyand adds no device code, but the sweep is owed on any change that touches the
decode path and it cannot be produced on this route - the four tools cannot
attach to the device here, which issue #258 holds. Stated rather than left
looking answered.
No second person read this change. The evidence above stands in place of one.