1use std::time::Duration;
2
3use hdrhistogram::Histogram;
4
5#[derive(Default)]
6pub struct Stats {
7 pub total_size: u64,
8 pub total_duration: Duration,
9 pub streams: usize,
10 pub stream_stats: StreamStats,
11}
12
13impl Stats {
14 pub fn stream_finished(&mut self, stream_result: TransferResult) {
15 self.total_size += stream_result.size;
16 self.streams += 1;
17
18 self.stream_stats
19 .duration_hist
20 .record(stream_result.duration.as_millis() as u64)
21 .unwrap();
22 self.stream_stats
23 .throughput_hist
24 .record(stream_result.throughput as u64)
25 .unwrap();
26 }
27
28 pub fn print(&self, stat_name: &str) {
29 println!("Overall {stat_name} stats:\n");
30 println!(
31 "Transferred {} bytes on {} streams in {:4.2?} ({:.2} MiB/s)\n",
32 self.total_size,
33 self.streams,
34 self.total_duration,
35 throughput_bps(self.total_duration, self.total_size) / 1024.0 / 1024.0
36 );
37
38 println!("Stream {stat_name} metrics:\n");
39
40 println!(" │ Throughput │ Duration ");
41 println!("──────┼───────────────┼──────────");
42
43 let print_metric = |label: &'static str, get_metric: fn(&Histogram<u64>) -> u64| {
44 println!(
45 " {} │ {:7.2} MiB/s │ {:>9.2?}",
46 label,
47 get_metric(&self.stream_stats.throughput_hist) as f64 / 1024.0 / 1024.0,
48 Duration::from_millis(get_metric(&self.stream_stats.duration_hist))
49 );
50 };
51
52 print_metric("AVG ", |hist| hist.mean() as u64);
53 print_metric("P0 ", |hist| hist.value_at_quantile(0.00));
54 print_metric("P10 ", |hist| hist.value_at_quantile(0.10));
55 print_metric("P50 ", |hist| hist.value_at_quantile(0.50));
56 print_metric("P90 ", |hist| hist.value_at_quantile(0.90));
57 print_metric("P100", |hist| hist.value_at_quantile(1.00));
58 }
59}
60
61pub struct StreamStats {
62 pub duration_hist: Histogram<u64>,
63 pub throughput_hist: Histogram<u64>,
64}
65
66impl Default for StreamStats {
67 fn default() -> Self {
68 Self {
69 duration_hist: Histogram::<u64>::new(3).unwrap(),
70 throughput_hist: Histogram::<u64>::new(3).unwrap(),
71 }
72 }
73}
74
75#[derive(Debug)]
76pub struct TransferResult {
77 pub duration: Duration,
78 pub size: u64,
79 pub throughput: f64,
80}
81
82impl TransferResult {
83 pub fn new(duration: Duration, size: u64) -> Self {
84 let throughput = throughput_bps(duration, size);
85 Self {
86 duration,
87 size,
88 throughput,
89 }
90 }
91}
92
93pub fn throughput_bps(duration: Duration, size: u64) -> f64 {
94 (size as f64) / (duration.as_secs_f64())
95}