Skip to content

Record that pinned gather staging does not pay on the frame path [#135] - #341

Merged
iderex merged 1 commit into
mainfrom
issue/135
Aug 10, 2026
Merged

Record that pinned gather staging does not pay on the frame path [#135]#341
iderex merged 1 commit into
mainfrom
issue/135

Conversation

@iderex

@iderex iderex commented Aug 10, 2026

Copy link
Copy Markdown
Owner

Closes #135.

Measured-negative. No decoder code ships. This PR is the record.

What was measured

The gather in DecodeAndAssemble was changed to write into a
cudec_cuda::PinnedBuf of total_src bytes instead of a pageable
std::vector, keeping the single H2D, the byte layout, the order and the
offsets. That is exactly the shape the issue's Done-when names.

The claim was that removing the driver's internal bounce copy shows up in the
end-to-end wall time. It does not.

Numbers

Three interleaved passes, after / before / after / before / after / before in
one session, both binaries built from the same tree, recorded 2026-08-10 inside
the digest-pinned nvidia/cuda:12.6.2-devel-ubuntu24.04 container
(sha256:738fba0fbdb225b7a2931c58a5c8f03a84d3cd2f6a84975826a157339ef750b8) on
the RTX 3080 (sm_86, driver 560.94, CUDA 12.6, nvcc V12.6.77), 3 warmup + 30
measured runs per rung, output byte-verified against the original once per rung
before timing. Baseline is 2962282.

bench_lz4 --frame bench/corpora/silesia/* --warmup 3 --runs 30
Block max Before p50 (3 samples) After p50 (3 samples) Change
64 KB 399.628 / 433.568 / 424.567 ms 432.287 / 441.727 / 433.930 ms +4.0 %
256 KB 425.677 / 416.421 / 422.092 ms 419.494 / 431.301 / 414.693 ms +0.1 %
1 MB 472.230 / 441.407 / 452.914 ms 444.834 / 450.241 / 443.524 ms -2.0 %

The sides overlap at every rung and disagree in direction between rungs, off
samples that interleave with each other. One distribution, not two. The issue's
own rule is that flat refuses the change, so it is refused.

Why it is flat

The bounce copy this removes is one pass over 102 MB. The timed call also pays
the gather's own memcpy over the same bytes, the content checksum over 212 MB
of output, and both transfers, which is the roughly 450 ms floor every rung
shares. Pinning 102 MB is not free either, and cudaHostAlloc lands on every
call because the frame entry point owns no reusable context to amortize it
across.

What this does not claim

Only the shape the issue names was measured: one pinned buffer of total_src,
one H2D. A bounded pinned wave that overlaps the gather with the transfer is a
different mechanism and nothing here evaluates it. That belongs where overlap is
owned, in the streaming context, which holds a buffer across calls and so does
not pay the pinning per call.

The hostile-input concern in the issue's constraints is not resolved by this
result and is not made worse by it either: total_src is still the sum of an
untrusted frame's parsed block lengths, and the gather still allocates that much
pageable memory, which is the state the tree was already in.

Gate

The pinned variant built and passed the full suite before it was reverted:

100% tests passed, 0 tests failed out of 36

frame_twin and frame_host_negative are both in that set. The tree as shipped
here differs from main only in docs/BENCHMARKS.md.

The device-side sanitizer net did NOT run. Compute Sanitizer cannot attach to
this device through the WSL2 paravirtualized path, which is #258, and the remedy
the tool names is a machine-wide elevation that is not taken here. Nothing here
should be read as having passed that gate.

This change had no second reader. The evidence above stands in place of one.

The frame gather stages through a pageable vector, so the driver bounces it
through pinned memory of its own before the H2D. Gathering straight into a
pinned buffer removes that bounce. Measured over three interleaved passes
against 2962282, it changes nothing: +4.0 % at the 64 KB rung, +0.1 % at
256 KB, -2.0 % at 1 MB, with the two sides overlapping at every rung and
disagreeing in direction between rungs. That is one distribution rather than
two.

Flat refuses it under the rule the issue pre-registered, so no decoder code
ships. The record says why it is flat: the removed bounce is one pass over
102 MB against a ~450 ms floor made of the gather's own memcpy, the content
checksum over 212 MB of output, and the transfers, and the frame entry point
owns no reusable context so it pays cudaHostAlloc on every call.

Only the shape the issue names was measured. A bounded pinned wave that
overlaps the gather with the transfer is a different mechanism and nothing
here evaluates it.
@iderex
iderex merged commit 40a9f54 into main Aug 10, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Frame source gather stages through pageable memory; use pinned host staging for the H2D

1 participant