Skip to content

Commit 3f4952e

Browse files
author
B Vadlamani
committed
Merge remote-tracking branch 'refs/remotes/origin/optimize_count_distinct' into optimize_count_distinct
2 parents 5a2918a + 61dc8e1 commit 3f4952e

33 files changed

Lines changed: 2618 additions & 1484 deletions

File tree

datafusion/core/tests/memory_limit/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ async fn sort_merge_join_spill() {
213213
.with_config(config)
214214
.with_disk_manager_builder(DiskManagerBuilder::default())
215215
.with_scenario(Scenario::AccessLogStreaming)
216+
.with_expected_success()
216217
.run()
217218
.await
218219
}

datafusion/datasource/src/file_scan_config.rs renamed to datafusion/datasource/src/file_scan_config/mod.rs

Lines changed: 14 additions & 538 deletions
Large diffs are not rendered by default.

datafusion/datasource/src/file_scan_config/sort_pushdown.rs

Lines changed: 577 additions & 0 deletions
Large diffs are not rendered by default.

datafusion/functions-nested/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ name = "array_min_max"
7979
harness = false
8080
name = "array_expression"
8181

82+
[[bench]]
83+
harness = false
84+
name = "arrays_zip"
85+
8286
[[bench]]
8387
harness = false
8488
name = "array_has"

datafusion/functions-nested/benches/array_remove.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ fn create_f64_list_array(
389389
if rng.random::<f64>() < null_density {
390390
None
391391
} else {
392-
Some(rng.random_range(0.0..array_size as f64))
392+
Some(rng.random_range(0..array_size as i64) as f64)
393393
}
394394
})
395395
.collect::<Float64Array>();
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
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+
use std::hint::black_box;
19+
use std::sync::Arc;
20+
21+
use arrow::array::{ArrayRef, Int64Array, ListArray};
22+
use arrow::buffer::{NullBuffer, OffsetBuffer};
23+
use arrow::datatypes::{DataType, Field};
24+
use criterion::{Criterion, criterion_group, criterion_main};
25+
use datafusion_common::config::ConfigOptions;
26+
use datafusion_expr::{ColumnarValue, ScalarFunctionArgs, ScalarUDFImpl};
27+
use datafusion_functions_nested::arrays_zip::ArraysZip;
28+
use rand::rngs::StdRng;
29+
use rand::{Rng, SeedableRng};
30+
31+
const NUM_ROWS: usize = 8192;
32+
const LIST_SIZE: usize = 10;
33+
const SEED: u64 = 42;
34+
35+
/// Build a ListArray of Int64 with `num_rows` rows, each containing
36+
/// `list_size` elements. If `null_density > 0`, that fraction of
37+
/// rows will be null at the list level.
38+
fn make_list_array(
39+
rng: &mut StdRng,
40+
num_rows: usize,
41+
list_size: usize,
42+
null_density: f64,
43+
) -> ArrayRef {
44+
let total = num_rows * list_size;
45+
let values: Vec<i64> = (0..total).map(|_| rng.random_range(0..1000i64)).collect();
46+
let values_array = Arc::new(Int64Array::from(values)) as ArrayRef;
47+
48+
let offsets: Vec<i32> = (0..=num_rows).map(|i| (i * list_size) as i32).collect();
49+
50+
let nulls = if null_density > 0.0 {
51+
let valid: Vec<bool> = (0..num_rows)
52+
.map(|_| rng.random::<f64>() >= null_density)
53+
.collect();
54+
Some(NullBuffer::from(valid))
55+
} else {
56+
None
57+
};
58+
59+
Arc::new(
60+
ListArray::try_new(
61+
Arc::new(Field::new_list_field(DataType::Int64, true)),
62+
OffsetBuffer::new(offsets.into()),
63+
values_array,
64+
nulls,
65+
)
66+
.unwrap(),
67+
)
68+
}
69+
70+
fn bench_arrays_zip(c: &mut Criterion, name: &str, null_density: f64) {
71+
let mut rng = StdRng::seed_from_u64(SEED);
72+
let arr1 = make_list_array(&mut rng, NUM_ROWS, LIST_SIZE, null_density);
73+
let arr2 = make_list_array(&mut rng, NUM_ROWS, LIST_SIZE, null_density);
74+
let arr3 = make_list_array(&mut rng, NUM_ROWS, LIST_SIZE, null_density);
75+
76+
let udf = ArraysZip::new();
77+
let args_vec = vec![
78+
ColumnarValue::Array(Arc::clone(&arr1)),
79+
ColumnarValue::Array(Arc::clone(&arr2)),
80+
ColumnarValue::Array(Arc::clone(&arr3)),
81+
];
82+
let return_type = udf
83+
.return_type(&[
84+
arr1.data_type().clone(),
85+
arr2.data_type().clone(),
86+
arr3.data_type().clone(),
87+
])
88+
.unwrap();
89+
let return_field = Arc::new(Field::new("f", return_type, true));
90+
let arg_fields: Vec<_> = (0..3)
91+
.map(|_| Arc::new(Field::new("a", arr1.data_type().clone(), true)))
92+
.collect();
93+
let config_options = Arc::new(ConfigOptions::default());
94+
95+
c.bench_function(name, |b| {
96+
b.iter(|| {
97+
black_box(
98+
udf.invoke_with_args(ScalarFunctionArgs {
99+
args: args_vec.clone(),
100+
arg_fields: arg_fields.clone(),
101+
number_rows: NUM_ROWS,
102+
return_field: Arc::clone(&return_field),
103+
config_options: Arc::clone(&config_options),
104+
})
105+
.expect("arrays_zip should work"),
106+
)
107+
})
108+
});
109+
}
110+
111+
fn criterion_benchmark(c: &mut Criterion) {
112+
bench_arrays_zip(c, "arrays_zip_no_nulls_8192", 0.0);
113+
bench_arrays_zip(c, "arrays_zip_10pct_nulls_8192", 0.1);
114+
}
115+
116+
criterion_group!(benches, criterion_benchmark);
117+
criterion_main!(benches);

datafusion/functions-nested/src/array_has.rs

Lines changed: 34 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use arrow::array::{
2121
Array, ArrayRef, AsArray, BooleanArray, BooleanBufferBuilder, Datum, Scalar,
2222
StringArrayType,
2323
};
24-
use arrow::buffer::BooleanBuffer;
24+
use arrow::buffer::{BooleanBuffer, NullBuffer};
2525
use arrow::datatypes::DataType;
2626
use arrow::row::{RowConverter, Rows, SortField};
2727
use datafusion_common::cast::{as_fixed_size_list_array, as_generic_list_array};
@@ -314,7 +314,7 @@ impl<'a> ArrayWrapper<'a> {
314314
}
315315
}
316316

317-
fn nulls(&self) -> Option<&arrow::buffer::NullBuffer> {
317+
fn nulls(&self) -> Option<&NullBuffer> {
318318
match self {
319319
ArrayWrapper::FixedSizeList(arr) => arr.nulls(),
320320
ArrayWrapper::List(arr) => arr.nulls(),
@@ -327,20 +327,21 @@ fn array_has_dispatch_for_array<'a>(
327327
haystack: ArrayWrapper<'a>,
328328
needle: &ArrayRef,
329329
) -> Result<ArrayRef> {
330-
let mut boolean_builder = BooleanArray::builder(haystack.len());
330+
let combined_nulls = NullBuffer::union(haystack.nulls(), needle.nulls());
331+
let mut result = BooleanBufferBuilder::new(haystack.len());
331332
for (i, arr) in haystack.iter().enumerate() {
332-
if arr.is_none() || needle.is_null(i) {
333-
boolean_builder.append_null();
333+
if combined_nulls.as_ref().is_some_and(|n| n.is_null(i)) {
334+
result.append(false);
334335
continue;
335336
}
336337
let arr = arr.unwrap();
337338
let is_nested = arr.data_type().is_nested();
338339
let needle_row = Scalar::new(needle.slice(i, 1));
339340
let eq_array = compare_with_eq(&arr, &needle_row, is_nested)?;
340-
boolean_builder.append_value(eq_array.true_count() > 0);
341+
result.append(eq_array.true_count() > 0);
341342
}
342343

343-
Ok(Arc::new(boolean_builder.finish()))
344+
Ok(Arc::new(BooleanArray::new(result.finish(), combined_nulls)))
344345
}
345346

346347
fn array_has_dispatch_for_scalar(
@@ -435,9 +436,8 @@ fn general_array_has_for_all_and_any<'a>(
435436
let h_offsets: Vec<usize> = haystack.offsets().collect();
436437
let n_offsets: Vec<usize> = needle.offsets().collect();
437438

438-
let h_nulls = haystack.nulls();
439-
let n_nulls = needle.nulls();
440-
let mut builder = BooleanArray::builder(num_rows);
439+
let combined_nulls = NullBuffer::union(haystack.nulls(), needle.nulls());
440+
let mut result = BooleanBufferBuilder::new(num_rows);
441441

442442
for chunk_start in (0..num_rows).step_by(ROW_CONVERSION_CHUNK_SIZE) {
443443
let chunk_end = (chunk_start + ROW_CONVERSION_CHUNK_SIZE).min(num_rows);
@@ -460,13 +460,11 @@ fn general_array_has_for_all_and_any<'a>(
460460
let chunk_n_rows = converter.convert_columns(&[n_vals])?;
461461

462462
for i in chunk_start..chunk_end {
463-
if h_nulls.is_some_and(|n| n.is_null(i))
464-
|| n_nulls.is_some_and(|n| n.is_null(i))
465-
{
466-
builder.append_null();
463+
if combined_nulls.as_ref().is_some_and(|n| n.is_null(i)) {
464+
result.append(false);
467465
continue;
468466
}
469-
builder.append_value(general_array_has_all_and_any_kernel(
467+
result.append(general_array_has_all_and_any_kernel(
470468
&chunk_h_rows,
471469
(h_offsets[i] - h_elem_start)..(h_offsets[i + 1] - h_elem_start),
472470
&chunk_n_rows,
@@ -476,7 +474,7 @@ fn general_array_has_for_all_and_any<'a>(
476474
}
477475
}
478476

479-
Ok(Arc::new(builder.finish()))
477+
Ok(Arc::new(BooleanArray::new(result.finish(), combined_nulls)))
480478
}
481479

482480
// String comparison for array_has_all and array_has_any
@@ -490,9 +488,8 @@ fn array_has_all_and_any_string_internal<'a>(
490488
let h_offsets: Vec<usize> = haystack.offsets().collect();
491489
let n_offsets: Vec<usize> = needle.offsets().collect();
492490

493-
let h_nulls = haystack.nulls();
494-
let n_nulls = needle.nulls();
495-
let mut builder = BooleanArray::builder(num_rows);
491+
let combined_nulls = NullBuffer::union(haystack.nulls(), needle.nulls());
492+
let mut result = BooleanBufferBuilder::new(num_rows);
496493

497494
for chunk_start in (0..num_rows).step_by(ROW_CONVERSION_CHUNK_SIZE) {
498495
let chunk_end = (chunk_start + ROW_CONVERSION_CHUNK_SIZE).min(num_rows);
@@ -513,25 +510,23 @@ fn array_has_all_and_any_string_internal<'a>(
513510
let chunk_n_strings = string_array_to_vec(n_vals.as_ref());
514511

515512
for i in chunk_start..chunk_end {
516-
if h_nulls.is_some_and(|n| n.is_null(i))
517-
|| n_nulls.is_some_and(|n| n.is_null(i))
518-
{
519-
builder.append_null();
513+
if combined_nulls.as_ref().is_some_and(|n| n.is_null(i)) {
514+
result.append(false);
520515
continue;
521516
}
522517
let h_start = h_offsets[i] - h_elem_start;
523518
let h_end = h_offsets[i + 1] - h_elem_start;
524519
let n_start = n_offsets[i] - n_elem_start;
525520
let n_end = n_offsets[i + 1] - n_elem_start;
526-
builder.append_value(array_has_string_kernel(
521+
result.append(array_has_string_kernel(
527522
&chunk_h_strings[h_start..h_end],
528523
&chunk_n_strings[n_start..n_end],
529524
comparison_type,
530525
));
531526
}
532527
}
533528

534-
Ok(Arc::new(builder.finish()))
529+
Ok(Arc::new(BooleanArray::new(result.finish(), combined_nulls)))
535530
}
536531

537532
fn array_has_all_and_any_dispatch<'a>(
@@ -687,16 +682,16 @@ impl<'a> ScalarStringLookup<'a> {
687682
fn array_has_any_string_inner<'a, C: StringArrayType<'a> + Copy>(
688683
col_strings: C,
689684
col_offsets: &[usize],
690-
col_nulls: Option<&arrow::buffer::NullBuffer>,
685+
col_nulls: Option<&NullBuffer>,
691686
has_null_scalar: bool,
692687
scalar_lookup: &ScalarStringLookup<'_>,
693688
) -> ArrayRef {
694689
let num_rows = col_offsets.len() - 1;
695-
let mut builder = BooleanArray::builder(num_rows);
690+
let mut result = BooleanBufferBuilder::new(num_rows);
696691

697692
for i in 0..num_rows {
698693
if col_nulls.is_some_and(|v| v.is_null(i)) {
699-
builder.append_null();
694+
result.append(false);
700695
continue;
701696
}
702697
let start = col_offsets[i];
@@ -708,10 +703,10 @@ fn array_has_any_string_inner<'a, C: StringArrayType<'a> + Copy>(
708703
scalar_lookup.contains(col_strings.value(j))
709704
}
710705
});
711-
builder.append_value(found);
706+
result.append(found);
712707
}
713708

714-
Arc::new(builder.finish())
709+
Arc::new(BooleanArray::new(result.finish(), col_nulls.cloned()))
715710
}
716711

717712
/// General scalar fast path for `array_has_any`, using RowConverter for
@@ -734,7 +729,7 @@ fn array_has_any_with_scalar_general(
734729
let col_offsets: Vec<usize> = col_list.offsets().collect();
735730
let col_nulls = col_list.nulls();
736731

737-
let mut builder = BooleanArray::builder(col_list.len());
732+
let mut result = BooleanBufferBuilder::new(col_list.len());
738733
let num_scalar = scalar_rows.num_rows();
739734

740735
if num_scalar > SCALAR_SMALL_THRESHOLD {
@@ -745,38 +740,39 @@ fn array_has_any_with_scalar_general(
745740

746741
for i in 0..col_list.len() {
747742
if col_nulls.is_some_and(|v| v.is_null(i)) {
748-
builder.append_null();
743+
result.append(false);
749744
continue;
750745
}
751746
let start = col_offsets[i];
752747
let end = col_offsets[i + 1];
753748
let found =
754749
(start..end).any(|j| scalar_set.contains(col_rows.row(j).as_ref()));
755-
builder.append_value(found);
750+
result.append(found);
756751
}
757752
} else {
758753
// Small scalar: linear scan avoids HashSet hashing overhead
759754
for i in 0..col_list.len() {
760755
if col_nulls.is_some_and(|v| v.is_null(i)) {
761-
builder.append_null();
756+
result.append(false);
762757
continue;
763758
}
764759
let start = col_offsets[i];
765760
let end = col_offsets[i + 1];
766761
let found = (start..end)
767762
.any(|j| (0..num_scalar).any(|k| col_rows.row(j) == scalar_rows.row(k)));
768-
builder.append_value(found);
763+
result.append(found);
769764
}
770765
}
771766

772-
let result: ArrayRef = Arc::new(builder.finish());
767+
let output: ArrayRef =
768+
Arc::new(BooleanArray::new(result.finish(), col_nulls.cloned()));
773769

774770
if is_scalar_output {
775771
Ok(ColumnarValue::Scalar(ScalarValue::try_from_array(
776-
&result, 0,
772+
&output, 0,
777773
)?))
778774
} else {
779-
Ok(ColumnarValue::Array(result))
775+
Ok(ColumnarValue::Array(output))
780776
}
781777
}
782778

0 commit comments

Comments
 (0)