diff --git a/benches/allocation_hot_paths.rs b/benches/allocation_hot_paths.rs index 259d1dce..5cc1fbfe 100644 --- a/benches/allocation_hot_paths.rs +++ b/benches/allocation_hot_paths.rs @@ -35,7 +35,7 @@ mod allocation_contracts { use std::{hint::black_box, num::NonZeroUsize, time::Duration}; use thiserror::Error; - use super::bench_utils::{bench_option, bench_result}; + use super::bench_utils::{OrAbort, OrAbortWithContext}; const CANARY_COUNT_2D: usize = 4_000; const CANARY_COUNT_3D: usize = 750; @@ -95,21 +95,13 @@ mod allocation_contracts { } fn benchmark_bounds() -> CoordinateRange { - bench_result( - CoordinateRange::try_new(-100.0_f64, 100.0), - "allocation benchmark bounds must be valid", - ) + CoordinateRange::try_new(-100.0_f64, 100.0).or_abort() } fn canary_vertices(count: usize, seed: u64) -> Vec> { - let points = bench_result( - generate_random_points_in_range_seeded::(count, benchmark_bounds(), seed), - "failed to generate allocation benchmark points", - ); - bench_result( - try_vertices_from_points(&points), - "failed to create allocation benchmark vertices", - ) + let points = + generate_random_points_in_range_seeded::(count, benchmark_bounds(), seed).or_abort(); + try_vertices_from_points(&points).or_abort() } fn first_simplex_key( @@ -195,10 +187,7 @@ mod allocation_contracts { } } - let vertex_count = bench_result( - u32::try_from(points.len()), - "simplex vertex count should fit in u32", - ); + let vertex_count = u32::try_from(points.len()).or_abort(); let inv_vertex_count = 1.0 / f64::from(vertex_count); for coord in &mut coords { *coord *= inv_vertex_count; @@ -214,20 +203,10 @@ mod allocation_contracts { attempts, base_seed: Some(seed), }); - let dt = bench_result( - BenchTriangulation::::try_new_with_options(&vertices, options), - format!("failed to build {D}D allocation benchmark triangulation"), - ); - let simplex_key = - bench_result(representative_simplex_key(&dt), "missing benchmark simplex"); - let facet_vertices = bench_result( - first_facet_vertices(&dt, simplex_key), - "failed to prepare benchmark facet vertices", - ); - let query = bench_result( - simplex_barycenter(&dt, simplex_key), - "failed to prepare benchmark locate query", - ); + let dt = BenchTriangulation::::try_new_with_options(&vertices, options).or_abort(); + let simplex_key = representative_simplex_key(&dt).or_abort(); + let facet_vertices = first_facet_vertices(&dt, simplex_key).or_abort(); + let query = simplex_barycenter(&dt, simplex_key).or_abort(); let simplex_count = dt.tds().simplices().count(); let vertex_count = dt.tds().vertices().count(); @@ -349,10 +328,7 @@ mod allocation_contracts { let (vertex_count, info) = measure_with_result(|| { tds.simplex_vertices(simplex_key).map(|keys| keys.len()) }); - assert_eq!( - bench_result(vertex_count, "Tds::simplex_vertices should succeed"), - D + 1 - ); + assert_eq!(vertex_count.or_abort(), D + 1); assert_zero_allocations(&info, "Tds::simplex_vertices"); }); }, @@ -364,10 +340,9 @@ mod allocation_contracts { fixture: &DimensionFixture, ) { let tds = fixture.dt.tds(); - let simplex = bench_option( - tds.simplex(fixture.simplex_key), - format!("{D}D benchmark simplex should exist"), - ); + let simplex = tds + .simplex(fixture.simplex_key) + .or_abort(format!("{D}D benchmark simplex should exist")); group.bench_function( BenchmarkId::new( @@ -381,10 +356,7 @@ mod allocation_contracts { .vertex_uuid_iter(tds) .try_fold(0usize, |count, uuid| uuid.map(|_| count + 1)) }); - assert_eq!( - bench_result(uuid_count, "Simplex::vertex_uuid_iter should succeed"), - D + 1 - ); + assert_eq!(uuid_count.or_abort(), D + 1); assert_zero_allocations(&info, "Simplex::vertex_uuid_iter"); }); }, @@ -429,8 +401,7 @@ mod allocation_contracts { let (locate_result, info) = measure_with_result(|| { locate_with_stats(fixture.dt.tds(), &kernel, &fixture.query, Some(simplex_key)) }); - let (location, stats) = - bench_result(locate_result, "hinted locate_with_stats should succeed"); + let (location, stats) = locate_result.or_abort(); assert_matches!(location, LocateResult::InsideSimplex(found) if found == simplex_key); assert!(stats.used_hint); diff --git a/benches/boundary_uuid_iter.rs b/benches/boundary_uuid_iter.rs index 77ff04a8..931e04e9 100644 --- a/benches/boundary_uuid_iter.rs +++ b/benches/boundary_uuid_iter.rs @@ -7,7 +7,7 @@ //! preserving the quick performance probes. use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main}; -use delaunay::prelude::construction::DelaunayTriangulation; +use delaunay::prelude::construction::{DelaunayTriangulation, Vertex}; use delaunay::prelude::generators::generate_random_points_in_range_seeded; use delaunay::prelude::geometry::CoordinateRange; use delaunay::prelude::query::BoundaryAnalysis; @@ -20,15 +20,12 @@ use std::hint::black_box; /// Shared benchmark setup error helpers. #[path = "common/bench_utils.rs"] pub mod bench_utils; -use bench_utils::{bench_option, bench_result}; +use bench_utils::{OrAbort, OrAbortWithContext}; const BOUNDARY_COUNTS_3D: &[usize] = &[20, 40, 60, 80]; fn benchmark_bounds() -> CoordinateRange { - bench_result( - CoordinateRange::try_new(-100.0_f64, 100.0), - "boundary benchmark bounds must be valid", - ) + CoordinateRange::try_new(-100.0_f64, 100.0).or_abort() } fn boundary_triangulation_3d( @@ -39,15 +36,9 @@ fn boundary_triangulation_3d( benchmark_bounds(), 0xB0DA_FACE_0000_0000 ^ requested_vertices as u64, ); - let points = bench_result(points, "failed to generate boundary benchmark points"); - let vertices = bench_result( - try_vertices_from_points(&points), - "failed to create boundary benchmark vertices", - ); - bench_result( - DelaunayTriangulation::try_new(&vertices), - "failed to build 3D boundary benchmark triangulation", - ) + let points = points.or_abort(); + let vertices = try_vertices_from_points(&points).or_abort(); + DelaunayTriangulation::try_new(&vertices).or_abort() } fn bench_boundary_facets_micro(c: &mut Criterion) { @@ -55,35 +46,35 @@ fn bench_boundary_facets_micro(c: &mut Criterion) { for &requested_vertices in BOUNDARY_COUNTS_3D { let dt = boundary_triangulation_3d(requested_vertices); - let boundary_count = bench_result( - bench_result(dt.boundary_facets(), "boundary facets should be available") - .try_fold(0_usize, |count, facet| facet.map(|_| count + 1)), - "boundary facets should be valid", - ); - group.throughput(Throughput::Elements(bench_result( - u64::try_from(boundary_count), - "boundary facet count fits in u64", - ))); + let boundary_count = dt + .boundary_facets() + .or_abort() + .try_fold(0_usize, |count, facet| facet.map(|_| count + 1)) + .or_abort(); + group.throughput(Throughput::Elements( + u64::try_from(boundary_count).or_abort(), + )); group.bench_with_input( BenchmarkId::new("boundary_facets_count_3d", requested_vertices), &dt, |b, dt| { b.iter(|| { - black_box(bench_result( - bench_result(dt.boundary_facets(), "boundary facets should be available") - .try_fold(0_usize, |count, facet| facet.map(|_| count + 1)), - "boundary facets should be valid", - )); + black_box( + dt.boundary_facets() + .or_abort() + .try_fold(0_usize, |count, facet| facet.map(|_| count + 1)) + .or_abort(), + ); }); }, ); - let boundary_facets = bench_result( - bench_result(dt.boundary_facets(), "boundary facets should be available") - .collect::, _>>(), - "boundary facets should be valid", - ); + let boundary_facets = dt + .boundary_facets() + .or_abort() + .collect::, _>>() + .or_abort(); group.bench_with_input( BenchmarkId::new("is_boundary_facet_3d", requested_vertices), &(&dt, boundary_facets), @@ -91,12 +82,7 @@ fn bench_boundary_facets_micro(c: &mut Criterion) { b.iter(|| { let confirmed = facets .iter() - .filter(|facet| { - bench_result( - dt.tds().is_boundary_facet(facet), - "boundary facet check should succeed", - ) - }) + .filter(|facet| dt.tds().is_boundary_facet(facet).or_abort()) .count(); black_box(confirmed); }); @@ -110,48 +96,32 @@ fn bench_boundary_facets_micro(c: &mut Criterion) { fn uuid_iter_source() -> DelaunayTriangulation, (), (), 3> { let vertices = vec![ - bench_result( - delaunay::prelude::Vertex::<(), _>::try_new([0.0, 0.0, 0.0]), - "finite benchmark vertex coordinates", - ), - bench_result( - delaunay::prelude::Vertex::<(), _>::try_new([1.0, 0.0, 0.0]), - "finite benchmark vertex coordinates", - ), - bench_result( - delaunay::prelude::Vertex::<(), _>::try_new([0.0, 1.0, 0.0]), - "finite benchmark vertex coordinates", - ), - bench_result( - delaunay::prelude::Vertex::<(), _>::try_new([0.0, 0.0, 1.0]), - "finite benchmark vertex coordinates", - ), + Vertex::<(), _>::try_new([0.0, 0.0, 0.0]).or_abort(), + Vertex::<(), _>::try_new([1.0, 0.0, 0.0]).or_abort(), + Vertex::<(), _>::try_new([0.0, 1.0, 0.0]).or_abort(), + Vertex::<(), _>::try_new([0.0, 0.0, 1.0]).or_abort(), ]; - bench_result( - DelaunayTriangulation::try_new(&vertices), - "failed to build UUID iterator benchmark triangulation", - ) + DelaunayTriangulation::try_new(&vertices).or_abort() } fn bench_vertex_uuid_iter(c: &mut Criterion) { let mut group = c.benchmark_group("vertex_uuid_iter"); let dt = uuid_iter_source(); - let (_simplex_key, simplex) = bench_option( - dt.simplices().next(), - "simplex should exist for UUID iterator benchmark", - ); + let (_simplex_key, simplex) = dt + .simplices() + .next() + .or_abort("simplex should exist for UUID iterator benchmark"); - group.throughput(Throughput::Elements(bench_result( - u64::try_from(simplex.vertices().len()), - "vertex count fits in u64", - ))); + group.throughput(Throughput::Elements( + u64::try_from(simplex.vertices().len()).or_abort(), + )); group.bench_function("by_value", |b| { b.iter(|| { let unique_uuids = simplex .vertex_uuid_iter(dt.tds()) .collect::, _>>(); - let unique_uuids = bench_result(unique_uuids, "UUID iteration should succeed"); + let unique_uuids = unique_uuids.or_abort(); black_box(unique_uuids); }); }); @@ -161,7 +131,7 @@ fn bench_vertex_uuid_iter(c: &mut Criterion) { let uuid_values: Vec = simplex .vertices() .iter() - .map(|&vkey| bench_option(dt.tds().vertex(vkey), "vertex should exist").uuid()) + .map(|&vkey| dt.tds().vertex(vkey).or_abort("vertex should exist").uuid()) .collect(); let uuid_refs: Vec<&Uuid> = uuid_values.iter().collect(); black_box(uuid_refs); diff --git a/benches/ci_performance_suite.rs b/benches/ci_performance_suite.rs index 12c10e9a..81e8de2d 100644 --- a/benches/ci_performance_suite.rs +++ b/benches/ci_performance_suite.rs @@ -58,7 +58,7 @@ use tracing::warn; /// Shared benchmark setup error helpers. #[path = "common/bench_utils.rs"] pub mod bench_utils; -use bench_utils::{abort_benchmark, bench_option, bench_result}; +use bench_utils::{OrAbort, OrAbortWithContext, abort_benchmark}; #[path = "common/flip_fixtures.rs"] mod flip_fixtures; @@ -340,8 +340,7 @@ fn prepare_data( // Slow fallback: runtime search from the base seed let base_seed = dim_seed.wrapping_add(count as u64); let search_limit = seed_search_limit(); - bench_option( - find_seed_vertices::(base_seed, count, bounds, search_limit, attempts), + find_seed_vertices::(base_seed, count, bounds, search_limit, attempts).or_abort( format_args!( "No stable benchmark seed found for {D}D/{count}: \ start_seed={base_seed}; search_limit={search_limit}; bounds={bounds}" @@ -370,10 +369,7 @@ fn warn_known_seed_failed(seed: u64, count: usize, dataset: Data } fn prepare_dt(dim_seed: u64, count: usize) -> BenchTriangulation { - let bounds = bench_result( - CoordinateRange::try_new(-100.0_f64, 100.0), - "well-conditioned benchmark bounds must be valid", - ); + let bounds = CoordinateRange::try_new(-100.0_f64, 100.0).or_abort(); let attempts = retry_attempts(6); let (seed, _, vertices) = prepare_data::(dim_seed, count, bounds, attempts); let options = ConstructionOptions::default().with_retry_policy(RetryPolicy::Shuffled { @@ -381,10 +377,7 @@ fn prepare_dt(dim_seed: u64, count: usize) -> BenchTriangulation base_seed: Some(seed), }); - bench_result( - BenchTriangulation::::try_new_with_options(&vertices, options), - format!("failed to prepare {D}D benchmark triangulation with {count} vertices"), - ) + BenchTriangulation::::try_new_with_options(&vertices, options).or_abort() } fn prepare_adv_dt(dim_seed: u64, count: usize) -> BenchTriangulation { @@ -395,10 +388,7 @@ fn prepare_adv_dt(dim_seed: u64, count: usize) -> BenchTriangula base_seed: Some(seed), }); - bench_result( - BenchTriangulation::::try_new_with_options(&vertices, options), - format!("failed to prepare adversarial {D}D benchmark triangulation with {count} vertices"), - ) + BenchTriangulation::::try_new_with_options(&vertices, options).or_abort() } fn prepare_inserts( @@ -411,23 +401,15 @@ fn prepare_inserts( seed ^= 0xA5A5_A5A5; } let points = match dataset { - Dataset::WellConditioned => bench_result( - generate_random_points_in_range_seeded::( - count, - bench_result( - CoordinateRange::try_new(-50.0_f64, 50.0), - "insert benchmark bounds must be valid", - ), - seed, - ), - "failed to generate insert benchmark points", - ), + Dataset::WellConditioned => generate_random_points_in_range_seeded::( + count, + CoordinateRange::try_new(-50.0_f64, 50.0).or_abort(), + seed, + ) + .or_abort(), Dataset::Adversarial => generate_adv_points::(count, seed), }; - bench_result( - try_vertices_from_points(&points), - "failed to create insert benchmark vertices", - ) + try_vertices_from_points(&points).or_abort() } fn find_seed_vertices( @@ -439,14 +421,9 @@ fn find_seed_vertices( ) -> SeedSearchResult { for offset in 0..limit { let candidate_seed = start_seed.wrapping_add(offset as u64); - let points = bench_result( - generate_random_points_in_range_seeded::(count, bounds, candidate_seed), - "failed to generate candidate benchmark points", - ); - let vertices = bench_result( - try_vertices_from_points(&points), - "failed to create candidate benchmark vertices", - ); + let points = + generate_random_points_in_range_seeded::(count, bounds, candidate_seed).or_abort(); + let vertices = try_vertices_from_points(&points).or_abort(); let options = ConstructionOptions::default().with_retry_policy(RetryPolicy::Shuffled { attempts, @@ -467,10 +444,7 @@ fn stable_adv_points( attempts: NonZeroUsize, ) -> SeedSearchResult { let points = generate_adv_points::(count, seed); - let vertices = bench_result( - try_vertices_from_points(&points), - "failed to create adversarial benchmark vertices", - ); + let vertices = try_vertices_from_points(&points).or_abort(); let options = ConstructionOptions::default().with_retry_policy(RetryPolicy::Shuffled { attempts, base_seed: Some(seed), @@ -519,29 +493,21 @@ fn prepare_adv_data( } fn generate_adv_points(count: usize, seed: u64) -> Vec> { - let base_points = bench_result( - generate_random_points_in_range_seeded::( - count, - bench_result( - CoordinateRange::try_new(-1.0_f64, 1.0), - "adversarial benchmark bounds must be valid", - ), - seed, - ), - "failed to generate adversarial benchmark base points", - ); + let base_points = generate_random_points_in_range_seeded::( + count, + CoordinateRange::try_new(-1.0_f64, 1.0).or_abort(), + seed, + ) + .or_abort(); base_points .iter() .enumerate() .map(|(index, point)| { - let index = bench_result( - u32::try_from(index), - "benchmark point index should fit in u32", - ); + let index = u32::try_from(index).or_abort(); let mut coords = [0.0_f64; D]; for (axis, coord) in coords.iter_mut().enumerate() { - let axis_number = bench_result(u32::try_from(axis + 1), "axis should fit in u32"); + let axis_number = u32::try_from(axis + 1).or_abort(); let base = point.coords()[axis]; let cluster_offset = f64::from(index % 7) * 1.0e-3; let axis_offset = f64::from(axis_number) * 0.25; @@ -559,10 +525,7 @@ fn generate_adv_points(count: usize, seed: u64) -> Vec> /// both stable and adversarial fixtures stay deterministic across runs and /// Criterion measures only the public flip operation. fn build_flip_dt(points: &[[f64; D]]) -> FlipTriangulation { - bench_result( - flip_workflows::build_flip_dt(points), - format!("failed to build {D}D flip fixture triangulation"), - ) + flip_workflows::build_flip_dt(points).or_abort() } /// Selects a non-degenerate simplex for deterministic k=1 benchmark setup. @@ -574,10 +537,8 @@ fn largest_volume_simplex(dt: &FlipTriangulation) -> SimplexK fn adversarial_largest_volume_simplex(dt: &FlipTriangulation) -> SimplexKey { let simplex_key = largest_volume_simplex_matching(dt, CandidateFilter::TouchesAdversarialFeature); - let touches_feature = bench_result( - flip_workflows::simplex_touches_adversarial_feature(dt, simplex_key), - format!("failed to inspect adversarial k=1 simplex support for {D}D"), - ); + let touches_feature = + flip_workflows::simplex_touches_adversarial_feature(dt, simplex_key).or_abort(); if !touches_feature { abort_benchmark(format_args!( "selected adversarial {D}D k=1 simplex does not touch an adversarial fixture feature" @@ -591,18 +552,12 @@ fn largest_volume_simplex_matching( dt: &FlipTriangulation, filter: CandidateFilter, ) -> SimplexKey { - bench_result( - flip_workflows::largest_volume_simplex(dt, filter), - format!("failed to select {filter:?} k=1 simplex for {D}D flip benchmark"), - ) + flip_workflows::largest_volume_simplex(dt, filter).or_abort() } /// Exercises the public k=1 insert and remove APIs as one benchmark workflow. fn roundtrip_k1(dt: &mut FlipTriangulation, simplex_key: SimplexKey) { - bench_result( - flip_workflows::roundtrip_k1(dt, simplex_key), - format!("k=1 roundtrip should succeed in {D}D"), - ); + flip_workflows::roundtrip_k1(dt, simplex_key).or_abort(); } /// Finds a deterministic k=2 facet candidate before Criterion opens the timed group. @@ -628,10 +583,8 @@ fn adversarial_flippable_k2_facet( require_inverse, CandidateFilter::TouchesAdversarialFeature, ); - let touches_feature = bench_result( - flip_workflows::facet_support_touches_adversarial_feature(dt, facet), - format!("failed to inspect adversarial k=2 facet support for {D}D"), - ); + let touches_feature = + flip_workflows::facet_support_touches_adversarial_feature(dt, facet).or_abort(); if !touches_feature { abort_benchmark(format_args!( "selected adversarial {D}D k=2 facet does not touch an adversarial fixture feature" @@ -646,26 +599,17 @@ fn flippable_k2_facet_matching( require_inverse: bool, filter: CandidateFilter, ) -> FacetHandle { - bench_result( - flip_workflows::flippable_k2_facet(dt, require_inverse, filter), - format!("failed to select {filter:?} k=2 facet for {D}D flip benchmark"), - ) + flip_workflows::flippable_k2_facet(dt, require_inverse, filter).or_abort() } /// Exercises the public k=2 forward flip API for dimensions without inversion. fn forward_k2(dt: &mut FlipTriangulation, facet: FacetHandle) { - bench_result( - flip_workflows::forward_k2(dt, facet), - format!("k=2 flip should succeed for preselected {D}D benchmark facet"), - ); + flip_workflows::forward_k2(dt, facet).or_abort(); } /// Exercises the public k=2 flip and inverse APIs as one benchmark workflow. fn roundtrip_k2(dt: &mut FlipTriangulation, facet: FacetHandle) { - bench_result( - flip_workflows::roundtrip_k2(dt, facet), - format!("k=2 roundtrip should succeed in {D}D"), - ); + flip_workflows::roundtrip_k2(dt, facet).or_abort(); } /// Finds a deterministic k=3 ridge candidate before Criterion opens the timed group. @@ -691,10 +635,8 @@ fn adversarial_flippable_k3_ridge( require_inverse, CandidateFilter::TouchesAdversarialFeature, ); - let touches_feature = bench_result( - flip_workflows::ridge_support_touches_adversarial_feature(dt, ridge), - format!("failed to inspect adversarial k=3 ridge support for {D}D"), - ); + let touches_feature = + flip_workflows::ridge_support_touches_adversarial_feature(dt, ridge).or_abort(); if !touches_feature { abort_benchmark(format_args!( "selected adversarial {D}D k=3 ridge does not touch an adversarial fixture feature" @@ -709,26 +651,17 @@ fn flippable_k3_ridge_matching( require_inverse: bool, filter: CandidateFilter, ) -> RidgeHandle { - bench_result( - flip_workflows::flippable_k3_ridge(dt, require_inverse, filter), - format!("failed to select {filter:?} k=3 ridge for {D}D flip benchmark"), - ) + flip_workflows::flippable_k3_ridge(dt, require_inverse, filter).or_abort() } /// Exercises the public k=3 forward flip API for dimensions without inversion. fn forward_k3(dt: &mut FlipTriangulation, ridge: RidgeHandle) { - bench_result( - flip_workflows::forward_k3(dt, ridge), - format!("k=3 flip should succeed for preselected {D}D benchmark ridge"), - ); + flip_workflows::forward_k3(dt, ridge).or_abort(); } /// Exercises the public k=3 flip and inverse APIs as one benchmark workflow. fn roundtrip_k3(dt: &mut FlipTriangulation, ridge: RidgeHandle) { - bench_result( - flip_workflows::roundtrip_k3(dt, ridge), - format!("k=3 roundtrip should succeed in {D}D"), - ); + flip_workflows::roundtrip_k3(dt, ridge).or_abort(); } /// Registers one k=1 insert/remove roundtrip flip benchmark case. @@ -738,10 +671,7 @@ fn bench_k1_roundtrip_case( base_dt: &FlipTriangulation, simplex_key: SimplexKey, ) { - bench_result( - flip_workflows::verify_k1_roundtrip(base_dt, simplex_key, name), - format!("k=1 setup roundtrip should recover exact topology for {name}"), - ); + flip_workflows::verify_k1_roundtrip(base_dt, simplex_key, name).or_abort(); group.bench_function(name, |b| { b.iter_batched( || base_dt.clone(), @@ -780,10 +710,7 @@ fn bench_k2_roundtrip_case( base_dt: &FlipTriangulation, facet: FacetHandle, ) { - bench_result( - flip_workflows::verify_k2_roundtrip(base_dt, facet, name), - format!("k=2 setup roundtrip should recover exact topology for {name}"), - ); + flip_workflows::verify_k2_roundtrip(base_dt, facet, name).or_abort(); group.bench_function(name, |b| { b.iter_batched( || base_dt.clone(), @@ -822,10 +749,7 @@ fn bench_k3_roundtrip_case( base_dt: &FlipTriangulation, ridge: RidgeHandle, ) { - bench_result( - flip_workflows::verify_k3_roundtrip(base_dt, ridge, name), - format!("k=3 setup roundtrip should recover exact topology for {name}"), - ); + flip_workflows::verify_k3_roundtrip(base_dt, ridge, name).or_abort(); group.bench_function(name, |b| { b.iter_batched( || base_dt.clone(), @@ -887,10 +811,7 @@ fn emit_construction_metric( vertices: &[Vertex<(), D>], options: ConstructionOptions, ) { - let dt = bench_result( - BenchTriangulation::::try_new_with_options(vertices, options), - format!("failed to collect construction metrics for {benchmark_id}"), - ); + let dt = BenchTriangulation::::try_new_with_options(vertices, options).or_abort(); println!( "api_benchmark_metric benchmark_id={benchmark_id} vertices={} simplices={}", vertices.len(), @@ -930,10 +851,7 @@ macro_rules! benchmark_tds_new_dimension { // We avoid `std::process::exit` here so that destructors run and Criterion // can clean up state on both success and failure. if discover_seeds_enabled() { - let bounds = bench_result( - CoordinateRange::try_new(-100.0_f64, 100.0), - "well-conditioned benchmark bounds must be valid", - ); + let bounds = CoordinateRange::try_new(-100.0_f64, 100.0).or_abort(); let filters = criterion_filters(); let bench_id = format!("tds_new_{}d/tds_new/{count}", stringify!($dim)); @@ -978,10 +896,7 @@ macro_rules! benchmark_tds_new_dimension { format!("tds_new_{}d/tds_new_adversarial/{count}", stringify!($dim)); if benchmark_selected(&filters, &bench_id) { - let bounds = bench_result( - CoordinateRange::try_new(-100.0_f64, 100.0), - "well-conditioned benchmark bounds must be valid", - ); + let bounds = CoordinateRange::try_new(-100.0_f64, 100.0).or_abort(); let attempts = retry_attempts(6); let (seed, _, vertices) = prepare_data::<$dim>($seed, count, bounds, attempts); let options = ConstructionOptions::default().with_retry_policy( @@ -1021,10 +936,7 @@ macro_rules! benchmark_tds_new_dimension { group.bench_with_input(BenchmarkId::new("tds_new", count), &count, |b, &count| { // Reduce variance: pre-generate deterministic inputs outside the measured loop, // then benchmark only triangulation construction. - let bounds = bench_result( - CoordinateRange::try_new(-100.0_f64, 100.0), - "well-conditioned benchmark bounds must be valid", - ); + let bounds = CoordinateRange::try_new(-100.0_f64, 100.0).or_abort(); let attempts = retry_attempts(6); let (seed, points, vertices) = prepare_data::<$dim>($seed, count, bounds, attempts); diff --git a/benches/circumsphere_containment.rs b/benches/circumsphere_containment.rs index 8be92baf..74015f00 100644 --- a/benches/circumsphere_containment.rs +++ b/benches/circumsphere_containment.rs @@ -22,14 +22,14 @@ 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}; +use bench_utils::{OrAbort, OrAbortWithContext, abort_benchmark}; fn finite_point(coords: [f64; D]) -> Point { Point::try_new(coords).unwrap_or_else(|_| std::process::abort()) } -fn coordinate_range(min: f64, max: f64, context: &'static str) -> CoordinateRange { - bench_result(CoordinateRange::try_new(min, max), context) +fn coordinate_range(min: f64, max: f64) -> CoordinateRange { + CoordinateRange::try_new(min, max).or_abort() } /// Generate a standard D-dimensional simplex (D+1 vertices) @@ -55,25 +55,17 @@ fn standard_simplex() -> Vec> { /// Generate a random 3D simplex (tetrahedron) for benchmarking using seeded generation fn generate_random_simplex_3d(seed: u64) -> Vec> { - bench_result( - generate_random_points_in_range_seeded( - 4, - coordinate_range(-10.0, 10.0, "random simplex bounds must be valid"), - seed, - ), - "failed to generate random simplex points", - ) + generate_random_points_in_range_seeded(4, coordinate_range(-10.0, 10.0), seed).or_abort() } /// Generate a random 3D test point using seeded generation fn generate_random_test_point_3d(seed: u64) -> Point<3> { - let points = generate_random_points_in_range_seeded( - 1, - coordinate_range(-5.0, 5.0, "random test point bounds must be valid"), - seed, - ); - let points = bench_result(points, "failed to generate random test point"); - bench_option(points.into_iter().next(), "expected exactly one test point") + let points = generate_random_points_in_range_seeded(1, coordinate_range(-5.0, 5.0), seed); + let points = points.or_abort(); + points + .into_iter() + .next() + .or_abort("expected exactly one test point") } /// Benchmark with many random queries @@ -82,12 +74,9 @@ 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_in_range_seeded( - 1000, - coordinate_range(-5.0, 5.0, "random query bounds must be valid"), - 123, - ); - let test_points = bench_result(test_points, "failed to generate random query points"); + let test_points = + generate_random_points_in_range_seeded(1000, coordinate_range(-5.0, 5.0), 123); + let test_points = test_points.or_abort(); c.bench_function("random/insphere_1000_queries", |b| { b.iter(|| { diff --git a/benches/cold_path_predicates.rs b/benches/cold_path_predicates.rs index 6e6ab1bd..de29f2b8 100644 --- a/benches/cold_path_predicates.rs +++ b/benches/cold_path_predicates.rs @@ -43,14 +43,14 @@ 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}; +use bench_utils::{OrAbort, abort_benchmark}; fn finite_point(coords: [f64; D]) -> Point { Point::try_new(coords).unwrap_or_else(|_| std::process::abort()) } -fn coordinate_range(min: f64, max: f64, context: &'static str) -> CoordinateRange { - bench_result(CoordinateRange::try_new(min, max), context) +fn coordinate_range(min: f64, max: f64) -> CoordinateRange { + CoordinateRange::try_new(min, max).or_abort() } /// Deterministic seed for query-point generation in the hot path. @@ -81,14 +81,8 @@ fn standard_simplex() -> Vec> { /// Uses the range `[-10, 10]` against a unit simplex so that the Shewchuk /// errbound comfortably resolves the sign in Stage 1. fn hot_queries() -> Vec> { - bench_result( - generate_random_points_in_range_seeded( - HOT_QUERIES, - coordinate_range(-10.0, 10.0, "hot-path query bounds must be valid"), - HOT_SEED, - ), - "failed to generate hot-path query points", - ) + generate_random_points_in_range_seeded(HOT_QUERIES, coordinate_range(-10.0, 10.0), HOT_SEED) + .or_abort() } /// Generate near-boundary query points for dimension `D`. @@ -99,14 +93,12 @@ fn near_boundary_queries() -> Vec> { // 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. - bench_result( - generate_random_points_in_range_seeded( - NEAR_BOUNDARY_QUERIES, - coordinate_range(0.40, 0.60, "near-boundary query bounds must be valid"), - NEAR_BOUNDARY_SEED, - ), - "failed to generate near-boundary query points", + generate_random_points_in_range_seeded( + NEAR_BOUNDARY_QUERIES, + coordinate_range(0.40, 0.60), + NEAR_BOUNDARY_SEED, ) + .or_abort() } /// Run `insphere` across `queries` against `simplex`, black-boxing each result. diff --git a/benches/common/bench_utils.rs b/benches/common/bench_utils.rs index 9d9ceef1..a9f6ddcc 100644 --- a/benches/common/bench_utils.rs +++ b/benches/common/bench_utils.rs @@ -1,11 +1,14 @@ -use std::{fmt::Display, process}; +//! Shared benchmark setup helpers for fatal setup failures. +//! +//! Criterion benchmark targets cannot return [`Result`] from ordinary setup +//! helpers. These adapters keep benchmark setup code concise while preserving +//! the original error message from fallible constructors and setup routines. + +use std::{fmt::Display, process, sync::Once}; -#[cfg(feature = "bench-logging")] -use std::sync::Once; -#[cfg(feature = "bench-logging")] use tracing_subscriber::EnvFilter; -#[cfg(feature = "bench-logging")] +/// Installs a default error-level tracing subscriber for fatal setup diagnostics. fn init_tracing() { static INIT: Once = Once::new(); INIT.call_once(|| { @@ -14,29 +17,46 @@ fn init_tracing() { }); } -/// Logs a benchmark setup failure when bench logging is enabled, then exits. -#[cfg(feature = "bench-logging")] +/// Emits a benchmark setup failure through tracing and exits with failure. 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); +/// Converts fallible [`Result`] benchmark setup values into abort-on-failure values. +pub trait OrAbort { + /// The successful setup value. + type Output; + + /// Returns the setup value or aborts the benchmark with the underlying error. + fn or_abort(self) -> Self::Output; } -/// Unwraps a benchmark setup result or aborts with context. -pub fn bench_result(result: Result, context: impl Display) -> T { - match result { - Ok(value) => value, - Err(error) => abort_benchmark(format_args!("{context}: {error}")), +impl OrAbort for Result { + type Output = T; + + fn or_abort(self) -> Self::Output { + match self { + Ok(value) => value, + Err(error) => abort_benchmark(error), + } } } -/// Unwraps a benchmark setup option or aborts with context. -pub fn bench_option(option: Option, context: impl Display) -> T { - option.unwrap_or_else(|| abort_benchmark(context)) +/// Converts optional [`Option`] benchmark setup values into abort-on-missing values. +pub trait OrAbortWithContext { + /// The successful setup value. + type Output; + + /// Returns the setup value or aborts the benchmark with context. + fn or_abort(self, context: impl Display) -> Self::Output; +} + +impl OrAbortWithContext for Option { + type Output = T; + + fn or_abort(self, context: impl Display) -> Self::Output { + self.unwrap_or_else(|| abort_benchmark(context)) + } } diff --git a/benches/common/flip_workflows.rs b/benches/common/flip_workflows.rs index 01f46056..55191cad 100644 --- a/benches/common/flip_workflows.rs +++ b/benches/common/flip_workflows.rs @@ -478,7 +478,7 @@ pub fn build_flip_dt( ) -> FlipWorkflowResult> { let vertices = points .iter() - .map(|coords| delaunay::prelude::Vertex::<(), _>::try_new(*coords)) + .map(|coords| Vertex::<(), _>::try_new(*coords)) .collect::>, _>>()?; let options = ConstructionOptions::default().with_insertion_order(InsertionOrderStrategy::Input); @@ -894,8 +894,7 @@ pub fn roundtrip_k1( dt: &mut FlipTriangulation, simplex_key: SimplexKey, ) -> FlipWorkflowResult<()> { - let new_vertex = - delaunay::prelude::Vertex::<(), _>::try_new(simplex_centroid(dt, simplex_key)?)?; + let new_vertex = Vertex::<(), _>::try_new(simplex_centroid(dt, simplex_key)?)?; let new_uuid = new_vertex.uuid(); dt.flip_k1_insert(simplex_key, new_vertex) .map_err(|source| FlipWorkflowError::FlipFailed { diff --git a/benches/profiling_suite.rs b/benches/profiling_suite.rs index d4132956..99f661eb 100644 --- a/benches/profiling_suite.rs +++ b/benches/profiling_suite.rs @@ -88,7 +88,7 @@ use sysinfo::{ProcessRefreshKind, ProcessesToUpdate, RefreshKind, System, get_cu /// Shared benchmark setup error helpers. #[path = "common/bench_utils.rs"] pub mod bench_utils; -use bench_utils::{abort_benchmark, bench_result}; +use bench_utils::{OrAbort, abort_benchmark}; /// Builds a benchmark point from hard-coded finite coordinates. fn finite_point(coords: [f64; D]) -> Point { @@ -104,18 +104,18 @@ fn retry_attempts(value: usize) -> NonZeroUsize { } /// Parses benchmark coordinate bounds and aborts if the fixture is invalid. -fn coordinate_range(min: f64, max: f64, context: &'static str) -> CoordinateRange { - bench_result(CoordinateRange::try_new(min, max), context) +fn coordinate_range(min: f64, max: f64) -> CoordinateRange { + CoordinateRange::try_new(min, max).or_abort() } /// Returns broad bounds used by general random benchmark point clouds. fn wide_bounds() -> CoordinateRange { - coordinate_range(-100.0, 100.0, "wide benchmark bounds must be valid") + coordinate_range(-100.0, 100.0) } /// Returns compact bounds used by adversarial benchmark point clouds. fn adversarial_bounds() -> CoordinateRange { - coordinate_range(-1.0, 1.0, "adversarial benchmark bounds must be valid") + coordinate_range(-1.0, 1.0) } #[cfg(feature = "bench-logging")] @@ -221,13 +221,13 @@ fn memory_usage_kib() -> u64 { bench_info!("Memory measurements in KiB (sysinfo::Process::memory() / 1024)"); }); - let pid = bench_result(get_current_pid(), "failed to get current PID"); + let pid = get_current_pid().or_abort(); let sys = SYS.get_or_init(|| { Mutex::new(System::new_with_specifics( RefreshKind::nothing().with_processes(ProcessRefreshKind::nothing().with_memory()), )) }); - let mut system = bench_result(sys.lock(), "failed to lock System"); + let mut system = sys.lock().or_abort(); system.refresh_processes_specifics( ProcessesToUpdate::Some(&[pid]), true, @@ -296,23 +296,14 @@ fn construct_triangulation( vertices: &[Vertex<(), D>], seed: u64, ) -> DelaunayTriangulation, (), (), D> { - bench_result( - DelaunayTriangulation::try_new_with_options(vertices, construction_options(seed)), - format!( - "failed to create triangulation (dim={D}, n_vertices={}, seed={seed})", - vertices.len() - ), - ) + DelaunayTriangulation::try_new_with_options(vertices, construction_options(seed)).or_abort() } /// Converts generated point fixtures into benchmark vertices without attaching data. fn benchmark_vertices_from_generated_points( points: &[Point], ) -> Vec> { - bench_result( - try_vertices_from_points(points), - "failed to create benchmark vertices", - ) + try_vertices_from_points(points).or_abort() } /// Generates deterministic benchmark points inside validated bounds. @@ -321,10 +312,7 @@ fn generated_points_in_range( bounds: CoordinateRange, seed: u64, ) -> Vec> { - bench_result( - generate_random_points_in_range_seeded::(count, bounds, seed), - "failed to generate benchmark points", - ) + generate_random_points_in_range_seeded::(count, bounds, seed).or_abort() } /// Measure memory delta during triangulation construction. @@ -393,13 +381,10 @@ fn gen_points( .iter() .enumerate() .map(|(index, point)| { - let index = bench_result( - u32::try_from(index), - "benchmark point index should fit in u32", - ); + let index = u32::try_from(index).or_abort(); let mut coords = [0.0_f64; D]; for (axis, coord) in coords.iter_mut().enumerate() { - let axis_number = bench_result(u32::try_from(axis + 1), "axis should fit in u32"); + let axis_number = u32::try_from(axis + 1).or_abort(); let base: f64 = point.coords()[axis]; let cluster_offset = f64::from(index % 7) * 1.0e-3; let axis_offset = f64::from(axis_number) * 0.25; @@ -849,10 +834,9 @@ fn bench_memory_usage( let start_time = Instant::now(); let alloc_info = measure(|| { - let dt = bench_result( - DelaunayTriangulationBuilder::new(&vertices).build::<()>(), - "allocation benchmark triangulation construction failed", - ); + let dt = DelaunayTriangulationBuilder::new(&vertices) + .build::<()>() + .or_abort(); black_box(dt); }); diff --git a/benches/remove_vertex.rs b/benches/remove_vertex.rs index b3f48479..e76c6906 100644 --- a/benches/remove_vertex.rs +++ b/benches/remove_vertex.rs @@ -27,7 +27,7 @@ use std::time::Duration; /// Shared benchmark setup error helpers. #[path = "common/bench_utils.rs"] pub mod bench_utils; -use bench_utils::{bench_option, bench_result}; +use bench_utils::{OrAbort, OrAbortWithContext, abort_benchmark}; const INTERIOR_RADIUS_MIN: f64 = 0.15; const INTERIOR_RADIUS_SPAN: f64 = 0.70; @@ -42,10 +42,7 @@ fn finite_point(coords: [f64; D]) -> Point { } fn interior_bounds() -> CoordinateRange { - bench_result( - CoordinateRange::try_new(0.0_f64, 1.0), - "interior benchmark bounds must be valid", - ) + CoordinateRange::try_new(0.0_f64, 1.0).or_abort() } const LARGE_COORDINATE_JITTER: f64 = 1.0e3; const SEED_SALT: u64 = 0x9E37_79B9_7F4A_7C15; @@ -105,11 +102,8 @@ impl FixtureKind { /// Derive a deterministic, dimension-specific seed for one benchmark case. fn seed_for_case(requested_vertices: usize, seed_base: u64) -> u64 { - let vertices = bench_result( - u64::try_from(requested_vertices), - "vertex count does not fit in u64", - ); - let dimension = bench_result(u64::try_from(D), "dimension does not fit in u64"); + let vertices = u64::try_from(requested_vertices).or_abort(); + let dimension = u64::try_from(D).or_abort(); seed_base ^ vertices.wrapping_mul(SEED_SALT) ^ dimension.rotate_left(32) } @@ -124,8 +118,8 @@ const fn fixture_kind_for_attempt(preferred_kind: FixtureKind, attempt: usize) - } /// Convert a bounded benchmark index to `f64` without unchecked casts. -fn usize_to_f64(value: usize, context: &str) -> f64 { - f64::from(bench_result(u32::try_from(value), context)) +fn usize_to_f64(value: usize) -> f64 { + f64::from(u32::try_from(value).or_abort()) } /// Generate a reproducible canonical simplex with selected adversarial points. @@ -145,18 +139,13 @@ fn generate_vertices( }; points.extend(generated_points); - bench_result( - try_vertices_from_points(&points), - "failed to create remove-vertex benchmark vertices", - ) + try_vertices_from_points(&points).or_abort() } /// Generate well-conditioned interior points inside the canonical simplex. fn generate_interior_points(count: usize, seed: u64) -> Vec> { - let raw_points = bench_result( - generate_random_points_in_range_seeded::(count, interior_bounds(), seed), - "failed to generate interior benchmark points", - ); + let raw_points = + generate_random_points_in_range_seeded::(count, interior_bounds(), seed).or_abort(); let mut points = Vec::with_capacity(count); for (index, raw_point) in raw_points.iter().enumerate() { @@ -174,10 +163,8 @@ fn generate_interior_points(count: usize, seed: u64) -> Vec(count: usize, seed: u64) -> Vec> { - let raw_points = bench_result( - generate_random_points_in_range_seeded::(count, interior_bounds(), seed), - "failed to generate near-boundary benchmark points", - ); + let raw_points = + generate_random_points_in_range_seeded::(count, interior_bounds(), seed).or_abort(); let mut points = Vec::with_capacity(count); for (index, raw_point) in raw_points.iter().enumerate() { @@ -187,8 +174,7 @@ fn generate_near_boundary_points(count: usize, seed: u64) -> Vec for (coord, direction_coord) in coords.iter_mut().zip(direction) { *coord = 0.98 * direction_coord; } - coords[near_boundary_axis] = - NEAR_BOUNDARY_EPSILON * usize_to_f64(index + 1, "near-boundary index too large"); + coords[near_boundary_axis] = NEAR_BOUNDARY_EPSILON * usize_to_f64(index + 1); points.push(finite_point(coords)); } @@ -197,10 +183,8 @@ fn generate_near_boundary_points(count: usize, seed: u64) -> Vec /// Generate points on a shared sphere to stress cospherical predicates. fn generate_cospherical_points(count: usize, seed: u64) -> Vec> { - let raw_points = bench_result( - generate_random_points_in_range_seeded::(count, interior_bounds(), seed), - "failed to generate cospherical benchmark points", - ); + let raw_points = + generate_random_points_in_range_seeded::(count, interior_bounds(), seed).or_abort(); let mut points = Vec::with_capacity(count); for raw_point in &raw_points { @@ -217,19 +201,16 @@ fn generate_cospherical_points(count: usize, seed: u64) -> Vec

(count: usize, seed: u64) -> Vec> { - let seed_offset = f64::from(bench_result( - u32::try_from(seed % 997), - "near-degenerate seed phase does not fit in u32", - )) * 1.0e-14; - let denominator = usize_to_f64(count + 1, "near-degenerate count too large"); + let seed_offset = f64::from(u32::try_from(seed % 997).or_abort()) * 1.0e-14; + let denominator = usize_to_f64(count + 1); let mut points = Vec::with_capacity(count); for index in 0..count { - let index_factor = usize_to_f64(index + 1, "near-degenerate index too large"); + let index_factor = usize_to_f64(index + 1); let diagonal = index_factor / denominator; let mut coords = [0.0; D]; for (axis, coord) in coords.iter_mut().enumerate() { - let axis_factor = usize_to_f64(axis + 1, "near-degenerate axis too large"); + let axis_factor = usize_to_f64(axis + 1); *coord = (NEAR_DEGENERATE_EPSILON * axis_factor) .mul_add(index_factor, diagonal + seed_offset); } @@ -241,17 +222,15 @@ fn generate_near_degenerate_simplex(count: usize, seed: u64) -> /// Generate finite points with large coordinates to stress scale-sensitive paths. fn generate_large_coordinate_points(count: usize, seed: u64) -> Vec> { - let raw_points = bench_result( - generate_random_points_in_range_seeded::(count, interior_bounds(), seed), - "failed to generate large-coordinate benchmark points", - ); + let raw_points = + generate_random_points_in_range_seeded::(count, interior_bounds(), seed).or_abort(); let mut points = Vec::with_capacity(count); for (index, raw_point) in raw_points.iter().enumerate() { - let index_offset = usize_to_f64(index + 1, "large-coordinate index too large"); + let index_offset = usize_to_f64(index + 1); let mut coords = [0.0; D]; for (axis, (coord, raw_coord)) in coords.iter_mut().zip(raw_point.coords()).enumerate() { - let axis_factor = usize_to_f64(axis + 1, "large-coordinate axis too large"); + let axis_factor = usize_to_f64(axis + 1); *coord = LARGE_COORDINATE_SCALE.mul_add( axis_factor, LARGE_COORDINATE_JITTER.mul_add(*raw_coord, index_offset), @@ -279,18 +258,12 @@ fn simplex_points() -> Vec> { /// Generate the minimal full-dimensional simplex for the rollback benchmark. fn simplex_vertices() -> Vec> { - bench_result( - try_vertices_from_points(&simplex_points::()), - "failed to create rollback benchmark simplex vertices", - ) + try_vertices_from_points(&simplex_points::()).or_abort() } /// Deterministic radial coordinate for a point inside the canonical simplex. fn interior_radius(index: usize) -> f64 { - let numerator = bench_result( - u32::try_from(index.wrapping_mul(37) % 997), - "interior radius numerator does not fit in u32", - ); + let numerator = u32::try_from(index.wrapping_mul(37) % 997).or_abort(); INTERIOR_RADIUS_MIN + INTERIOR_RADIUS_SPAN * f64::from(numerator) / 997.0 } @@ -355,7 +328,7 @@ fn build_success_source( preferred_kind: FixtureKind, ) -> RemovalSource { for attempt in 0..SEED_SEARCH_ATTEMPTS { - let attempt_seed = bench_result(u64::try_from(attempt), "seed attempt does not fit in u64"); + let attempt_seed = u64::try_from(attempt).or_abort(); let seed = seed_for_case::(requested_vertices, seed_base) ^ attempt_seed.wrapping_mul(SEED_SALT.rotate_left(17)); let fixture_kind = fixture_kind_for_attempt(preferred_kind, attempt); @@ -376,26 +349,21 @@ fn build_success_source( }; } - bench_option( - None, - format!( - "no successful {D}D remove_vertex fixture found for {requested_vertices} vertices \ + abort_benchmark(format!( + "no successful {D}D remove_vertex fixture found for {requested_vertices} vertices \ after {SEED_SEARCH_ATTEMPTS} seeds across all fixture kinds" - ), - ) + )) } /// Build the source triangulation for invalid-removal rollback measurements. fn build_rollback_source() -> RemovalSource { let vertices = simplex_vertices::(); - let triangulation: BenchTriangulation = bench_result( - DelaunayTriangulation::try_new(&vertices), - format!("failed to build {D}D rollback benchmark simplex"), - ); - let vertex_key = bench_option( - triangulation.vertices().next().map(|(key, _)| key), - format!("rollback benchmark simplex has no {D}D vertices"), - ); + let triangulation: BenchTriangulation = DelaunayTriangulation::try_new(&vertices).or_abort(); + let vertex_key = triangulation + .vertices() + .next() + .map(|(key, _)| key) + .or_abort(format!("rollback benchmark simplex has no {D}D vertices")); RemovalSource { vertex_count: triangulation.number_of_vertices(), @@ -409,10 +377,7 @@ fn build_rollback_source() -> RemovalSource { /// Report benchmark throughput in total stored vertices plus simplices. fn triangulation_element_count(source: &RemovalSource) -> u64 { let total_elements = source.vertex_count + source.simplex_count; - bench_result( - u64::try_from(total_elements), - "triangulation element count does not fit in u64", - ) + u64::try_from(total_elements).or_abort() } /// Register the successful-removal cases for one dimension and input-size schedule. @@ -450,10 +415,7 @@ fn bench_success_dimension( b.iter_batched( || source.triangulation.clone(), |mut triangulation| { - black_box(bench_result( - triangulation.remove_vertex(source.vertex_key), - "successful remove_vertex benchmark unexpectedly failed", - )); + black_box(triangulation.remove_vertex(source.vertex_key).or_abort()); }, BatchSize::SmallInput, ); diff --git a/benches/tds_clone.rs b/benches/tds_clone.rs index 6cc24099..77626fba 100644 --- a/benches/tds_clone.rs +++ b/benches/tds_clone.rs @@ -26,7 +26,7 @@ use std::time::Duration; /// Shared benchmark setup error helpers. #[path = "common/bench_utils.rs"] pub mod bench_utils; -use bench_utils::bench_result; +use bench_utils::OrAbort; const SEED_SALT: u64 = 0x9E37_79B9_7F4A_7C15; const SAMPLE_SIZE: usize = 10; @@ -36,10 +36,7 @@ const MEASUREMENT_TIME: Duration = Duration::from_secs(2); type BenchTriangulation = DelaunayTriangulation, (), (), D>; fn benchmark_bounds() -> CoordinateRange { - bench_result( - CoordinateRange::try_new(-100.0_f64, 100.0), - "clone benchmark bounds must be valid", - ) + CoordinateRange::try_new(-100.0_f64, 100.0).or_abort() } struct CloneSource { @@ -50,34 +47,24 @@ struct CloneSource { /// Derive a deterministic, dimension-specific seed for one benchmark case. fn seed_for_case(requested_vertices: usize, seed_base: u64) -> u64 { - let vertices = bench_result( - u64::try_from(requested_vertices), - "vertex count does not fit in u64", - ); - let dimension = bench_result(u64::try_from(D), "dimension does not fit in u64"); + let vertices = u64::try_from(requested_vertices).or_abort(); + let dimension = u64::try_from(D).or_abort(); seed_base ^ vertices.wrapping_mul(SEED_SALT) ^ dimension.rotate_left(32) } /// Generate a reproducible vertex set for one clone-cost benchmark fixture. fn generate_vertices(requested_vertices: usize, seed: u64) -> Vec> { - let points = bench_result( - generate_random_points_in_range_seeded::(requested_vertices, benchmark_bounds(), seed), - "failed to generate clone benchmark points", - ); - bench_result( - try_vertices_from_points(&points), - "failed to create clone benchmark vertices", - ) + let points = + generate_random_points_in_range_seeded::(requested_vertices, benchmark_bounds(), seed) + .or_abort(); + try_vertices_from_points(&points).or_abort() } /// Build the triangulation snapshot that each benchmark iteration clones. fn build_clone_source(requested_vertices: usize, seed_base: u64) -> CloneSource { let seed = seed_for_case::(requested_vertices, seed_base); let vertices = generate_vertices::(requested_vertices, seed); - let triangulation: BenchTriangulation = bench_result( - DelaunayTriangulation::try_new(&vertices), - format!("failed to build {D}D benchmark triangulation"), - ); + let triangulation: BenchTriangulation = DelaunayTriangulation::try_new(&vertices).or_abort(); let tds = triangulation.tds().clone(); CloneSource { @@ -90,10 +77,7 @@ fn build_clone_source(requested_vertices: usize, seed_base: u64) /// Report benchmark throughput in total stored vertices plus simplices. fn tds_element_count(source: &CloneSource) -> u64 { let total_elements = source.vertex_count + source.simplex_count; - bench_result( - u64::try_from(total_elements), - "TDS element count does not fit in u64", - ) + u64::try_from(total_elements).or_abort() } /// Register the clone-cost cases for one dimension and input-size schedule. diff --git a/benches/topology_guarantee_construction.rs b/benches/topology_guarantee_construction.rs index 9a30899c..8abcaea2 100644 --- a/benches/topology_guarantee_construction.rs +++ b/benches/topology_guarantee_construction.rs @@ -15,7 +15,7 @@ //! ``` use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main}; -use delaunay::prelude::construction::{DelaunayTriangulation, TopologyGuarantee}; +use delaunay::prelude::construction::{DelaunayTriangulation, TopologyGuarantee, Vertex}; use delaunay::prelude::generators::generate_random_points_in_range_seeded; use delaunay::prelude::geometry::CoordinateRange; use delaunay::prelude::repair::DelaunayRepairPolicy; @@ -26,15 +26,12 @@ use std::time::Duration; /// Shared benchmark setup error helpers. #[path = "common/bench_utils.rs"] pub mod bench_utils; -use bench_utils::{abort_benchmark, bench_result}; +use bench_utils::{OrAbort, abort_benchmark}; const SEED_SALT: u64 = 0x9E37_79B9_7F4A_7C15; fn benchmark_bounds() -> CoordinateRange { - bench_result( - CoordinateRange::try_new(-100.0_f64, 100.0), - "topology-guarantee benchmark bounds must be valid", - ) + CoordinateRange::try_new(-100.0_f64, 100.0).or_abort() } fn bench_dimension( @@ -54,18 +51,12 @@ fn bench_dimension( // Deterministic input per (dimension, count). let seed = seed_base ^ (n_points as u64).wrapping_mul(SEED_SALT); - let points = bench_result( - generate_random_points_in_range_seeded::(n_points, benchmark_bounds(), seed), - "failed to generate benchmark points", - ); + let points = + generate_random_points_in_range_seeded::(n_points, benchmark_bounds(), seed) + .or_abort(); let vertices = points .into_iter() - .map(|p| { - bench_result( - delaunay::prelude::Vertex::<(), _>::try_new(p.into()), - "finite benchmark vertex coordinates", - ) - }) + .map(|p| Vertex::<(), _>::try_new(p.into()).or_abort()) .collect::>(); group.bench_with_input(