Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
768bf0f
forward pass case when
palaska Mar 5, 2026
1b26a69
Merge branch 'develop' into bp/case-when
palaska Mar 5, 2026
1d4b947
assert_arrays_eq
palaska Mar 5, 2026
145fced
Merge branch 'bp/case-when' of github.com:vortex-data/vortex into bp/…
palaska Mar 5, 2026
91b71a3
add andnot
palaska Mar 5, 2026
2506209
cast in zip_impl
palaska Mar 5, 2026
db79be4
tests
palaska Mar 5, 2026
6ea92f0
Merge branch 'develop' into bp/case-when
palaska Mar 5, 2026
dbc323d
public api
palaska Mar 5, 2026
f52fa2c
Merge branch 'develop' into bp/case-when
palaska Mar 5, 2026
9d57dff
add todo
palaska Mar 6, 2026
39f2a04
Merge branch 'develop' into bp/case-when
palaska Mar 6, 2026
9c75c72
Merge branch 'bp/case-when' of github.com:vortex-data/vortex into bp/…
palaska Mar 6, 2026
6994f6e
Merge branch 'develop' into bp/case-when
palaska Mar 6, 2026
cb59e4f
mask::bitand_not uses fused bitbuffer method, also owned
palaska Mar 6, 2026
7091207
cleaner
palaska Mar 9, 2026
5b74a42
public api
palaska Mar 9, 2026
0c427ac
Merge branch 'develop' into bp/case-when
palaska Mar 9, 2026
f26fc23
rm long running bench
palaska Mar 9, 2026
f175876
Merge branch 'bp/case-when' of github.com:vortex-data/vortex into bp/…
palaska Mar 9, 2026
675173c
zip_impl_with_builder accepts mask values
palaska Mar 10, 2026
d21575d
cap vec
palaska Mar 10, 2026
6c89ee0
bit or
palaska Mar 10, 2026
9c65970
cast arrays
palaska Mar 10, 2026
56150dc
early exit when first branch matches all
palaska Mar 10, 2026
3908a45
iterate over spans once on row_by_row
palaska Mar 10, 2026
8f3c513
Merge branch 'develop' into bp/case-when
palaska Mar 10, 2026
482b6d0
clippy
palaska Mar 10, 2026
b0e81dc
fmt
palaska Mar 10, 2026
74497f3
Merge branch 'develop' into bp/case-when
palaska Mar 10, 2026
50f3ed8
update comments
palaska Mar 10, 2026
84b42db
fix
palaska Mar 10, 2026
a905aaa
early return when no branch matches
palaska Mar 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions vortex-array/benches/expr/case_when_bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ use vortex_array::ArrayRef;
use vortex_array::Canonical;
use vortex_array::IntoArray;
use vortex_array::VortexSessionExecute;
use vortex_array::arrays::BoolArray;
use vortex_array::arrays::StructArray;
use vortex_array::expr::case_when;
use vortex_array::expr::case_when_no_else;
use vortex_array::expr::eq;
use vortex_array::expr::get_item;
use vortex_array::expr::gt;
use vortex_array::expr::lit;
use vortex_array::expr::lt;
use vortex_array::expr::nested_case_when;
use vortex_array::expr::root;
use vortex_array::session::ArraySession;
Expand All @@ -39,6 +41,22 @@ fn make_struct_array(size: usize) -> ArrayRef {
.into_array()
}

/// Array with boolean columns cycling through thirds: `c0[i] = i%3==0`, `c1[i] = i%3==1`.
fn make_fragmented_array(size: usize) -> ArrayRef {
StructArray::from_fields(&[
(
"c0",
BoolArray::from_iter((0..size).map(|i| i % 3 == 0)).into_array(),
),
(
"c1",
BoolArray::from_iter((0..size).map(|i| i % 3 == 1)).into_array(),
),
])
.unwrap()
.into_array()
}

/// Benchmark a simple binary CASE WHEN with varying array sizes.
#[divan::bench(args = [1000, 10000, 100000])]
fn case_when_simple(bencher: Bencher, size: usize) {
Expand Down Expand Up @@ -185,6 +203,39 @@ fn case_when_all_true(bencher: Bencher, size: usize) {
});
}

/// Benchmark n-ary CASE WHEN where the first branch dominates (~90% of rows).
/// This highlights the early-exit and deferred-merge optimizations: subsequent conditions
/// match no remaining rows and are skipped entirely.
#[divan::bench(args = [1000, 10000, 100000])]
fn case_when_nary_early_dominant(bencher: Bencher, size: usize) {
let array = make_struct_array(size);

// CASE WHEN value < 90% THEN 1 WHEN value < 95% THEN 2 WHEN value < 97.5% THEN 3 ELSE 4
let t1 = (size as i32 * 9) / 10;
let t2 = (size as i32 * 19) / 20;
let t3 = (size as i32 * 39) / 40;

let expr = nested_case_when(
vec![
(lt(get_item("value", root()), lit(t1)), lit(1i32)),
(lt(get_item("value", root()), lit(t2)), lit(2i32)),
(lt(get_item("value", root()), lit(t3)), lit(3i32)),
],
Some(lit(4i32)),
);

bencher
.with_inputs(|| (&expr, &array))
.bench_refs(|(expr, array)| {
let mut ctx = SESSION.create_execution_ctx();
array
.apply(expr)
.unwrap()
.execute::<Canonical>(&mut ctx)
.unwrap()
});
}

/// Benchmark CASE WHEN where all conditions are false.
#[divan::bench(args = [1000, 10000, 100000])]
fn case_when_all_false(bencher: Bencher, size: usize) {
Expand All @@ -208,3 +259,30 @@ fn case_when_all_false(bencher: Bencher, size: usize) {
.unwrap()
});
}

/// Benchmark CASE WHEN cycling through 3 branches per row (triggers merge_row_by_row).
/// Run length = 1; exercises branch 0, branch 1, and the else fallback at every 3rd row.
#[divan::bench(args = [1000, 10000])]
fn case_when_fragmented(bencher: Bencher, size: usize) {
let array = make_fragmented_array(size);

// CASE WHEN c0 THEN 0 WHEN c1 THEN 1 ELSE 2 END
let expr = nested_case_when(
vec![
(get_item("c0", root()), lit(0i32)),
(get_item("c1", root()), lit(1i32)),
],
Some(lit(2i32)),
);

bencher
.with_inputs(|| (&expr, &array))
.bench_refs(|(expr, array)| {
let mut ctx = SESSION.create_execution_ctx();
array
.apply(expr)
.unwrap()
.execute::<Canonical>(&mut ctx)
.unwrap()
});
}
Loading
Loading