IN LIST: add UInt8 bitmap filter (#23011) · apache/datafusion@d0f8c34 · GitHub
Skip to content

Commit d0f8c34

Browse files
IN LIST: add UInt8 bitmap filter (#23011)
## Which issue does this PR close? - Part of #19241. - Stacked on #21927. - Next in stack: #23012. - Extracted from #19390. ## Rationale for this change `IN LIST` evaluates expressions like `x IN (1, 3, 7)`. The list on the right is fixed, so DataFusion can precompute a small lookup structure once and then reuse it for every input row. For `UInt8`, there are only 256 possible values: 0 through 255. That means the lookup can be a tiny checklist with one bit per possible value: - If the list contains `3`, set bit `3`. - If the list contains `7`, set bit `7`. - To check whether an input value is present, read that one bit. So instead of hashing each input value or comparing it against the list, membership becomes one indexed bit test. The bitmap is only 32 bytes, because 256 bits = 32 bytes. This PR adds the first specialized primitive path in the stack as a concrete `UInt8` filter. The `UInt16` version is added in #23012, and the shared bitmap abstraction is introduced only after both concrete implementations are visible in #23035. ## What changes are included in this PR? - Adds `UInt8BitmapFilter`, a 32-byte bitmap built from the non-null constants in the `IN` list. - Routes `UInt8` constant-list filtering to that bitmap path. - Keeps the same SQL null behavior as the generic path for both `IN` and `NOT IN`. - Moves shared dictionary-needle handling into `static_filter.rs`, so specialized filters can reuse it consistently. - Adds focused tests for `UInt8` null handling and dictionary-encoded needles. ## Are these changes tested? Yes. - `cargo fmt --all` - `cargo test -p datafusion-physical-expr bitmap_filter_u8 --lib` - `cargo test -p datafusion-physical-expr in_list_int_types --lib` - `cargo clippy -p datafusion-physical-expr --all-targets --all-features -- -D warnings` ## Are there any user-facing changes? No. This is an internal performance optimization only. <!-- codex-benchmark-start --> ## Local benchmark snapshot Benchmark command: ```bash cargo bench -p datafusion-physical-expr --profile release-nonlto --bench in_list_strategy -- --save-baseline <name> ``` Method: compare adjacent saved baselines using raw Criterion sample minima (`min(time / iters)`). Lower is better; changes within +/-5% are treated as noise. These numbers were not rerun after splitting the bitmap abstraction into #23035. Compared baselines: [#21927](#21927) -> [#23011](#23011) Relevant scope: UInt8 narrow-integer rows. Summary: 5 relevant rows, 5 faster, 0 slower, 0 within +/-5%. | Benchmark | Before | After | Change | |---|---:|---:|---:| | `narrow_integer/u8/list=16/match=0%` | 20.39 us | 3.94 us | -80.7% (5.18x faster) | | `narrow_integer/u8/list=16/match=50%` | 38.38 us | 3.98 us | -89.6% (9.65x faster) | | `narrow_integer/u8/list=4/match=0%` | 18.18 us | 3.93 us | -78.4% (4.62x faster) | | `narrow_integer/u8/list=4/match=50%` | 34.63 us | 3.96 us | -88.6% (8.75x faster) | | `nulls/narrow_integer/u8/list=16/match=50%/nulls=20%` | 37.12 us | 4.16 us | -88.8% (8.93x faster) | <!-- codex-benchmark-end --> --------- Co-authored-by: Andrew Lamb <andrew@nerdnetworks.org>
1 parent 96ff37a commit d0f8c34

3 files changed

Lines changed: 166 additions & 24 deletions

File tree

datafusion/physical-expr/src/expressions/in_list/primitive_filter.rs

Lines changed: 148 additions & 23 deletions

datafusion/physical-expr/src/expressions/in_list/static_filter.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,20 @@ pub(super) trait StaticFilter {
3535
/// implementation unwraps the dictionary and operates on its values.
3636
fn contains(&self, v: &dyn Array, negated: bool) -> Result<BooleanArray>;
3737
}
38+
39+
/// Evaluate dictionary-encoded needles by applying a filter to dictionary
40+
/// values and remapping the result through the keys.
41+
macro_rules! handle_dictionary {
42+
($self:ident, $v:ident, $negated:ident) => {
43+
arrow::array::downcast_dictionary_array! {
44+
$v => {
45+
let values_contains = $self.contains($v.values().as_ref(), $negated)?;
46+
let result = arrow::compute::take(&values_contains, $v.keys(), None)?;
47+
return Ok(arrow::array::downcast_array(result.as_ref()))
48+
}
49+
_ => {}
50+
}
51+
};
52+
}
53+
54+
pub(super) use handle_dictionary;

datafusion/physical-expr/src/expressions/in_list/strategy.rs

Lines changed: 1 addition & 1 deletion

0 commit comments

Comments
 (0)