From f39b497bddb51ca2024dd8129b6a74a4e271c447 Mon Sep 17 00:00:00 2001 From: Devon Hollowood Date: Thu, 20 Oct 2022 18:34:05 -0700 Subject: [PATCH 1/4] Add elapsed time to progress bar --- src/output/progress_bar.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/output/progress_bar.rs b/src/output/progress_bar.rs index ce2dd1a..ff91cd0 100644 --- a/src/output/progress_bar.rs +++ b/src/output/progress_bar.rs @@ -15,7 +15,7 @@ pub fn get_progress_bar(length: u64, msg: &str, option: OutputStyleOption) -> Pr OutputStyleOption::Basic | OutputStyleOption::Color => ProgressStyle::default_bar(), _ => ProgressStyle::default_spinner() .tick_chars(TICK_SETTINGS.0) - .template(" {spinner} {msg:<30} {wide_bar} ETA {eta_precise} ") + .template(" {spinner} {msg:<30} {wide_bar} Time: {elapsed} ETA {eta_precise} ") .expect("no template error"), }; From ce704016f98e25faf11d62671aac64b61c14f113 Mon Sep 17 00:00:00 2001 From: Devon Hollowood Date: Sun, 30 Oct 2022 20:55:12 -0700 Subject: [PATCH 2/4] Make time elapsed only show up initially --- src/benchmark/mod.rs | 13 +++++++++---- src/output/progress_bar.rs | 31 ++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/benchmark/mod.rs b/src/benchmark/mod.rs index c0d3eac..958e2ba 100644 --- a/src/benchmark/mod.rs +++ b/src/benchmark/mod.rs @@ -14,7 +14,9 @@ use crate::options::{ }; use crate::outlier_detection::OUTLIER_THRESHOLD; use crate::output::console_writeln; -use crate::output::progress_bar::get_progress_bar; +use crate::output::progress_bar::{ + get_progress_bar, replace_message_template, reset_progress_template, +}; use crate::output::warnings::{OutlierWarningOptions, Warnings}; use crate::parameter::ParameterNameAndValue; use crate::quantity::{const_time_from_seconds, ratio, FormatQuantity, Time, Zero}; @@ -242,11 +244,13 @@ impl<'a> Benchmark<'a> { // Set up progress bar (and spinner for initial measurement) let progress_bar = if self.options.output_style != OutputStyleOption::Disabled { - Some(get_progress_bar( + let temp_bar = get_progress_bar( self.options.run_bounds.min, - "Initial time measurement", + "Initial time measurement:", self.options.output_style, - )) + ); + let template = format!("{{msg}} {{elapsed:<{}}}", 30 - temp_bar.message().len() - 1,); + Some(replace_message_template(temp_bar, &template)) } else { None }; @@ -298,6 +302,7 @@ impl<'a> Benchmark<'a> { all_succeeded = all_succeeded && success; // Re-configure the progress bar + let progress_bar = progress_bar.map(reset_progress_template); if let Some(bar) = progress_bar.as_ref() { bar.set_length(count) } diff --git a/src/output/progress_bar.rs b/src/output/progress_bar.rs index ff91cd0..fd9f4ca 100644 --- a/src/output/progress_bar.rs +++ b/src/output/progress_bar.rs @@ -9,13 +9,42 @@ const TICK_SETTINGS: (&str, u64) = ("⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏ ", 80); #[cfg(windows)] const TICK_SETTINGS: (&str, u64) = (r"+-x| ", 200); +const DEFAULT_MESSAGE_TEMPLATE: &str = "{msg:<30}"; + +fn create_progress_template(msg_template: &str) -> String { + format!( + " {{spinner}} {} {{wide_bar}} ETA {{eta_precise}} ", + msg_template + ) +} + +/// Replace the usual `message` in a progress bar with the result of evaluating `template`. +/// The `template` may contain a `ProgressBar`'s templated fields. +#[must_use] +pub fn replace_message_template(bar: ProgressBar, template: &str) -> ProgressBar { + let old_style = bar.style(); + let new_template = create_progress_template(template); + let new_style = old_style + .template(&new_template) + .expect("no template error"); + bar.with_style(new_style) +} + +/// Reset a progress bar's template to the default. +/// This is useful after calling `replace_message_template()`. +#[must_use] +pub fn reset_progress_template(bar: ProgressBar) -> ProgressBar { + replace_message_template(bar, DEFAULT_MESSAGE_TEMPLATE) +} + /// Return a pre-configured progress bar pub fn get_progress_bar(length: u64, msg: &str, option: OutputStyleOption) -> ProgressBar { + let template = create_progress_template(DEFAULT_MESSAGE_TEMPLATE); let progressbar_style = match option { OutputStyleOption::Basic | OutputStyleOption::Color => ProgressStyle::default_bar(), _ => ProgressStyle::default_spinner() .tick_chars(TICK_SETTINGS.0) - .template(" {spinner} {msg:<30} {wide_bar} Time: {elapsed} ETA {eta_precise} ") + .template(&template) .expect("no template error"), }; From 0f2871bd665d3f19fba032c4a3b2effadfaef4a2 Mon Sep 17 00:00:00 2001 From: David Peter Date: Mon, 5 Oct 2026 14:16:33 +0200 Subject: [PATCH 3/4] Address review feedback --- src/benchmark/mod.rs | 40 ++++++++++++++++++-------- src/output/progress_bar.rs | 59 +++++++++++++++++++++----------------- 2 files changed, 60 insertions(+), 39 deletions(-) diff --git a/src/benchmark/mod.rs b/src/benchmark/mod.rs index 958e2ba..caa589f 100644 --- a/src/benchmark/mod.rs +++ b/src/benchmark/mod.rs @@ -6,6 +6,7 @@ pub mod scheduler; use std::cmp; use std::io::{self, Write}; +use std::time::Instant; use crate::benchmark::executor::BenchmarkIteration; use crate::command::Command; @@ -15,7 +16,7 @@ use crate::options::{ use crate::outlier_detection::OUTLIER_THRESHOLD; use crate::output::console_writeln; use crate::output::progress_bar::{ - get_progress_bar, replace_message_template, reset_progress_template, + finish_initial_measurement, get_progress_bar, start_initial_measurement, }; use crate::output::warnings::{OutlierWarningOptions, Warnings}; use crate::parameter::ParameterNameAndValue; @@ -244,13 +245,15 @@ impl<'a> Benchmark<'a> { // Set up progress bar (and spinner for initial measurement) let progress_bar = if self.options.output_style != OutputStyleOption::Disabled { - let temp_bar = get_progress_bar( + Some(get_progress_bar( self.options.run_bounds.min, - "Initial time measurement:", + if preparation_command.is_some() { + "Running preparation command" + } else { + "Initial run" + }, self.options.output_style, - ); - let template = format!("{{msg}} {{elapsed:<{}}}", 30 - temp_bar.message().len() - 1,); - Some(replace_message_template(temp_bar, &template)) + )) } else { None }; @@ -262,6 +265,9 @@ impl<'a> Benchmark<'a> { }); // Initial timing run + if let Some(bar) = progress_bar.as_ref() { + start_initial_measurement(bar, Instant::now()); + } let res = self.executor.run_command_and_measure( self.command, benchmark_iteration, @@ -270,6 +276,18 @@ impl<'a> Benchmark<'a> { )?; let success = res.exit_status.success(); + if let Some(bar) = progress_bar.as_ref() { + let time_unit = self + .options + .time_unit + .unwrap_or(res.time_wall_clock.suitable_unit()); + let estimate = res.time_wall_clock.format(time_unit); + finish_initial_measurement( + bar, + format!("Current estimate: {}", estimate.to_string().green()), + ); + } + let conclusion_result = run_conclusion_command(benchmark_iteration)?; let conclusion_overhead = conclusion_result.map_or(Time::zero(), |res| { res.time_wall_clock + self.executor.time_overhead() @@ -302,18 +320,14 @@ impl<'a> Benchmark<'a> { all_succeeded = all_succeeded && success; // Re-configure the progress bar - let progress_bar = progress_bar.map(reset_progress_template); if let Some(bar) = progress_bar.as_ref() { - bar.set_length(count) - } - if let Some(bar) = progress_bar.as_ref() { - bar.inc(1) + bar.set_length(count); + bar.inc(1); } // Gather statistics (perform the actual benchmark) for i in 0..count_remaining { let benchmark_iteration = BenchmarkIteration::Benchmark(i + 1); - run_preparation_command(benchmark_iteration)?; let msg = { let t_wall_clock_mean = measurements.time_wall_clock_mean(); @@ -329,6 +343,8 @@ impl<'a> Benchmark<'a> { bar.set_message(msg.to_owned()) } + run_preparation_command(benchmark_iteration)?; + let res = self.executor.run_command_and_measure( self.command, benchmark_iteration, diff --git a/src/output/progress_bar.rs b/src/output/progress_bar.rs index fd9f4ca..aaccdf7 100644 --- a/src/output/progress_bar.rs +++ b/src/output/progress_bar.rs @@ -1,5 +1,6 @@ -use indicatif::{ProgressBar, ProgressStyle}; -use std::time::Duration; +use indicatif::{FormattedDuration, ProgressBar, ProgressState, ProgressStyle}; +use std::fmt; +use std::time::{Duration, Instant}; use crate::options::OutputStyleOption; @@ -9,42 +10,46 @@ const TICK_SETTINGS: (&str, u64) = ("⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏ ", 80); #[cfg(windows)] const TICK_SETTINGS: (&str, u64) = (r"+-x| ", 200); -const DEFAULT_MESSAGE_TEMPLATE: &str = "{msg:<30}"; +// Keep the message area 30 columns wide across preparation, the initial run, +// and estimates so that switching between them does not move the bar. +const DEFAULT_TEMPLATE: &str = " {spinner} {msg:<30} {wide_bar} ETA {eta_precise} "; +const INITIAL_TEMPLATE: &str = + " {spinner} Initial run: {initial_elapsed:<17} {wide_bar} ETA {eta_precise} "; -fn create_progress_template(msg_template: &str) -> String { - format!( - " {{spinner}} {} {{wide_bar}} ETA {{eta_precise}} ", - msg_template - ) +/// Show the time elapsed since the initial benchmark command started. +pub fn start_initial_measurement(bar: &ProgressBar, started: Instant) { + // Keep a separate clock: the progress bar's clock and ETA estimator must + // continue to include preparation time. + let style = bar + .style() + .template(INITIAL_TEMPLATE) + .expect("no template error") + .with_key( + "initial_elapsed", + move |_: &ProgressState, w: &mut dyn fmt::Write| { + write!(w, "{}", FormattedDuration(started.elapsed())).unwrap(); + }, + ); + bar.set_style(style); } -/// Replace the usual `message` in a progress bar with the result of evaluating `template`. -/// The `template` may contain a `ProgressBar`'s templated fields. -#[must_use] -pub fn replace_message_template(bar: ProgressBar, template: &str) -> ProgressBar { - let old_style = bar.style(); - let new_template = create_progress_template(template); - let new_style = old_style - .template(&new_template) - .expect("no template error"); - bar.with_style(new_style) -} - -/// Reset a progress bar's template to the default. -/// This is useful after calling `replace_message_template()`. -#[must_use] -pub fn reset_progress_template(bar: ProgressBar) -> ProgressBar { - replace_message_template(bar, DEFAULT_MESSAGE_TEMPLATE) +/// Stop showing the initial timer and display the next phase's message. +pub fn finish_initial_measurement(bar: &ProgressBar, message: String) { + bar.set_message(message); + bar.set_style( + bar.style() + .template(DEFAULT_TEMPLATE) + .expect("no template error"), + ); } /// Return a pre-configured progress bar pub fn get_progress_bar(length: u64, msg: &str, option: OutputStyleOption) -> ProgressBar { - let template = create_progress_template(DEFAULT_MESSAGE_TEMPLATE); let progressbar_style = match option { OutputStyleOption::Basic | OutputStyleOption::Color => ProgressStyle::default_bar(), _ => ProgressStyle::default_spinner() .tick_chars(TICK_SETTINGS.0) - .template(&template) + .template(DEFAULT_TEMPLATE) .expect("no template error"), }; From 8745487c60b7da391dacaae4c9baf93d8c870487 Mon Sep 17 00:00:00 2001 From: David Peter Date: Mon, 5 Oct 2026 14:21:04 +0200 Subject: [PATCH 4/4] Remove comment --- src/output/progress_bar.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/output/progress_bar.rs b/src/output/progress_bar.rs index aaccdf7..893685a 100644 --- a/src/output/progress_bar.rs +++ b/src/output/progress_bar.rs @@ -18,8 +18,6 @@ const INITIAL_TEMPLATE: &str = /// Show the time elapsed since the initial benchmark command started. pub fn start_initial_measurement(bar: &ProgressBar, started: Instant) { - // Keep a separate clock: the progress bar's clock and ETA estimator must - // continue to include preparation time. let style = bar .style() .template(INITIAL_TEMPLATE)