1use serde::{Deserialize, Serialize};
4
5#[cfg(feature = "cuda")]
6use crate::cuda::CudaDeviceSelector;
7
8use crate::mab::{CalibrationConfig, MabKnobs};
9use crate::pool::DEFAULT_POOL_SIZE;
10use prometheus::Registry;
11
12#[derive(Debug, Clone, Deserialize, Serialize)]
17pub struct LoomConfig {
18 #[serde(default = "default_prefix")]
20 pub prefix: String,
21
22 #[serde(default)]
24 pub cpuset: Option<String>,
25
26 #[serde(default)]
28 pub tokio_threads: Option<usize>,
29
30 #[serde(default)]
32 pub rayon_threads: Option<usize>,
33
34 #[serde(default = "default_compute_pool_size")]
36 pub compute_pool_size: usize,
37
38 #[cfg(feature = "cuda")]
40 #[serde(default)]
41 pub cuda_device: Option<CudaDeviceSelector>,
42
43 #[serde(default)]
46 pub mab_knobs: Option<MabKnobs>,
47
48 #[serde(default)]
51 pub calibration: Option<CalibrationConfig>,
52
53 #[serde(skip)]
57 pub prometheus_registry: Option<Registry>,
58}
59
60fn default_compute_pool_size() -> usize {
61 DEFAULT_POOL_SIZE
62}
63
64fn default_prefix() -> String {
65 "loom".to_string()
66}
67
68impl Default for LoomConfig {
69 fn default() -> Self {
70 Self {
71 prefix: default_prefix(),
72 cpuset: None,
73 tokio_threads: None,
74 rayon_threads: None,
75 compute_pool_size: default_compute_pool_size(),
76 #[cfg(feature = "cuda")]
77 cuda_device: None,
78 mab_knobs: None,
79 calibration: None,
80 prometheus_registry: None,
81 }
82 }
83}
84
85impl LoomConfig {
86 pub fn new() -> Self {
88 Self::default()
89 }
90
91 pub fn effective_tokio_threads(&self) -> usize {
95 self.tokio_threads.unwrap_or(1)
96 }
97
98 pub fn effective_rayon_threads(&self, available_cpus: usize) -> usize {
103 self.rayon_threads
104 .unwrap_or_else(|| available_cpus.saturating_sub(self.effective_tokio_threads()))
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn test_default_config() {
114 let config = LoomConfig::default();
115 assert_eq!(config.prefix, "loom");
116 assert!(config.cpuset.is_none());
117 assert!(config.tokio_threads.is_none());
118 assert!(config.rayon_threads.is_none());
119 assert_eq!(config.compute_pool_size, 64);
120 assert!(config.mab_knobs.is_none());
121 assert!(config.calibration.is_none());
122 }
123
124 #[test]
125 fn test_effective_tokio_threads() {
126 let mut config = LoomConfig::default();
127 assert_eq!(config.effective_tokio_threads(), 1);
128
129 config.tokio_threads = Some(4);
130 assert_eq!(config.effective_tokio_threads(), 4);
131 }
132
133 #[test]
134 fn test_effective_rayon_threads() {
135 let mut config = LoomConfig::default();
136 assert_eq!(config.effective_rayon_threads(8), 7);
138
139 config.tokio_threads = Some(2);
140 assert_eq!(config.effective_rayon_threads(8), 6);
142
143 config.rayon_threads = Some(4);
144 assert_eq!(config.effective_rayon_threads(8), 4);
146 }
147
148 #[test]
149 fn test_deserialize_config() {
150 let toml = r#"
151 prefix = "myapp"
152 cpuset = "0-3"
153 tokio_threads = 2
154 rayon_threads = 6
155 compute_pool_size = 128
156 "#;
157
158 let config: LoomConfig = toml::from_str(toml).unwrap();
159 assert_eq!(config.prefix, "myapp");
160 assert_eq!(config.cpuset, Some("0-3".to_string()));
161 assert_eq!(config.tokio_threads, Some(2));
162 assert_eq!(config.rayon_threads, Some(6));
163 assert_eq!(config.compute_pool_size, 128);
164 }
165}