IN LIST: reuse primitive filters for FixedSizeBinary - #24102
IN LIST: reuse primitive filters for FixedSizeBinary#24102geoffreyclaude wants to merge 2 commits into
Conversation
9915d25 to
6f820d7
Compare
|
run benchmark in_list |
|
🤖 Benchmark running (GKE) | trigger CPU Details (lscpu)Comparing codex/in-list-fixed-size-binary-hashset (6f820d7) to b6cccfe diff Run configurationrun benchmark in_list
baseline:
ref: "b6cccfeef1a78ab424ffec87afb40958c33fb897"Results will be posted here when complete File an issue against this benchmark runner |
|
🤖 Benchmark completed (GKE) | trigger Instance: Comparing codex/in-list-fixed-size-binary-hashset (6f820d7) to b6cccfe diff Run configurationrun benchmark in_list
baseline:
ref: "b6cccfeef1a78ab424ffec87afb40958c33fb897"CPU Details (lscpu)Details
Resource Usagein_list — base (merge-base)
in_list — branch
File an issue against this benchmark runner |
|
run benchmark in_list_strategy |
|
🤖 Benchmark running (GKE) | trigger CPU Details (lscpu)Comparing codex/in-list-fixed-size-binary-hashset (6f820d7) to 426b351 (merge-base) diff Run configurationrun benchmark in_list_strategyResults will be posted here when complete File an issue against this benchmark runner |
|
🤖 Benchmark running (GKE) | trigger CPU Details (lscpu)Comparing codex/in-list-fixed-size-binary-hashset (6f820d7) to b6cccfe diff Run configurationrun benchmark in_list_strategy
baseline:
ref: "b6cccfeef1a78ab424ffec87afb40958c33fb897"Results will be posted here when complete File an issue against this benchmark runner |
|
🤖 Benchmark completed (GKE) | trigger Instance: Comparing codex/in-list-fixed-size-binary-hashset (6f820d7) to 426b351 (merge-base) diff Run configurationrun benchmark in_list_strategyCPU Details (lscpu)Details
Resource Usagein_list_strategy — base (merge-base)
in_list_strategy — branch
File an issue against this benchmark runner |
|
🤖 Benchmark completed (GKE) | trigger Instance: Comparing codex/in-list-fixed-size-binary-hashset (6f820d7) to b6cccfe diff Run configurationrun benchmark in_list_strategy
baseline:
ref: "b6cccfeef1a78ab424ffec87afb40958c33fb897"CPU Details (lscpu)Details
Resource Usagein_list_strategy — base (merge-base)
in_list_strategy — branch
File an issue against this benchmark runner |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #24102 +/- ##
==========================================
- Coverage 81.32% 81.32% -0.01%
==========================================
Files 1117 1118 +1
Lines 396269 396517 +248
Branches 396269 396517 +248
==========================================
+ Hits 322260 322454 +194
- Misses 55186 55202 +16
- Partials 18823 18861 +38 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
0ae4133 to
89d1595
Compare
59667db to
0a20207
Compare
12224fb to
2117edf
Compare
|
🤖 Benchmark completed (GKE) | trigger Instance: Comparing b2e4ecc (b2e4ecc) to 3f95830 diff Run configurationrun benchmark in_list_strategy
baseline:
ref: "3f958303e4e4ea4610ce91434f0c38ab75c99658"
changed:
ref: "b2e4eccd68ce8d694b987cc6667dabcbe53f1582"CPU Details (lscpu)Details
Resource Usagein_list_strategy — base (merge-base)
in_list_strategy — branch
File an issue against this benchmark runner |
b2e4ecc to
df7980b
Compare
9b316c1 to
7ff49f9
Compare
…#24283) ## Which issue does this PR close? - Part of apache#19241. - Stacked on [apache#23014](apache#23014). - Next in stack: [apache#24102](apache#24102). ## Rationale for this change apache#23014 adds fast direct comparisons for short primitive `IN` lists. For longer lists, DataFusion uses a bitmap, a hash set, or another specialized filter when one exists, and otherwise uses the general filter. apache#24102 needs to make that same choice after reading `FixedSizeBinary` values as same-width primitive keys. Today, the choice is part of the native primitive path, so apache#24102 would have to repeat its limits and fallback rules. This PR moves that choice beside the primitive filters and makes it reusable. The limits count only non-null list values. A separate commit uses one `PrimitiveHashSetFilter<T, K>` for the existing integer and floating-point hash-set paths. Integers keep their native keys; `Float32` and `Float64` keep their bitwise keys, including distinct signed zeros and NaN encodings. Filter selection and SQL behavior are unchanged. The only new lookup path is for `Decimal128`. Lists with at most four non-null values still use direct comparisons. Larger lists now use a hash set over the stored `i128` values instead of the general Arrow filter. Values are not rescaled, and the existing precision, scale, and null rules are unchanged. apache#24102 also uses this path for 16-byte `FixedSizeBinary` values. ## What changes are included in this PR? - Makes primitive filter selection reusable by apache#24102. - Uses one primitive hash-set implementation for integer and floating-point values, while preserving their existing key semantics. - Uses `PrimitiveHashSetFilter<Decimal128Type>` for `Decimal128` lists with more than four non-null values. - Adds four `Decimal128` benchmark cases at list sizes 5 and 64. ## Are these changes tested? Tests cover the direct-comparison boundary, an all-null list, and bitwise float hash keys, including signed zero and distinct NaN encodings. Existing end-to-end Decimal tests cover hits, misses, nulls, `IN`, and `NOT IN`. ## Are there any user-facing changes? No. SQL results and public APIs are unchanged. ## Benchmark snapshot The benchmark uses list size 5, the first size above the direct-comparison limit, and a larger size of 64. Each size is measured with 0% and 50% matches. Filter construction is outside the timed loop. Lower is better. An [automated run](apache#24283 (comment)) compared this PR's [benchmark-only first commit](apache@8649e80) with the [completed implementation](apache@510bf3e). The benchmark source is identical in both states, so the comparison isolates the production changes. All four cases improved, with a 34.2% geometric-mean reduction in execution time. | Benchmark | Before | After | Change | |---|---:|---:|---:| | `primitive/decimal128/large_list/list=5/match=0%` | 20.8 us | 13.5 us | -35.1% (1.54x faster) | | `primitive/decimal128/large_list/list=5/match=50%` | 34.3 us | 26.6 us | -22.4% (1.29x faster) | | `primitive/decimal128/large_list/list=64/match=0%` | 20.3 us | 13.2 us | -35.0% (1.54x faster) | | `primitive/decimal128/large_list/list=64/match=50%` | 34.8 us | 19.9 us | -42.8% (1.75x faster) |
7ff49f9 to
77498a0
Compare
|
run benchmark in_list_strategy |
|
🤖 Benchmark running (GKE) | trigger CPU Details (lscpu)Comparing codex/in-list-fixed-size-binary-hashset (77498a0) to a1c8762 diff Run configurationrun benchmark in_list_strategy
baseline:
ref: "a1c876203c7655dae4d218ad51b53067b3110b96"Results will be posted here when complete File an issue against this benchmark runner |
|
🤖 Benchmark completed (GKE) | trigger Instance: Comparing codex/in-list-fixed-size-binary-hashset (77498a0) to a1c8762 diff Run configurationrun benchmark in_list_strategy
baseline:
ref: "a1c876203c7655dae4d218ad51b53067b3110b96"CPU Details (lscpu)Details
Resource Usagein_list_strategy — base (merge-base)
in_list_strategy — branch
File an issue against this benchmark runner |
7770d0b to
20399e4
Compare
20399e4 to
1217f5e
Compare
|
run benchmark in_list_strategy |
|
🤖 Benchmark running (GKE) | trigger CPU Details (lscpu)Comparing codex/in-list-fixed-size-binary-hashset (1217f5e) to ab4b5e0 diff Run configurationrun benchmark in_list_strategy
baseline:
ref: "ab4b5e0d964dd2b76a68ba515764d186bb476298"Results will be posted here when complete File an issue against this benchmark runner |
|
🤖 Benchmark completed (GKE) | trigger Instance: Comparing codex/in-list-fixed-size-binary-hashset (1217f5e) to ab4b5e0 diff Run configurationrun benchmark in_list_strategy
baseline:
ref: "ab4b5e0d964dd2b76a68ba515764d186bb476298"CPU Details (lscpu)Details
Resource Usagein_list_strategy — base (merge-base)
in_list_strategy — branch
File an issue against this benchmark runner |
Which issue does this PR close?
Rationale for this change
Today,
FixedSizeBinaryIN LISTexpressions use the general filter, even when their width matches a primitive type with a faster specialized filter. This makes repeated evaluation slower than necessary for common fixed-width values.A
FixedSizeBinary(N)value is exactlyNbytes long. For widths 1, 2, 4, 8, and 16, DataFusion already has fastIN LISTfilters for primitive values of the same size. This PR letsFixedSizeBinaryreuse them.For example, a 4-byte value can be read as a
UInt32lookup key. This does not turn the value into a number or perform arithmetic. The list and input use the same mapping, so two keys match exactly when the original bytes match. Machine byte order may change the numeric interpretation, but it does not change equality.The filter depends on the width and the number of non-null values in the list:
UInt8UInt16UInt32UInt64Decimal128(i128storage)#24283 provides this choice, so this PR only needs to map each supported width to the matching primitive key. Other widths keep using the general filter.
Arrow buffers are normally aligned for the selected primitive key and can be read without copying. If a buffer is not aligned for that key type, the list is copied once when the filter is built, while an unaligned input is copied before each evaluation. After unwrapping any dictionary, the input must have the same
FixedSizeBinary(N)type as the list.What changes are included in this PR?
FixedSizeBinarywidths 1, 2, 4, 8, and 16.IN, andNOT IN.FixedSizeBinarybenchmark coverage from 8 to 14 cases, including both match rates for an input unaligned fori128access.Are these changes tested?
Tests cover every supported width around its direct-comparison limit; bitmap and hash-set hits and misses; slices and dictionaries; input and list nulls;
INandNOT IN; wrong widths and Arrow types; unsupported widths; and aligned and unaligned buffers.Are there any user-facing changes?
No. SQL results and public APIs are unchanged.
Benchmark snapshot
The benchmark repeatedly evaluates an 8,192-row batch; filter construction is outside the timed loop. Each selected configuration is measured with 0% and 50% matches:
fsb1/list=16exercises the direct-comparison boundary for 1-byte keys.fsb2/list=64exercises the bitmap path.fsb16/list=4exercises the direct-comparison boundary for 16-byte keys.fsb16/list=64,list=256, andlist=10000exercise hash-set scaling.fsb16/list=64/input=unalignedexercises the per-evaluation input-copy path for buffers unaligned fori128access.Lower is better.
An automated run compared this PR's benchmark-only first commit with the completed implementation. The benchmark source is identical in both states, so the comparison isolates the production changes.
Across this selected case set, speedups range from 1.51x to 12.11x, and the geometric mean is 3.17x (68.5% lower execution time).
The aligned and unaligned
fsb16/list=64cases contain identical values, so their results show the per-evaluation input-copy overhead: +1.6 us (12%) with 0% matches and +3.5 us (17%) with 50% matches.fixed_size_binary/fsb1/list=16/match=0%fixed_size_binary/fsb1/list=16/match=50%fixed_size_binary/fsb2/list=64/match=0%fixed_size_binary/fsb2/list=64/match=50%fixed_size_binary/fsb16/list=4/match=0%fixed_size_binary/fsb16/list=4/match=50%fixed_size_binary/fsb16/list=64/match=0%fixed_size_binary/fsb16/list=64/match=50%fixed_size_binary/fsb16/list=256/match=0%fixed_size_binary/fsb16/list=256/match=50%fixed_size_binary/fsb16/list=10000/match=0%fixed_size_binary/fsb16/list=10000/match=50%fixed_size_binary/fsb16/list=64/match=0%/input=unalignedfixed_size_binary/fsb16/list=64/match=50%/input=unaligned