Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
57 changes: 56 additions & 1 deletion vortex-array/benches/cast_primitive.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: Copyright the Vortex contributors

//! Primitive casts over dense and nullable inputs.
//!
//! Integer widening cases run on the walltime CPU-feature legs to track their generated loops.

#![expect(clippy::unwrap_used)]

use std::sync::LazyLock;
Expand Down Expand Up @@ -35,13 +39,16 @@ fn main() {
// to keep the CodSpeed simulation under 1ms per benchmark.
const SIZES: &[usize] = &[8_192];

const WIDEN_SIZES: &[usize] = &[65, 128, 129, 8_192, 16_384];

static SESSION: LazyLock<VortexSession> = LazyLock::new(|| {
VortexSession::empty()
.with::<KernelSession>()
.with::<AggregateFnSession>()
});

#[divan::bench(args = SIZES)]
#[vortex_bench_support::cpu_features]
#[divan::bench(args = WIDEN_SIZES)]
fn cast_u16_to_u32(bencher: Bencher, n: usize) {
let mut rng = StdRng::seed_from_u64(42);
let arr = PrimitiveArray::from_option_iter((0..n).map(|i| {
Expand All @@ -62,6 +69,54 @@ fn cast_u16_to_u32(bencher: Bencher, n: usize) {
});
}

#[vortex_bench_support::cpu_features]
#[divan::bench(args = WIDEN_SIZES)]
fn cast_u16_to_u32_dense(bencher: Bencher, n: usize) {
let arr = PrimitiveArray::from_iter((0..n).map(|i| u16::try_from(i).unwrap())).into_array();

bencher
.with_inputs(|| (arr.clone(), SESSION.create_execution_ctx()))
.bench_refs(|(a, ctx)| {
a.cast(DType::Primitive(PType::U32, Nullability::NonNullable))
.unwrap()
.execute::<Canonical>(ctx)
});
}

#[vortex_bench_support::cpu_features]
#[divan::bench(args = WIDEN_SIZES)]
fn cast_i32_to_i64_dense(bencher: Bencher, n: usize) {
let arr =
PrimitiveArray::from_iter((0..n).map(|i| i32::try_from(i).unwrap() - 8_192)).into_array();

bencher
.with_inputs(|| (arr.clone(), SESSION.create_execution_ctx()))
.bench_refs(|(a, ctx)| {
a.cast(DType::Primitive(PType::I64, Nullability::NonNullable))
.unwrap()
.execute::<Canonical>(ctx)
});
}

#[vortex_bench_support::cpu_features]
#[divan::bench(args = WIDEN_SIZES)]
fn cast_i32_to_i64_nullable(bencher: Bencher, n: usize) {
let mut rng = StdRng::seed_from_u64(42);
let arr = PrimitiveArray::from_option_iter((0..n).map(|i| {
rng.random_bool(0.5)
.then_some(i32::try_from(i).unwrap() - 8_192)
}))
.into_array();

bencher
.with_inputs(|| (arr.clone(), SESSION.create_execution_ctx()))
.bench_refs(|(a, ctx)| {
a.cast(DType::Primitive(PType::I64, Nullability::Nullable))
.unwrap()
.execute::<Canonical>(ctx)
});
}

/// Narrowing fallible cast that goes through `try_map_with_mask`. Inputs are bounded
/// so every value fits, isolating the kernel's per-lane checked-cast overhead.
#[divan::bench(args = SIZES)]
Expand Down
67 changes: 62 additions & 5 deletions vortex-compute/benches/lane_kernels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,16 @@
//! - propagates valid-lane overflow as `Err`, and
//! - suppresses null-lane overflow without the closure ever inspecting `valid`.
//!
//! Each Vortex kernel bench has a sibling `arrow_*` baseline bench using the
//! equivalent arrow-rs kernel over the same data shape, so the divan report
//! lines up side-by-side.
//! Arrow baselines cover the corresponding cast and checked-add operations over the same
//! data shape. The unmasked widening cases also compare the two Vortex loop shapes directly.
//!
//! `lanezip_checked_add_u32` carries `#[cpu_features]`, so it is measured on every
//! walltime CPU-feature leg rather than in simulation: it is one lane loop compiled
//! differently per leg. Its arrow-rs sibling is compiled only outside CodSpeed. The
//! arrow kernel is not Vortex code, so a change in its walltime is never actionable,
//! and it flipped by up to 67% between runs of identical code. Run `cargo bench`
//! locally to compare the pair under the same build flags. The cast benches stay in
//! simulation.
//! locally to compare the pair under the same build flags. The paired `map_into` and
//! `map_into_widening` benches also run on the walltime legs. Other cast benches stay in simulation.

#![expect(clippy::unwrap_used)]
#![expect(clippy::clone_on_ref_ptr)]
Expand Down Expand Up @@ -66,6 +65,9 @@ fn main() {
/// Lanes per cast input; the casts run in simulation.
const SIZES: &[usize] = &[16_384];

/// Widening inputs cover both sides of the tiled and untiled loop boundaries.
const WIDEN_SIZES: &[usize] = &[0, 1, 63, 64, 65, 127, 128, 129, 8_192, 16_384, 16_385];

/// Lanes per checked-add input; the checked adds run on the walltime legs.
///
/// Walltime needs longer iterations than simulation: at 16_384 lanes an iteration took a few
Expand Down Expand Up @@ -216,6 +218,61 @@ fn map_with_mask_widen_u16_u32(bencher: Bencher, n: usize) {
.with_inputs(|| (f.values_u16.clone(), uninit_out::<u32>(n)))
.bench_values(|(values, mut out)| {
values.as_slice().map_into(out.as_mut_slice(), |v| v.as_());
out
});
}

// Both widening variants use the existing map to establish baseline results for the stacked
// optimization before it selects a different loop. Keep their benchmark names stable.
#[vortex_bench_support::cpu_features]
#[divan::bench(args = WIDEN_SIZES)]
fn map_into_i32_i64(bencher: Bencher, n: usize) {
let f = cast_fixture(n);

bencher
.with_inputs(|| uninit_out::<i64>(n))
.bench_local_values(|mut out| {
f.values_i32.as_slice().map_into(&mut out, Into::into);
out
});
}

#[vortex_bench_support::cpu_features]
#[divan::bench(args = WIDEN_SIZES)]
fn map_into_widening_i32_i64(bencher: Bencher, n: usize) {
let f = cast_fixture(n);

bencher
.with_inputs(|| uninit_out::<i64>(n))
.bench_local_values(|mut out| {
f.values_i32.as_slice().map_into(&mut out, Into::into);
out
});
}

#[vortex_bench_support::cpu_features]
#[divan::bench(args = WIDEN_SIZES)]
fn map_into_u16_u32(bencher: Bencher, n: usize) {
let f = cast_fixture(n);

bencher
.with_inputs(|| uninit_out::<u32>(n))
.bench_local_values(|mut out| {
f.values_u16.as_slice().map_into(&mut out, Into::into);
out
});
}

#[vortex_bench_support::cpu_features]
#[divan::bench(args = WIDEN_SIZES)]
fn map_into_widening_u16_u32(bencher: Bencher, n: usize) {
let f = cast_fixture(n);

bencher
.with_inputs(|| uninit_out::<u32>(n))
.bench_local_values(|mut out| {
f.values_u16.as_slice().map_into(&mut out, Into::into);
out
});
}

Expand Down
Loading