bft_bench_core/
stats.rs

1use std::ops::Sub;
2use std::{fmt::Display, time::Instant};
3
4use histogram::Histogram;
5use serde_derive::Serialize;
6
7#[derive(Clone)]
8pub(crate) struct Counter {
9    pub(crate) start: Instant,
10    pub(crate) now: Instant,
11    pub(crate) count: u64,
12}
13
14impl Counter {
15    pub(crate) fn new(init: Instant) -> Self {
16        Counter {
17            start: init,
18            now: init,
19            count: 0,
20        }
21    }
22}
23
24#[derive(Clone)]
25pub(crate) struct Stat {
26    pub(crate) histogram: Histogram,
27    pub(crate) counter: Counter,
28}
29
30impl Stat {
31    pub(crate) fn new(init: Instant) -> Self {
32        Stat {
33            histogram: new_default_histogram(),
34            counter: Counter::new(init),
35        }
36    }
37}
38
39#[derive(Clone)]
40pub(crate) struct OpStat {
41    pub(crate) successful: Stat,
42    pub(crate) failed: Stat,
43}
44
45impl OpStat {
46    pub(crate) fn new(init: Instant) -> Self {
47        OpStat {
48            successful: Stat::new(init),
49            failed: Stat::new(init),
50        }
51    }
52}
53
54#[derive(Clone)]
55pub(crate) struct ReadStat {
56    pub(crate) op: OpStat,
57    pub(crate) round_trip: Stat,
58}
59
60impl ReadStat {
61    pub(crate) fn new(init: Instant) -> Self {
62        ReadStat {
63            op: OpStat::new(init),
64            round_trip: Stat::new(init),
65        }
66    }
67}
68
69pub(crate) struct Stats {
70    pub(crate) global_write: OpStat,
71    pub(crate) global_read: ReadStat,
72    pub(crate) nodes_writes: Vec<OpStat>,
73    pub(crate) nodes_reads: Vec<ReadStat>,
74}
75
76impl Stats {
77    pub(crate) fn new(init: Instant, write_nodes: usize, read_nodes: usize) -> Self {
78        Stats {
79            global_write: OpStat::new(init),
80            global_read: ReadStat::new(init),
81            nodes_writes: vec![OpStat::new(init); write_nodes],
82            nodes_reads: vec![ReadStat::new(init); read_nodes],
83        }
84    }
85}
86
87#[derive(Serialize)]
88pub struct Report {
89    pub count: u64,
90    pub rate_s: Option<u64>,
91    pub micros_avg: Option<u64>,
92    pub micros_95: Option<u64>,
93    pub micros_99: Option<u64>,
94}
95
96impl From<&Stat> for Report {
97    fn from(stat: &Stat) -> Self {
98        Report {
99            count: stat.counter.count,
100            rate_s: if stat.counter.count == 0 {
101                None
102            } else {
103                Some(
104                    (1.0 / (stat
105                        .counter
106                        .now
107                        .sub(stat.counter.start)
108                        .div_f64(stat.counter.count as f64)
109                        .as_secs_f64())) as u64,
110                )
111            },
112            micros_avg: mean(&stat.histogram),
113            micros_95: perc(95.0, &stat.histogram),
114            micros_99: perc(99.0, &stat.histogram),
115        }
116    }
117}
118
119#[derive(Serialize)]
120pub struct OpReport {
121    pub successful: Report,
122    pub failed: Report,
123}
124
125impl From<&OpStat> for OpReport {
126    fn from(write_stat: &OpStat) -> Self {
127        OpReport {
128            successful: (&write_stat.successful).into(),
129            failed: (&write_stat.failed).into(),
130        }
131    }
132}
133
134#[derive(Serialize)]
135pub struct ReadReport {
136    pub op: OpReport,
137    pub round_trip: Report,
138}
139
140impl From<&ReadStat> for ReadReport {
141    fn from(read_stat: &ReadStat) -> Self {
142        ReadReport {
143            op: (&read_stat.op).into(),
144            round_trip: (&read_stat.round_trip).into(),
145        }
146    }
147}
148
149#[derive(Serialize)]
150pub struct WithNodeIndex<R> {
151    pub node_index: usize,
152    pub report: R,
153}
154
155#[derive(Serialize)]
156pub struct Reports {
157    pub global_write: OpReport,
158    pub global_read: ReadReport,
159    pub nodes_writes: Vec<WithNodeIndex<OpReport>>,
160    pub nodes_reads: Vec<WithNodeIndex<ReadReport>>,
161}
162
163impl From<&Stats> for Reports {
164    fn from(stats: &Stats) -> Self {
165        Reports {
166            global_write: (&stats.global_write).into(),
167            global_read: (&stats.global_read).into(),
168            nodes_writes: into_reports(stats.nodes_writes.iter().collect()),
169            nodes_reads: into_reports(stats.nodes_reads.iter().collect()),
170        }
171    }
172}
173
174fn into_reports<S, R: From<S>>(stats: Vec<S>) -> Vec<WithNodeIndex<R>> {
175    stats
176        .into_iter()
177        .enumerate()
178        .map(|(pos, x)| WithNodeIndex {
179            node_index: pos,
180            report: x.into(),
181        })
182        .collect()
183}
184
185impl Display for Reports {
186    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
187        let json = serde_json::to_string_pretty(self).map_err(|_| std::fmt::Error)?;
188        write!(f, "{}", json)
189    }
190}
191
192fn new_default_histogram() -> Histogram {
193    Histogram::new(16, 64).unwrap()
194}
195
196fn mean(histogram: &Histogram) -> Option<u64> {
197    perc(50.0, histogram)
198}
199
200fn perc(percentile: f64, histogram: &Histogram) -> Option<u64> {
201    histogram
202        .percentile(percentile)
203        .iter()
204        .flat_map(|bucket| bucket.as_ref().map(|b| b.end()))
205        .collect::<Vec<_>>()
206        .pop()
207}