Lines
21.43 %
Functions
34.67 %
Branches
0 %
use std::cell::RefCell;
use std::collections::HashMap;
use std::io;
use std::io::Write;
use std::time::Instant;
use log::info;
/// A timing object to measure the time of different parts of the program. This
/// is useful for debugging and profiling.
#[derive(Default)]
pub struct Timing {
/// Stores the results of finished timers.
results: RefCell<Vec<(String, f32)>>,
}
/// Aggregated timing summary for a named timer.
struct Aggregate {
name: String,
min: f32,
max: f32,
total: f32,
avg: f32,
count: usize,
impl Timing {
/// Creates a new timing object to track timers.
pub fn new() -> Self {
Self {
results: RefCell::new(Vec::new()),
/// Starts a new timer with the given name.
pub fn measure<F, O>(&self, name: &str, function: F) -> O
where
F: FnOnce() -> O,
{
let start = Instant::now();
let result = function();
let time = start.elapsed().as_secs_f64();
info!("Time {}: {:.3}s", name, time);
// Register the result.
self.results.borrow_mut().push((name.to_string(), time as f32));
result
/// Aggregate results by name and compute (min, max, avg, count, total) for each.
fn aggregate_results(&self) -> Vec<Aggregate> {
let mut map: HashMap<String, Aggregate> = HashMap::new();
for (name, time) in self.results.borrow().iter() {
map.entry(name.clone())
.and_modify(|ag| {
ag.count += 1;
ag.total += *time;
ag.min = ag.min.min(*time);
ag.max = ag.max.max(*time);
})
.or_insert(Aggregate {
name: name.clone(),
min: *time,
max: *time,
total: *time,
avg: 0.0,
count: 1,
});
// Compute the averages and sort by name.
let mut out: Vec<Aggregate> = map
.into_values()
.map(|mut ag| {
ag.avg = if ag.count > 0 {
ag.total / (ag.count as f32)
} else {
0.0
};
ag
.collect();
out.sort_by(|a, b| a.name.cmp(&b.name));
out
/// Prints all the finished timers aggregated by name (total first; omit metrics when n == 1).
pub fn print(&self) {
for ag in self.aggregate_results() {
if ag.count == 1 {
eprintln!("Time {}: {:.3}s", ag.name, ag.total);
eprintln!(
"Time {}: {:.3}s, min: {:.3}s, max: {:.3}s, avg: {:.3}s, n: {}",
ag.name, ag.total, ag.min, ag.max, ag.avg, ag.count
);
/// Writes a YAML report of the finished timers to the given writer.
pub fn print_yaml<W: Write>(&self, tool_name: &str, writer: &mut W) -> io::Result<()> {
writeln!(writer, "- tool: {tool_name}")?;
writeln!(writer, " timing:")?;
writeln!(writer, " {}:", ag.name)?;
writeln!(writer, " total: {total:.3}s", total = ag.total)?;
if ag.count > 1 {
writeln!(writer, " count: {}", ag.count)?;
writeln!(writer, " min: {min:.3}s", min = ag.min)?;
writeln!(writer, " max: {max:.3}s", max = ag.max)?;
writeln!(writer, " avg: {avg:.3}s", avg = ag.avg)?;
Ok(())