1use super::{logical_cpu_count, physical_cpu_count};
18
19#[derive(Debug, Clone)]
21#[allow(clippy::struct_excessive_bools)]
22pub struct RecommendedConfig {
23 pub num_cores: usize,
26 pub cpu_pinning: bool,
28 pub numa_aware: bool,
30
31 pub use_io_uring: bool,
34 pub io_uring_sqpoll: bool,
36 pub io_uring_iopoll: bool,
38
39 pub use_xdp: bool,
42
43 pub use_huge_pages: bool,
46 pub arena_size: usize,
48 pub state_store_size: usize,
50 pub queue_capacity: usize,
52
53 pub cache_line_size: usize,
56}
57
58impl RecommendedConfig {
59 #[must_use]
61 pub fn from_capabilities(caps: &super::SystemCapabilities) -> Self {
62 Self {
63 num_cores: Self::calculate_num_cores(caps),
65 cpu_pinning: caps.cpu_count > 1,
66 numa_aware: caps.numa_nodes > 1,
67
68 use_io_uring: caps.io_uring.is_usable(),
70 io_uring_sqpoll: caps.io_uring.sqpoll_supported,
71 io_uring_iopoll: caps.io_uring.iopoll_supported
72 && caps.storage.device_type.supports_iopoll(),
73
74 use_xdp: caps.xdp.is_usable(),
76
77 use_huge_pages: caps.memory.huge_pages_available && caps.memory.huge_pages_free > 0,
79 arena_size: Self::calculate_arena_size(caps),
80 state_store_size: Self::calculate_state_store_size(caps),
81 queue_capacity: Self::calculate_queue_capacity(caps),
82
83 cache_line_size: caps.cache_line_size,
85 }
86 }
87
88 fn calculate_num_cores(_caps: &super::SystemCapabilities) -> usize {
90 let physical = physical_cpu_count();
91 let logical = logical_cpu_count();
92
93 if physical <= 2 {
96 logical.min(4)
97 } else {
98 if physical > 8 {
100 physical - 1
101 } else {
102 physical
103 }
104 }
105 }
106
107 fn calculate_arena_size(caps: &super::SystemCapabilities) -> usize {
109 let memory_per_core = caps.memory.available_memory / caps.cpu_count as u64;
110
111 let arena = (memory_per_core / 8) as usize;
113 arena.clamp(1024 * 1024, 64 * 1024 * 1024) }
115
116 fn calculate_state_store_size(caps: &super::SystemCapabilities) -> usize {
118 let memory_per_core = caps.memory.available_memory / caps.cpu_count as u64;
119
120 let state_size = (memory_per_core / 4) as usize;
122 state_size.clamp(16 * 1024 * 1024, 1024 * 1024 * 1024) }
124
125 fn calculate_queue_capacity(caps: &super::SystemCapabilities) -> usize {
127 if caps.memory.total_memory > 32 * 1024 * 1024 * 1024 {
129 131_072 } else if caps.memory.total_memory > 8 * 1024 * 1024 * 1024 {
131 65_536 } else {
133 16_384 }
135 }
136
137 #[must_use]
139 pub fn summary(&self) -> String {
140 use std::fmt::Write;
141
142 let mut s = String::new();
143
144 let _ = writeln!(s, "Recommended Configuration:");
145 let _ = writeln!(
146 s,
147 " Cores: {} (pinned: {})",
148 self.num_cores, self.cpu_pinning
149 );
150 let _ = writeln!(s, " NUMA-aware: {}", self.numa_aware);
151
152 let _ = writeln!(
153 s,
154 " io_uring: {} (SQPOLL: {}, IOPOLL: {})",
155 self.use_io_uring, self.io_uring_sqpoll, self.io_uring_iopoll
156 );
157 let _ = writeln!(s, " XDP: {}", self.use_xdp);
158
159 let _ = writeln!(s, " Huge pages: {}", self.use_huge_pages);
160 let _ = writeln!(s, " Arena size: {} MB", self.arena_size / (1024 * 1024));
161 let _ = writeln!(
162 s,
163 " State store: {} MB",
164 self.state_store_size / (1024 * 1024)
165 );
166 let _ = writeln!(s, " Queue capacity: {}", self.queue_capacity);
167 let _ = writeln!(s, " Cache line: {} bytes", self.cache_line_size);
168
169 s
170 }
171
172 #[must_use]
174 pub fn is_optimal(&self) -> bool {
175 self.use_io_uring && self.io_uring_sqpoll && self.cpu_pinning
176 }
177
178 #[must_use]
180 pub fn performance_tier(&self) -> PerformanceTier {
181 if self.use_io_uring && self.io_uring_sqpoll && self.io_uring_iopoll && self.numa_aware {
182 PerformanceTier::Maximum
183 } else if self.use_io_uring && self.io_uring_sqpoll {
184 PerformanceTier::High
185 } else if self.use_io_uring {
186 PerformanceTier::Good
187 } else {
188 PerformanceTier::Basic
189 }
190 }
191}
192
193impl Default for RecommendedConfig {
194 fn default() -> Self {
195 Self {
196 num_cores: logical_cpu_count(),
197 cpu_pinning: false,
198 numa_aware: false,
199 use_io_uring: false,
200 io_uring_sqpoll: false,
201 io_uring_iopoll: false,
202 use_xdp: false,
203 use_huge_pages: false,
204 arena_size: 16 * 1024 * 1024,
205 state_store_size: 256 * 1024 * 1024,
206 queue_capacity: 65_536,
207 cache_line_size: 64,
208 }
209 }
210}
211
212#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
214pub enum PerformanceTier {
215 Basic,
217 Good,
219 High,
221 Maximum,
223}
224
225impl std::fmt::Display for PerformanceTier {
226 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
227 match self {
228 PerformanceTier::Basic => write!(f, "Basic"),
229 PerformanceTier::Good => write!(f, "Good"),
230 PerformanceTier::High => write!(f, "High"),
231 PerformanceTier::Maximum => write!(f, "Maximum"),
232 }
233 }
234}
235
236#[cfg(test)]
237#[allow(clippy::field_reassign_with_default)]
238mod tests {
239 use super::super::SystemCapabilities;
240 use super::*;
241
242 #[test]
243 fn test_recommended_config_default() {
244 let config = RecommendedConfig::default();
245 assert!(config.num_cores >= 1);
246 assert!(!config.cpu_pinning);
247 assert!(!config.use_io_uring);
248 }
249
250 #[test]
251 fn test_recommended_config_from_capabilities() {
252 let caps = SystemCapabilities::detect();
253 let config = RecommendedConfig::from_capabilities(caps);
254
255 assert!(config.num_cores >= 1);
256 assert!(config.arena_size >= 1024 * 1024);
257 assert!(config.state_store_size >= 16 * 1024 * 1024);
258 assert!(config.cache_line_size >= 32);
259 }
260
261 #[test]
262 fn test_recommended_config_summary() {
263 let config = RecommendedConfig::default();
264 let summary = config.summary();
265
266 assert!(summary.contains("Cores:"));
267 assert!(summary.contains("io_uring:"));
268 assert!(summary.contains("NUMA-aware:"));
269 }
270
271 #[test]
272 fn test_performance_tier_ordering() {
273 assert!(PerformanceTier::Basic < PerformanceTier::Good);
274 assert!(PerformanceTier::Good < PerformanceTier::High);
275 assert!(PerformanceTier::High < PerformanceTier::Maximum);
276 }
277
278 #[test]
279 fn test_performance_tier_display() {
280 assert_eq!(format!("{}", PerformanceTier::Basic), "Basic");
281 assert_eq!(format!("{}", PerformanceTier::Maximum), "Maximum");
282 }
283
284 #[test]
285 fn test_is_optimal() {
286 let mut config = RecommendedConfig::default();
287 assert!(!config.is_optimal());
288
289 config.use_io_uring = true;
290 config.io_uring_sqpoll = true;
291 config.cpu_pinning = true;
292 assert!(config.is_optimal());
293 }
294
295 #[test]
296 fn test_performance_tier_basic() {
297 let config = RecommendedConfig::default();
298 assert_eq!(config.performance_tier(), PerformanceTier::Basic);
299 }
300
301 #[test]
302 fn test_performance_tier_good() {
303 let mut config = RecommendedConfig::default();
304 config.use_io_uring = true;
305 assert_eq!(config.performance_tier(), PerformanceTier::Good);
306 }
307
308 #[test]
309 fn test_performance_tier_high() {
310 let mut config = RecommendedConfig::default();
311 config.use_io_uring = true;
312 config.io_uring_sqpoll = true;
313 assert_eq!(config.performance_tier(), PerformanceTier::High);
314 }
315
316 #[test]
317 fn test_performance_tier_maximum() {
318 let mut config = RecommendedConfig::default();
319 config.use_io_uring = true;
320 config.io_uring_sqpoll = true;
321 config.io_uring_iopoll = true;
322 config.numa_aware = true;
323 assert_eq!(config.performance_tier(), PerformanceTier::Maximum);
324 }
325}