TLA Line data Source code
1 : use std::time::Duration;
2 :
3 : use anyhow::Context;
4 :
5 : pub(crate) struct Stats {
6 : latency_histo: hdrhistogram::Histogram<u64>,
7 : }
8 :
9 : impl Stats {
10 UBC 0 : pub(crate) fn new() -> Self {
11 0 : Self {
12 0 : // Initialize with fixed bounds so that we panic at runtime instead of resizing the histogram,
13 0 : // which would skew the benchmark results.
14 0 : latency_histo: hdrhistogram::Histogram::new_with_bounds(1, 1_000_000_000, 3).unwrap(),
15 0 : }
16 0 : }
17 0 : pub(crate) fn observe(&mut self, latency: Duration) -> anyhow::Result<()> {
18 0 : let micros: u64 = latency
19 0 : .as_micros()
20 0 : .try_into()
21 0 : .context("latency greater than u64")?;
22 0 : self.latency_histo
23 0 : .record(micros)
24 0 : .context("add to histogram")?;
25 0 : Ok(())
26 0 : }
27 0 : pub(crate) fn output(&self) -> Output {
28 0 : let latency_percentiles = std::array::from_fn(|idx| {
29 0 : let micros = self
30 0 : .latency_histo
31 0 : .value_at_percentile(LATENCY_PERCENTILES[idx]);
32 0 : Duration::from_micros(micros)
33 0 : });
34 0 : Output {
35 0 : request_count: self.latency_histo.len(),
36 0 : latency_mean: Duration::from_micros(self.latency_histo.mean() as u64),
37 0 : latency_percentiles: LatencyPercentiles {
38 0 : latency_percentiles,
39 0 : },
40 0 : }
41 0 : }
42 0 : pub(crate) fn add(&mut self, other: &Self) {
43 0 : let Self {
44 0 : ref mut latency_histo,
45 0 : } = self;
46 0 : latency_histo.add(&other.latency_histo).unwrap();
47 0 : }
48 : }
49 :
50 : impl Default for Stats {
51 0 : fn default() -> Self {
52 0 : Self::new()
53 0 : }
54 : }
55 :
56 : const LATENCY_PERCENTILES: [f64; 4] = [95.0, 99.00, 99.90, 99.99];
57 :
58 : struct LatencyPercentiles {
59 : latency_percentiles: [Duration; 4],
60 : }
61 :
62 : impl serde::Serialize for LatencyPercentiles {
63 0 : fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
64 0 : where
65 0 : S: serde::Serializer,
66 0 : {
67 : use serde::ser::SerializeMap;
68 0 : let mut ser = serializer.serialize_map(Some(LATENCY_PERCENTILES.len()))?;
69 0 : for p in LATENCY_PERCENTILES {
70 0 : ser.serialize_entry(
71 0 : &format!("p{p}"),
72 0 : &format!(
73 0 : "{}",
74 0 : &humantime::format_duration(self.latency_percentiles[0])
75 0 : ),
76 0 : )?;
77 : }
78 0 : ser.end()
79 0 : }
80 : }
81 :
82 0 : #[derive(serde::Serialize)]
83 : pub(crate) struct Output {
84 : request_count: u64,
85 : #[serde(with = "humantime_serde")]
86 : latency_mean: Duration,
87 : latency_percentiles: LatencyPercentiles,
88 : }
|