This commit is contained in:
David Peter committed 2026-10-06 12:54:39 +02:00
1 parent 55dfe6722c
commit a027c1eb22
21 files changed
+346 -154

No files matched your search

+26
View File
@@ -100,8 +100,34 @@ pub struct Measurement {
pub exit_status: ExitStatus,
}
impl Measurement {
/// Total CPU time for this run, including user and kernel mode.
pub fn time_cpu(&self) -> Time {
self.time_user + self.time_system
}
}
fn serialize_measurements<S>(measurements: &[Measurement], serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
#[derive(Serialize)]
struct WithCpuTime<'a> {
#[serde(flatten)]
measurement: &'a Measurement,
#[serde(serialize_with = "serialize_time")]
time_cpu: Time,
}
serializer.collect_seq(measurements.iter().map(|measurement| WithCpuTime {
measurement,
time_cpu: measurement.time_cpu(),
}))
}
#[derive(Debug, Default, Clone, Serialize, PartialEq)]
pub struct Measurements {
#[serde(serialize_with = "serialize_measurements")]
pub measurements: Vec<Measurement>,
}
+14 -2
View File
@@ -81,7 +81,7 @@ fn generate_results(args: &[&'static str]) -> Result<Vec<BenchmarkResult>> {
#[test]
fn scheduler_basic() -> Result<()> {
insta::assert_yaml_snapshot!(generate_results(&["--runs=2", "sleep 0.123", "sleep 0.456"])?, @r#"
insta::assert_yaml_snapshot!(generate_results(&["--runs=2", "sleep 0.123", "sleep 0.456"])?, @"
- command: sleep 0.123
measurements:
- time_wall_clock:
@@ -97,6 +97,9 @@ fn scheduler_basic() -> Result<()> {
value: 0
unit: byte
exit_code: 0
time_cpu:
value: 0
unit: second
- time_wall_clock:
value: 0.123
unit: second
@@ -110,6 +113,9 @@ fn scheduler_basic() -> Result<()> {
value: 0
unit: byte
exit_code: 0
time_cpu:
value: 0
unit: second
- command: sleep 0.456
measurements:
- time_wall_clock:
@@ -125,6 +131,9 @@ fn scheduler_basic() -> Result<()> {
value: 0
unit: byte
exit_code: 0
time_cpu:
value: 0
unit: second
- time_wall_clock:
value: 0.456
unit: second
@@ -138,7 +147,10 @@ fn scheduler_basic() -> Result<()> {
value: 0
unit: byte
exit_code: 0
"#);
time_cpu:
value: 0
unit: second
");
Ok(())
}
+3 -3
View File
@@ -57,11 +57,11 @@ fn build_command() -> Command {
.default_value("default")
.help("Metrics to display, in order. The first is used for non-JSON exports. \
Compare all commands with the first command. Presets: default (wall-clock \
time and peak RSS; wall-clock time only on Windows), speed (wall-clock, user, \
and system time), all (all available metrics, skipping unavailable \
time and peak RSS; wall-clock time only on Windows), time (wall-clock, total CPU, \
user, and system time), all (all available metrics, skipping unavailable \
measurements). All presets use automatic \
units with wall-clock time first; use a preset on its own. Metrics: time_wall_clock, \
time_user, time_system, memory_peak_resident, cpu_cycles, instructions, \
time_cpu (user + system), time_user, time_system, memory_peak_resident, cpu_cycles, instructions, \
cache_references, cache_misses, branch_misses. Optional units: ns/us/ms/s/min/h \
for time, B/kB/MB/GB/TB/KiB/MiB/GiB/TiB for memory, count/k/M/B for counters \
(k = thousand, M = million, B = billion). Example: \
+34 -3
View File
@@ -9,6 +9,7 @@ use crate::quantity::{byte, ratio, second, Ratio};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Metric {
TimeWallClock,
TimeCpu,
TimeUser,
TimeSystem,
MemoryPeakResident,
@@ -20,8 +21,9 @@ pub enum Metric {
}
impl Metric {
pub const ALL: [Self; 9] = [
pub const ALL: [Self; 10] = [
Self::TimeWallClock,
Self::TimeCpu,
Self::TimeUser,
Self::TimeSystem,
Self::MemoryPeakResident,
@@ -35,6 +37,7 @@ impl Metric {
pub fn name(self) -> &'static str {
match self {
Self::TimeWallClock => "time_wall_clock",
Self::TimeCpu => "time_cpu",
Self::TimeUser => "time_user",
Self::TimeSystem => "time_system",
Self::MemoryPeakResident => "memory_peak_resident",
@@ -49,6 +52,7 @@ impl Metric {
pub fn label(self) -> &'static str {
match self {
Self::TimeWallClock => "Wall Time",
Self::TimeCpu => "CPU time",
Self::TimeUser => "User time",
Self::TimeSystem => "System time",
Self::MemoryPeakResident => "Memory",
@@ -65,6 +69,7 @@ impl Metric {
let counters = &measurement.hardware_counters;
match self {
Self::TimeWallClock => Some(measurement.time_wall_clock.get::<second>()),
Self::TimeCpu => Some(measurement.time_cpu().get::<second>()),
Self::TimeUser => Some(measurement.time_user.get::<second>()),
Self::TimeSystem => Some(measurement.time_system.get::<second>()),
Self::MemoryPeakResident => measurement.memory_peak_resident.map(|v| v.get::<byte>()),
@@ -79,7 +84,7 @@ impl Metric {
pub fn is_time(self) -> bool {
matches!(
self,
Self::TimeWallClock | Self::TimeUser | Self::TimeSystem
Self::TimeWallClock | Self::TimeCpu | Self::TimeUser | Self::TimeSystem
)
}
@@ -349,7 +354,33 @@ impl Stats {
#[cfg(test)]
mod tests {
use super::*;
use crate::quantity::Information;
use crate::quantity::{Information, Time};
#[test]
fn cpu_time_statistics_use_per_run_totals() {
let measurements = Measurements::new(
[(1.0, 3.0), (3.0, 1.0)]
.iter()
.map(|&(user, system)| Measurement {
time_user: Time::new::<second>(user),
time_system: Time::new::<second>(system),
..Measurement::default()
})
.collect(),
);
let selection = MetricSelection::parse_list("time_cpu:ms").unwrap()[0];
let stats = selection.metric.summarize(&measurements).unwrap();
assert_eq!(selection.csv_unit().format(stats.mean), "4000.0 ms");
// User and system time vary, but their per-run total is constant.
assert_eq!(stats.stddev, Some(0.0));
assert_eq!(stats.min, 4.0);
assert_eq!(stats.max, 4.0);
let json = serde_json::to_value(&measurements).unwrap();
for sample in json["measurements"].as_array().unwrap() {
assert_eq!(sample["time_cpu"]["value"], 4.0);
assert_eq!(sample["time_cpu"]["unit"], "second");
}
}
#[test]
fn selection_units_and_validation() {
+9 -4
View File
@@ -397,10 +397,15 @@ impl Options {
match matches.get_one::<String>("metrics").unwrap().as_str() {
"default" => {}
"speed" => {
options.metrics = [Metric::TimeWallClock, Metric::TimeUser, Metric::TimeSystem]
.map(|metric| MetricSelection { metric, unit: None })
.to_vec();
"time" => {
options.metrics = [
Metric::TimeWallClock,
Metric::TimeCpu,
Metric::TimeUser,
Metric::TimeSystem,
]
.map(|metric| MetricSelection { metric, unit: None })
.to_vec();
}
"all" => {
let shell = matches!(