rustkernel_core/runtime/
config.rs

1//! Runtime Configuration
2//!
3//! Configuration types for the RustKernels runtime, supporting:
4//! - Programmatic configuration via builders
5//! - Environment variable overrides
6//! - File-based configuration (TOML/JSON)
7
8use serde::{Deserialize, Serialize};
9use std::time::Duration;
10
11/// Runtime configuration
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct RuntimeConfig {
14    /// Enable GPU backends
15    pub gpu_enabled: bool,
16    /// Primary GPU backend (cuda, wgpu, metal, cpu)
17    pub primary_backend: String,
18    /// Fallback backend if primary unavailable
19    pub fallback_backend: Option<String>,
20    /// Maximum concurrent kernel instances
21    pub max_kernel_instances: usize,
22    /// Maximum message queue depth per kernel
23    pub max_queue_depth: usize,
24    /// Drain timeout for graceful shutdown
25    pub drain_timeout: Duration,
26    /// Health check interval
27    pub health_check_interval: Duration,
28    /// Enable hot configuration reload
29    pub hot_reload_enabled: bool,
30    /// Enable structured JSON logging
31    pub structured_logging: bool,
32    /// Log level (trace, debug, info, warn, error)
33    pub log_level: String,
34    /// Metrics export interval
35    pub metrics_interval: Duration,
36    /// Backend-specific configuration
37    pub backend: BackendConfig,
38    /// Worker thread count (0 = auto-detect)
39    pub worker_threads: usize,
40}
41
42impl Default for RuntimeConfig {
43    fn default() -> Self {
44        Self::development()
45    }
46}
47
48impl RuntimeConfig {
49    /// Development configuration - minimal overhead, verbose logging
50    pub fn development() -> Self {
51        Self {
52            gpu_enabled: false,
53            primary_backend: "cpu".to_string(),
54            fallback_backend: None,
55            max_kernel_instances: 100,
56            max_queue_depth: 1000,
57            drain_timeout: Duration::from_secs(5),
58            health_check_interval: Duration::from_secs(30),
59            hot_reload_enabled: true,
60            structured_logging: false,
61            log_level: "debug".to_string(),
62            metrics_interval: Duration::from_secs(60),
63            backend: BackendConfig::default(),
64            worker_threads: 0,
65        }
66    }
67
68    /// Production configuration - optimized settings
69    pub fn production() -> Self {
70        Self {
71            gpu_enabled: true,
72            primary_backend: "cuda".to_string(),
73            fallback_backend: Some("cpu".to_string()),
74            max_kernel_instances: 1000,
75            max_queue_depth: 10_000,
76            drain_timeout: Duration::from_secs(30),
77            health_check_interval: Duration::from_secs(10),
78            hot_reload_enabled: false,
79            structured_logging: true,
80            log_level: "info".to_string(),
81            metrics_interval: Duration::from_secs(15),
82            backend: BackendConfig::production(),
83            worker_threads: 0,
84        }
85    }
86
87    /// High-performance configuration - maximum throughput
88    pub fn high_performance() -> Self {
89        Self {
90            gpu_enabled: true,
91            primary_backend: "cuda".to_string(),
92            fallback_backend: None,
93            max_kernel_instances: 10_000,
94            max_queue_depth: 100_000,
95            drain_timeout: Duration::from_secs(10),
96            health_check_interval: Duration::from_secs(5),
97            hot_reload_enabled: false,
98            structured_logging: true,
99            log_level: "warn".to_string(),
100            metrics_interval: Duration::from_secs(5),
101            backend: BackendConfig::high_performance(),
102            worker_threads: 0,
103        }
104    }
105
106    /// Testing configuration - deterministic behavior
107    pub fn testing() -> Self {
108        Self {
109            gpu_enabled: false,
110            primary_backend: "cpu".to_string(),
111            fallback_backend: None,
112            max_kernel_instances: 10,
113            max_queue_depth: 100,
114            drain_timeout: Duration::from_millis(100),
115            health_check_interval: Duration::from_secs(1),
116            hot_reload_enabled: false,
117            structured_logging: false,
118            log_level: "trace".to_string(),
119            metrics_interval: Duration::from_secs(1),
120            backend: BackendConfig::testing(),
121            worker_threads: 1,
122        }
123    }
124
125    /// Load configuration from environment variables
126    pub fn from_env() -> Self {
127        let mut config = Self::default();
128
129        if let Ok(val) = std::env::var("RUSTKERNEL_GPU_ENABLED") {
130            config.gpu_enabled = val.parse().unwrap_or(config.gpu_enabled);
131        }
132        if let Ok(val) = std::env::var("RUSTKERNEL_BACKEND") {
133            config.primary_backend = val;
134        }
135        if let Ok(val) = std::env::var("RUSTKERNEL_FALLBACK_BACKEND") {
136            config.fallback_backend = Some(val);
137        }
138        if let Ok(val) = std::env::var("RUSTKERNEL_MAX_INSTANCES") {
139            config.max_kernel_instances = val.parse().unwrap_or(config.max_kernel_instances);
140        }
141        if let Ok(val) = std::env::var("RUSTKERNEL_QUEUE_DEPTH") {
142            config.max_queue_depth = val.parse().unwrap_or(config.max_queue_depth);
143        }
144        if let Ok(val) = std::env::var("RUSTKERNEL_DRAIN_TIMEOUT_SECS") {
145            config.drain_timeout =
146                Duration::from_secs(val.parse().unwrap_or(config.drain_timeout.as_secs()));
147        }
148        if let Ok(val) = std::env::var("RUSTKERNEL_LOG_LEVEL") {
149            config.log_level = val;
150        }
151        if let Ok(val) = std::env::var("RUSTKERNEL_STRUCTURED_LOGGING") {
152            config.structured_logging = val.parse().unwrap_or(config.structured_logging);
153        }
154        if let Ok(val) = std::env::var("RUSTKERNEL_WORKER_THREADS") {
155            config.worker_threads = val.parse().unwrap_or(config.worker_threads);
156        }
157
158        config
159    }
160
161    /// Load configuration from file
162    pub fn from_file(path: &std::path::Path) -> Result<Self, ConfigError> {
163        let contents = std::fs::read_to_string(path).map_err(ConfigError::IoError)?;
164
165        let ext = path.extension().and_then(|e| e.to_str()).unwrap_or("toml");
166
167        match ext {
168            "json" => serde_json::from_str(&contents).map_err(ConfigError::JsonError),
169            "toml" => toml::from_str(&contents).map_err(ConfigError::TomlError),
170            _ => Err(ConfigError::UnsupportedFormat(ext.to_string())),
171        }
172    }
173
174    /// Create a builder for this configuration
175    pub fn builder() -> RuntimeConfigBuilder {
176        RuntimeConfigBuilder::default()
177    }
178
179    /// Validate configuration
180    pub fn validate(&self) -> Result<(), ConfigError> {
181        if self.max_kernel_instances == 0 {
182            return Err(ConfigError::InvalidValue(
183                "max_kernel_instances must be > 0".to_string(),
184            ));
185        }
186        if self.max_queue_depth == 0 {
187            return Err(ConfigError::InvalidValue(
188                "max_queue_depth must be > 0".to_string(),
189            ));
190        }
191        if self.drain_timeout.is_zero() {
192            return Err(ConfigError::InvalidValue(
193                "drain_timeout must be > 0".to_string(),
194            ));
195        }
196        Ok(())
197    }
198}
199
200/// Backend-specific configuration
201#[derive(Debug, Clone, Serialize, Deserialize)]
202pub struct BackendConfig {
203    /// CUDA device index (for cuda backend)
204    pub cuda_device: i32,
205    /// Enable CUDA unified memory
206    pub cuda_unified_memory: bool,
207    /// GPU memory limit in bytes (0 = unlimited)
208    pub memory_limit_bytes: u64,
209    /// Enable memory pooling
210    pub memory_pooling: bool,
211    /// Memory pool initial size in bytes
212    pub pool_initial_bytes: u64,
213    /// Memory pool growth factor
214    pub pool_growth_factor: f32,
215    /// Enable async memory operations
216    pub async_memory: bool,
217    /// Enable kernel fusion optimizations
218    pub kernel_fusion: bool,
219    /// Maximum batch size for batched execution
220    pub max_batch_size: usize,
221}
222
223impl Default for BackendConfig {
224    fn default() -> Self {
225        Self {
226            cuda_device: 0,
227            cuda_unified_memory: false,
228            memory_limit_bytes: 0,
229            memory_pooling: true,
230            pool_initial_bytes: 64 * 1024 * 1024, // 64 MB
231            pool_growth_factor: 2.0,
232            async_memory: true,
233            kernel_fusion: false,
234            max_batch_size: 1024,
235        }
236    }
237}
238
239impl BackendConfig {
240    /// Production backend configuration
241    pub fn production() -> Self {
242        Self {
243            cuda_device: 0,
244            cuda_unified_memory: false,
245            memory_limit_bytes: 0,
246            memory_pooling: true,
247            pool_initial_bytes: 256 * 1024 * 1024, // 256 MB
248            pool_growth_factor: 1.5,
249            async_memory: true,
250            kernel_fusion: true,
251            max_batch_size: 4096,
252        }
253    }
254
255    /// High-performance backend configuration
256    pub fn high_performance() -> Self {
257        Self {
258            cuda_device: 0,
259            cuda_unified_memory: false,
260            memory_limit_bytes: 0,
261            memory_pooling: true,
262            pool_initial_bytes: 1024 * 1024 * 1024, // 1 GB
263            pool_growth_factor: 1.25,
264            async_memory: true,
265            kernel_fusion: true,
266            max_batch_size: 16384,
267        }
268    }
269
270    /// Testing backend configuration
271    pub fn testing() -> Self {
272        Self {
273            cuda_device: 0,
274            cuda_unified_memory: false,
275            memory_limit_bytes: 16 * 1024 * 1024, // 16 MB limit
276            memory_pooling: false,
277            pool_initial_bytes: 1024 * 1024, // 1 MB
278            pool_growth_factor: 2.0,
279            async_memory: false,
280            kernel_fusion: false,
281            max_batch_size: 64,
282        }
283    }
284}
285
286/// Builder for RuntimeConfig
287#[derive(Debug, Clone, Default)]
288pub struct RuntimeConfigBuilder {
289    config: RuntimeConfig,
290}
291
292impl RuntimeConfigBuilder {
293    /// Create builder with development defaults
294    pub fn development() -> Self {
295        Self {
296            config: RuntimeConfig::development(),
297        }
298    }
299
300    /// Create builder with production defaults
301    pub fn production() -> Self {
302        Self {
303            config: RuntimeConfig::production(),
304        }
305    }
306
307    /// Create builder with high-performance defaults
308    pub fn high_performance() -> Self {
309        Self {
310            config: RuntimeConfig::high_performance(),
311        }
312    }
313
314    /// Enable or disable GPU
315    pub fn gpu_enabled(mut self, enabled: bool) -> Self {
316        self.config.gpu_enabled = enabled;
317        self
318    }
319
320    /// Set primary backend
321    pub fn primary_backend(mut self, backend: impl Into<String>) -> Self {
322        self.config.primary_backend = backend.into();
323        self
324    }
325
326    /// Set fallback backend
327    pub fn fallback_backend(mut self, backend: impl Into<String>) -> Self {
328        self.config.fallback_backend = Some(backend.into());
329        self
330    }
331
332    /// Set max kernel instances
333    pub fn max_kernel_instances(mut self, count: usize) -> Self {
334        self.config.max_kernel_instances = count;
335        self
336    }
337
338    /// Set max queue depth
339    pub fn max_queue_depth(mut self, depth: usize) -> Self {
340        self.config.max_queue_depth = depth;
341        self
342    }
343
344    /// Set drain timeout
345    pub fn drain_timeout(mut self, timeout: Duration) -> Self {
346        self.config.drain_timeout = timeout;
347        self
348    }
349
350    /// Set health check interval
351    pub fn health_check_interval(mut self, interval: Duration) -> Self {
352        self.config.health_check_interval = interval;
353        self
354    }
355
356    /// Enable hot reload
357    pub fn hot_reload(mut self, enabled: bool) -> Self {
358        self.config.hot_reload_enabled = enabled;
359        self
360    }
361
362    /// Enable structured logging
363    pub fn structured_logging(mut self, enabled: bool) -> Self {
364        self.config.structured_logging = enabled;
365        self
366    }
367
368    /// Set log level
369    pub fn log_level(mut self, level: impl Into<String>) -> Self {
370        self.config.log_level = level.into();
371        self
372    }
373
374    /// Set metrics interval
375    pub fn metrics_interval(mut self, interval: Duration) -> Self {
376        self.config.metrics_interval = interval;
377        self
378    }
379
380    /// Set backend configuration
381    pub fn backend_config(mut self, config: BackendConfig) -> Self {
382        self.config.backend = config;
383        self
384    }
385
386    /// Set worker thread count
387    pub fn worker_threads(mut self, count: usize) -> Self {
388        self.config.worker_threads = count;
389        self
390    }
391
392    /// Build the configuration
393    pub fn build(self) -> Result<RuntimeConfig, ConfigError> {
394        self.config.validate()?;
395        Ok(self.config)
396    }
397
398    /// Build without validation
399    pub fn build_unchecked(self) -> RuntimeConfig {
400        self.config
401    }
402}
403
404/// Configuration error types
405#[derive(Debug, thiserror::Error)]
406pub enum ConfigError {
407    /// IO error reading config file
408    #[error("IO error: {0}")]
409    IoError(#[from] std::io::Error),
410
411    /// JSON parsing error
412    #[error("JSON parse error: {0}")]
413    JsonError(#[from] serde_json::Error),
414
415    /// TOML parsing error
416    #[error("TOML parse error: {0}")]
417    TomlError(#[from] toml::de::Error),
418
419    /// Unsupported config format
420    #[error("Unsupported config format: {0}")]
421    UnsupportedFormat(String),
422
423    /// Invalid configuration value
424    #[error("Invalid config value: {0}")]
425    InvalidValue(String),
426
427    /// Missing required field
428    #[error("Missing required field: {0}")]
429    MissingField(String),
430}
431
432#[cfg(test)]
433mod tests {
434    use super::*;
435
436    #[test]
437    fn test_default_config() {
438        let config = RuntimeConfig::default();
439        assert!(!config.gpu_enabled);
440        assert_eq!(config.primary_backend, "cpu");
441    }
442
443    #[test]
444    fn test_production_config() {
445        let config = RuntimeConfig::production();
446        assert!(config.gpu_enabled);
447        assert_eq!(config.primary_backend, "cuda");
448        assert!(config.structured_logging);
449    }
450
451    #[test]
452    fn test_config_validation() {
453        let config = RuntimeConfig {
454            max_kernel_instances: 0,
455            ..RuntimeConfig::default()
456        };
457        assert!(config.validate().is_err());
458
459        let config = RuntimeConfig {
460            max_kernel_instances: 100,
461            max_queue_depth: 0,
462            ..RuntimeConfig::default()
463        };
464        assert!(config.validate().is_err());
465
466        let config = RuntimeConfig {
467            max_kernel_instances: 100,
468            max_queue_depth: 1000,
469            ..RuntimeConfig::default()
470        };
471        assert!(config.validate().is_ok());
472    }
473
474    #[test]
475    fn test_config_builder() {
476        let config = RuntimeConfigBuilder::production()
477            .gpu_enabled(false)
478            .max_kernel_instances(500)
479            .build()
480            .unwrap();
481
482        assert!(!config.gpu_enabled);
483        assert_eq!(config.max_kernel_instances, 500);
484    }
485}