Lock the parse-bound Snappy corpus and record both denominators - #346
Merged
Conversation
The M3 worst-case rung had one constructed corpus, the copy chain, which reaches maximum element density at four decoded bytes per element. Throughput is reported per decoded byte, so a corpus locked at four says nothing about the regime that costs four times the parse work for the same reported byte. That regime had no owner anywhere in the harness. Adds the literal chain: the same 64 KiB block of one repeated byte, spelled out as one length-1 literal element per output byte. Same element rate per compressed byte, a quarter of the decoded bytes each. Two locks, because the corpus can stop being adversarial in two directions and neither stops it decoding. Longer literals keep the round trip and drop the element rate to a third; a revert to the copy chain keeps the element rate and quadruples the decoded bytes per element. The compressed share the sibling locks on is useless here, since this stream expands rather than compresses. Each lock was proven by breaking the generator the way it names and watching the selfcheck red, and each mutation was also run with both locks disabled to show the oracle gate passes it. The recorded denominators go in with the corpus rather than after it: the claim this corpus exists to support is a comparison between the two regimes, and one row alone does not carry it. The measurement contradicts the element count, the copy chain being 20% slower per output byte despite a quarter of the elements, and the section says so and says why that ordering must not be carried over to a device row.
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.
What & why
Closes #119.
The M3 worst-case rung was locked on one constructed corpus, the copy chain at
maximum element density: back-to-back minimum-cost copies at offset 1, four
decoded bytes per element. Throughput here is reported per decoded byte, so a
corpus locked at four decoded bytes an element says nothing about the regime
that costs four times the parse work for the same reported byte. That regime
had no owner in the harness, and a density lock on the copy chain would not
have noticed its absence.
This adds the literal chain beside it. Same 64 KiB block of one repeated byte,
same 3200-chunk scale, same one element per two compressed bytes, spelled out
as one length-1 literal element per output byte instead of copied. The two
corpora decode to identical bytes and differ only in what an element costs to
produce, which is what makes the pair a comparison rather than two numbers.
The means is C++ in
bench/bench_snappy.cppbeside the corpus it contrastswith, because the two share the report, the timing loop, the oracle gate and
the replicator, and differ only in how one block is built. The replicator is
now shared rather than copied, so the 64 KiB chunk unit has one authority.
The recorded denominators land with the corpus rather than after it: the claim
this corpus exists to support is the comparison between the two regimes, and
one row alone does not carry it. No kernel moves in this change, so the rule
that numbers and kernel changes never travel together is not engaged.
Type of change
Engineering checklist
Fail-closed preserved. The construction refuses a block below 256 output
bytes,
--worstlitrefuses to be combined with corpus files or a shapeflag, and
--worst --worstlittogether is refused because two regimescannot share one methodology block. All three exercised below.
Oracle coverage: the pinned snappy 1.2.2 decoder passes verdict on every
constructed stream and the round trip is compared byte for byte, before
anything is timed. That gate is necessary and demonstrably not
sufficient, which is what the two locks are for.
Determinism preserved. The corpus is constructed from fixed constants
with no PRNG and no input files; the reported digest
a475ef89da01c0e4is reproducible from the flags alone.
Every CUDA API call's error is checked; no exceptions cross the C ABI.
Not applicable: no CUDA in this change.
bench_snappylinks neither theCUDA runtime nor a kernel header.
An adversarial review (
/security-review) was run.Review record. None: see above.
GPU sanitizer gate
bench_snappyisCPU-only by construction, and the two ctest entries it registers carry
no
gpulabel.The device-side sanitizer net has not run successfully on this project at all,
per #258, and nothing here changes that.
Performance checklist
docs/BENCHMARKS.mdwith the full methodology block the harness emits,including host CPU, corpus digest, run protocol and element density.
Recorded inside the digest-pinned
nvidia/cuda:12.6.2-devel-ubuntu24.04container.that had none and changes no existing number.
The measurement contradicts what the element count predicts, and the section
says so rather than around it. The literal chain carries four times the
elements per decoded byte (209,715,200 against 52,432,000 over the same 209.72
MB) and finishes 20% sooner. Per element the ordering reverses five to one:
215.8 M elements/s against 42.9 M. The reference's offset-1 copy is the cost
on that corpus, not its parse. The section records that this ordering belongs
to the reference decoder and must not be carried over to a device row, because
a warp-cooperative decoder attacks the two regimes with different machinery
and which one floors it is a measurement nobody has taken.
Quality checklist
are shared with the sibling corpus rather than duplicated; the new code
is one block builder, one lock function and one flag.
the copy chain's, which is the non-obvious part.
bench_snappy_worstlit_selfcheck, with the timeout assertion updated sothe configure step still refuses an entry without one.
Verification
Everything below ran inside the digest-pinned container
(
nvidia/cuda:12.6.2-devel-ubuntu24.04@sha256:738fba0fbdb225b7a2931c58a5c8f03a84d3cd2f6a84975826a157339ef750b8),--gpus all, against this branch. Device: NVIDIA GeForce RTX 3080, driver560.94. Toolchain: nvcc release 12.6, V12.6.77.
cmake --build build-cuda -jbuilds clean.ctest --test-dir build-cuda --output-on-failure:**/*.{md,yml,yaml}:All matched files use Prettier code style!docs/BENCHMARKS.mdgains the section. No README orMASTERPLAN change is owed, since no behaviour, API or status row moves.
The locks, proven by breaking them
Each mutation was applied to the generator, built, run, and reverted. Each was
then run a second time with both locks disabled, to show what the oracle gate
alone accepts.
The element-rate floor. The drift it names is a generator whose literals
grew longer. Length-2 literals instead of length-1:
The second run is the point: the stream is a valid Snappy stream, the pinned
oracle accepts every one of them and every one round-trips, so the run goes
green and prints "max parse work per output byte" over a corpus carrying a
third of the element rate it claims.
The decoded-bytes-per-element ceiling. The drift it names is a generator
that reverted to the copy chain:
That run is also why one lock is not enough. 0.4999 elements per compressed
byte clears the floor comfortably, so the floor never fires on this drift, and
the corpus would have been reported as parse-bound while measuring the copy
chain.
The refusals
Notes
What this does not do, and where it went instead. #119 was opened as the whole
M3 bench rung and is superseded on every other part of that: the Silesia path
and CPU denominator, the copy-chain corpus, and the first recorded GPU
baselines all have their own owners, and the recorded GPU row for these
corpora belongs to #167 and waits on the Snappy kernel. The literal chain was
the one thing #119 uniquely carried, and it is what landed here.
The comparator row for the copy-chain corpus is recorded alongside rather than
in its own change. That corpus landed without a number, and a parse-bound
denominator with nothing to compare against does not carry the claim the
section is about.