1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// Copyright (c) Facebook, Inc. and its affiliates.
use anyhow::Result;
use chrono::prelude::*;
use serde::{Deserialize, Serialize};
use std::time::SystemTime;

use rd_util::*;

pub const BENCH_FILENAME: &str = "bench.json";

const BENCH_DOC: &str = "\
//
// rd-agent benchmark results
//
//  timestamp: When this report was generated
//  hashd_seq: Current rd-hashd bench result sequence, see cmd.json
//  iocost_seq: Current iocost bench result sequence, see cmd.json
//  hashd[].hash_size: Mean hash size which determines CPU usage
//  hashd[].rps_max: Maximum RPS
//  hashd[].mem_size: Memory size base
//  hashd[].mem_frac: Memory size is mem_size * mem_frac, tune this if needed
//  hashd[].chunk_pages: Memory access chunk size in pages
//  hashd[].fake_cpu_load: Bench was run with --bench-fake-cpu-load
//  iocost.devnr: Storage device devnr
//  iocost.model: Model parameters
//  iocost.qos: QoS parameters
//
";

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct HashdKnobs {
    pub hash_size: usize,
    pub rps_max: u32,
    pub mem_size: u64,
    pub mem_frac: f64,
    pub chunk_pages: usize,
    pub fake_cpu_load: bool,
}

impl std::fmt::Display for HashdKnobs {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "hash_size={} rps_max={} mem_actual={} chunk_pages={}{}",
            format_size(self.hash_size),
            self.rps_max,
            format_size(self.actual_mem_size()),
            self.chunk_pages,
            if self.fake_cpu_load {
                " fake_cpu_load"
            } else {
                ""
            }
        )
    }
}

impl HashdKnobs {
    pub fn actual_mem_size(&self) -> u64 {
        (self.mem_size as f64 * self.mem_frac).ceil() as u64
    }
}

#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
pub struct IoCostKnobs {
    pub devnr: String,
    pub model: IoCostModelParams,
    pub qos: IoCostQoSParams,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct BenchKnobs {
    pub timestamp: DateTime<Local>,
    pub hashd_seq: u64,
    pub iocost_seq: u64,
    pub hashd: HashdKnobs,
    pub iocost: IoCostKnobs,
    pub iocost_dev_model: String,
    pub iocost_dev_fwrev: String,
    pub iocost_dev_size: u64,
}

impl Default for BenchKnobs {
    fn default() -> Self {
        Self {
            timestamp: DateTime::from(SystemTime::now()),
            hashd_seq: 0,
            iocost_seq: 0,
            hashd: Default::default(),
            iocost: Default::default(),
            iocost_dev_model: String::new(),
            iocost_dev_fwrev: String::new(),
            iocost_dev_size: 0,
        }
    }
}

impl JsonLoad for BenchKnobs {
    fn loaded(&mut self, _prev: Option<&mut Self>) -> Result<()> {
        self.iocost.qos.sanitize();
        Ok(())
    }
}

impl JsonSave for BenchKnobs {
    fn preamble() -> Option<String> {
        Some(BENCH_DOC.to_string())
    }
}