1use chrono::prelude::*;
3use serde::{Deserialize, Serialize};
4use std::ops;
5use std::time::UNIX_EPOCH;
6
7use rd_util::*;
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
10pub enum Phase {
11 Prep,
12 Running,
13 BenchCpuSinglePrep,
14 BenchCpuSingle,
15 BenchCpuSaturationPrep,
16 BenchCpuSaturation,
17 BenchMemPrep,
18 BenchMemUp,
19 BenchMemBisect,
20 BenchMemRefine,
21}
22
23impl Default for Phase {
24 fn default() -> Self {
25 Phase::Prep
26 }
27}
28
29impl Phase {
30 pub fn name(&self) -> &'static str {
31 match self {
32 Self::Prep => "prep",
33 Self::Running => "run",
34 Self::BenchCpuSinglePrep => "1cpu-prep",
35 Self::BenchCpuSingle => "1cpu",
36 Self::BenchCpuSaturationPrep => "cpu-prep",
37 Self::BenchCpuSaturation => "cpu",
38 Self::BenchMemPrep => "mem-prep",
39 Self::BenchMemUp => "mem-up",
40 Self::BenchMemBisect => "mem-bisect",
41 Self::BenchMemRefine => "mem-refine",
42 }
43 }
44}
45
46#[derive(Clone, Debug, Default, Serialize, Deserialize)]
47pub struct Latencies {
48 pub min: f64,
49 pub p01: f64,
50 pub p05: f64,
51 pub p10: f64,
52 pub p16: f64,
53 pub p50: f64,
54 pub p84: f64,
55 pub p90: f64,
56 pub p95: f64,
57 pub p99: f64,
58 pub p99_9: f64,
59 pub p99_99: f64,
60 pub p99_999: f64,
61 pub max: f64,
62 pub ctl: f64,
63}
64
65impl ops::AddAssign<&Latencies> for Latencies {
66 fn add_assign(&mut self, rhs: &Latencies) {
67 self.min += rhs.min;
68 self.p01 += rhs.p01;
69 self.p05 += rhs.p05;
70 self.p10 += rhs.p10;
71 self.p16 += rhs.p16;
72 self.p50 += rhs.p50;
73 self.p84 += rhs.p84;
74 self.p90 += rhs.p90;
75 self.p95 += rhs.p95;
76 self.p99 += rhs.p99;
77 self.p99_9 += rhs.p99_9;
78 self.p99_99 += rhs.p99_99;
79 self.p99_999 += rhs.p99_999;
80 self.max += rhs.max;
81 self.ctl += rhs.ctl;
82 }
83}
84
85impl<T: Into<f64>> ops::DivAssign<T> for Latencies {
86 fn div_assign(&mut self, rhs: T) {
87 let div = rhs.into();
88 self.min /= div;
89 self.p01 /= div;
90 self.p05 /= div;
91 self.p10 /= div;
92 self.p16 /= div;
93 self.p50 /= div;
94 self.p84 /= div;
95 self.p90 /= div;
96 self.p95 /= div;
97 self.p99 /= div;
98 self.p99_9 /= div;
99 self.p99_99 /= div;
100 self.p99_999 /= div;
101 self.max /= div;
102 self.ctl /= div;
103 }
104}
105
106const STAT_DOC: &str = "\
107// rps: Request per second in the last control period
108// concurrency: Current number of active worker threads
109// concurrency_max: Current concurrency max from latency target
110// file_addr_frac: Current file footprint fraction
111// anon_addr_frac: Current anon footprint fraction
112// nr_in_flight: The number of requests in flight
113// nr_done: Total number of hashes calculated
114// nr_workers: Number of worker threads
115// nr_idle_workers: Number of idle workers
116// lat.p*: Latency percentiles
117// lat.ctl: Latency percentile used for rps control (params.lat_target_pct)
118";
119
120#[derive(Clone, Debug, Default, Serialize, Deserialize)]
121pub struct Stat {
122 pub rps: f64,
123 pub concurrency: f64,
124 pub concurrency_max: f64,
125 pub file_addr_frac: f64,
126 pub anon_addr_frac: f64,
127 pub nr_in_flight: u32,
128 pub nr_done: u64,
129 pub nr_workers: usize,
130 pub nr_idle_workers: usize,
131 pub lat: Latencies,
132
133 pub file_size: u64,
134 pub file_dist: Vec<u64>,
135 pub anon_size: usize,
136 pub anon_dist: Vec<u64>,
137}
138
139impl ops::AddAssign<&Stat> for Stat {
140 fn add_assign(&mut self, rhs: &Stat) {
141 self.rps += rhs.rps;
142 self.concurrency += rhs.concurrency;
143 self.concurrency_max += rhs.concurrency_max;
144 self.file_addr_frac += rhs.file_addr_frac;
145 self.anon_addr_frac += rhs.anon_addr_frac;
146 self.nr_in_flight += rhs.nr_in_flight;
147 self.nr_done += rhs.nr_done;
148 self.nr_workers += rhs.nr_workers;
149 self.nr_idle_workers += rhs.nr_idle_workers;
150 self.lat += &rhs.lat;
151 }
152}
153
154impl Stat {
155 pub fn avg<T: Into<f64>>(&mut self, div: T)
156 where
157 Latencies: ops::DivAssign<f64>,
158 {
159 let divf64 = div.into();
160 self.rps /= divf64;
161 self.concurrency /= divf64;
162 self.concurrency_max /= divf64;
163 self.file_addr_frac /= divf64;
164 self.anon_addr_frac /= divf64;
165 self.nr_in_flight = (self.nr_in_flight as f64 / divf64).round() as u32;
166 self.nr_done = (self.nr_done as f64 / divf64).round() as u64;
167 self.nr_workers = (self.nr_workers as f64 / divf64).round() as usize;
168 self.nr_idle_workers = (self.nr_idle_workers as f64 / divf64).round() as usize;
169 self.lat /= divf64;
170 }
171}
172
173const REPORT_DOC_HEADER: &str = "\
174//
175// rd-hashd runtime report
176//
177// timestamp: The time this report was created at
178// phase: The current phase
179// rotational: Are testfiles and/or swap on hard disk drives?
180// rotational_testfiles: Are testfiles on hard disk drives?
181// rotational_swap: Is swap on hard disk drives?
182// testfiles_progress: Testfiles preparation progress, 1.0 indicates completion
183// params_modified: Modified timestamp of the loaded params file
184// mem_probe_frac: Memory frac benchmark is currently probing
185// mem_probe_at: The timestamp this memory probing started at
186";
187
188#[derive(Clone, Debug, Serialize, Deserialize)]
189pub struct Report {
190 pub timestamp: DateTime<Local>,
191 pub phase: Phase,
192 pub rotational: bool,
193 pub rotational_testfiles: bool,
194 pub rotational_swap: bool,
195 pub testfiles_progress: f64,
196 pub params_modified: DateTime<Local>,
197 pub mem_probe_size: usize,
198 pub mem_probe_at: DateTime<Local>,
199 #[serde(flatten)]
200 pub hasher: Stat,
201}
202
203impl Default for Report {
204 fn default() -> Self {
205 Self {
206 timestamp: DateTime::from(UNIX_EPOCH),
207 phase: Default::default(),
208 rotational: false,
209 rotational_testfiles: false,
210 rotational_swap: false,
211 testfiles_progress: 0.0,
212 params_modified: DateTime::from(UNIX_EPOCH),
213 mem_probe_size: 0,
214 mem_probe_at: DateTime::from(UNIX_EPOCH),
215 hasher: Default::default(),
216 }
217 }
218}
219
220impl JsonLoad for Report {}
221
222impl JsonSave for Report {
223 fn preamble() -> Option<String> {
224 Some(REPORT_DOC_HEADER.to_string() + STAT_DOC + "//\n")
225 }
226}