#![cfg(all(not(miri), feature = "alloc_tracker", feature = "all_the_time"))]
use std::time::Duration;
use many_cpus::SystemHardware;
use new_zealand::nz;
use par_bench::{ResourceUsageExt, Run, ThreadPool};
#[global_allocator]
static ALLOCATOR: alloc_tracker::Allocator<std::alloc::System> = alloc_tracker::Allocator::system();
#[test]
fn resource_usage_output_provides_meaningful_allocation_data() {
let allocs = alloc_tracker::Session::new();
let mut pool = ThreadPool::new(
SystemHardware::current()
.processors()
.to_builder()
.take(nz!(1))
.unwrap(),
);
let results = Run::new()
.measure_resource_usage("test_allocations", |measure| measure.allocs(&allocs))
.iter(|_| {
let _data = vec![1_u64; 1000]; let _more_data = vec![2_u64; 500]; })
.execute_on(&mut pool, 10);
let measure_outputs: Vec<_> = results.measure_outputs().collect();
assert!(!measure_outputs.is_empty());
for output in &measure_outputs {
let alloc_report = output.allocs().unwrap();
assert!(!alloc_report.is_empty());
let operations: Vec<_> = alloc_report.operations().collect();
assert_eq!(operations.len(), 1);
let (operation_name, operation_stats) = operations.first().unwrap();
assert_eq!(*operation_name, "test_allocations");
assert!(operation_stats.total_iterations() > 0);
assert!(operation_stats.total_bytes_allocated() > 0);
let mean_bytes = operation_stats.mean();
assert!(
mean_bytes > 10_000,
"Mean allocation per iteration should be reasonable, got {mean_bytes} bytes"
);
assert!(
mean_bytes < 50_000,
"Mean allocation per iteration should not be excessive, got {mean_bytes} bytes"
);
println!(
"Thread allocated {} bytes total across {} iterations (mean: {} bytes)",
operation_stats.total_bytes_allocated(),
operation_stats.total_iterations(),
mean_bytes
);
}
let session_report = allocs.to_report();
assert!(!session_report.is_empty());
println!("Session report shows operations were tracked successfully");
}
#[test]
fn resource_usage_output_provides_meaningful_processor_time_data() {
let processor_time = all_the_time::Session::new();
let mut pool = ThreadPool::new(
SystemHardware::current()
.processors()
.to_builder()
.take(nz!(1))
.unwrap(),
);
let results = Run::new()
.measure_resource_usage("test_cpu_work", |measure| {
measure.processor_time(&processor_time)
})
.iter(|_| {
let mut sum = 0_u64;
for i in 0_u64..100_000 {
sum = sum.wrapping_add(i.wrapping_mul(i).wrapping_add(17));
}
for i in 0_u64..50_000 {
sum = sum.wrapping_mul(3).wrapping_add(i);
}
std::hint::black_box(sum);
})
.execute_on(&mut pool, 10);
let measure_outputs: Vec<_> = results.measure_outputs().collect();
assert!(!measure_outputs.is_empty());
for output in &measure_outputs {
let time_report = output.processor_time().unwrap();
assert!(!time_report.is_empty());
let operations: Vec<_> = time_report.operations().collect();
assert_eq!(operations.len(), 1);
let (operation_name, operation_stats) = operations.first().unwrap();
assert_eq!(*operation_name, "test_cpu_work");
assert!(operation_stats.total_iterations() > 0);
if operation_stats.total_processor_time() == Duration::ZERO {
println!(
"Processor time was not measurable on this system (total: {:?}, iterations: {})",
operation_stats.total_processor_time(),
operation_stats.total_iterations()
);
println!("This is expected on systems with low timer precision or fast execution");
} else {
println!(
"Thread used {:?} processor time total across {} iterations (mean: {:?})",
operation_stats.total_processor_time(),
operation_stats.total_iterations(),
operation_stats.mean()
);
let mean_time = operation_stats.mean();
assert!(
mean_time < Duration::from_millis(1000),
"Mean processor time per iteration should be reasonable, got {mean_time:?}"
);
}
}
let session_report = processor_time.to_report();
assert!(!session_report.is_empty());
println!("Session report shows operations were tracked successfully");
}
#[test]
fn resource_usage_output_provides_meaningful_combined_data() {
let allocs = alloc_tracker::Session::new();
let processor_time = all_the_time::Session::new();
let mut pool = ThreadPool::new(
SystemHardware::current()
.processors()
.to_builder()
.take(nz!(1))
.unwrap(),
);
let results = Run::new()
.measure_resource_usage("combined_work", |measure| {
measure.allocs(&allocs).processor_time(&processor_time)
})
.iter(|_| {
let mut data = Vec::with_capacity(1000);
for i in 0_u64..1000 {
data.push(i.wrapping_mul(i).wrapping_add(13));
}
let mut sum = 0_u64;
for &value in &data {
sum = sum.wrapping_add(value);
}
for i in 0_u64..10_000 {
sum = sum.wrapping_mul(3).wrapping_add(i);
}
std::hint::black_box(sum);
})
.execute_on(&mut pool, 5);
let measure_outputs: Vec<_> = results.measure_outputs().collect();
assert!(!measure_outputs.is_empty());
for output in &measure_outputs {
let alloc_report = output.allocs().unwrap();
assert!(!alloc_report.is_empty());
let alloc_operations: Vec<_> = alloc_report.operations().collect();
assert_eq!(alloc_operations.len(), 1);
let (alloc_name, alloc_stats) = alloc_operations.first().unwrap();
assert_eq!(*alloc_name, "combined_work");
assert!(alloc_stats.total_iterations() > 0);
assert!(alloc_stats.total_bytes_allocated() > 0);
println!(
"Combined work - Allocations: {} bytes total, {} iterations, {} bytes mean",
alloc_stats.total_bytes_allocated(),
alloc_stats.total_iterations(),
alloc_stats.mean()
);
let time_report = output.processor_time().unwrap();
assert!(!time_report.is_empty());
let time_operations: Vec<_> = time_report.operations().collect();
assert_eq!(time_operations.len(), 1);
let (time_name, time_stats) = time_operations.first().unwrap();
assert_eq!(*time_name, "combined_work");
assert!(time_stats.total_iterations() > 0);
if time_stats.total_processor_time() == Duration::ZERO {
println!(
"Combined work - Processor time: {:?} total, {} iterations (not measurable due to timer precision)",
time_stats.total_processor_time(),
time_stats.total_iterations()
);
} else {
println!(
"Combined work - Processor time: {:?} total, {} iterations, {:?} mean",
time_stats.total_processor_time(),
time_stats.total_iterations(),
time_stats.mean()
);
}
assert_eq!(
alloc_stats.total_iterations(),
time_stats.total_iterations()
);
}
let alloc_session_report = allocs.to_report();
let time_session_report = processor_time.to_report();
assert!(!alloc_session_report.is_empty());
assert!(!time_session_report.is_empty());
println!("Both session reports show operations were tracked successfully");
}
#[test]
fn resource_usage_output_handles_no_allocations_gracefully() {
let allocs = alloc_tracker::Session::new();
let mut pool = ThreadPool::new(
SystemHardware::current()
.processors()
.to_builder()
.take(nz!(1))
.unwrap(),
);
let results = Run::new()
.measure_resource_usage("no_alloc_work", |measure| measure.allocs(&allocs))
.iter(|_| {
let mut sum = 0_u64;
for i in 0_u64..1000 {
sum = sum.wrapping_add(i);
}
std::hint::black_box(sum);
})
.execute_on(&mut pool, 10);
let measure_outputs: Vec<_> = results.measure_outputs().collect();
assert!(!measure_outputs.is_empty());
for output in &measure_outputs {
let alloc_report = output.allocs().unwrap();
if alloc_report.is_empty() {
println!("No allocations detected, which is expected for this workload");
} else {
let operations: Vec<_> = alloc_report.operations().collect();
for (operation_name, operation_stats) in operations {
println!(
"Unexpected allocations in '{}': {} bytes total",
operation_name,
operation_stats.total_bytes_allocated()
);
}
}
}
}
#[test]
fn resource_usage_output_tracks_multiple_operations() {
let allocs = alloc_tracker::Session::new();
let mut pool = ThreadPool::new(
SystemHardware::current()
.processors()
.to_builder()
.take(nz!(1))
.unwrap(),
);
let results1 = Run::new()
.measure_resource_usage("operation_a", |measure| measure.allocs(&allocs))
.iter(|_| {
let _data = vec![1_u64; 100]; })
.execute_on(&mut pool, 5);
let results2 = Run::new()
.measure_resource_usage("operation_b", |measure| measure.allocs(&allocs))
.iter(|_| {
let _data = vec![2_u64; 1000]; })
.execute_on(&mut pool, 3);
let session_report = allocs.to_report();
assert!(!session_report.is_empty());
let operations: Vec<_> = session_report.operations().collect();
println!("Found {} operations in session report", operations.len());
let operation_names: std::collections::HashSet<_> =
operations.iter().map(|(name, _)| *name).collect();
if operation_names.contains("operation_a") {
println!("Found operation_a in session report");
}
if operation_names.contains("operation_b") {
println!("Found operation_b in session report");
}
assert!(!operations.is_empty());
for (name, stats) in operations {
println!(
"Operation '{}': {} bytes total, {} iterations",
name,
stats.total_bytes_allocated(),
stats.total_iterations()
);
assert!(stats.total_iterations() > 0);
}
for results in [results1, results2] {
let measure_outputs: Vec<_> = results.measure_outputs().collect();
assert!(!measure_outputs.is_empty());
for output in measure_outputs {
if let Some(alloc_report) = output.allocs()
&& !alloc_report.is_empty()
{
let ops: Vec<_> = alloc_report.operations().collect();
for (name, stats) in ops {
println!(
"Individual result - Operation '{}': {} bytes",
name,
stats.total_bytes_allocated()
);
}
}
}
}
}