perf(decompress): accelerate bzip2 decompression with 8-bit Huffman LUT, cold-outlined drain, and Slice-by-4 parallel CRC32 (+60.1% radar, +18.4% universal Silesia speedup) - #141
Conversation
|
Some flesh and blood comments. I don't have my heart set on any of these, and willing to re-work, re-factor, or drop items as needed. This was mostly an experiment in agentic benchmarking and coding. But I am hopeful that they may have some use. This came out of some experiments with pulling in a lot of NEXRAD data and trying to optimize that process. |
cb07122 to
86947ba
Compare
Codecov Report❌ Patch coverage is
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
|
Neat, I can at least reproduce some speedups locally now, though I'll have to look into it more: We'll probably cherry-pick parts out of this as we validate them. |
|
At least some of the clippy errors are pre-existing, I'll go fix those. |
afe2856 to
fcb7590
Compare
PR Update: 128-Bit SIMD Move-to-Front (MTF), Clean 4-Commit Rebase & Differential Test HardeningWe have rebased and updated this PR on top of the latest 1. Commit Structure & Rebase CleanlinessThe branch has been rebased and organized into 4 clean, self-contained logical commits:
2. Comparative Benchmark Matrix: Initial PR vs Updated PRAll benchmarks executed using a 20-iteration iso-thermal alternating interleaved harness pinned to physical CPU Core 2 on an AMD Ryzen 7 7840HS.
Compression throughput remains at full 100% parity across all datasets (+1.1% on Radar, +1.4% on Silesia). 3. Codecov Target Resolution & Differential Test SuitesTo address the Codecov patch coverage requirement (86.56%
|
|
Tried to clean up the various CI issues as well as added a new SIMD commit. |
|
as a methodological note, can you make the LLM create more smaller commits, and then split out the results per-commit (e.g. in the commit message)? So each commit should either have some tangible performance benefit, or be a neutral change that makes a subsequent improvement simpler to implement. Because some of these changes are just, I think, aesthetics: doing manually what LLVM will already do for us in a fairly reliable way. |
Yeah give me another day or two and I can force push an updated set of commits trying to incorporate this feedback. |
Introduces BZ2_CRC32TABLE_4: [[u32; 256]; 4] generated via compile-time const fn generate_crc32_table_4. This prepares 4-byte parallel polynomial matrix lookup with a compact 4 KB memory footprint that fits entirely within L1 data cache. Includes unit tests verifying mathematical equivalence against scalar CRC-32 across initial CRC states and byte values. Verification: - cargo test --workspace passed (100% test parity including new tests). - Neutral preparatory commit (pure table generation and test assertions).
…ter pressure in hot loop Extracts the multi-byte repeated byte run expansion and CRC update logic from the hot un_rle_obuf_to_output_fast loop into a dedicated cold routine drain_rle_bulk_cold marked #[cold] #[inline(never)]. By removing large core::ptr::write_bytes calls and bulk CRC branches from the main loop, LLVM can allocate critical decompression state variables directly into CPU registers with zero spill-to-stack overhead. Benchmark Impact (10 iterations vs prior commit): - NOAA NEXRAD Radar: 153.51 -> 159.99 MB/s (+4.2% / +6.48 MB/s) - Silesia Corpus: 54.08 -> 55.93 MB/s (+3.4% / +1.86 MB/s) Selected Silesia Decompression Gains: - ooffice (Executable): 37.38 -> 39.36 MB/s (+5.3%) - mozilla (Binaries): 50.90 -> 53.14 MB/s (+4.4%) - samba (C Source): 68.96 -> 71.51 MB/s (+3.7%) - osdb (Database): 46.88 -> 48.58 MB/s (+3.6%) - mr (Medical MRI): 63.91 -> 66.04 MB/s (+3.3%) Verification: - cargo test --workspace passed (100% C-parity). - Zero memory footprint change (0 new allocations).
…allel lookup Replaces the single-byte serial CRC loop inside drain_rle_bulk_cold with unrolled 4-byte parallel lookup steps using the compile-time generated BZ2_CRC32TABLE_4 (Slice-by-4). Processes 16 bytes and 4 bytes per iteration when expanding long homogeneous repeated byte runs, reducing memory lookup latency by 4x. Benchmark Impact (10 iterations vs prior commit): - NOAA NEXRAD Radar: 156.05 -> 180.09 MB/s (+15.4% / +24.04 MB/s) - Silesia Corpus: 55.68 -> 55.67 MB/s (-0.0% / neutral) - Silesia Compression: +1.4% across corpus Verification: - cargo test --workspace passed (100% C-parity). - Zero regression on non-repetitive Silesia corpus files.
Adds huffman_lut: [[u32; 256]; 6] (6 KB footprint) to DState and populates prefix lookup entries for canonical Huffman codes <= 8 bits during block header decoding. Each entry packs (symbol << 16) | code_length, mapping 8-bit bitstream prefix lookups directly to decoded symbols without tree traversal. Verification: - cargo test --workspace passed (100% test parity). - Neutral preparatory commit (allocates and precomputes table; hot loop decode paths not yet switched).
…a direct LUT Replaces iterative bit-by-bit tree traversal with a direct 8-bit prefix lookup table (s.huffman_lut) in BZ_X_MTF_1, BZ_X_MTF_3, and BZ_X_MTF_5. Decodes the vast majority of short Huffman symbols (<= 8 bits) in a single indexed memory load, advancing the bitstream by code_len and directly yielding the Move-to-Front symbol. Long codes (> 8 bits) seamlessly fall back to standard multi-bit sequential tree traversal. Benchmark Impact (10 iterations vs prior commit): - NOAA NEXRAD Radar: 171.31 -> 204.34 MB/s (+19.3% / +33.03 MB/s) - Silesia Corpus: 53.87 -> 62.87 MB/s (+16.7% / +9.00 MB/s) Universal Silesia Decompression Gains: - ooffice (Executable): 37.85 -> 46.05 MB/s (+21.7%) - webster (ASCII Text): 52.89 -> 63.73 MB/s (+20.5%) - dickens (ASCII Text): 44.56 -> 53.47 MB/s (+20.0%) - mozilla (Binaries): 50.22 -> 59.65 MB/s (+18.8%) - reymont (PDF): 54.24 -> 64.05 MB/s (+18.1%) - mr (Medical MRI): 65.34 -> 76.64 MB/s (+17.3%) - samba (C Source): 69.61 -> 80.54 MB/s (+15.7%) - x-ray (Medical X-Ray): 40.06 -> 45.70 MB/s (+14.1%) - osdb (Database): 46.95 -> 53.05 MB/s (+13.0%) - sao (Catalog): 32.40 -> 36.50 MB/s (+12.6%) Verification: - cargo test --workspace passed (100% C-parity). - Memory footprint: 6 KB inside existing DState heap allocation.
fcb7590 to
f4b8b56
Compare
|
I have tried to break it up, remove irrelevant changes, and lean it out as best I could. Things I viewed as marginal or overly convoluted I dropped. This leaves us with effectively 3 core changes or enhancements.
|
perf(decompress): accelerate bzip2 decompression with 8-bit Huffman LUT, cold-outlined drain, and Slice-by-4 parallel CRC32
Overview & Motivation
This PR introduces two orthogonal, highly synergistic performance optimizations to the decompression hot path in
libbz2-rs-sys:decompress.rs): Replaces bit-serial decoding loops with a 1-cycle direct lookup table for codesbzlib.rs/crctable.rs): Accelerates repeated byte sequences (Benchmark Highlights:
unsafe: Huffman LUT is 100% pure safe Rust; 0 external dependencies.Key Architectural Improvements
1. 8-Bit Canonical Huffman Decode LUT (
decompress.rs)create_decode_tables), we populate an 8-bit prefix table per Huffman group (huffman_lut: [[u32; 256]; 6]).2. Cold-Outlined Slice-by-4 Bulk Drain (
bzlib.rs)BZ2_CRC32TABLE_4).#[cold] #[inline(never)] drain_rle_bulk_coldremoves all register pressure and live-range spills from the hot3. Supporting Algorithmic Cleanups (
huffman.rs&blocksort.rs)create_decode_tableswith anmainSortusing nativeleading_zeros()intrinsics.Memory Footprint, Trade-offs & Invariants
origin/main)DState).rodata)DStateis an opaque pointer (void*) inbz_streamunsafeblocksBenchmark Results (20 Iterations, True Interleaved B1 <-> T1, LTO Pinned)
Collected using an open-source, zero-allocation in-memory verification harness with True Iteration-by-Iteration Interleaved A/B execution on a dedicated CPU core.
Test Environment
rustc 1.97.1(--release,lto = "fat",codegen-units = 1,-O3)1. Overall Aggregate Throughput (Median ± MAD% Dispersion)
origin/mainv0.2.5)cb07122)2. Silesia Corpus Decompression Breakdown (Sorted by Speedup)
main)cb07122)websterdickensmozillaoofficereymontmrsambasaoosdbx-rayxmlnciReproducibility & Benchmark Suite
The complete benchmark suite, dataset downloader with cryptographic SHA-256 validation, and 100% pure Rust runner are available at:
https://github.com/gheffern/bzip2-benchmarksTo reproduce locally:
git clone --recurse-submodules https://github.com/gheffern/bzip2-benchmarks.git cd bzip2-benchmarks ./run_benchmark.sh --fetch-data ./run_benchmark.sh --iterations 20Verification & Test Suite
cargo test --all(148 tests passed).bzip2 1.0.8.