Skip to content

perf: compute cardinality from list/map offsets - #25079

Open
neilconway wants to merge 1 commit into
apache:mainfrom
neilconway:neilc/perf-cardinality-from-offsets
Open

perf: compute cardinality from list/map offsets#25079
neilconway wants to merge 1 commit into
apache:mainfrom
neilconway:neilc/perf-cardinality-from-offsets

Conversation

@neilconway

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Rationale for this change

cardinality was implemented by iterating over the values of the input GenericListArray, creating a slice for each logical row, and then fetching the length of the slice. This incurs a lot of unnecessary per-row overhead; we can instead determine the cardinality of each list by looking at the OffsetBuffer. This is roughly 100x-500x faster.

For non-nested lists, this is straightforward. It would be possible to use a similar approach to compute the cardinality of nested lists, but for now this PR falls back to the previous approach for that case.

Benchmarks: (M4 Max)

list/valid/8192x32, 203.951 µs → 0.984 µs, -99.52%
large_list/nullable/8192x32, 172.699 µs → 1.049 µs, -99.39%
map/valid/8192x32, 471.074 µs → 0.958 µs, -99.80%

What changes are included in this PR?

  • Add optimized implementation of cardinality for non-nested lists/maps
  • Add unit tests
  • Add benchmark

What is the testing strategy for this PR?

Existing tests pass; new tests added.

Are there any user-facing changes?

No.

`cardinality` was implemented by iterating over the `values` of the
input `GenericListArray`, creating a slice for each logical row, and
then fetching the length of the slice. This incurs a lot of unnecessary
per-row overhead; we can instead determine the cardinality of each list
by looking at the `OffsetBuffer`.

For non-nested lists, this is straightforward. It would be possible to
use a similar approach to compute the cardinality of nested lists, but
for now this PR falls back to the previous approach for that case.

Add a benchmark, plus tests for lists (sliced, unsliced, LargeList) and
maps, including nulls and empty batches.

Benchmarks: (M4 Max)

  list/valid/8192x32, 203.951 µs → 0.984 µs, -99.52%
  large_list/nullable/8192x32, 172.699 µs → 1.049 µs, -99.39%
  map/valid/8192x32, 471.074 µs → 0.958 µs, -99.80%
@github-actions github-actions Bot added the functions Changes to functions implementation label Sep 8, 2026
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.72131% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.74%. Comparing base (a5c809f) to head (6951d8a).

Files with missing lines Patch % Lines
datafusion/functions-nested/src/cardinality.rs 96.72% 0 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #25079      +/-   ##
==========================================
- Coverage   81.74%   81.74%   -0.01%     
==========================================
  Files        1128     1128              
  Lines      416644   416698      +54     
  Branches   416644   416698      +54     
==========================================
+ Hits       340592   340635      +43     
- Misses      55995    56000       +5     
- Partials    20057    20063       +6     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

functions Changes to functions implementation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Compute list/map cardinality from offsets

2 participants