//! Legacy AutoTuner for backward compatibility. use std::collections::HashMap; use std::time::{Duration, Instant}; use crate::error::SynthesisResult; use crate::hardware::HardwareProfile; use crate::templates::{KernelOperation, KernelTemplate}; use super::bayesian_tuner::BayesianAutoTuner; use super::config::TuningConfig; use super::gaussian_process::CacheEntry; use super::metrics::{PerformanceMetrics, TuningResult}; use super::parameters::ParameterRange; use super::types::SearchStrategy; /// Legacy AutoTuner for backward compatibility #[derive(Debug, Clone)] pub struct AutoTuner { hardware_profile: Option, cache: HashMap, rng_state: u64, } impl AutoTuner { pub fn new() -> SynthesisResult { Ok(Self { hardware_profile: None, cache: HashMap::new(), rng_state: 12345, }) } pub fn set_hardware_profile(&mut self, profile: HardwareProfile) { self.hardware_profile = Some(profile); } /// Create tuning configuration based on kernel operation and hardware pub fn create_tuning_config( &self, operation: &KernelOperation, ) -> SynthesisResult { let mut config = TuningConfig::default(); match operation { KernelOperation::Gemm { m, n, k, .. } => { config.parameter_ranges = vec![ ParameterRange::with_candidates("block_size_m", vec![16, 32, 64, 128]), ParameterRange::with_candidates("block_size_n", vec![16, 32, 64, 128]), ParameterRange::with_candidates("block_size_k", vec![8, 16, 32]), ParameterRange::with_candidates("thread_tile_m", vec![4, 8, 16]), ParameterRange::with_candidates("thread_tile_n", vec![4, 8, 16]), ParameterRange::with_candidates("warp_tile_m", vec![16, 32, 64]), ParameterRange::with_candidates("warp_tile_n", vec![16, 32, 64]), ParameterRange::with_candidates("unroll_factor", vec![1, 2, 4, 8]), ]; config.search_strategy = SearchStrategy::BayesianOptimization { iterations: 50 }; let problem_size = (*m as u64) * (*n as u64) * (*k as u64); if problem_size > 1_000_000 { config.timeout = Duration::from_secs(600); config.max_iterations = 100; } } KernelOperation::Convolution { .. } => { config.parameter_ranges = vec![ ParameterRange::with_candidates("block_size_x", vec![8, 16, 32]), ParameterRange::with_candidates("block_size_y", vec![8, 16, 32]), ParameterRange::with_candidates("tile_size", vec![1, 2, 4, 8]), ParameterRange::with_candidates("unroll_factor", vec![1, 2, 4]), ]; config.search_strategy = SearchStrategy::BayesianOptimization { iterations: 30 }; } KernelOperation::Attention { sequence_length, .. } => { config.parameter_ranges = vec![ ParameterRange::with_candidates("block_size", vec![64, 128, 256, 512]), ParameterRange::with_candidates("head_block_size", vec![1, 2, 4, 8]), ParameterRange::with_candidates("sequence_tile", vec![32, 64, 128]), ]; config.search_strategy = SearchStrategy::BayesianOptimization { iterations: 100 }; if *sequence_length > 2048 { config.timeout = Duration::from_secs(900); } } KernelOperation::Reduction { .. } => { config.parameter_ranges = vec![ ParameterRange::with_candidates("block_size", vec![128, 256, 512, 1024]), ParameterRange::with_candidates("items_per_thread", vec![1, 2, 4, 8]), ParameterRange::with_candidates("unroll_factor", vec![1, 2, 4]), ]; config.search_strategy = SearchStrategy::BayesianOptimization { iterations: 20 }; config.timeout = Duration::from_secs(120); } KernelOperation::Elementwise { .. } => { config.parameter_ranges = vec![ ParameterRange::with_candidates("block_size", vec![256, 512, 1024]), ParameterRange::with_candidates("elements_per_thread", vec![1, 2, 4, 8, 16]), ]; config.search_strategy = SearchStrategy::BayesianOptimization { iterations: 15 }; config.timeout = Duration::from_secs(60); } } Ok(config) } /// Generate a cache key for the operation and hardware fn generate_cache_key(&self, operation: &KernelOperation) -> String { let hw_hash = self.hardware_profile.as_ref().map_or_else( || "unknown_hw".to_string(), |p| { format!( "{}_{}.{}", p.architecture, p.compute_capability.0, p.compute_capability.1 ) }, ); match operation { KernelOperation::Gemm { m, n, k, transpose_a, transpose_b, } => { format!("gemm_{m}x{n}x{k}_ta{transpose_a}_tb{transpose_b}_{hw_hash}") } KernelOperation::Convolution { batch_size, in_channels, out_channels, height, width, kernel_size, } => { format!( "conv_{batch_size}x{in_channels}x{out_channels}x{height}x{width}x{kernel_size}_{hw_hash}" ) } KernelOperation::Attention { sequence_length, head_dim, num_heads, } => { format!("attention_{sequence_length}x{head_dim}x{num_heads}_{hw_hash}") } KernelOperation::Reduction { operation: op, input_size, axis, } => { format!("reduction_{op:?}_{input_size}x{axis}_{hw_hash}") } KernelOperation::Elementwise { operation: op, size, } => { format!("elementwise_{op:?}_{size}_{hw_hash}") } } } /// Simulate kernel execution and return performance metrics fn profile_kernel_performance( &mut self, _template: &KernelTemplate, parameters: &HashMap, ) -> SynthesisResult { let block_size = parameters .get("block_size") .or(parameters.get("block_size_m")) .unwrap_or(&128); let unroll_factor = parameters.get("unroll_factor").unwrap_or(&4); let base_gflops = if let Some(ref hw) = self.hardware_profile { hw.peak_flops.fp32 * 0.7 } else { 1000.0 }; let block_efficiency = match block_size { 64 => 0.85, 128 => 0.95, 256 => 0.90, 512 => 0.80, _ => 0.75, }; let unroll_efficiency = match unroll_factor { 1 => 0.70, 2 => 0.85, 4 => 0.95, 8 => 0.90, _ => 0.80, }; let gflops = base_gflops * block_efficiency * unroll_efficiency; let execution_time = Duration::from_nanos((1_000_000.0 / gflops) as u64 * 1000); let metrics = PerformanceMetrics { execution_time, gflops, memory_bandwidth: gflops * 8.0, occupancy: block_efficiency, energy_efficiency: gflops / 300.0, }; Ok(metrics) } /// Simple LCG for reproducible random numbers fn next_random(&mut self) -> u32 { self.rng_state = (self .rng_state .wrapping_mul(1_103_515_245) .wrapping_add(12345)) & 0x7fffffff; (self.rng_state >> 16) as u32 } /// Main autotuning entry point (legacy interface) pub async fn tune_kernel( &mut self, template: &KernelTemplate, operation: &KernelOperation, ) -> SynthesisResult { let _start_time = Instant::now(); // Check cache first let cache_key = self.generate_cache_key(operation); if let Some(cached_entry) = self.cache.get(&cache_key) { return Ok(TuningResult { optimal_parameters: cached_entry.parameters.clone(), best_metrics: cached_entry.metrics.clone(), iterations_completed: 0, total_time: Duration::from_nanos(1), convergence_achieved: true, pareto_frontier: None, acquisition_history: None, }); } let config = self.create_tuning_config(operation)?; // Use BayesianAutoTuner for actual optimization let mut bayesian_tuner = BayesianAutoTuner::new()?; if let Some(ref hw_profile) = self.hardware_profile { bayesian_tuner.set_hardware_profile(hw_profile.clone())?; } let result = bayesian_tuner.tune_parameters(template, operation, &config)?; // Cache the result if enabled if config.cache_results { let hw_hash = self.hardware_profile.as_ref().map_or(0, |p| { use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; let mut hasher = DefaultHasher::new(); p.architecture.hash(&mut hasher); hasher.finish() }); let cache_entry = CacheEntry { parameters: result.optimal_parameters.clone(), metrics: result.best_metrics.clone(), timestamp: Duration::from_nanos(1), hardware_hash: hw_hash, }; self.cache.insert(cache_key, cache_entry); } Ok(result) } /// Clear the autotuning cache pub fn clear_cache(&mut self) { self.cache.clear(); } /// Get cache statistics pub fn cache_stats(&self) -> (usize, usize) { let total_entries = self.cache.len(); let expired_entries = self .cache .values() .filter(|entry| entry.timestamp > Duration::from_secs(86400)) .count(); (total_entries, expired_entries) } }