From cbb3eb15322303ce9fc0b9112e8c8cb4db58411b Mon Sep 17 00:00:00 2001 From: zhuqi-lucas <821684824@qq.com> Date: Sat, 19 Jul 2025 19:58:16 +0800 Subject: [PATCH 1/2] add bench mark for ByteViewGroupValueBuilder --- datafusion/physical-plan/Cargo.toml | 4 + .../benches/aggregate_vectorized.rs | 89 +++++++++++++++++++ .../src/aggregates/group_values/mod.rs | 2 +- .../group_values/multi_group_by/bytes_view.rs | 6 ++ .../group_values/multi_group_by/mod.rs | 7 +- 5 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 datafusion/physical-plan/benches/aggregate_vectorized.rs diff --git a/datafusion/physical-plan/Cargo.toml b/datafusion/physical-plan/Cargo.toml index 095ee78cd0d6..97b1cff77739 100644 --- a/datafusion/physical-plan/Cargo.toml +++ b/datafusion/physical-plan/Cargo.toml @@ -92,3 +92,7 @@ name = "spill_io" [[bench]] harness = false name = "sort_preserving_merge" + +[[bench]] +harness = false +name = "aggregate_vectorized" diff --git a/datafusion/physical-plan/benches/aggregate_vectorized.rs b/datafusion/physical-plan/benches/aggregate_vectorized.rs new file mode 100644 index 000000000000..6c27b7c61f5f --- /dev/null +++ b/datafusion/physical-plan/benches/aggregate_vectorized.rs @@ -0,0 +1,89 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use arrow::array::{Array, ArrayRef}; +use arrow::datatypes::StringViewType; +use arrow::util::bench_util::{ + create_string_view_array_with_len, create_string_view_array_with_max_len, +}; +use criterion::{criterion_group, criterion_main, Criterion}; +use datafusion_physical_plan::aggregates::group_values::multi_group_by::bytes_view::ByteViewGroupValueBuilder; +use datafusion_physical_plan::aggregates::group_values::multi_group_by::GroupColumn; +use std::sync::Arc; + +const SIZES: [usize; 3] = [1_000, 10_000, 100_000]; +const NULL_DENSITIES: [f32; 3] = [0.0, 0.1, 0.5]; + +fn bench_vectorized_append(c: &mut Criterion) { + let mut group = c.benchmark_group("ByteViewGroupValueBuilder_vectorized_append"); + + // Inlined-only scenarios + for &size in &SIZES { + for &null_density in &NULL_DENSITIES { + let input = create_string_view_array_with_len(size, null_density, 8, false); + let input: ArrayRef = Arc::new(input); + let id = format!("inlined_null_{null_density:.1}_size_{size}"); + group.bench_function(id, |b| { + b.iter(|| { + let mut builder = ByteViewGroupValueBuilder::::new(); + builder + .vectorized_append(&input, &(0..input.len()).collect::>()) + .unwrap(); + }); + }); + } + } + + // Mixed-length scenarios + for &size in &SIZES { + for &null_density in &NULL_DENSITIES { + let input = create_string_view_array_with_len(size, null_density, 64, true); + let input: ArrayRef = Arc::new(input); + let id = format!("mixed_null_{null_density:.1}_size_{size}"); + group.bench_function(id, |b| { + b.iter(|| { + let mut builder = ByteViewGroupValueBuilder::::new(); + builder + .vectorized_append(&input, &(0..input.len()).collect::>()) + .unwrap(); + }); + }); + } + } + + // Random max-length scenarios + for &size in &SIZES { + for &null_density in &NULL_DENSITIES { + let input = create_string_view_array_with_max_len(size, null_density, 400); + let input: ArrayRef = Arc::new(input); + let id = format!("random_null_{null_density:.1}_size_{size}"); + group.bench_function(id, |b| { + b.iter(|| { + let mut builder = ByteViewGroupValueBuilder::::new(); + builder + .vectorized_append(&input, &(0..input.len()).collect::>()) + .unwrap(); + }); + }); + } + } + + group.finish(); +} + +criterion_group!(benches, bench_vectorized_append); +criterion_main!(benches); diff --git a/datafusion/physical-plan/src/aggregates/group_values/mod.rs b/datafusion/physical-plan/src/aggregates/group_values/mod.rs index e09688649eab..f2f489b7223c 100644 --- a/datafusion/physical-plan/src/aggregates/group_values/mod.rs +++ b/datafusion/physical-plan/src/aggregates/group_values/mod.rs @@ -28,7 +28,7 @@ use datafusion_common::Result; use datafusion_expr::EmitTo; -pub(crate) mod multi_group_by; +pub mod multi_group_by; mod row; mod single_group_by; diff --git a/datafusion/physical-plan/src/aggregates/group_values/multi_group_by/bytes_view.rs b/datafusion/physical-plan/src/aggregates/group_values/multi_group_by/bytes_view.rs index 63018874a1e4..599268baec67 100644 --- a/datafusion/physical-plan/src/aggregates/group_values/multi_group_by/bytes_view.rs +++ b/datafusion/physical-plan/src/aggregates/group_values/multi_group_by/bytes_view.rs @@ -71,6 +71,12 @@ pub struct ByteViewGroupValueBuilder { _phantom: PhantomData, } +impl Default for ByteViewGroupValueBuilder { + fn default() -> Self { + Self::new() + } +} + impl ByteViewGroupValueBuilder { pub fn new() -> Self { Self { diff --git a/datafusion/physical-plan/src/aggregates/group_values/multi_group_by/mod.rs b/datafusion/physical-plan/src/aggregates/group_values/multi_group_by/mod.rs index 2ac0389454de..722bc6049c80 100644 --- a/datafusion/physical-plan/src/aggregates/group_values/multi_group_by/mod.rs +++ b/datafusion/physical-plan/src/aggregates/group_values/multi_group_by/mod.rs @@ -18,7 +18,7 @@ //! `GroupValues` implementations for multi group by cases mod bytes; -mod bytes_view; +pub mod bytes_view; mod primitive; use std::mem::{self, size_of}; @@ -91,6 +91,11 @@ pub trait GroupColumn: Send + Sync { /// Returns the number of rows stored in this builder fn len(&self) -> usize; + /// true if len == 0 + fn is_empty(&self) -> bool { + self.len() == 0 + } + /// Returns the number of bytes used by this [`GroupColumn`] fn size(&self) -> usize; From db44b30f000c6cff80c13eee537b29ccde2aa46d Mon Sep 17 00:00:00 2001 From: zhuqi-lucas <821684824@qq.com> Date: Mon, 21 Jul 2025 13:04:17 +0800 Subject: [PATCH 2/2] Add more hot functions --- .../benches/aggregate_vectorized.rs | 132 +++++++++++++++--- 1 file changed, 115 insertions(+), 17 deletions(-) diff --git a/datafusion/physical-plan/benches/aggregate_vectorized.rs b/datafusion/physical-plan/benches/aggregate_vectorized.rs index 6c27b7c61f5f..13a408b2da9e 100644 --- a/datafusion/physical-plan/benches/aggregate_vectorized.rs +++ b/datafusion/physical-plan/benches/aggregate_vectorized.rs @@ -15,12 +15,12 @@ // specific language governing permissions and limitations // under the License. -use arrow::array::{Array, ArrayRef}; +use arrow::array::ArrayRef; use arrow::datatypes::StringViewType; use arrow::util::bench_util::{ create_string_view_array_with_len, create_string_view_array_with_max_len, }; -use criterion::{criterion_group, criterion_main, Criterion}; +use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; use datafusion_physical_plan::aggregates::group_values::multi_group_by::bytes_view::ByteViewGroupValueBuilder; use datafusion_physical_plan::aggregates::group_values::multi_group_by::GroupColumn; use std::sync::Arc; @@ -31,52 +31,150 @@ const NULL_DENSITIES: [f32; 3] = [0.0, 0.1, 0.5]; fn bench_vectorized_append(c: &mut Criterion) { let mut group = c.benchmark_group("ByteViewGroupValueBuilder_vectorized_append"); - // Inlined-only scenarios for &size in &SIZES { + let rows: Vec = (0..size).collect(); + for &null_density in &NULL_DENSITIES { let input = create_string_view_array_with_len(size, null_density, 8, false); let input: ArrayRef = Arc::new(input); - let id = format!("inlined_null_{null_density:.1}_size_{size}"); + + // vectorized_append + let id = BenchmarkId::new( + format!("inlined_null_{null_density:.1}_size_{size}"), + "vectorized_append", + ); + group.bench_function(id, |b| { + b.iter(|| { + let mut builder = ByteViewGroupValueBuilder::::new(); + builder.vectorized_append(&input, &rows).unwrap(); + }); + }); + + // append_val + let id = BenchmarkId::new( + format!("inlined_null_{null_density:.1}_size_{size}"), + "append_val", + ); group.bench_function(id, |b| { b.iter(|| { let mut builder = ByteViewGroupValueBuilder::::new(); - builder - .vectorized_append(&input, &(0..input.len()).collect::>()) - .unwrap(); + for &i in &rows { + builder.append_val(&input, i).unwrap(); + } + }); + }); + + // vectorized_equal_to + let id = BenchmarkId::new( + format!("inlined_null_{null_density:.1}_size_{size}"), + "vectorized_equal_to", + ); + group.bench_function(id, |b| { + let mut builder = ByteViewGroupValueBuilder::::new(); + builder.vectorized_append(&input, &rows).unwrap(); + let mut results = vec![true; size]; + b.iter(|| { + builder.vectorized_equal_to(&rows, &input, &rows, &mut results); }); }); } } - // Mixed-length scenarios for &size in &SIZES { + let rows: Vec = (0..size).collect(); + for &null_density in &NULL_DENSITIES { + let scenario = "mixed"; let input = create_string_view_array_with_len(size, null_density, 64, true); let input: ArrayRef = Arc::new(input); - let id = format!("mixed_null_{null_density:.1}_size_{size}"); + + // vectorized_append + let id = BenchmarkId::new( + format!("{scenario}_null_{null_density:.1}_size_{size}"), + "vectorized_append", + ); group.bench_function(id, |b| { b.iter(|| { let mut builder = ByteViewGroupValueBuilder::::new(); - builder - .vectorized_append(&input, &(0..input.len()).collect::>()) - .unwrap(); + builder.vectorized_append(&input, &rows).unwrap(); + }); + }); + + // append_val + let id = BenchmarkId::new( + format!("{scenario}_null_{null_density:.1}_size_{size}"), + "append_val", + ); + group.bench_function(id, |b| { + b.iter(|| { + let mut builder = ByteViewGroupValueBuilder::::new(); + for &i in &rows { + builder.append_val(&input, i).unwrap(); + } + }); + }); + + // vectorized_equal_to + let id = BenchmarkId::new( + format!("{scenario}_null_{null_density:.1}_size_{size}"), + "vectorized_equal_to", + ); + group.bench_function(id, |b| { + let mut builder = ByteViewGroupValueBuilder::::new(); + builder.vectorized_append(&input, &rows).unwrap(); + let mut results = vec![true; size]; + b.iter(|| { + builder.vectorized_equal_to(&rows, &input, &rows, &mut results); }); }); } } - // Random max-length scenarios for &size in &SIZES { + let rows: Vec = (0..size).collect(); + for &null_density in &NULL_DENSITIES { + let scenario = "random"; let input = create_string_view_array_with_max_len(size, null_density, 400); let input: ArrayRef = Arc::new(input); - let id = format!("random_null_{null_density:.1}_size_{size}"); + + // vectorized_append + let id = BenchmarkId::new( + format!("{scenario}_null_{null_density:.1}_size_{size}"), + "vectorized_append", + ); group.bench_function(id, |b| { b.iter(|| { let mut builder = ByteViewGroupValueBuilder::::new(); - builder - .vectorized_append(&input, &(0..input.len()).collect::>()) - .unwrap(); + builder.vectorized_append(&input, &rows).unwrap(); + }); + }); + + // append_val + let id = BenchmarkId::new( + format!("{scenario}_null_{null_density:.1}_size_{size}"), + "append_val", + ); + group.bench_function(id, |b| { + b.iter(|| { + let mut builder = ByteViewGroupValueBuilder::::new(); + for &i in &rows { + builder.append_val(&input, i).unwrap(); + } + }); + }); + + // vectorized_equal_to + let id = BenchmarkId::new( + format!("{scenario}_null_{null_density:.1}_size_{size}"), + "vectorized_equal_to", + ); + group.bench_function(id, |b| { + let mut builder = ByteViewGroupValueBuilder::::new(); + builder.vectorized_append(&input, &rows).unwrap(); + let mut results = vec![true; size]; + b.iter(|| { + builder.vectorized_equal_to(&rows, &input, &rows, &mut results); }); }); }