|
| 1 | +// Licensed to the Apache Software Foundation (ASF) under one |
| 2 | +// or more contributor license agreements. See the NOTICE file |
| 3 | +// distributed with this work for additional information |
| 4 | +// regarding copyright ownership. The ASF licenses this file |
| 5 | +// to you under the Apache License, Version 2.0 (the |
| 6 | +// "License"); you may not use this file except in compliance |
| 7 | +// with the License. You may obtain a copy of the License at |
| 8 | +// |
| 9 | +// http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | +// |
| 11 | +// Unless required by applicable law or agreed to in writing, |
| 12 | +// software distributed under the License is distributed on an |
| 13 | +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| 14 | +// KIND, either express or implied. See the License for the |
| 15 | +// specific language governing permissions and limitations |
| 16 | +// under the License. |
| 17 | + |
| 18 | +//! Dynamic IN over dictionary floats, with and without signed-zero rewriting. |
| 19 | +//! An all-valid values bitmap can add work when computing logical nulls. |
| 20 | +//! Physical expressions preserve this exact Arrow layout, which SQL does not |
| 21 | +//! specify. Float32 suffices to exercise the bitmap mechanism. |
| 22 | +
|
| 23 | +use std::hint::black_box; |
| 24 | +use std::sync::Arc; |
| 25 | + |
| 26 | +use arrow::array::{ |
| 27 | + ArrayRef, AsArray, BooleanArray, DictionaryArray, Float32Array, Int32Array, |
| 28 | +}; |
| 29 | +use arrow::buffer::NullBuffer; |
| 30 | +use arrow::datatypes::Int32Type; |
| 31 | +use arrow::record_batch::RecordBatch; |
| 32 | +use criterion::{Criterion, criterion_group, criterion_main}; |
| 33 | +use datafusion_common::ScalarValue; |
| 34 | +use datafusion_physical_expr::{ |
| 35 | + PhysicalExpr, |
| 36 | + expressions::{InListExpr, col, lit}, |
| 37 | +}; |
| 38 | + |
| 39 | +fn benchmark(c: &mut Criterion) { |
| 40 | + const ROWS: usize = 8192; |
| 41 | + const CARDINALITY: usize = 16; |
| 42 | + const ZERO_KEY: usize = CARDINALITY / 2; |
| 43 | + |
| 44 | + for negative_zero in [false, true] { |
| 45 | + for all_valid_bitmap in [false, true] { |
| 46 | + let zero = if negative_zero { -0.0_f32 } else { 0.0 }; |
| 47 | + let values = Float32Array::new( |
| 48 | + (0..CARDINALITY) |
| 49 | + .map(|i| if i == ZERO_KEY { zero } else { (i + 1) as f32 }) |
| 50 | + .collect::<Vec<_>>() |
| 51 | + .into(), |
| 52 | + all_valid_bitmap.then(|| NullBuffer::new_valid(CARDINALITY)), |
| 53 | + ); |
| 54 | + let a: ArrayRef = Arc::new(DictionaryArray::<Int32Type>::new( |
| 55 | + Int32Array::from_iter_values((0..ROWS).map(|i| (i % CARDINALITY) as i32)), |
| 56 | + Arc::new(values), |
| 57 | + )); |
| 58 | + // Keep the RHS bitmap-free to isolate the LHS representation. |
| 59 | + let b: ArrayRef = Arc::new(DictionaryArray::<Int32Type>::new( |
| 60 | + Int32Array::from(vec![0; ROWS]), |
| 61 | + Arc::new(Float32Array::from(vec![0.0])), |
| 62 | + )); |
| 63 | + let batch = RecordBatch::try_from_iter([("a", a), ("b", b)]).unwrap(); |
| 64 | + let schema = batch.schema(); |
| 65 | + // A column forces dynamic evaluation; positive nonzero needles |
| 66 | + // cannot match the negative literals, so all four terms are visited. |
| 67 | + let expr = InListExpr::try_new( |
| 68 | + col("a", &schema).unwrap(), |
| 69 | + vec![ |
| 70 | + col("b", &schema).unwrap(), |
| 71 | + lit(ScalarValue::Float32(Some(-1.0))), |
| 72 | + lit(ScalarValue::Float32(Some(-2.0))), |
| 73 | + lit(ScalarValue::Float32(Some(-3.0))), |
| 74 | + ], |
| 75 | + false, |
| 76 | + &schema, |
| 77 | + ) |
| 78 | + .unwrap(); |
| 79 | + let expected: BooleanArray = (0..ROWS) |
| 80 | + .map(|i| Some(i % CARDINALITY == ZERO_KEY)) |
| 81 | + .collect(); |
| 82 | + let result = expr.evaluate(&batch).unwrap().into_array(ROWS).unwrap(); |
| 83 | + assert_eq!(result.as_boolean(), &expected); |
| 84 | + |
| 85 | + let name = format!( |
| 86 | + "dictionary_float_zero/negative_zero={negative_zero}/all_valid_bitmap={all_valid_bitmap}" |
| 87 | + ); |
| 88 | + c.bench_function(&name, |b| { |
| 89 | + // Reuse the original input; setup and validation are untimed. |
| 90 | + b.iter(|| black_box(expr.evaluate(black_box(&batch)).unwrap())) |
| 91 | + }); |
| 92 | + } |
| 93 | + } |
| 94 | +} |
| 95 | + |
| 96 | +criterion_group!(benches, benchmark); |
| 97 | +criterion_main!(benches); |
0 commit comments