Skip to content
Merged
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
269 changes: 161 additions & 108 deletions benches/ci_performance_suite.rs

Large diffs are not rendered by default.

127 changes: 105 additions & 22 deletions benches/circumsphere_containment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ use delaunay::prelude::generators::generate_random_points_seeded;
use delaunay::prelude::query::*;
use std::hint::black_box;

/// Shared benchmark setup error helpers.
#[path = "common/bench_utils.rs"]
pub mod bench_utils;
use bench_utils::{abort_benchmark, bench_option, bench_result};

/// Generate a standard D-dimensional simplex (D+1 vertices)
///
/// Creates a simplex with vertices at:
Expand All @@ -41,17 +46,19 @@ fn standard_simplex<const D: usize>() -> Vec<Point<f64, D>> {

/// Generate a random 3D simplex (tetrahedron) for benchmarking using seeded generation
fn generate_random_simplex_3d(seed: u64) -> Vec<Point<f64, 3>> {
generate_random_points_seeded(4, (-10.0, 10.0), seed)
.expect("Failed to generate random simplex points")
bench_result(
generate_random_points_seeded(4, (-10.0, 10.0), seed),
"failed to generate random simplex points",
)
}

/// Generate a random 3D test point using seeded generation
fn generate_random_test_point_3d(seed: u64) -> Point<f64, 3> {
generate_random_points_seeded(1, (-5.0, 5.0), seed)
.expect("Failed to generate random test point")
.into_iter()
.next()
.expect("Expected exactly one test point")
let points = bench_result(
generate_random_points_seeded(1, (-5.0, 5.0), seed),
"failed to generate random test point",
);
bench_option(points.into_iter().next(), "expected exactly one test point")
}

/// Benchmark with many random queries
Expand All @@ -60,33 +67,51 @@ fn benchmark_random_queries(c: &mut Criterion) {
let simplex_points = generate_random_simplex_3d(42);

// Generate many test points using seeded generation for reproducible results
let test_points = generate_random_points_seeded(1000, (-5.0, 5.0), 123)
.expect("Failed to generate random test points");
let test_points = bench_result(
generate_random_points_seeded(1000, (-5.0, 5.0), 123),
"failed to generate random test points",
);

c.bench_function("random/insphere_1000_queries", |b| {
b.iter(|| {
for test_point in &test_points {
black_box(insphere(black_box(&simplex_points), black_box(*test_point)).unwrap());
let result = match insphere(black_box(&simplex_points), black_box(*test_point)) {
Ok(value) => value,
Err(error) => abort_benchmark(format_args!("insphere query failed: {error}")),
};
black_box(result);
}
});
});

c.bench_function("random/insphere_distance_1000_queries", |b| {
b.iter(|| {
for test_point in &test_points {
black_box(
insphere_distance(black_box(&simplex_points), black_box(*test_point)).unwrap(),
);
let result =
match insphere_distance(black_box(&simplex_points), black_box(*test_point)) {
Ok(value) => value,
Err(error) => {
abort_benchmark(format_args!(
"insphere_distance query failed: {error}"
));
}
};
black_box(result);
}
});
});

c.bench_function("random/insphere_lifted_1000_queries", |b| {
b.iter(|| {
for test_point in &test_points {
black_box(
insphere_lifted(black_box(&simplex_points), black_box(*test_point)).unwrap(),
);
let result =
match insphere_lifted(black_box(&simplex_points), black_box(*test_point)) {
Ok(value) => value,
Err(error) => {
abort_benchmark(format_args!("insphere_lifted query failed: {error}"));
}
};
black_box(result);
}
});
});
Expand All @@ -96,13 +121,41 @@ fn benchmark_random_queries(c: &mut Criterion) {
macro_rules! bench_simplex {
($c:ident, $dim:literal, $simplex:expr, $pt:expr) => {{
$c.bench_function(concat!($dim, "d/insphere"), |b| {
b.iter(|| black_box(insphere(black_box(&$simplex), black_box($pt)).unwrap()))
b.iter(|| {
let result = match insphere(black_box(&$simplex), black_box($pt)) {
Ok(value) => value,
Err(error) => {
abort_benchmark(format_args!("insphere benchmark query failed: {error}"));
}
};
black_box(result)
})
});
$c.bench_function(concat!($dim, "d/insphere_distance"), |b| {
b.iter(|| black_box(insphere_distance(black_box(&$simplex), black_box($pt)).unwrap()))
b.iter(|| {
let result = match insphere_distance(black_box(&$simplex), black_box($pt)) {
Ok(value) => value,
Err(error) => {
abort_benchmark(format_args!(
"insphere_distance benchmark query failed: {error}"
));
}
};
black_box(result)
})
});
$c.bench_function(concat!($dim, "d/insphere_lifted"), |b| {
b.iter(|| black_box(insphere_lifted(black_box(&$simplex), black_box($pt)).unwrap()))
b.iter(|| {
let result = match insphere_lifted(black_box(&$simplex), black_box($pt)) {
Ok(value) => value,
Err(error) => {
abort_benchmark(format_args!(
"insphere_lifted benchmark query failed: {error}"
));
}
};
black_box(result)
})
});
}};
}
Expand All @@ -112,18 +165,48 @@ macro_rules! bench_edge_case {
($c:ident, $dim:literal, $case:literal, $simplex:expr, $pt:expr) => {{
$c.bench_function(
concat!("edge_cases_", $dim, "d/", $case, "_insphere"),
|b| b.iter(|| black_box(insphere(black_box(&$simplex), black_box($pt)).unwrap())),
|b| {
b.iter(|| {
let result = match insphere(black_box(&$simplex), black_box($pt)) {
Ok(value) => value,
Err(error) => {
abort_benchmark(format_args!(
"edge-case insphere benchmark query failed: {error}"
));
}
};
black_box(result)
})
},
);
$c.bench_function(
concat!("edge_cases_", $dim, "d/", $case, "_distance"),
|b| {
b.iter(|| {
black_box(insphere_distance(black_box(&$simplex), black_box($pt)).unwrap())
let result = match insphere_distance(black_box(&$simplex), black_box($pt)) {
Ok(value) => value,
Err(error) => {
abort_benchmark(format_args!(
"edge-case insphere_distance benchmark query failed: {error}"
));
}
};
black_box(result)
})
},
);
$c.bench_function(concat!("edge_cases_", $dim, "d/", $case, "_lifted"), |b| {
b.iter(|| black_box(insphere_lifted(black_box(&$simplex), black_box($pt)).unwrap()))
b.iter(|| {
let result = match insphere_lifted(black_box(&$simplex), black_box($pt)) {
Ok(value) => value,
Err(error) => {
abort_benchmark(format_args!(
"edge-case insphere_lifted benchmark query failed: {error}"
));
}
};
black_box(result)
})
});
}};
}
Expand Down
31 changes: 25 additions & 6 deletions benches/cold_path_predicates.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![forbid(unsafe_code)]

//! Microbenchmark for `core::hint::cold_path` adoption in geometric predicates.
//!
//! This benchmark exercises the hot Stage-1 path of the [`insphere`] / [`insphere_lifted`]
Expand Down Expand Up @@ -37,6 +39,11 @@ use delaunay::prelude::generators::generate_random_points_seeded;
use delaunay::prelude::query::*;
use std::hint::black_box;

/// Shared benchmark setup error helpers.
#[path = "common/bench_utils.rs"]
pub mod bench_utils;
use bench_utils::{abort_benchmark, bench_result};

Comment thread
coderabbitai[bot] marked this conversation as resolved.
/// Deterministic seed for query-point generation in the hot path.
const HOT_SEED: u64 = 0xC01D_BEEF_0000_CAFE_u64;
/// Deterministic seed for query-point generation in the near-boundary group.
Expand Down Expand Up @@ -65,8 +72,10 @@ fn standard_simplex<const D: usize>() -> Vec<Point<f64, D>> {
/// Uses the range `[-10, 10]` against a unit simplex so that the Shewchuk
/// errbound comfortably resolves the sign in Stage 1.
fn hot_queries<const D: usize>() -> Vec<Point<f64, D>> {
generate_random_points_seeded(HOT_QUERIES, (-10.0, 10.0), HOT_SEED)
.expect("failed to generate hot-path query points")
bench_result(
generate_random_points_seeded(HOT_QUERIES, (-10.0, 10.0), HOT_SEED),
"failed to generate hot-path query points",
)
}

/// Generate near-boundary query points for dimension `D`.
Expand All @@ -77,21 +86,31 @@ fn near_boundary_queries<const D: usize>() -> Vec<Point<f64, D>> {
// Centered near the circumsphere radius of the standard simplex (~0.5 for
// the D = 3 unit case); the exact value is unimportant — we just want a
// high rate of errbound-ambiguous inputs.
generate_random_points_seeded(NEAR_BOUNDARY_QUERIES, (0.40, 0.60), NEAR_BOUNDARY_SEED)
.expect("failed to generate near-boundary query points")
bench_result(
generate_random_points_seeded(NEAR_BOUNDARY_QUERIES, (0.40, 0.60), NEAR_BOUNDARY_SEED),
"failed to generate near-boundary query points",
)
}

/// Run `insphere` across `queries` against `simplex`, black-boxing each result.
fn run_insphere<const D: usize>(simplex: &[Point<f64, D>], queries: &[Point<f64, D>]) {
for q in queries {
black_box(insphere(black_box(simplex), black_box(*q)).unwrap());
let result = match insphere(black_box(simplex), black_box(*q)) {
Ok(value) => value,
Err(error) => abort_benchmark(format_args!("insphere query failed: {error}")),
};
black_box(result);
}
}

/// Run `insphere_lifted` across `queries` against `simplex`, black-boxing each result.
fn run_insphere_lifted<const D: usize>(simplex: &[Point<f64, D>], queries: &[Point<f64, D>]) {
for q in queries {
black_box(insphere_lifted(black_box(simplex), black_box(*q)).unwrap());
let result = match insphere_lifted(black_box(simplex), black_box(*q)) {
Ok(value) => value,
Err(error) => abort_benchmark(format_args!("insphere_lifted query failed: {error}")),
};
black_box(result);
}
}

Expand Down
42 changes: 42 additions & 0 deletions benches/common/bench_utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use std::{fmt::Display, process};

#[cfg(feature = "bench-logging")]
use std::sync::Once;
#[cfg(feature = "bench-logging")]
use tracing_subscriber::EnvFilter;

#[cfg(feature = "bench-logging")]
fn init_tracing() {
static INIT: Once = Once::new();
INIT.call_once(|| {
let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("error"));
let _ = tracing_subscriber::fmt().with_env_filter(filter).try_init();
});
}

/// Logs a benchmark setup failure when bench logging is enabled, then exits.
#[cfg(feature = "bench-logging")]
pub fn abort_benchmark(message: impl Display) -> ! {
init_tracing();
tracing::error!("{message}");
process::exit(1);
}

/// Exits after a benchmark setup failure when bench logging is disabled.
#[cfg(not(feature = "bench-logging"))]
pub fn abort_benchmark(_message: impl Display) -> ! {
process::exit(1);
}

/// Unwraps a benchmark setup result or aborts with context.
pub fn bench_result<T, E: Display>(result: Result<T, E>, context: impl Display) -> T {
match result {
Ok(value) => value,
Err(error) => abort_benchmark(format_args!("{context}: {error}")),
}
}

/// Unwraps a benchmark setup option or aborts with context.
pub fn bench_option<T>(option: Option<T>, context: impl Display) -> T {
option.unwrap_or_else(|| abort_benchmark(context))
}
Loading
Loading