simplebench_runtime/
config.rs1use serde::{Deserialize, Serialize};
2use std::fs;
3use std::path::Path;
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct MeasurementConfig {
8 #[serde(default = "default_samples")]
10 pub samples: usize,
11
12 #[serde(default = "default_warmup_duration")]
14 pub warmup_duration_secs: u64,
15}
16
17fn default_samples() -> usize {
18 1000
19}
20fn default_warmup_duration() -> u64 {
21 3 }
23
24impl Default for MeasurementConfig {
25 fn default() -> Self {
26 Self {
27 samples: default_samples(),
28 warmup_duration_secs: default_warmup_duration(),
29 }
30 }
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct ComparisonConfig {
36 #[serde(default = "default_threshold")]
38 pub threshold: f64,
39
40 #[serde(default)]
42 pub ci_mode: bool,
43
44 #[serde(default = "default_window_size")]
46 pub window_size: usize,
47
48 #[serde(default = "default_confidence_level")]
50 pub confidence_level: f64,
51
52 #[serde(default = "default_cp_threshold")]
54 pub cp_threshold: f64,
55
56 #[serde(default = "default_hazard_rate")]
58 pub hazard_rate: f64,
59}
60
61fn default_threshold() -> f64 {
62 5.0
63}
64
65fn default_window_size() -> usize {
66 10
67}
68
69fn default_confidence_level() -> f64 {
70 0.95
71}
72
73fn default_cp_threshold() -> f64 {
74 0.8
75}
76
77fn default_hazard_rate() -> f64 {
78 0.1
79}
80
81impl Default for ComparisonConfig {
82 fn default() -> Self {
83 Self {
84 threshold: default_threshold(),
85 ci_mode: false,
86 window_size: default_window_size(),
87 confidence_level: default_confidence_level(),
88 cp_threshold: default_cp_threshold(),
89 hazard_rate: default_hazard_rate(),
90 }
91 }
92}
93
94#[derive(Debug, Clone, Serialize, Deserialize, Default)]
96pub struct BenchmarkConfig {
97 #[serde(default)]
98 pub measurement: MeasurementConfig,
99
100 #[serde(default)]
101 pub comparison: ComparisonConfig,
102}
103
104impl BenchmarkConfig {
105 pub fn load() -> Self {
109 let mut config = Self::default();
111
112 if let Ok(file_config) = Self::from_file("simplebench.toml") {
114 config = file_config;
115 }
116
117 config.apply_env_overrides();
119
120 config
121 }
122
123 pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self, Box<dyn std::error::Error>> {
125 let contents = fs::read_to_string(path)?;
126 let config: BenchmarkConfig = toml::from_str(&contents)?;
127 Ok(config)
128 }
129
130 pub fn apply_env_overrides(&mut self) {
134 if let Ok(samples) = std::env::var("SIMPLEBENCH_SAMPLES") {
136 if let Ok(val) = samples.parse() {
137 self.measurement.samples = val;
138 }
139 }
140
141 if let Ok(warmup) = std::env::var("SIMPLEBENCH_WARMUP_DURATION") {
142 if let Ok(val) = warmup.parse() {
143 self.measurement.warmup_duration_secs = val;
144 }
145 }
146
147 if std::env::var("SIMPLEBENCH_CI").is_ok() {
149 self.comparison.ci_mode = true;
150 }
151
152 if let Ok(threshold) = std::env::var("SIMPLEBENCH_THRESHOLD") {
153 if let Ok(val) = threshold.parse() {
154 self.comparison.threshold = val;
155 }
156 }
157
158 if let Ok(window) = std::env::var("SIMPLEBENCH_WINDOW") {
160 if let Ok(val) = window.parse() {
161 self.comparison.window_size = val;
162 }
163 }
164
165 if let Ok(confidence) = std::env::var("SIMPLEBENCH_CONFIDENCE") {
166 if let Ok(val) = confidence.parse() {
167 self.comparison.confidence_level = val;
168 }
169 }
170
171 if let Ok(cp_threshold) = std::env::var("SIMPLEBENCH_CP_THRESHOLD") {
172 if let Ok(val) = cp_threshold.parse() {
173 self.comparison.cp_threshold = val;
174 }
175 }
176
177 if let Ok(hazard_rate) = std::env::var("SIMPLEBENCH_HAZARD_RATE") {
178 if let Ok(val) = hazard_rate.parse() {
179 self.comparison.hazard_rate = val;
180 }
181 }
182 }
183
184 pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<(), Box<dyn std::error::Error>> {
186 let toml = toml::to_string_pretty(self)?;
187 fs::write(path, toml)?;
188 Ok(())
189 }
190}
191
192#[cfg(test)]
193mod tests {
194 use super::*;
195 use std::env;
196 use tempfile::NamedTempFile;
197
198 #[test]
199 fn test_default_config() {
200 let config = BenchmarkConfig::default();
201 assert_eq!(config.measurement.samples, 1000);
202 assert_eq!(config.measurement.warmup_duration_secs, 3);
203 assert_eq!(config.comparison.threshold, 5.0);
204 assert!(!config.comparison.ci_mode);
205 }
206
207 #[test]
208 fn test_save_and_load_config() {
209 let config = BenchmarkConfig::default();
210 let temp_file = NamedTempFile::new().unwrap();
211
212 config.save(temp_file.path()).unwrap();
213 let loaded = BenchmarkConfig::from_file(temp_file.path()).unwrap();
214
215 assert_eq!(loaded.measurement.samples, 1000);
216 assert_eq!(loaded.measurement.warmup_duration_secs, 3);
217 }
218
219 #[test]
220 fn test_env_overrides() {
221 env::set_var("SIMPLEBENCH_SAMPLES", "300");
222 env::set_var("SIMPLEBENCH_WARMUP_DURATION", "5");
223 env::set_var("SIMPLEBENCH_CI", "1");
224 env::set_var("SIMPLEBENCH_THRESHOLD", "10.0");
225
226 let mut config = BenchmarkConfig::default();
227 config.apply_env_overrides();
228
229 assert_eq!(config.measurement.samples, 300);
230 assert_eq!(config.measurement.warmup_duration_secs, 5);
231 assert!(config.comparison.ci_mode);
232 assert_eq!(config.comparison.threshold, 10.0);
233
234 env::remove_var("SIMPLEBENCH_SAMPLES");
236 env::remove_var("SIMPLEBENCH_WARMUP_DURATION");
237 env::remove_var("SIMPLEBENCH_CI");
238 env::remove_var("SIMPLEBENCH_THRESHOLD");
239 }
240
241 #[test]
242 fn test_partial_config_file() {
243 let toml_content = r#"
244 [measurement]
245 samples = 150
246
247 [comparison]
248 threshold = 7.5
249 "#;
250
251 let temp_file = NamedTempFile::new().unwrap();
252 fs::write(temp_file.path(), toml_content).unwrap();
253
254 let config = BenchmarkConfig::from_file(temp_file.path()).unwrap();
255
256 assert_eq!(config.measurement.samples, 150);
258 assert_eq!(config.comparison.threshold, 7.5);
259
260 assert_eq!(config.measurement.warmup_duration_secs, 3);
262 assert!(!config.comparison.ci_mode);
263 }
264}