diff --git a/benches/Cargo.toml b/benches/Cargo.toml index 1447d12c9..e3e58ed89 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -11,6 +11,7 @@ publish = false [features] _single_precision = [] +profiling = [] # deps diff --git a/benches/src/cut_edges.rs b/benches/src/cut_edges.rs index c9a76c2e4..3a8e65f0d 100644 --- a/benches/src/cut_edges.rs +++ b/benches/src/cut_edges.rs @@ -11,7 +11,7 @@ use honeycomb::{ prelude::{CMap2, CMapBuilder, CoordsFloat, DartIdType, EdgeIdType}, }; -use crate::{cli::CutEdgesArgs, utils::hash_file}; +use crate::{cli::CutEdgesArgs, prof_start, prof_stop, utils::hash_file}; // const MAX_RETRY: u8 = 10; @@ -64,8 +64,10 @@ pub fn bench_cut_edges(args: CutEdgesArgs) -> CMap2 { let mut step = 0; print!(" {step:>4} "); // Step + prof_start!("HCBENCH_CUTS"); // compute first batch + prof_start!("HCBENCH_CUTS_COMPUTE"); instant = Instant::now(); let mut edges: Vec = map.iter_edges().collect(); print!("| {:>12} ", edges.len()); // n_edge_total @@ -83,11 +85,13 @@ pub fn bench_cut_edges(args: CutEdgesArgs) -> CMap2 { print!("| {n_e:>17} "); // n_edge_to_process let mut nd = map.add_free_darts(6 * n_e); // 2 for edge split + 2*2 for new edges in neighbor tets let mut darts: Vec = (nd..nd + 6 * n_e as DartIdType).collect(); + prof_stop!("HCBENCH_CUTS_COMPUTE"); print!("| {:>18.6e} ", instant.elapsed().as_secs_f64()); // t_compute_batch // while there are edges to cut while !edges.is_empty() { // process batch + prof_start!("HCBENCH_CUTS_PROCESS"); instant = Instant::now(); let n_retry = match args.backend { crate::cli::Backend::RayonIter => dispatch_rayon(&map, &mut edges, &darts), @@ -98,6 +102,7 @@ pub fn bench_cut_edges(args: CutEdgesArgs) -> CMap2 { dispatch_std_threads(&map, &mut edges, &darts, n_threads) } }; + prof_stop!("HCBENCH_CUTS_PROCESS"); print!("| {:>18.6e} ", instant.elapsed().as_secs_f64()); // t_process_batch println!("| {n_retry:>15}",); // n_transac_retry @@ -110,6 +115,7 @@ pub fn bench_cut_edges(args: CutEdgesArgs) -> CMap2 { // compute the new batch step += 1; print!(" {step:>4} "); // Step + prof_start!("HCBENCH_CUTS_COMPUTE"); instant = Instant::now(); edges.extend(map.iter_edges()); print!("| {:>12} ", edges.len()); // n_edge_total @@ -128,6 +134,7 @@ pub fn bench_cut_edges(args: CutEdgesArgs) -> CMap2 { nd = map.add_free_darts(6 * n_e); darts.par_drain(..); // is there a better way? darts.extend(nd..nd + 6 * n_e as DartIdType); + prof_stop!("HCBENCH_CUTS_COMPUTE"); if n_e != 0 { print!("| {:>18.6e} ", instant.elapsed().as_secs_f64()); // t_compute_batch } else { @@ -136,6 +143,7 @@ pub fn bench_cut_edges(args: CutEdgesArgs) -> CMap2 { println!("| {:>15}", 0); // n_transac_retry } } + prof_stop!("HCBENCH_CUTS"); map } diff --git a/benches/src/main.rs b/benches/src/main.rs index 205c2b31e..2e308b2f0 100644 --- a/benches/src/main.rs +++ b/benches/src/main.rs @@ -8,6 +8,7 @@ use honeycomb_benches::{ cut_edges::bench_cut_edges, grid_gen::bench_generate_2d_grid, grisubal::bench_grisubal, + prof_init, prof_start, prof_stop, remesh::bench_remesh, shift::bench_shift, }; @@ -23,6 +24,9 @@ fn main() { } fn run_benchmarks(cli: Cli) { + prof_init!(); + + prof_start!("HCBENCH"); let map: CMap2 = match cli.benches { Benches::Generate2dGrid(args) => bench_generate_2d_grid(args), Benches::CutEdges(args) => bench_cut_edges(args), @@ -30,6 +34,8 @@ fn run_benchmarks(cli: Cli) { Benches::Remesh(args) => bench_remesh(args), Benches::Shift(args) => bench_shift(args), }; + prof_stop!("HCBENCH"); + // all bench currently generate a map, // we may have to move this to match arms if this changes if let Some(f) = cli.save_as { diff --git a/benches/src/remesh.rs b/benches/src/remesh.rs index 6c4b502fe..e506296e3 100644 --- a/benches/src/remesh.rs +++ b/benches/src/remesh.rs @@ -14,7 +14,7 @@ use honeycomb::{ stm::{StmClosureResult, Transaction, abort, atomically, atomically_with_err, retry}, }; -use crate::{cli::RemeshArgs, utils::hash_file}; +use crate::{cli::RemeshArgs, prof_start, prof_stop, utils::hash_file}; pub fn bench_remesh(args: RemeshArgs) -> CMap2 { let input_map = args.input.to_str().unwrap(); @@ -44,6 +44,7 @@ pub fn bench_remesh(args: RemeshArgs) -> CMap2 { // -- triangulation instant = Instant::now(); + prof_start!("HCBENCH_REMESH_TRIANGULATION"); let n_tot = map .iter_faces() .map(|id| (map.orbit(OrbitPolicy::Face, id as DartIdType).count() - 3) * 2) @@ -96,6 +97,7 @@ pub fn bench_remesh(args: RemeshArgs) -> CMap2 { } }); let triangulation_time = instant.elapsed(); + prof_stop!("HCBENCH_REMESH_TRIANGULATION"); // check that the mesh is triangular, consistently oriented and fully classified debug_assert!( @@ -145,12 +147,14 @@ pub fn bench_remesh(args: RemeshArgs) -> CMap2 { // b. cut / collapse // c. swap // check for ending condition after each relax + prof_start!("HCBENCH_REMESH_MAINLOOP"); let mut n = 0; let mut r; loop { print!("{:>5}", n); // -- relax + prof_start!("HCBENCH_REMESH_RELAX"); instant = Instant::now(); r = 0; loop { @@ -182,6 +186,7 @@ pub fn bench_remesh(args: RemeshArgs) -> CMap2 { break; } } + prof_stop!("HCBENCH_REMESH_RELAX"); print!(" | {:>14.6e}", instant.elapsed().as_secs_f64()); debug_assert!( @@ -228,6 +233,7 @@ pub fn bench_remesh(args: RemeshArgs) -> CMap2 { print!(" | {:>17.6e}", instant.elapsed().as_secs_f64()); // -- cut / collapse + prof_start!("HCBENCH_REMESH_CC"); instant = Instant::now(); for e in edges_to_process { if map.is_unused(e as DartIdType) { @@ -308,9 +314,11 @@ pub fn bench_remesh(args: RemeshArgs) -> CMap2 { } } } + prof_stop!("HCBENCH_REMESH_CC"); print!(" | {:>16.6e}", instant.elapsed().as_secs_f64()); // -- swap + prof_start!("HCBENCH_REMESH_SWAP"); instant = Instant::now(); for (e, diff) in map .iter_edges() @@ -367,6 +375,7 @@ pub fn bench_remesh(args: RemeshArgs) -> CMap2 { ); } } + prof_stop!("HCBENCH_REMESH_SWAP"); println!(" | {:>8.6e}", instant.elapsed().as_secs_f64()); debug_assert!(map.iter_faces().all(|f| { @@ -379,6 +388,7 @@ pub fn bench_remesh(args: RemeshArgs) -> CMap2 { break; } } + prof_stop!("HCBENCH_REMESH_MAINLOOP"); debug_assert!(map.iter_faces().all(|f| { map.orbit(OrbitPolicy::FaceLinear, f).count() == 3 diff --git a/benches/src/shift.rs b/benches/src/shift.rs index 57f5badf0..7ad191d56 100644 --- a/benches/src/shift.rs +++ b/benches/src/shift.rs @@ -25,6 +25,7 @@ use honeycomb::prelude::{ use crate::cli::ShiftArgs; use crate::utils::hash_file; +use crate::{prof_start, prof_stop}; pub fn bench_shift(args: ShiftArgs) -> CMap2 { let mut instant = std::time::Instant::now(); @@ -45,6 +46,7 @@ pub fn bench_shift(args: ShiftArgs) -> CMap2 { }; let build_time = instant.elapsed(); + prof_start!("HCBENCH_SHIFT"); if args.no_conflict { todo!("TODO: require a partitioning algorithm") } else { @@ -117,6 +119,7 @@ pub fn bench_shift(args: ShiftArgs) -> CMap2 { } } } + prof_stop!("HCBENCH_SHIFT"); map } diff --git a/benches/src/utils.rs b/benches/src/utils.rs index 376e88241..88a5ad5f2 100644 --- a/benches/src/utils.rs +++ b/benches/src/utils.rs @@ -32,3 +32,72 @@ pub fn hash_file(path: &str) -> Result { Ok(hasher.finish()) } + +#[cfg(feature = "profiling")] +pub static mut PERF_FIFO: Option = None; + +/// Attempt to open a fifo at the path `/tmp/hc_perf_control`. +/// +/// This macro doesn't generate any code if the `profiling` feature is disabled. +#[macro_export] +macro_rules! prof_init { + () => { + #[cfg(feature = "profiling")] + { + unsafe { + $crate::utils::PERF_FIFO = Some( + std::fs::OpenOptions::new() + .write(true) + .open("/tmp/hc_perf_control") + .expect("Failed to open FIFO"), + ); + } + } + }; +} + +/// Write to the `/tmp/hc_perf_control` to enable perf sampling if `${$var}` is defined. +/// +/// This macro doesn't generate any code if the `profiling` feature is disabled. +#[macro_export] +macro_rules! prof_start { + ($var: literal) => { + #[cfg(feature = "profiling")] + { + // use an env variable to select profiled section + if std::env::var_os($var).is_some() { + use std::io::Write; + unsafe { + if let Some(ref mut f) = $crate::utils::PERF_FIFO { + f.write_all(b"enable\n") + .expect("E: failed to write to FIFO"); + f.flush().expect("E: failed to flush FIFO"); + } + } + } + } + }; +} + +/// Write to the `/tmp/hc_perf_control` to disable perf sampling if `${$var}` is defined. +/// +/// This macro doesn't generate any code if the `profiling` feature is disabled. +#[macro_export] +macro_rules! prof_stop { + ($var: literal) => { + #[cfg(feature = "profiling")] + { + // use an env variable to select profiled section + if std::env::var_os($var).is_some() { + use std::io::Write; + unsafe { + if let Some(ref mut f) = $crate::utils::PERF_FIFO { + f.write_all(b"disable\n") + .expect("E: failed to write to FIFO"); + f.flush().expect("E: failed to flush FIFO"); + } + } + } + } + }; +}