Repository navigation
perf(native): inline VLC symbol decoding and pass refill state by value - #19
Merged
Merged
Conversation
Entropy decoding is most of a decode on both routes, and the HP coefficient loop is about 60% of it. That loop called each per-symbol VLC helper out of line, so every symbol reloaded and stored the bit reader's fields, returned its Result through memory, and re-checked its table indices. The helpers were 21% of main-thread samples on a 2048x2048 RGB q8 image. decode_index, decode_abs_level, decode_first_index, decode_run_cached, and read_bits are now forced inline, and the bit-serial run decoder they fall back to near a packet end is cold and out of line. The out-of-line cache refill and the end-of-packet error constructor take and return plain values instead of the reader, so calling them no longer forces it to memory. Decoding logic, error variants, and error positions are unchanged. Single-image entropy decoding is 1.04-1.13x faster, and batch tile preparation 1.06-1.29x faster, on an M4 Pro, an M4, and an M5 Max.
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.
CPU entropy decoding is most of a decode on both routes. On a 2048² RGB q8 image it is about 50 of 55 ms on the Metal route, and for batches of 256×256 tiles it is the preparation stage. Profiling showed that the HP coefficient loop, about 60% of entropy time, called each per-symbol VLC helper out of line. Each call reloaded and stored the bit reader's fields, returned its
Resultthrough memory, and re-checked its table indices. Those helpers were 21% of main-thread samples on that image.decode_index,decode_abs_level,decode_first_index, anddecode_run_cachedare forced inline into the LP and HP block loops. The bit-serial run decoder they fall back to near a packet end is now cold and out of line.read_bitsis forced inline. This mostly helps flexbit-heavy 16-bit images: on its own, the inlinedread_bitsgained nothing there, and the by-value helpers gave most of the gain.#[inline(always)]carries#[expect(clippy::inline_always)]with its measured reason.Performance. "Before" is
mainat 8a7ccbe, which includes #12–#18, and each figure is the median of 5 interleaved rounds. Single images,jxr-load-bench, ms. Prepare is the Metal stage (parse, plan, and entropy into staging); CPU is the full CPU-route decode. The M4 and M5 Max columns show speedups:Images are 2048² unless noted.
256×256 Boat tiles,
jxr-pathology-bench, ms. Prepare is the Metal host route's preparation, and the other columns are route totals:Batch preparation is 1.06–1.29x faster on every machine. The M5 Max CPU-host route at 64 tiles (not shown) measured 5.54 → 5.64 ms; its 32- and 128-tile rows are faster.
MpsGraphBatchDecoder::preparethenrun_prepared_groupon the same tiles with thergb8_nhwc_referencegraph, M4 Pro, ms:Three variants measured slower or neutral and are not included: decoding each block from a local copy of the reader so its fields stay in registers (neutral), inlining flexbit decoding into the YUV HP loop (0.67–0.86x), and an inline fast path for refills away from the packet end (0.87–0.94x).
Validation on an M4 Pro, on the earlier
main(e52d95c) with this change:cargo fmt --all -- --check;cargo clippy --workspace --all-targets --all-features -- -D warnings, and the same with--target x86_64-unknown-linux-gnu;cargo test --workspace --all-features(272 passed);cargo test -p jxr --no-default-features(24 passed);RUSTDOCFLAGS="-D warnings" cargo doc --workspace --all-features --no-deps; fuzz-package formatting and Clippy;cargo test -p jxr-mpsgraph --test metal -- --ignored --test-threads=1(7 passed); T.834/T.835 conformance Metal 517/517 and CPU 517/517;jxr-diff --backend metalon the 13 large generated images, all identical. On the currentmaintree (with #12–#18) with this change, the workspace tests (278) and mpsgraph Metal tests (8) also passed, and T.834 Metal and CPU were 517/517 and the 13 large-image diffs identical on the M4 Pro, an M4, and an M5 Max. The owner-onlymetal-hardwareworkflow has not been run on this revision.