//! Fusion configuration and statistics //! //! This module provides configuration options for the automatic kernel fusion //! system, along with statistics tracking for monitoring fusion performance. use serde::{Deserialize, Serialize}; use std::sync::atomic::{AtomicU64, Ordering}; /// Configuration for the automatic kernel fusion system #[derive(Debug, Clone, Serialize, Deserialize)] pub struct FusionConfig { /// Whether fusion is enabled pub enabled: bool, /// Maximum number of pending operations before forcing a flush pub max_pending_ops: usize, /// Minimum operations required to consider fusion worthwhile pub min_fusion_ops: usize, /// Maximum operations allowed in a single fused kernel pub max_fusion_ops: usize, /// Whether to cache compiled fused kernels pub cache_enabled: bool, /// Maximum entries in the kernel cache (LRU eviction) pub cache_max_entries: usize, /// Whether to enable advanced pattern detection (softmax, layer norm) pub detect_advanced_patterns: bool, /// Whether to track detailed statistics pub track_stats: bool, } impl Default for FusionConfig { fn default() -> Self { Self { enabled: true, max_pending_ops: 64, min_fusion_ops: 2, max_fusion_ops: 16, cache_enabled: true, cache_max_entries: 1024, detect_advanced_patterns: true, track_stats: true, } } } impl FusionConfig { /// Create a new fusion config with default settings pub fn new() -> Self { Self::default() } /// Disable fusion (passthrough mode) pub fn disabled() -> Self { Self { enabled: false, ..Self::default() } } /// Builder: set enabled state pub fn with_enabled(mut self, enabled: bool) -> Self { self.enabled = enabled; self } /// Builder: set maximum pending operations pub fn with_max_pending_ops(mut self, max: usize) -> Self { self.max_pending_ops = max; self } /// Builder: set minimum fusion operations pub fn with_min_fusion_ops(mut self, min: usize) -> Self { self.min_fusion_ops = min; self } /// Builder: set maximum fusion operations pub fn with_max_fusion_ops(mut self, max: usize) -> Self { self.max_fusion_ops = max; self } /// Builder: enable/disable kernel cache pub fn with_cache(mut self, enabled: bool) -> Self { self.cache_enabled = enabled; self } /// Builder: set cache size pub fn with_cache_size(mut self, size: usize) -> Self { self.cache_max_entries = size; self } /// Builder: enable/disable advanced pattern detection pub fn with_advanced_patterns(mut self, enabled: bool) -> Self { self.detect_advanced_patterns = enabled; self } } /// Statistics for monitoring fusion performance #[derive(Debug, Default)] pub struct FusionStats { /// Total operations recorded pub operations_recorded: AtomicU64, /// Operations executed immediately (sync points) pub immediate_executions: AtomicU64, /// Operations executed via fused kernels pub fused_executions: AtomicU64, /// Total fusion opportunities detected pub fusion_opportunities: AtomicU64, /// Successful fusions executed pub successful_fusions: AtomicU64, /// Number of kernel launches saved pub kernel_launches_saved: AtomicU64, /// Cache hits for compiled kernels pub cache_hits: AtomicU64, /// Cache misses requiring compilation pub cache_misses: AtomicU64, /// Total memory bandwidth saved (estimated bytes) pub memory_saved_bytes: AtomicU64, } impl FusionStats { /// Create new statistics tracker pub fn new() -> Self { Self::default() } /// Record an operation pub fn record_operation(&self) { self.operations_recorded.fetch_add(1, Ordering::Relaxed); } /// Record an immediate execution (sync point) pub fn record_immediate(&self) { self.immediate_executions.fetch_add(1, Ordering::Relaxed); } /// Record a fused execution pub fn record_fused(&self, ops_count: u64) { self.fused_executions.fetch_add(ops_count, Ordering::Relaxed); self.successful_fusions.fetch_add(1, Ordering::Relaxed); // Each fusion saves (ops_count - 1) kernel launches self.kernel_launches_saved .fetch_add(ops_count.saturating_sub(1), Ordering::Relaxed); } /// Record a fusion opportunity pub fn record_opportunity(&self) { self.fusion_opportunities.fetch_add(1, Ordering::Relaxed); } /// Record a cache hit pub fn record_cache_hit(&self) { self.cache_hits.fetch_add(1, Ordering::Relaxed); } /// Record a cache miss pub fn record_cache_miss(&self) { self.cache_misses.fetch_add(1, Ordering::Relaxed); } /// Record memory bandwidth savings pub fn record_memory_saved(&self, bytes: u64) { self.memory_saved_bytes.fetch_add(bytes, Ordering::Relaxed); } /// Get a snapshot of current statistics pub fn snapshot(&self) -> FusionStatsSnapshot { FusionStatsSnapshot { operations_recorded: self.operations_recorded.load(Ordering::Relaxed), immediate_executions: self.immediate_executions.load(Ordering::Relaxed), fused_executions: self.fused_executions.load(Ordering::Relaxed), fusion_opportunities: self.fusion_opportunities.load(Ordering::Relaxed), successful_fusions: self.successful_fusions.load(Ordering::Relaxed), kernel_launches_saved: self.kernel_launches_saved.load(Ordering::Relaxed), cache_hits: self.cache_hits.load(Ordering::Relaxed), cache_misses: self.cache_misses.load(Ordering::Relaxed), memory_saved_bytes: self.memory_saved_bytes.load(Ordering::Relaxed), } } /// Reset all statistics pub fn reset(&self) { self.operations_recorded.store(0, Ordering::Relaxed); self.immediate_executions.store(0, Ordering::Relaxed); self.fused_executions.store(0, Ordering::Relaxed); self.fusion_opportunities.store(0, Ordering::Relaxed); self.successful_fusions.store(0, Ordering::Relaxed); self.kernel_launches_saved.store(0, Ordering::Relaxed); self.cache_hits.store(0, Ordering::Relaxed); self.cache_misses.store(0, Ordering::Relaxed); self.memory_saved_bytes.store(0, Ordering::Relaxed); } } /// A snapshot of fusion statistics (non-atomic, safe to clone) #[derive(Debug, Clone, Serialize, Deserialize)] pub struct FusionStatsSnapshot { pub operations_recorded: u64, pub immediate_executions: u64, pub fused_executions: u64, pub fusion_opportunities: u64, pub successful_fusions: u64, pub kernel_launches_saved: u64, pub cache_hits: u64, pub cache_misses: u64, pub memory_saved_bytes: u64, } impl FusionStatsSnapshot { /// Calculate the fusion rate (fused / total operations) pub fn fusion_rate(&self) -> f64 { if self.operations_recorded == 0 { return 0.0; } self.fused_executions as f64 / self.operations_recorded as f64 } /// Calculate the cache hit rate pub fn cache_hit_rate(&self) -> f64 { let total = self.cache_hits + self.cache_misses; if total == 0 { return 0.0; } self.cache_hits as f64 / total as f64 } /// Calculate kernel launch reduction percentage pub fn launch_reduction_pct(&self) -> f64 { if self.operations_recorded == 0 { return 0.0; } self.kernel_launches_saved as f64 / self.operations_recorded as f64 * 100.0 } } #[cfg(test)] mod tests { use super::*; #[test] fn test_config_defaults() { let config = FusionConfig::default(); assert!(config.enabled); assert_eq!(config.min_fusion_ops, 2); assert_eq!(config.max_fusion_ops, 16); } #[test] fn test_config_builder() { let config = FusionConfig::new() .with_enabled(false) .with_min_fusion_ops(3) .with_max_fusion_ops(8); assert!(!config.enabled); assert_eq!(config.min_fusion_ops, 3); assert_eq!(config.max_fusion_ops, 8); } #[test] fn test_stats_recording() { let stats = FusionStats::new(); stats.record_operation(); stats.record_operation(); stats.record_fused(3); stats.record_cache_hit(); let snapshot = stats.snapshot(); assert_eq!(snapshot.operations_recorded, 2); assert_eq!(snapshot.fused_executions, 3); assert_eq!(snapshot.successful_fusions, 1); assert_eq!(snapshot.kernel_launches_saved, 2); // 3 ops - 1 assert_eq!(snapshot.cache_hits, 1); } #[test] fn test_stats_rates() { let snapshot = FusionStatsSnapshot { operations_recorded: 100, immediate_executions: 20, fused_executions: 80, fusion_opportunities: 30, successful_fusions: 25, kernel_launches_saved: 60, cache_hits: 90, cache_misses: 10, memory_saved_bytes: 1024, }; assert!((snapshot.fusion_rate() - 0.8).abs() < 0.001); assert!((snapshot.cache_hit_rate() - 0.9).abs() < 0.001); assert!((snapshot.launch_reduction_pct() - 60.0).abs() < 0.001); } }