Repository navigation
Conversation
Codecov Report❌ Patch coverage is
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 2 files with indirect coverage changes 🚀 New features to boost your workflow:
|
This was referenced Nov 5, 2026
Collaborator
|
It seems miri has detected some UB here, which I believe is caused by 15c8b70 because of reads past |
jesup
force-pushed
the
decompress-pgo-robustness
branch
from
November 6, 2026 16:52
4a1eb13 to
7311004
Compare
Contributor
Author
|
Thanks, fixed. Ran miri across all the PRs I have up as well |
LLVM lowers inline thresholds for code that is cold in a PGO profile, which leaves calls like `Writer::subslice` in the sequence loop. Firefox PGO training does not exercise zstd, so this is the configuration it ships.
PGO builds turned the element-wise `fill` into two byte stores per entry.
LLVM vectorized the per-byte loop on x86-64 only without PGO, and not at all on aarch64.
`ZSTD_execSequence` already guarantees the sequence fits before `oend`, so the per-sequence `Writer` bounds check is redundant. Keep `new_seq_state` out of line: inlining it makes the x86-64 loop allocate registers noticeably worse. On targets other than x86_64, force it inline with the new `pgo` feature: out of line the sequence state stays in memory, which made trained and cold PGO builds up to 10% slower on Apple silicon. Without PGO forcing it inline is slower, so the compiler decides. The long sequence decoder always calls the out of line version.
jesup
force-pushed
the
decompress-pgo-robustness
branch
from
November 10, 2026 17:08
7311004 to
73e22bc
Compare
Member
|
This has a variety of kind of unrelated changes, some more controversial than others. I think to merge this I'd like to see it split up basically per-commit so we can test, benchmark, and review it. |
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.
When the decoder is cold in a PGO profile, LLVM lowers its inline thresholds, and the Rust port suffers much more than C because it relies on many small helpers being inlined (for example,
Writer::subsliceends up as an out-of-line call in the sequence loop). The table builders also lose vectorization of their element-wise loops under PGO.Writer,BIT_DStream_t,ZSTD_copy16,ZSTD_fseState::new).fill(), fewer bounds checks,HUF_rescaleStats8 bytes at a time, noilog2zero check per entry.ZSTD_execSequencealready guarantees the sequence fits beforeoend.pgofeature that forcesnew_seq_stateinline on targets other than x86_64 (the long sequence decoder keeps the out of line version). Out of line, the sequence state stays in memory instead of registers, which is slower in PGO builds on aarch64. It is slower without PGO, so it is opt-in: enable it for PGO builds. x86_64 is unaffected and always keeps it out of line.Measured on Zen 3 with a harness that decodes the way Firefox does (fresh DStream per response, 16 KB input chunks, 128 KB output buffer), cycles per decompressed byte. "Cold PGO" is instrumented with a profile that never decodes zstd, which is what Firefox ships, since its PGO training has no zstd content; "trained" includes zstd decoding in the training.
Upstream benchmark (non-PGO, rs/C cycles): silesia-small 1.053 -> 1.016, re2-exhaustive 1.034 -> 1.013, zip64support 1.029 -> 1.001.
On an Apple M-series Mac (wall-clock, same harness), this PR alone decodes about 3% faster than main without PGO, 9% faster with cold PGO and 3% faster with trained PGO. On top of the whole stack, enabling
pgomakes streaming decompression of non-dictionary data about 11% faster with cold PGO and 10% faster with trained PGO, and about 4% slower without PGO.Each commit builds, is rustfmt-clean and passes
cargo test; the tip also passes debug tests.