Skip to content

perf(opt): fold BT-walk coordinate decode; drop dead hash-chain optimal path#462

Merged
polaz merged 6 commits into
mainfrom
refactor/drop-dead-hc-optimal-fallback
Jul 1, 2026
Merged

perf(opt): fold BT-walk coordinate decode; drop dead hash-chain optimal path#462
polaz merged 6 commits into
mainfrom
refactor/drop-dead-hc-optimal-fallback

Conversation

@polaz

@polaz polaz commented Jun 30, 2026

Copy link
Copy Markdown
Member

Summary

Cleans up the optimal parser's match-finder and shaves the per-node cost of its
hot binary-tree walk. Three changes, all byte-identical output:

  1. Drop the non-upstream hash-chain optimal fallback. The optimal parser's
    per-position match-finder always took the binary-tree branch in every shipped
    build (the USE_BT_MATCHFINDER=false chain path was const-folded out — absent
    from the optimized binary). Upstream zstd's optimal parser
    (ZSTD_insertBtAndGetAllMatches) is binary-tree only; the hash chain is its
    lazy parser (ZSTD_HcFindBestMatch), a separate path. The fallback's
    else-branch + the chain_candidates helper had no upstream counterpart and
    were dead. Removed them and the two tests that exercised the chain path through
    the test-only dispatcher; the BT path keeps its own skip-window / rebase
    coverage.

  2. Drop the now-vestigial USE_BT_MATCHFINDER const generic. With the chain
    fallback gone the per-position finder is unconditionally the BT walk, so the
    const generic (and the macro metavars that only fed the removed branch) carried
    no information. Removed from the collect entry points and the four kernel
    wrappers.

  3. Fold the per-node coordinate decode in the BT walk (perf). The hot
    per-node loop decoded each chain entry into three coordinates per iteration
    (absolute position, BT pair slot, history index), reloading four struct fields
    and round-tripping index_shift through the slot computation. Precompute the
    loop-invariant biases once before the walk so each coordinate is a single
    wrapping_add from the stored entry — the single-coordinate form of upstream's
    window-relative matchIndex (match = base + matchIndex, slot
    2*(matchIndex & btMask)). The BT slots are now keyed context-relative to
    match upstream. Byte-identical: the folded values equal the originals for every
    gate-validated entry.

Results (i9, x86_64, ours-vs-c_ffi, flat control)

compress-dict level_19_btultra2 small-10k-random: −1.3 % on the
per-position BT find (flat c_ffi control), byte-identical. Net −55 lines from
the dead-code removal.

Testing

  • Byte-identical — pure refactor + folded arithmetic, identical match decisions.
    cargo nextest run -p structured-zstd --features hash,std,dict_builder — 837 pass;
    -p ffi-bench --features bench_internals,dict_builder — 59 pass
    (cross-validation round-trips + fuzz_interop).
  • clippy (default + --tests, --no-default-features --features kernel_scalar,hash)
    and cargo fmt --check — clean.

Note on the optimal-collect dispatcher

Optimal candidate collection is binary-tree only (btopt / btultra / btultra2
/ btlazy2). The Fast / Dfast / Greedy / Lazy strategies never run the
optimal parser — they drive their own match finders and keep chain_table as an
HC chain, not BT pair slots. The public collect_optimal_candidates dispatcher's
non-BT arm is therefore unreachable!(): a non-BT tag reaching it is a caller
bug and panics loudly instead of walking the HC chain as a binary tree. Only the
test-only shim calls this entry, and it tags a BT strategy. The on-encode hot
path bypasses the dispatcher entirely (it calls build_optimal_plan_impl_<kernel>
with a BT strategy directly).

Summary by CodeRabbit

  • Refactor / Performance
    • Optimized BT-based candidate generation by precomputing coordinate “biases” and using a unified insert-and-collect path for optimal planning.
  • Bug Fixes
    • Improved match-finding skip-window advancement and strengthened stability for candidate tracking after 32-bit boundary rebasing.
  • Tests
    • Updated/expanded matcher and dispatcher tests to enforce BT-only routing, refine skip and rebase invariants, and remove obsolete sentinel/termination chain-candidate assertions.

polaz added 3 commits June 30, 2026 16:59
The optimal parser's per-position match-finder always used the binary-tree
branch (every production call site selects it): upstream zstd's optimal
parser (ZSTD_insertBtAndGetAllMatches) is binary-tree only, with no
hash-chain variant — the chain is upstream's lazy parser
(ZSTD_HcFindBestMatch), a different path. The else-branch hash-chain
optimal walk and its chain_candidates helper had no upstream counterpart and
were dead in every shipped binary (const-folded out, absent from objdump).

Remove the dead else-branch, the chain_candidates helper, and the two tests
that exercised the chain optimal path through the test-only dispatcher; the
BT path's own skip-window / rebase coverage remains. Byte-identical.
With the hash-chain optimal fallback gone, the per-position match-finder is
unconditionally the binary-tree walk, so the USE_BT_MATCHFINDER const generic
(and the macro metavars that only fed the removed chain branch:
bt_insert_and_collect method handle, for_each_repcode, hash3) carried no
information. Remove the const generic from the collect entry points and the
four kernel wrappers, and drop the dead macro parameters. Byte-identical.
The BT match-finder's hot per-node loop decoded the chain entry into three
coordinates each iteration: candidate_abs (position_base + stored - 1 -
index_shift), the BT pair slot (via bt_pair_index_for_abs, which re-read
index_shift and bt_mask and round-tripped index_shift back in), and
candidate_idx (candidate_abs - history_abs_start). That reloaded four struct
fields and ran ~6 arithmetic ops per node.

Precompute the three loop-invariant biases once before the walk so each
coordinate is a single wrapping_add from the stored entry — the
single-coordinate form of upstream zstd's window-relative matchIndex
(match = base + matchIndex, slot = 2*(matchIndex & btMask)). Byte-identical:
the folded values equal the originals for every gate-validated entry.
@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 484be57d-8587-428b-a4f5-8063f6a413e3

📥 Commits

Reviewing files that changed from the base of the PR and between 5ef5c04 and 8d3387f.

📒 Files selected for processing (1)
  • zstd/src/encoding/match_generator/tests.rs

📝 Walkthrough

Walkthrough

This PR precomputes BT-walk decode biases, removes HcMatcher::chain_candidates, and simplifies optimal candidate collection by dropping the USE_BT_MATCHFINDER const generic and updating the affected dispatch and tests.

Changes

HC/BT matchfinder candidate decoding refactor

Layer / File(s) Summary
Bias-based candidate decoding in BT-walk loop
zstd/src/encoding/hc/generator.rs
Precomputes abs_bias, idx_bias, and bt_bias once before the loop and uses them to derive candidate_abs, next_pair_idx, and candidate_idx directly.
Removal of chain_candidates and related coverage
zstd/src/encoding/hc/mod.rs, zstd/src/encoding/hc/hc_tests.rs, zstd/src/encoding/match_generator/tests.rs
Deletes HcMatcher::chain_candidates, removes its unit tests, and updates match-generator coverage around BT-only dispatch, skip-window behavior, and rebase behavior.
Removal of USE_BT_MATCHFINDER from optimal collection
zstd/src/encoding/hc/optimal.rs
Drops the USE_BT_MATCHFINDER const generic from the optimal candidate-collection pipeline, simplifies the shared macro and wrappers, and updates the remaining call sites and dispatcher paths.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related issues

Possibly related PRs

Poem

A bunny hopped through bytey trails,
Prebaking math and trimming snags and tails.
One chain removed, one generic gone,
The BT path hums briskly on.
🐇✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the two main changes: BT-walk decode folding and removal of the dead hash-chain optimal path.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/drop-dead-hc-optimal-fallback

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jun 30, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.73684% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
zstd/src/encoding/hc/optimal.rs 94.73% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@greptile-apps

greptile-apps Bot commented Jun 30, 2026

Copy link
Copy Markdown

Greptile Summary

This PR simplifies optimal parsing and speeds up the binary-tree match walk.

  • Removed the unused hash-chain optimal fallback.
  • Made optimal candidate collection binary-tree only.
  • Dropped the now-unused USE_BT_MATCHFINDER generic.
  • Folded BT-walk coordinate decoding into precomputed biases.
  • Updated tests around BT dispatch, skip-window behavior, and rebasing.

Confidence Score: 5/5

Safe to merge based on the reviewed changes.

The changes are focused refactors and arithmetic simplifications in the optimal match finder, with tests updated around dispatch, skip-window, and rebasing behavior.

T-Rex T-Rex Logs

What T-Rex did

  • T-Rex attempted a base checkout/build/run and successfully checked out to commit 14e31ff, but the build failed due to cargo: command not found.
  • Environment adjustments led to cargo_status=1 and rustc_status=1, and a head checkout was blocked by existing tracked local changes.
  • A harness Rust program was captured that would perform deterministic comparisons of compressed FNV hashes and decoded roundtrip hashes for the requested optimal levels and input classes.
  • Before the dispatcher-bt-only run, trex-artifacts/dispatcher-bt-only-01-before.log shows Lazy accepted with candidates=1 and all BT tags completed; BtUltra/BtUltra2 produced hash3_match=true while BtOpt/Btlazy2 produced hash3_match=false.
  • After the dispatcher-bt-only run, trex-artifacts/dispatcher-bt-only-02-after.log shows head Lazy/non-BT test panicked as expected with collect_optimal_candidates is binary-tree only; non-BT strategies use their own match finder, and the all-BT dispatch test passed.
  • Before the BT invariants checks, trex-artifacts/bt-invariants-01-before.log shows a hard reset and checkout to 14e31ff followed by cargo test -p structured-zstd for the three BT invariant filters; all tests exited with code 0.
  • After the BT invariants checks, trex-artifacts/bt-invariants-02-after.log shows a hard reset and checkout to 8d3387f followed by the same three tests; all tests exited with code 0.
  • The invariant comparison confirms that base and head preserve the requested BT skip-window and rebase invariant scenarios across the checked commits.

View all artifacts

T-Rex Ran code and verified through T-Rex

Reviews (4): Last reviewed commit: "test(opt): assert per-tag dispatch via t..." | Re-trigger Greptile

Comment thread zstd/src/encoding/hc/optimal.rs
The optimal candidate collector is binary-tree only; the Fast / Dfast /
Greedy / Lazy strategies never run the optimal parser (they drive their own
match finders) and keep chain_table as an HC chain, not BT pair slots. The
public dispatcher's non-BT arm previously routed those tags into the BT
collect, which would walk the HC chain as a binary tree. Make that arm
unreachable so misuse fails loudly, and tag the test-only shim callers as a
BT strategy (BtOpt shares Lazy's OPT_LEVEL=0 / USE_HASH3=false consts, so the
collect behavior is unchanged). No production caller hit the non-BT arm (the
on-encode path goes through build_optimal_plan_impl directly).
Comment thread zstd/src/encoding/hc/optimal.rs
Adds a should_panic test proving a non-BT strategy tag reaching
collect_optimal_candidates panics (the binary-tree-only contract), covering
the unreachable arm, and a dispatch test exercising every BT tag (BtOpt /
BtUltra / BtUltra2 / Btlazy2) under the scalar kernel so the dispatcher's
per-tag and scalar arms are covered.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@zstd/src/encoding/match_generator/tests.rs`:
- Around line 1350-1392: The test in
hc_collect_optimal_candidates_dispatches_every_bt_strategy only checks that each
StrategyTag variant runs without panicking, so it can miss dispatch mistakes.
Strengthen it by adding a tag-specific observable from
collect_optimal_candidates or the resulting out buffer that differs per BT
strategy, and assert on that value inside the loop. Use the existing
HcMatchGenerator, StrategyTag variants, and FastpathKernel::Scalar setup to
verify each tag is routed to its intended collector specialization rather than
just surviving execution.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 38d29417-7c24-48ba-9872-a0052c252603

📥 Commits

Reviewing files that changed from the base of the PR and between faad79b and 5ef5c04.

📒 Files selected for processing (2)
  • zstd/src/encoding/hc/optimal.rs
  • zstd/src/encoding/match_generator/tests.rs

Comment thread zstd/src/encoding/match_generator/tests.rs
The dispatch test previously only proved each BT tag ran without panicking,
so a cross-group mis-mapping (e.g. BtUltra routed to the BtOpt
specialization) would slip through. Add a USE_HASH3 observable: a fixture
where `abc` repeats with a differing 4th byte, so the hash3 specializations
(BtUltra / BtUltra2) surface a length-3 match at offset 12 that the 4-byte BT
hash misses, while the non-hash3 ones (BtOpt / Btlazy2) do not. Assert the
match's presence equals the tag's USE_HASH3. (BtOpt vs Btlazy2 share identical
collect consts, so they are runtime-indistinguishable; that mapping is
compiler-enforced.)
@polaz

polaz commented Jun 30, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@polaz

polaz commented Jun 30, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@polaz polaz merged commit d7074b2 into main Jul 1, 2026
28 checks passed
@polaz polaz deleted the refactor/drop-dead-hc-optimal-fallback branch July 1, 2026 00:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant