Skip to content

Optimize skewed intersect lists - #828

Open
rahul-iyer wants to merge 2 commits into
LadybugDB:mainfrom
rahul-iyer:feature/intersect-fast-path
Open

Optimize skewed intersect lists#828
rahul-iyer wants to merge 2 commits into
LadybugDB:mainfrom
rahul-iyer:feature/intersect-fast-path

Conversation

@rahul-iyer

Copy link
Copy Markdown
Contributor

Ladybug currently intersects two sorted adjacency lists using a scalar merge. When one list is much smaller than the other, this can scan most of the larger list one element at a time.

This PR adds a galloping-search fast path for skewed intersections. It jumps through the larger list exponentially and then uses binary search to find each value from the smaller list.

Galloping is selected automatically when:

  • The larger list has at least 64 entries.
  • The larger list is at least 8× the size of the smaller list.
  • Both lists contain nodes from the same table.

Small, balanced, and mixed-table lists continue using the original scalar merge.

Isolated kernel performance

These numbers measure only list copying and intersection, not complete query execution.

List sizes Kernel speedup across overlap levels
16 × 128 2.34×–2.70×
16 × 256 2.99×–3.96×
32 × 2048 7.45×–7.86×
256 × 2048 2.24×–2.49×

@adsharma

Copy link
Copy Markdown
Contributor

Looks good. Some alternative designs if someone were to revisit:

Planner-level fix ("pick HJ under skew") — more general in principle, but skew here is per-chunk runtime state (which probe tuple meets which 2048-chunk), invisible to plan-time cardinality estimates. The existing TODO in cost_model.cpp shows this is known-hard. Not actionable now.

Runtime-adaptive escalation (start scalar; switch to galloping after N consecutive right-advances) — arguably more maintainable.

SIMD/shuffle techniques — premature at 2048-element scale; nodeID_t is 16 bytes so it would also require a repack after the homogeneity check.

@adsharma

Copy link
Copy Markdown
Contributor

Minor:

LBUG_API on both functions exports processor internals into the shared ABI purely so a test can reach them. Since the kernels are tiny and called from one hot site, consider a header-only implementation in src/include (inlining also helps here — the call is inside the vector-at-a-time hot loop), which removes the export and the new .cpp entirely. Minor, but it simplifies the file layout the PR adds.

Also could you run a cypher query that shows the benefit vs micro benchmark?

@adsharma
adsharma self-requested a review August 21, 2026 20:23

@adsharma adsharma left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Requesting changes for the LBUG_API and potential refactoring into a header only.

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.

2 participants