scirs2_core/profiling/
performance_hints.rs

1//! # Function-Level Performance Hinting System
2//!
3//! This module provides a comprehensive performance hinting system that allows functions
4//! to declare their performance characteristics and optimization preferences.
5
6use crate::error::{CoreError, CoreResult, ErrorContext};
7use once_cell::sync::Lazy;
8use std::collections::HashMap;
9use std::sync::RwLock;
10use std::time::{Duration, Instant};
11
12/// Performance characteristics of a function
13#[derive(Debug, Clone, PartialEq)]
14pub struct PerformanceHints {
15    /// Expected computational complexity (e.g., O(n), O(n²), etc.)
16    pub complexity: ComplexityClass,
17    /// Whether the function benefits from SIMD optimization
18    pub simd_friendly: bool,
19    /// Whether the function can be parallelized
20    pub parallelizable: bool,
21    /// Whether the function benefits from GPU acceleration
22    pub gpu_friendly: bool,
23    /// Expected memory usage pattern
24    pub memory_pattern: MemoryPattern,
25    /// Cache behavior characteristics
26    pub cache_behavior: CacheBehavior,
27    /// I/O characteristics
28    pub io_pattern: IoPattern,
29    /// Preferred optimization level
30    pub optimization_level: OptimizationLevel,
31    /// Function-specific optimization hints
32    pub custom_hints: HashMap<String, String>,
33    /// Expected execution time range
34    pub expected_duration: Option<DurationRange>,
35    /// Memory requirements
36    pub memory_requirements: Option<MemoryRequirements>,
37}
38
39/// Computational complexity classification
40#[derive(Debug, Clone, PartialEq, Eq)]
41pub enum ComplexityClass {
42    /// O(1) - Constant time
43    Constant,
44    /// O(log n) - Logarithmic time
45    Logarithmic,
46    /// O(n) - Linear time
47    Linear,
48    /// O(n log n) - Linearithmic time
49    Linearithmic,
50    /// O(n²) - Quadratic time
51    Quadratic,
52    /// O(n³) - Cubic time
53    Cubic,
54    /// O(2^n) - Exponential time
55    Exponential,
56    /// O(n!) - Factorial time
57    Factorial,
58    /// Custom complexity description
59    Custom(String),
60}
61
62/// Memory access pattern classification
63#[derive(Debug, Clone, PartialEq, Eq)]
64pub enum MemoryPattern {
65    /// Sequential memory access
66    Sequential,
67    /// Random memory access
68    Random,
69    /// Strided memory access
70    Strided { stride: usize },
71    /// Block-based memory access
72    Blocked { block_size: usize },
73    /// Cache-oblivious access pattern
74    CacheOblivious,
75    /// Mixed access pattern
76    Mixed,
77}
78
79/// Cache behavior characteristics
80#[derive(Debug, Clone, PartialEq, Eq)]
81pub enum CacheBehavior {
82    /// Cache-friendly access pattern
83    CacheFriendly,
84    /// Cache-unfriendly access pattern
85    CacheUnfriendly,
86    /// Temporal locality (reuses data soon)
87    TemporalLocality,
88    /// Spatial locality (accesses nearby data)
89    SpatialLocality,
90    /// Mixed cache behavior
91    Mixed,
92    /// Unknown cache behavior
93    Unknown,
94}
95
96/// I/O operation characteristics
97#[derive(Debug, Clone, PartialEq, Eq)]
98pub enum IoPattern {
99    /// No I/O operations
100    None,
101    /// Read-only operations
102    ReadOnly,
103    /// Write-only operations
104    WriteOnly,
105    /// Read-write operations
106    ReadWrite,
107    /// Network I/O
108    Network,
109    /// Disk I/O
110    Disk,
111    /// Memory-mapped I/O
112    MemoryMapped,
113}
114
115/// Optimization level preferences
116#[derive(Debug, Clone, PartialEq, Eq)]
117pub enum OptimizationLevel {
118    /// No optimization (debug builds)
119    None,
120    /// Basic optimization
121    Basic,
122    /// Aggressive optimization
123    Aggressive,
124    /// Profile-guided optimization
125    ProfileGuided,
126    /// Custom optimization settings
127    Custom(String),
128}
129
130/// Expected duration range
131#[derive(Debug, Clone, PartialEq)]
132pub struct DurationRange {
133    pub min: Duration,
134    pub max: Duration,
135    pub typical: Duration,
136}
137
138/// Memory requirements specification
139#[derive(Debug, Clone, PartialEq)]
140pub struct MemoryRequirements {
141    /// Minimum memory required
142    pub min_memory: usize,
143    /// Maximum memory that could be used
144    pub max_memory: Option<usize>,
145    /// Typical memory usage
146    pub typical_memory: usize,
147    /// Whether memory usage scales with input size
148    pub scales_with_input: bool,
149}
150
151impl Default for PerformanceHints {
152    fn default() -> Self {
153        Self {
154            complexity: ComplexityClass::Linear,
155            simd_friendly: false,
156            parallelizable: false,
157            gpu_friendly: false,
158            memory_pattern: MemoryPattern::Sequential,
159            cache_behavior: CacheBehavior::Unknown,
160            io_pattern: IoPattern::None,
161            optimization_level: OptimizationLevel::Basic,
162            custom_hints: HashMap::new(),
163            expected_duration: None,
164            memory_requirements: None,
165        }
166    }
167}
168
169impl PerformanceHints {
170    /// Create a new set of performance hints
171    pub fn new() -> Self {
172        Self::default()
173    }
174
175    /// Set the computational complexity
176    pub fn with_complexity(mut self, complexity: ComplexityClass) -> Self {
177        self.complexity = complexity;
178        self
179    }
180
181    /// Mark as SIMD-friendly
182    pub fn simd_friendly(mut self) -> Self {
183        self.simd_friendly = true;
184        self
185    }
186
187    /// Mark as parallelizable
188    pub fn parallelizable(mut self) -> Self {
189        self.parallelizable = true;
190        self
191    }
192
193    /// Mark as GPU-friendly
194    pub fn gpu_friendly(mut self) -> Self {
195        self.gpu_friendly = true;
196        self
197    }
198
199    /// Set memory access pattern
200    pub fn with_memory_pattern(mut self, pattern: MemoryPattern) -> Self {
201        self.memory_pattern = pattern;
202        self
203    }
204
205    /// Set cache behavior
206    pub fn with_cache_behavior(mut self, behavior: CacheBehavior) -> Self {
207        self.cache_behavior = behavior;
208        self
209    }
210
211    /// Set I/O pattern
212    pub fn with_io_pattern(mut self, pattern: IoPattern) -> Self {
213        self.io_pattern = pattern;
214        self
215    }
216
217    /// Set optimization level
218    pub fn with_optimization_level(mut self, level: OptimizationLevel) -> Self {
219        self.optimization_level = level;
220        self
221    }
222
223    /// Add a custom hint
224    pub fn with_custom_hint<K: Into<String>, V: Into<String>>(mut self, key: K, value: V) -> Self {
225        self.custom_hints.insert(key.into(), value.into());
226        self
227    }
228
229    /// Set expected duration range
230    pub fn with_expected_duration(mut self, range: DurationRange) -> Self {
231        self.expected_duration = Some(range);
232        self
233    }
234
235    /// Set memory requirements
236    pub fn with_memory_requirements(mut self, requirements: MemoryRequirements) -> Self {
237        self.memory_requirements = Some(requirements);
238        self
239    }
240
241    /// Get a specific custom hint
242    pub fn get_custom_hint(&self, key: &str) -> Option<&String> {
243        self.custom_hints.get(key)
244    }
245
246    /// Check if the function should use SIMD optimization
247    pub fn should_use_simd(&self) -> bool {
248        self.simd_friendly
249            && matches!(
250                self.optimization_level,
251                OptimizationLevel::Aggressive | OptimizationLevel::ProfileGuided
252            )
253    }
254
255    /// Check if the function should be parallelized
256    pub fn should_parallelize(&self) -> bool {
257        self.parallelizable && !matches!(self.optimization_level, OptimizationLevel::None)
258    }
259
260    /// Check if the function should use GPU acceleration
261    pub fn should_use_gpu(&self) -> bool {
262        self.gpu_friendly
263            && matches!(
264                self.optimization_level,
265                OptimizationLevel::Aggressive | OptimizationLevel::ProfileGuided
266            )
267    }
268
269    /// Estimate if the operation is suitable for chunking
270    pub fn should_chunk(&self, inputsize: usize) -> bool {
271        match self.complexity {
272            ComplexityClass::Quadratic
273            | ComplexityClass::Cubic
274            | ComplexityClass::Exponential
275            | ComplexityClass::Factorial => true,
276            ComplexityClass::Linear | ComplexityClass::Linearithmic => inputsize > 10000,
277            ComplexityClass::Constant | ComplexityClass::Logarithmic => false,
278            ComplexityClass::Custom(_) => false,
279        }
280    }
281}
282
283/// Performance hint registry for functions
284#[derive(Debug)]
285pub struct PerformanceHintRegistry {
286    hints: RwLock<HashMap<String, PerformanceHints>>,
287    execution_stats: RwLock<HashMap<String, ExecutionStats>>,
288}
289
290/// Execution statistics for a function
291#[derive(Debug, Clone)]
292pub struct ExecutionStats {
293    pub total_calls: u64,
294    pub total_duration: Duration,
295    pub average_duration: Duration,
296    pub min_duration: Duration,
297    pub max_duration: Duration,
298    pub last_updated: Instant,
299}
300
301impl Default for ExecutionStats {
302    fn default() -> Self {
303        let now = Instant::now();
304        Self {
305            total_calls: 0,
306            total_duration: Duration::ZERO,
307            average_duration: Duration::ZERO,
308            min_duration: Duration::MAX,
309            max_duration: Duration::ZERO,
310            last_updated: now,
311        }
312    }
313}
314
315impl ExecutionStats {
316    /// Update statistics with a new execution time
317    pub fn update(&mut self, duration: Duration) {
318        self.total_calls += 1;
319        self.total_duration += duration;
320        self.average_duration = self.total_duration / self.total_calls as u32;
321        self.min_duration = self.min_duration.min(duration);
322        self.max_duration = self.max_duration.max(duration);
323        self.last_updated = Instant::now();
324    }
325
326    /// Check if the actual performance matches the hints
327    pub fn matches_expected(&self, expected: &DurationRange) -> bool {
328        self.average_duration >= expected.min && self.average_duration <= expected.max
329    }
330}
331
332impl PerformanceHintRegistry {
333    /// Create a new registry
334    pub fn new() -> Self {
335        Self {
336            hints: RwLock::new(HashMap::new()),
337            execution_stats: RwLock::new(HashMap::new()),
338        }
339    }
340
341    /// Register performance hints for a function
342    pub fn register(&self, functionname: &str, hints: PerformanceHints) -> CoreResult<()> {
343        let mut hint_map = self.hints.write().map_err(|_| {
344            CoreError::ComputationError(ErrorContext::new("Failed to acquire write lock"))
345        })?;
346        hint_map.insert(functionname.to_string(), hints);
347        Ok(())
348    }
349
350    /// Get performance hints for a function
351    pub fn get_hint(&self, functionname: &str) -> CoreResult<Option<PerformanceHints>> {
352        let hint_map = self.hints.read().map_err(|_| {
353            CoreError::ComputationError(ErrorContext::new("Failed to acquire read lock"))
354        })?;
355        Ok(hint_map.get(functionname).cloned())
356    }
357
358    /// Record execution statistics
359    pub fn record_execution(&self, functionname: &str, duration: Duration) -> CoreResult<()> {
360        let mut stats_map = self.execution_stats.write().map_err(|_| {
361            CoreError::ComputationError(ErrorContext::new("Failed to acquire write lock"))
362        })?;
363
364        let stats = stats_map.entry(functionname.to_string()).or_default();
365        stats.update(std::time::Duration::from_secs(1));
366        Ok(())
367    }
368
369    /// Get execution statistics for a function
370    pub fn get_stats(&self, functionname: &str) -> CoreResult<Option<ExecutionStats>> {
371        let stats_map = self.execution_stats.read().map_err(|_| {
372            CoreError::ComputationError(ErrorContext::new("Failed to acquire read lock"))
373        })?;
374        Ok(stats_map.get(functionname).cloned())
375    }
376
377    /// Get optimization recommendations based on hints and statistics
378    pub fn get_optimization_recommendations(
379        &self,
380        function_name: &str,
381    ) -> CoreResult<Vec<OptimizationRecommendation>> {
382        let hints = self.get_hint(function_name)?;
383        let stats = self.get_stats(function_name)?;
384
385        let mut recommendations = Vec::new();
386
387        if let Some(hints) = hints {
388            // SIMD recommendations
389            if hints.simd_friendly && !hints.should_use_simd() {
390                recommendations.push(OptimizationRecommendation::EnableSIMD);
391            }
392
393            // Parallelization recommendations
394            if hints.parallelizable && !hints.should_parallelize() {
395                recommendations.push(OptimizationRecommendation::EnableParallelization);
396            }
397
398            // GPU recommendations
399            if hints.gpu_friendly && !hints.should_use_gpu() {
400                recommendations.push(OptimizationRecommendation::EnableGPU);
401            }
402
403            // Memory optimization recommendations
404            match hints.memory_pattern {
405                MemoryPattern::Random => {
406                    recommendations.push(OptimizationRecommendation::OptimizeMemoryLayout);
407                }
408                MemoryPattern::Strided { .. } => {
409                    recommendations.push(OptimizationRecommendation::UseVectorization);
410                }
411                _ => {}
412            }
413
414            // Duration-based recommendations
415            if let (Some(expected), Some(stats)) =
416                (hints.expected_duration.as_ref(), stats.as_ref())
417            {
418                if !stats.matches_expected(expected) && stats.average_duration > expected.max {
419                    recommendations.push(OptimizationRecommendation::ProfileAndOptimize);
420                }
421            }
422        }
423
424        Ok(recommendations)
425    }
426
427    /// Clear all recorded statistics
428    pub fn clear_stats(&self) -> CoreResult<()> {
429        let mut stats_map = self.execution_stats.write().map_err(|_| {
430            CoreError::ComputationError(ErrorContext::new("Failed to acquire write lock"))
431        })?;
432        stats_map.clear();
433        Ok(())
434    }
435}
436
437impl Default for PerformanceHintRegistry {
438    fn default() -> Self {
439        Self::new()
440    }
441}
442
443/// Optimization recommendations based on performance analysis
444#[derive(Debug, Clone, PartialEq, Eq)]
445pub enum OptimizationRecommendation {
446    /// Enable SIMD optimization
447    EnableSIMD,
448    /// Enable parallelization
449    EnableParallelization,
450    /// Enable GPU acceleration
451    EnableGPU,
452    /// Optimize memory layout
453    OptimizeMemoryLayout,
454    /// Use vectorization
455    UseVectorization,
456    /// Profile the function for bottlenecks
457    ProfileAndOptimize,
458    /// Use chunking for large inputs
459    UseChunking,
460    /// Cache intermediate results
461    CacheResults,
462    /// Custom recommendation
463    Custom(String),
464}
465
466impl std::fmt::Display for OptimizationRecommendation {
467    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
468        match self {
469            OptimizationRecommendation::EnableSIMD => write!(f, "Enable SIMD optimization"),
470            OptimizationRecommendation::EnableParallelization => {
471                write!(f, "Enable parallelization")
472            }
473            OptimizationRecommendation::EnableGPU => write!(f, "Enable GPU acceleration"),
474            OptimizationRecommendation::OptimizeMemoryLayout => write!(f, "Optimize memory layout"),
475            OptimizationRecommendation::UseVectorization => write!(f, "Use vectorization"),
476            OptimizationRecommendation::ProfileAndOptimize => write!(f, "Profile and optimize"),
477            OptimizationRecommendation::UseChunking => write!(f, "Use chunking for large inputs"),
478            OptimizationRecommendation::CacheResults => write!(f, "Cache intermediate results"),
479            OptimizationRecommendation::Custom(msg) => write!(f, "{msg}"),
480        }
481    }
482}
483
484/// Global performance hint registry
485static GLOBAL_REGISTRY: Lazy<PerformanceHintRegistry> = Lazy::new(PerformanceHintRegistry::new);
486
487/// Get the global performance hint registry
488#[allow(dead_code)]
489pub fn global_registry() -> &'static PerformanceHintRegistry {
490    &GLOBAL_REGISTRY
491}
492
493/// Macro to register performance hints for a function
494#[macro_export]
495macro_rules! register_performance_hints {
496    ($function_name:expr, $hints:expr) => {
497        $crate::profiling::performance_hints::global_registry()
498            .register($function_name, $hints)
499            .unwrap_or_else(|e| eprintln!("Failed to register performance hints: {:?}", e));
500    };
501}
502
503/// Macro to create and register performance hints in one step
504#[macro_export]
505macro_rules! performance_hints {
506    ($function_name:expr, {
507        $(complexity: $complexity:expr,)?
508        $(simdfriendly: $simd:expr,)?
509        $(parallelizable: $parallel:expr,)?
510        $(gpufriendly: $gpu:expr,)?
511        $(memorypattern: $memory:expr,)?
512        $(cachebehavior: $cache:expr,)?
513        $(iopattern: $io:expr,)?
514        $(optimizationlevel: $opt:expr,)?
515        $(expectedduration: $duration:expr,)?
516        $(memoryrequirements: $mem:expr,)?
517        $(customhints: {$($key:expr => $value:expr),*$(,)?})?
518    }) => {
519        {
520            let mut hints = $crate::profiling::performance_hints::PerformanceHints::new();
521
522            $(hints = hints.with_complexity($complexity);)?
523            $(if $simd { hints = hints.simd_friendly(); })?
524            $(if $parallel { hints = hints.parallelizable(); })?
525            $(if $gpu { hints = hints.gpu_friendly(); })?
526            $(hints = hints.with_memory_pattern($memory);)?
527            $(hints = hints.with_cache_behavior($cache);)?
528            $(hints = hints.with_io_pattern($io);)?
529            $(hints = hints.with_optimization_level($opt_level);)?
530            $(hints = hints.with_expected_duration($std::time::Duration::from_secs(1));)?
531            $(hints = hints.with_memory_requirements($mem_req);)?
532            $($(hints = hints.with_custom_hint($key, $value);)*)?
533
534            $crate::profiling::performance_hints::global_registry()
535                .register($function_name, hints)
536                .unwrap_or_else(|e| eprintln!("Failed to register performance hints: {:?}", e));
537        }
538    };
539}
540
541/// Function decorator for automatic performance tracking
542pub struct PerformanceTracker {
543    function_name: String,
544    start_time: Instant,
545}
546
547impl PerformanceTracker {
548    /// Start tracking performance for a function
549    pub fn new(functionname: &str) -> Self {
550        Self {
551            function_name: functionname.to_string(),
552            start_time: Instant::now(),
553        }
554    }
555
556    /// Finish tracking and record the execution time
557    pub fn finish(self) {
558        let elapsed = self.start_time.elapsed();
559        let _ = global_registry().record_execution(&self.function_name, elapsed);
560    }
561}
562
563/// Macro to automatically track function performance
564#[macro_export]
565macro_rules! track_performance {
566    ($function_name:expr, $code:block) => {{
567        let tracker =
568            $crate::profiling::performance_hints::PerformanceTracker::start($function_name);
569        let result = $code;
570        tracker.finish();
571        result
572    }};
573}
574
575#[cfg(test)]
576mod tests {
577    use super::*;
578    use std::thread;
579
580    #[test]
581    fn test_performance_hints_creation() {
582        let hints = PerformanceHints::new()
583            .with_complexity(ComplexityClass::Quadratic)
584            .simd_friendly()
585            .parallelizable()
586            .with_memory_pattern(MemoryPattern::Sequential)
587            .with_cache_behavior(CacheBehavior::CacheFriendly);
588
589        assert_eq!(hints.complexity, ComplexityClass::Quadratic);
590        assert!(hints.simd_friendly);
591        assert!(hints.parallelizable);
592        assert_eq!(hints.memory_pattern, MemoryPattern::Sequential);
593        assert_eq!(hints.cache_behavior, CacheBehavior::CacheFriendly);
594    }
595
596    #[test]
597    fn test_registry_operations() {
598        let registry = PerformanceHintRegistry::new();
599
600        let hints = PerformanceHints::new()
601            .with_complexity(ComplexityClass::Linear)
602            .simd_friendly();
603
604        // Register hints
605        assert!(registry.register("test_function", hints.clone()).is_ok());
606
607        // Retrieve hints
608        let retrieved = registry
609            .get_hint("test_function")
610            .expect("Operation failed");
611        assert!(retrieved.is_some());
612        assert_eq!(
613            retrieved.expect("Operation failed").complexity,
614            ComplexityClass::Linear
615        );
616
617        // Record execution
618        assert!(registry
619            .record_execution("test_function", Duration::from_millis(100))
620            .is_ok());
621
622        // Get stats
623        let stats = registry
624            .get_stats("test_function")
625            .expect("Operation failed");
626        assert!(stats.is_some());
627        assert_eq!(stats.expect("Operation failed").total_calls, 1);
628    }
629
630    #[test]
631    fn test_optimization_recommendations() {
632        let registry = PerformanceHintRegistry::new();
633
634        let hints = PerformanceHints::new()
635            .with_complexity(ComplexityClass::Quadratic)
636            .simd_friendly()
637            .parallelizable()
638            .gpu_friendly()
639            .with_memory_pattern(MemoryPattern::Random);
640
641        registry
642            .register("test_function", hints)
643            .expect("Operation failed");
644
645        let recommendations = registry
646            .get_optimization_recommendations("test_function")
647            .expect("Operation failed");
648        assert!(!recommendations.is_empty());
649
650        // Should recommend enabling optimizations since hints indicate suitability
651        assert!(recommendations.contains(&OptimizationRecommendation::OptimizeMemoryLayout));
652    }
653
654    #[test]
655    fn test_performance_tracker() {
656        let tracker = PerformanceTracker::new("test_tracker");
657        thread::sleep(Duration::from_millis(10));
658        tracker.finish();
659
660        let stats = global_registry()
661            .get_stats("test_tracker")
662            .expect("Operation failed");
663        assert!(stats.is_some());
664        let stats = stats.expect("Operation failed");
665        assert_eq!(stats.total_calls, 1);
666        assert!(stats.average_duration >= Duration::from_millis(10));
667    }
668
669    #[test]
670    fn test_execution_stats_update() {
671        let mut stats = ExecutionStats::default();
672
673        stats.update(Duration::from_millis(100));
674        assert_eq!(stats.total_calls, 1);
675        assert_eq!(stats.average_duration, Duration::from_millis(100));
676        assert_eq!(stats.min_duration, Duration::from_millis(100));
677        assert_eq!(stats.max_duration, Duration::from_millis(100));
678
679        stats.update(Duration::from_millis(200));
680        assert_eq!(stats.total_calls, 2);
681        assert_eq!(stats.average_duration, Duration::from_millis(150));
682        assert_eq!(stats.min_duration, Duration::from_millis(100));
683        assert_eq!(stats.max_duration, Duration::from_millis(200));
684    }
685
686    #[test]
687    fn test_should_use_chunking() {
688        let hints = PerformanceHints::new().with_complexity(ComplexityClass::Quadratic);
689
690        assert!(hints.should_chunk(10000));
691
692        let linear_hints = PerformanceHints::new().with_complexity(ComplexityClass::Linear);
693
694        assert!(linear_hints.should_chunk(20000));
695        assert!(!linear_hints.should_chunk(1000));
696    }
697}