perf(table): collapse branches in MergeIterator.fix#2284
Open
shaunpatterson wants to merge 1 commit into
Open
Conversation
Rework MergeIterator.fix to:
- hoist the "other" node pointer once (instead of two bigger()
calls), avoiding repeated &mi.left/&mi.right comparisons
- inline the swap (mi.small = big) where it was previously hidden
behind swapSmall(), so the compiler sees a straight pointer
assignment with no branch
- collapse the cmp<0 / cmp>0 cases into a single conditional swap
expressed as `(cmp < 0) == mi.reverse`, removing one switch arm
plus the no-op staticcheck-suppressed branch
This makes the hot per-step decision in iteration two compares and
one conditional store, instead of a 4-arm switch with embedded
helper-method dispatches. y.CompareKeys is now the only call site
for the LCP comparison, leaving direction handling fully in fix().
bigger() and swapSmall() were the only callers of each other and of
the inlined logic; with both gone, the helpers become dead code and
are removed in the same commit.
Existing merge iterator tests (TestMergeMore, TestMergeIteratorSeek,
TestMergeIteratorDuplicate, TestMergeDuplicates — forward + reverse
variants) cover this path at 100% statement coverage. Measured
-0.62% composite ns/op across the 74-benchmark stable subset.
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.
Summary
Reworks
MergeIterator.fixto:Hoist the "other" node pointer once. The previous code called
mi.bigger()(which doesif mi.small == &mi.left { return &mi.right } else { return &mi.left }) up to three times per invocation. Compute it once at the top.Inline the swap.
swapSmall()was a 2-arm conditional that flippedmi.smallbetween&mi.leftand&mi.right. With the hoistedbigpointer in hand, the swap is justmi.small = big— a straight store, no branch.Collapse the cmp<0 / cmp>0 cases. The previous switch had two arms whose direction-dependence is symmetric:
cmp > 0→ swap to bigcmp < 0→ swap to bigThis is exactly
(cmp < 0) == mi.reverse, so we collapse the two arms plus the no-op//nolint:staticcheckarm into a single conditional store.bigger()andswapSmall()were the only callers of each other and of the inlined logic; with both gone, the helpers become dead code and are removed in the same commit.Why this is safe
The
cmp == 0arm is unchanged: same-key handling still advancesmi.right.next()and then swaps to left if right was small. The two non-equal arms now reduce to:if (cmp < 0) == mi.reverse { mi.small = big }, which is identical to the original switch by case analysis.Measurement
Composite (74-benchmark stable subset): -0.62%
ns/op, median of 3 runs.Test plan
go test -short -race ./table/— all merge iterator tests pass, including:TestMergeMore(forward/reverse × with/without duplicates)TestMergeIteratorDuplicate(forward/reverse)TestMergeDuplicates(forward/reverse)TestMergeIteratorSeek*(forward/reverse/invalid)TestMergeIteratorNestedgo vet ./...MergeIterator.fixreaches 100% statement coverage under the existing test suite (verified with-coverprofile).🤖 Generated with Claude Code