Skip to content

commitment: refactor fold() into smaller methods#20152

Merged
awskii merged 3 commits into
mainfrom
awskii/fold-refactor
Mar 26, 2026
Merged

commitment: refactor fold() into smaller methods#20152
awskii merged 3 commits into
mainfrom
awskii/fold-refactor

Conversation

@awskii

@awskii awskii commented Mar 25, 2026

Copy link
Copy Markdown
Member

Break the 258-line fold() into focused methods for readability and maintainability.

Changes

Extract three methods from fold():

  • foldDelete() — handle deletion propagation (~40 lines)
  • foldPropagate() — single-child fuse/extension (~80 lines)
  • foldBranch() — multi-child branch encoding (~100 lines)

fold() becomes a ~50-line dispatcher.

Added Benchmark_HexPatriciaHashed_fold for targeted measurement.

Correctness

  • All commitment tests pass
  • Race detector clean
  • Fuzz tests pass
  • UniqueRepresentation tests pass (deterministic root hashes)

Performance

No regression — profiled with -benchtime=10s:

  • Baseline: 104,892 ns/op
  • Refactored: 94,652 ns/op (within variance, slightly faster)
  • fold cumulative: 4.61s → 4.43s (identical)
  • keccak dominates at 15.5% — fold is not the bottleneck

Co-Authored-By: Shuo shuo@erigon.dev

Extract foldDelete, foldPropagate, foldBranch from 258-line fold().
Add Benchmark_HexPatriciaHashed_fold for targeted measurement.
No behavior changes — all tests pass, race-clean, same performance.

fold() 258 lines → ~50 lines + 3 focused methods.

Co-Authored-By: Shuo <shuo@erigon.dev>
@yperbasis yperbasis added this to the 3.5.0 milestone Mar 26, 2026
@yperbasis yperbasis enabled auto-merge March 26, 2026 08:44
@yperbasis yperbasis disabled auto-merge March 26, 2026 08:45

@yperbasis yperbasis left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Bug: foldPropagate now evicts cache (not in original)

foldPropagate calls collectDeleteUpdate(updateKey, row, true) — the true means "evict cache". But the original propagate case had no cache eviction:

Original (hex_patricia_hashed.go:2020-2024 on main):
if hph.branchBefore[row] {
_, err := hph.branchEncoder.CollectUpdate(hph.ctx, updateKey, 0, hph.touchMap[row], 0, RetrieveCellNoop)
if err != nil {
return fmt.Errorf("failed to encode leaf node update: %w", err)
}
// ← no EvictBranch here
}

Refactored (foldPropagate):
if err := hph.collectDeleteUpdate(updateKey, row, true); err != nil { // ← true = evict

This should be false. The spec document even identified this as a risk in Task 2:

▎ "Note: the delete case has the cache eviction, propagate case does not — verify whether propagate should also evict"

In practice this is likely benign (the branch is being deleted anyway so evicting stale cache is harmless), but the PR states "No behavior changes" — and this is one. Fix: change true to false in the
foldPropagate call.


Should not check in plan documents

docs/plans/completed/20260325-fold-refactor-objective.md (35 lines) and docs/plans/completed/20260325-fold-refactor-spec.md (371 lines) are development process artifacts. The PR description already captures the
rationale and approach. Checking 406 lines of planning docs into the repository adds maintenance burden without serving future readers — anyone looking at this code will read the commit message and PR, not a
plans directory. Remove these files from the PR.


Nits (non-blocking)

  1. "save" commit message — the second commit (1f05bbc) has the message "save". Squash or rewrite before merge.
  2. childNibble naming in foldPropagate — nice improvement over the original's implicit shadowing of nibble. The original code declared nibble := bits.TrailingZeros16(...) which shadowed the outer nibble int16
    parameter. Using childNibble makes the intent explicit.
  3. feedBranchHashesToKeccak trace logging — the original non-deferred path logged empty(%d, %x, depth=%d) for trailing empty cells written to keccak2, but feedBranchHashesToKeccak (used by the deferred path)
    doesn't. This matches the original behavior (the deferred path never had that logging), so no issue — just noting it for clarity.
  4. Epilogue consolidation is correct — verified that max(upDepth-1, 0) is semantically equivalent to the original if upDepth > 0 { upDepth-1 } else { 0 } pattern across all three cases, including type safety
    (upDepth is int16, untyped 0 promotes correctly).
  5. nibblesLeftAfterUpdate recomputation is correct — foldBranch recomputes via bits.OnesCount16(hph.afterMap[row]), which is the same computation afterMapUpdateKind performed. Only the branch case used it, so
    discarding it with _ in fold() is fine.

@awskii

awskii commented Mar 26, 2026

Copy link
Copy Markdown
Member Author

@yperbasis deleted branch should be evicted from the cache, its a fix for existing bug.

@yperbasis yperbasis left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Minor nits

  1. Unused loop variable j in prepareBranchCells:
    for bitset, j := hph.afterMap[row], 0; bitset != 0; j++ {
  2. j is never read. Pre-existing issue from the original, but worth cleaning since we're touching this code. Replace with _ or remove.
  3. Double computation of nibblesLeftAfterUpdate: fold() discards it (updateKind, _ := afterMapUpdateKind(...)), then foldBranch recomputes it via bits.OnesCount16(hph.afterMap[row]). Not a bug — the values are
    identical — but you could pass it through to avoid the redundant call.

@awskii awskii added this pull request to the merge queue Mar 26, 2026
@awskii awskii mentioned this pull request Mar 26, 2026
15 tasks
Merged via the queue into main with commit 8e6ce4f Mar 26, 2026
35 checks passed
@awskii awskii deleted the awskii/fold-refactor branch March 26, 2026 18:22
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.

3 participants