Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
21 changes: 19 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -319,8 +319,25 @@ degenerate input, and tests under `tests/proptest_sos.rs` enforce that.
`Vertex`, `Cell`, `Facet`, `Ridge`, `InSphere`, `Orientation`,
`insphere`, `circumcenter`, `circumradius`. Avoid Rust‑ecosystem
abstractions that obscure the math.
- Use `tracing::{debug,info,warn,error}!` for all runtime diagnostics.
Never `eprintln!` / `println!` outside examples and benches.
- Use `tracing::{debug,info,warn,error}!` for committed diagnostics
across production code, tests, and benchmarks, especially for
library/runtime code, non-trivial test diagnostics, and debugging of
numerical or topological invariants.
- `eprintln!` is acceptable only for short-lived local debugging while
investigating a problem; remove it before landing changes.
- Never log inside hot benchmark loops or Criterion-measured closures.
Emit setup/summary diagnostics outside the measured path instead.
- Gate non-essential test/benchmark diagnostics behind feature flags.
In this repository use `test-debug` for test diagnostics and
`bench-logging` for benchmark diagnostics, e.g.:

```rust
#[cfg(feature = "test-debug")]
tracing::debug!("test diagnostic");

#[cfg(feature = "bench-logging")]
tracing::debug!("diagnostic message");
```

### Scientific notation in docs

Expand Down
60 changes: 50 additions & 10 deletions benches/large_scale_performance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,37 @@ use std::sync::{Mutex, OnceLock};
use std::time::Duration;
use sysinfo::{ProcessRefreshKind, ProcessesToUpdate, RefreshKind, System};

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

#[cfg(not(feature = "bench-logging"))]
const fn init_tracing() {}

macro_rules! bench_info {
($($arg:tt)*) => {{
#[cfg(feature = "bench-logging")]
{
init_tracing();
tracing::info!($($arg)*);
}
}};
}

/// Memory usage information for benchmarking (in KiB)
#[cfg_attr(
not(feature = "bench-logging"),
expect(
dead_code,
reason = "Memory fields are unpacked by optional bench diagnostics"
)
)]
#[derive(Debug, Clone)]
struct MemoryInfo {
before: u64,
Expand All @@ -104,7 +134,7 @@ fn get_memory_usage() -> u64 {

// Log memory unit on first call for clarity in all benchmark runs
UNIT_LOGGED.call_once(|| {
eprintln!("[INFO] Memory measurements in KiB (sysinfo::Process::memory() / 1024)");
bench_info!("Memory measurements in KiB (sysinfo::Process::memory() / 1024)");
});

let pid = sysinfo::get_current_pid().expect("Failed to get current PID");
Expand All @@ -126,7 +156,8 @@ fn get_memory_usage() -> u64 {

/// Get the deterministic base seed for random point generation.
/// Reads `DELAUNAY_BENCH_SEED` (decimal or 0x-hex). Defaults to 42.
/// Prints the resolved seed once on first use if `PRINT_BENCH_SEED` is set.
/// Logs the resolved seed once on first use if `PRINT_BENCH_SEED` is set and
/// the `bench-logging` feature is enabled.
fn get_benchmark_seed() -> u64 {
static SEED: OnceLock<u64> = OnceLock::new();
*SEED.get_or_init(|| {
Expand All @@ -141,7 +172,7 @@ fn get_benchmark_seed() -> u64 {
.unwrap_or(42);

if std::env::var("PRINT_BENCH_SEED").is_ok() {
eprintln!("Benchmark seed: 0x{seed:X} ({seed})");
bench_info!("Benchmark seed: 0x{seed:X} ({seed})");
}

seed
Expand Down Expand Up @@ -281,16 +312,24 @@ fn bench_memory_usage<const D: usize>(c: &mut Criterion, dimension_name: &str, n
// Single measurement for memory delta - reduce sample size
group.sample_size(10);

#[cfg(feature = "bench-logging")]
if std::env::var_os("BENCH_PRINT_MEM").is_some() {
let mem_info = measure_construction_with_memory::<D>(n_points, seed);
bench_info!(
"Memory sample: before={} KiB, after={} KiB, delta={} KiB (TDS-only: {} KiB)",
mem_info.before,
mem_info.after,
mem_info.delta,
mem_info.tds_delta
);
}

// Prime the one-time memory-unit log outside Criterion's measured closure.
let _ = get_memory_usage();

group.bench_function("construction_memory_delta", |b| {
b.iter(|| {
let mem_info = measure_construction_with_memory::<D>(n_points, seed);
// Report memory usage to stderr (won't interfere with benchmark timing)
if std::env::var_os("BENCH_PRINT_MEM").is_some() {
eprintln!(
"Memory: before={} KiB, after={} KiB, delta={} KiB (TDS-only: {} KiB)",
mem_info.before, mem_info.after, mem_info.delta, mem_info.tds_delta
);
}
black_box(mem_info)
});
});
Expand Down Expand Up @@ -527,6 +566,7 @@ fn bench_5d_suite(c: &mut Criterion) {
criterion_group!(
name = large_scale_benches;
config = {
init_tracing();
let sample_size = std::env::var("BENCH_SAMPLE_SIZE")
.ok()
.and_then(|v| v.parse::<usize>().ok())
Expand Down
45 changes: 40 additions & 5 deletions benches/microbenchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,43 @@ use delaunay::vertex;
use std::hint::black_box;
use std::sync::OnceLock;

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

#[cfg(not(feature = "bench-logging"))]
const fn init_tracing() {}

macro_rules! bench_info {
($($arg:tt)*) => {{
#[cfg(feature = "bench-logging")]
{
init_tracing();
tracing::info!($($arg)*);
}
}};
}

macro_rules! bench_warn {
($($arg:tt)*) => {{
#[cfg(feature = "bench-logging")]
{
init_tracing();
tracing::warn!($($arg)*);
}
}};
}

/// Get the deterministic seed for random point generation.
/// Reads `DELAUNAY_BENCH_SEED` (decimal or 0x-hex). Defaults to 0xD1EA.
/// Prints the resolved seed once on first use if `PRINT_BENCH_SEED` is set.
/// Logs the resolved seed once on first use if `PRINT_BENCH_SEED` is set and
/// the `bench-logging` feature is enabled.
fn get_benchmark_seed() -> u64 {
static SEED: OnceLock<u64> = OnceLock::new();
*SEED.get_or_init(|| {
Expand All @@ -36,7 +70,7 @@ fn get_benchmark_seed() -> u64 {
})
.unwrap_or(0xD1EA);
if std::env::var("PRINT_BENCH_SEED").is_ok() {
eprintln!("Benchmark seed: 0x{seed:X} ({seed})");
bench_info!("Benchmark seed: 0x{seed:X} ({seed})");
}
seed
})
Expand Down Expand Up @@ -330,6 +364,7 @@ generate_incremental_construction_benchmarks!(5);
/// This allows CI and local tuning without code changes.
fn bench_config() -> Criterion {
use std::time::Duration;
init_tracing();
let mut c = Criterion::default();

if let Some(v) = std::env::var("CRIT_SAMPLE_SIZE")
Expand All @@ -338,7 +373,7 @@ fn bench_config() -> Criterion {
{
c = c.sample_size(v);
} else if std::env::var("CRIT_SAMPLE_SIZE").is_ok() {
eprintln!("Warning: Failed to parse CRIT_SAMPLE_SIZE, using default");
bench_warn!("Failed to parse CRIT_SAMPLE_SIZE, using default");
}

if let Some(v) = std::env::var("CRIT_MEASUREMENT_MS")
Expand All @@ -347,7 +382,7 @@ fn bench_config() -> Criterion {
{
c = c.measurement_time(Duration::from_millis(v));
} else if std::env::var("CRIT_MEASUREMENT_MS").is_ok() {
eprintln!("Warning: Failed to parse CRIT_MEASUREMENT_MS, using default");
bench_warn!("Failed to parse CRIT_MEASUREMENT_MS, using default");
}

if let Some(v) = std::env::var("CRIT_WARMUP_MS")
Expand All @@ -356,7 +391,7 @@ fn bench_config() -> Criterion {
{
c = c.warm_up_time(Duration::from_millis(v));
} else if std::env::var("CRIT_WARMUP_MS").is_ok() {
eprintln!("Warning: Failed to parse CRIT_WARMUP_MS, using default");
bench_warn!("Failed to parse CRIT_WARMUP_MS, using default");
}

c
Expand Down
27 changes: 26 additions & 1 deletion benches/profiling_suite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,30 @@ use serde::{Serialize, de::DeserializeOwned};
use std::hint::black_box;
use std::time::{Duration, Instant};

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

#[cfg(not(feature = "bench-logging"))]
const fn init_tracing() {}

#[cfg(not(feature = "count-allocations"))]
macro_rules! bench_warn {
($($arg:tt)*) => {{
#[cfg(feature = "bench-logging")]
{
init_tracing();
tracing::warn!($($arg)*);
}
}};
}

// SmallBuffer size constants for different use cases
const BENCHMARK_ITERATION_BUFFER_SIZE: usize = 8; // For tracking allocation info across benchmark iterations
const SIMPLEX_VERTICES_BUFFER_SIZE: usize = 4; // 3D simplex = 4 vertices
Expand Down Expand Up @@ -102,7 +126,7 @@ fn print_count_allocations_banner_once() {
use std::sync::Once;
static ONCE: Once = Once::new();
ONCE.call_once(|| {
eprintln!("count-allocations feature not enabled; memory stats are placeholders.");
bench_warn!("count-allocations feature not enabled; memory stats are placeholders.");
});
}

Expand Down Expand Up @@ -826,6 +850,7 @@ fn benchmark_algorithmic_bottlenecks(c: &mut Criterion) {
criterion_group!(
name = profiling_benches;
config = {
init_tracing();
// Allow configuration via environment variables for CI stability
let sample_size = std::env::var("BENCH_SAMPLE_SIZE")
.ok()
Expand Down
Loading
Loading