perf(codec): dict-band hot-path — slice header parse, dms-walk floor, SIMD prefix leading word#458
Conversation
decode_all_with_dict_handle still routed its per-frame header read through the Read-trait reset_with_dict_handle (per-field read_exact via the io::impls shim) while the non-dict decode_all already parsed the header straight from the input slice. Add reset_from_slice_with_dict_handle, mirroring the Read-based dict reset but reading the header via read_frame_header_from_slice and applying it through the shared parsed-header path, then attaching the dictionary. Wire decode_all_with_dict_handle through it. Byte-identical (dict cross-validation + fuzz_interop round-trips pass).
The lazy hash-chain dict-match walk recomputed the candidate offset (current_idx - dict_idx) every iteration solely to gate on decoder reachability (offset <= max_window_size). The offset is only consumed when a candidate WINS, which is rare on the walk. Hoist the equivalent floor (current_idx.saturating_sub(max_window_size)) once before the loop and gate on dict_idx >= floor; the per-iteration subtract moves to the (rare) win branch and the max_window_size register is freed inside the hot loop. Mirrors C's ZSTD_HcFindBestMatch dms loop, which bounds reachability by chain construction rather than re-checking each candidate. Byte-identical: dict_idx >= current_idx - max_window_size is algebraically the same gate as current_idx - dict_idx <= max_window_size given dict_idx < current_idx (dict positions precede the cursor). Dict cross-validation + fuzz_interop round-trips pass.
…ernels The SIMD common_prefix_len_ptr (avx2 / sse42 / neon / simd128) ran the wide vector loop even when the prefix diverges within the first 8 bytes. On BT-tree node compares — the optimal parser calls the seeded prefix counter per visited node, and each node extends the running seed by only a short run before the smaller/larger split — that divergence is almost always inside the first word, so the vector load + compare is pure overhead. Upstream C ZSTD_count leads with one MEM_readST (8-byte) check and returns on a mismatch there; mirror it with a leading scalar word probe, falling through to the vector loop only when the first word matches (long matches). i686 (ours-vs-c_ffi, flat control): compress-dict L13 small-10k-random 782->725us (-7.2%); compress L19 btultra2 large-log-stream 64.5->59.5ms (-7.7%) — the long-match path benefits too, since per-node tree extensions are short regardless of total match length. Byte-identical (838 lib + 59 ffi incl cross-validation; scalar already word-at-a-time). The scalar fallback kernel is unchanged.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
📝 WalkthroughWalkthroughAdds ChangesFrameDecoder slice-based dict-handle reset
Encoder fastpath and HC loop optimizations
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
|
Summary
Two hot-path improvements for the per-label-dictionary band (the CoordiNode
target), one on each side of the codec. Both are byte-identical and mirror the
shape of the upstream C reference for the same path.
Slice-direct header parse on the dict-handle decode path
decode_all_with_dict_handlestill routed its per-frame header read through theRead-trait dict reset (per-fieldread_exactvia theio::implsshim) whilethe non-dict
decode_allalready parsed the header straight from the inputslice. Add
reset_from_slice_with_dict_handle(the slice-direct analog of theRead-based dict reset) and wiredecode_all_with_dict_handlethrough it. Ondecompress-dict/level_12_lazy/small-10k-randomthis is -2.4 % (i9,x86_64). The remaining dict-decode gap is the entropy / sequence-decode work
(FSE table build + the per-sequence decode-and-execute loop + the ext-dict
copy), a separate deeper track.
Hoist the dict-walk reachability floor out of the lazy chain loop
The lazy hash-chain dict-match walk recomputed the candidate offset
(
current_idx - dict_idx) every iteration solely to gate on decoderreachability (
offset <= max_window_size). The offset is only consumed when acandidate wins, which is rare on the walk. Hoist the equivalent floor
(
current_idx.saturating_sub(max_window_size)) once before the loop and gate ondict_idx >= floor; the per-iteration subtract moves to the rare win branch andthe
max_window_sizeregister is freed inside the hot loop (it matters on theregister-starved 32-bit path). Mirrors C's
ZSTD_HcFindBestMatchdms loop,which bounds reachability by chain construction rather than re-checking each
candidate.
dict_idx >= current_idx - max_window_sizeis algebraically the same gate ascurrent_idx - dict_idx <= max_window_sizegivendict_idx < current_idx(dictpositions precede the cursor), so it is byte-identical.
Leading scalar word probe in the SIMD common-prefix kernels
The SIMD
common_prefix_len_ptr(avx2 / sse42 / neon / simd128) ran the widevector loop even when the prefix diverges within the first 8 bytes. The
optimal parser calls the seeded prefix counter on every visited BT-tree node,
and each node extends the running seed by only a short run before the
smaller/larger split, so that divergence is almost always inside the first
word and the vector load + compare is pure overhead. Upstream C
ZSTD_countleads with one
MEM_readST(8-byte) check and returns on a mismatch there;mirror it with a leading scalar word probe, falling through to the vector loop
only when the first word matches (long matches). The scalar fallback kernel is
already word-at-a-time and is unchanged.
Same-host ours-vs-
c_ffi(flat control arm), both arches:compress-dict/level_13_lazy/small-10k-randomcompress/level_19_btultra2/large-log-streamThe long-match (
large-log-stream) path benefits too, since per-node treeextensions stay short regardless of total match length. Byte-identical.
Testing
cargo nextest run -p structured-zstd --features hash,std,dict_builder— 838 passcargo nextest run -p ffi-bench --features bench_internals,dict_builder— 59 pass(dict cross-validation: rust-compress/ffi-decompress + ffi-compress/rust-decompress + fuzz_interop)
cargo clippy(default + no-std +--tests) clean;cargo fmt --checkcleanc_ffiA/B with a flat control armSummary by CodeRabbit
Performance Improvements
Bug Fixes