use crate::error::{CoreError, CoreResult, ErrorContext};
use once_cell::sync::Lazy;
use std::collections::HashMap;
use std::sync::RwLock;
use std::time::{Duration, Instant};
#[derive(Debug, Clone, PartialEq)]
pub struct PerformanceHints {
pub complexity: ComplexityClass,
pub simd_friendly: bool,
pub parallelizable: bool,
pub gpu_friendly: bool,
pub memory_pattern: MemoryPattern,
pub cache_behavior: CacheBehavior,
pub io_pattern: IoPattern,
pub optimization_level: OptimizationLevel,
pub custom_hints: HashMap<String, String>,
pub expected_duration: Option<DurationRange>,
pub memory_requirements: Option<MemoryRequirements>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ComplexityClass {
Constant,
Logarithmic,
Linear,
Linearithmic,
Quadratic,
Cubic,
Exponential,
Factorial,
Custom(String),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MemoryPattern {
Sequential,
Random,
Strided { stride: usize },
Blocked { block_size: usize },
CacheOblivious,
Mixed,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CacheBehavior {
CacheFriendly,
CacheUnfriendly,
TemporalLocality,
SpatialLocality,
Mixed,
Unknown,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum IoPattern {
None,
ReadOnly,
WriteOnly,
ReadWrite,
Network,
Disk,
MemoryMapped,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum OptimizationLevel {
None,
Basic,
Aggressive,
ProfileGuided,
Custom(String),
}
#[derive(Debug, Clone, PartialEq)]
pub struct DurationRange {
pub min: Duration,
pub max: Duration,
pub typical: Duration,
}
#[derive(Debug, Clone, PartialEq)]
pub struct MemoryRequirements {
pub min_memory: usize,
pub max_memory: Option<usize>,
pub typical_memory: usize,
pub scales_with_input: bool,
}
impl Default for PerformanceHints {
fn default() -> Self {
Self {
complexity: ComplexityClass::Linear,
simd_friendly: false,
parallelizable: false,
gpu_friendly: false,
memory_pattern: MemoryPattern::Sequential,
cache_behavior: CacheBehavior::Unknown,
io_pattern: IoPattern::None,
optimization_level: OptimizationLevel::Basic,
custom_hints: HashMap::new(),
expected_duration: None,
memory_requirements: None,
}
}
}
impl PerformanceHints {
pub fn new() -> Self {
Self::default()
}
pub fn with_complexity(mut self, complexity: ComplexityClass) -> Self {
self.complexity = complexity;
self
}
pub fn simd_friendly(mut self) -> Self {
self.simd_friendly = true;
self
}
pub fn parallelizable(mut self) -> Self {
self.parallelizable = true;
self
}
pub fn gpu_friendly(mut self) -> Self {
self.gpu_friendly = true;
self
}
pub fn with_memory_pattern(mut self, pattern: MemoryPattern) -> Self {
self.memory_pattern = pattern;
self
}
pub fn with_cache_behavior(mut self, behavior: CacheBehavior) -> Self {
self.cache_behavior = behavior;
self
}
pub fn with_io_pattern(mut self, pattern: IoPattern) -> Self {
self.io_pattern = pattern;
self
}
pub fn with_optimization_level(mut self, level: OptimizationLevel) -> Self {
self.optimization_level = level;
self
}
pub fn with_custom_hint<K: Into<String>, V: Into<String>>(mut self, key: K, value: V) -> Self {
self.custom_hints.insert(key.into(), value.into());
self
}
pub fn with_expected_duration(mut self, range: DurationRange) -> Self {
self.expected_duration = Some(range);
self
}
pub fn with_memory_requirements(mut self, requirements: MemoryRequirements) -> Self {
self.memory_requirements = Some(requirements);
self
}
pub fn get_custom_hint(&self, key: &str) -> Option<&String> {
self.custom_hints.get(key)
}
pub fn should_use_simd(&self) -> bool {
self.simd_friendly
&& matches!(
self.optimization_level,
OptimizationLevel::Aggressive | OptimizationLevel::ProfileGuided
)
}
pub fn should_parallelize(&self) -> bool {
self.parallelizable && !matches!(self.optimization_level, OptimizationLevel::None)
}
pub fn should_use_gpu(&self) -> bool {
self.gpu_friendly
&& matches!(
self.optimization_level,
OptimizationLevel::Aggressive | OptimizationLevel::ProfileGuided
)
}
pub fn should_chunk(&self, inputsize: usize) -> bool {
match self.complexity {
ComplexityClass::Quadratic
| ComplexityClass::Cubic
| ComplexityClass::Exponential
| ComplexityClass::Factorial => true,
ComplexityClass::Linear | ComplexityClass::Linearithmic => inputsize > 10000,
ComplexityClass::Constant | ComplexityClass::Logarithmic => false,
ComplexityClass::Custom(_) => false,
}
}
}
#[derive(Debug)]
pub struct PerformanceHintRegistry {
hints: RwLock<HashMap<String, PerformanceHints>>,
execution_stats: RwLock<HashMap<String, ExecutionStats>>,
}
#[derive(Debug, Clone)]
pub struct ExecutionStats {
pub total_calls: u64,
pub total_duration: Duration,
pub average_duration: Duration,
pub min_duration: Duration,
pub max_duration: Duration,
pub last_updated: Instant,
}
impl Default for ExecutionStats {
fn default() -> Self {
let now = Instant::now();
Self {
total_calls: 0,
total_duration: Duration::ZERO,
average_duration: Duration::ZERO,
min_duration: Duration::MAX,
max_duration: Duration::ZERO,
last_updated: now,
}
}
}
impl ExecutionStats {
pub fn update(&mut self, duration: Duration) {
self.total_calls += 1;
self.total_duration += duration;
self.average_duration = self.total_duration / self.total_calls as u32;
self.min_duration = self.min_duration.min(duration);
self.max_duration = self.max_duration.max(duration);
self.last_updated = Instant::now();
}
pub fn matches_expected(&self, expected: &DurationRange) -> bool {
self.average_duration >= expected.min && self.average_duration <= expected.max
}
}
impl PerformanceHintRegistry {
pub fn new() -> Self {
Self {
hints: RwLock::new(HashMap::new()),
execution_stats: RwLock::new(HashMap::new()),
}
}
pub fn register(&self, functionname: &str, hints: PerformanceHints) -> CoreResult<()> {
let mut hint_map = self.hints.write().map_err(|_| {
CoreError::ComputationError(ErrorContext::new("Failed to acquire write lock"))
})?;
hint_map.insert(functionname.to_string(), hints);
Ok(())
}
pub fn get_hint(&self, functionname: &str) -> CoreResult<Option<PerformanceHints>> {
let hint_map = self.hints.read().map_err(|_| {
CoreError::ComputationError(ErrorContext::new("Failed to acquire read lock"))
})?;
Ok(hint_map.get(functionname).cloned())
}
pub fn record_execution(&self, functionname: &str, duration: Duration) -> CoreResult<()> {
let mut stats_map = self.execution_stats.write().map_err(|_| {
CoreError::ComputationError(ErrorContext::new("Failed to acquire write lock"))
})?;
let stats = stats_map.entry(functionname.to_string()).or_default();
stats.update(std::time::Duration::from_secs(1));
Ok(())
}
pub fn get_stats(&self, functionname: &str) -> CoreResult<Option<ExecutionStats>> {
let stats_map = self.execution_stats.read().map_err(|_| {
CoreError::ComputationError(ErrorContext::new("Failed to acquire read lock"))
})?;
Ok(stats_map.get(functionname).cloned())
}
pub fn get_optimization_recommendations(
&self,
function_name: &str,
) -> CoreResult<Vec<OptimizationRecommendation>> {
let hints = self.get_hint(function_name)?;
let stats = self.get_stats(function_name)?;
let mut recommendations = Vec::new();
if let Some(hints) = hints {
if hints.simd_friendly && !hints.should_use_simd() {
recommendations.push(OptimizationRecommendation::EnableSIMD);
}
if hints.parallelizable && !hints.should_parallelize() {
recommendations.push(OptimizationRecommendation::EnableParallelization);
}
if hints.gpu_friendly && !hints.should_use_gpu() {
recommendations.push(OptimizationRecommendation::EnableGPU);
}
match hints.memory_pattern {
MemoryPattern::Random => {
recommendations.push(OptimizationRecommendation::OptimizeMemoryLayout);
}
MemoryPattern::Strided { .. } => {
recommendations.push(OptimizationRecommendation::UseVectorization);
}
_ => {}
}
if let (Some(expected), Some(stats)) =
(hints.expected_duration.as_ref(), stats.as_ref())
{
if !stats.matches_expected(expected) && stats.average_duration > expected.max {
recommendations.push(OptimizationRecommendation::ProfileAndOptimize);
}
}
}
Ok(recommendations)
}
pub fn clear_stats(&self) -> CoreResult<()> {
let mut stats_map = self.execution_stats.write().map_err(|_| {
CoreError::ComputationError(ErrorContext::new("Failed to acquire write lock"))
})?;
stats_map.clear();
Ok(())
}
}
impl Default for PerformanceHintRegistry {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum OptimizationRecommendation {
EnableSIMD,
EnableParallelization,
EnableGPU,
OptimizeMemoryLayout,
UseVectorization,
ProfileAndOptimize,
UseChunking,
CacheResults,
Custom(String),
}
impl std::fmt::Display for OptimizationRecommendation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
OptimizationRecommendation::EnableSIMD => write!(f, "Enable SIMD optimization"),
OptimizationRecommendation::EnableParallelization => {
write!(f, "Enable parallelization")
}
OptimizationRecommendation::EnableGPU => write!(f, "Enable GPU acceleration"),
OptimizationRecommendation::OptimizeMemoryLayout => write!(f, "Optimize memory layout"),
OptimizationRecommendation::UseVectorization => write!(f, "Use vectorization"),
OptimizationRecommendation::ProfileAndOptimize => write!(f, "Profile and optimize"),
OptimizationRecommendation::UseChunking => write!(f, "Use chunking for large inputs"),
OptimizationRecommendation::CacheResults => write!(f, "Cache intermediate results"),
OptimizationRecommendation::Custom(msg) => write!(f, "{msg}"),
}
}
}
static GLOBAL_REGISTRY: Lazy<PerformanceHintRegistry> = Lazy::new(PerformanceHintRegistry::new);
#[allow(dead_code)]
pub fn global_registry() -> &'static PerformanceHintRegistry {
&GLOBAL_REGISTRY
}
#[macro_export]
macro_rules! register_performance_hints {
($function_name:expr, $hints:expr) => {
$crate::profiling::performance_hints::global_registry()
.register($function_name, $hints)
.unwrap_or_else(|e| eprintln!("Failed to register performance hints: {:?}", e));
};
}
#[macro_export]
macro_rules! performance_hints {
($function_name:expr, {
$(complexity: $complexity:expr,)?
$(simdfriendly: $simd:expr,)?
$(parallelizable: $parallel:expr,)?
$(gpufriendly: $gpu:expr,)?
$(memorypattern: $memory:expr,)?
$(cachebehavior: $cache:expr,)?
$(iopattern: $io:expr,)?
$(optimizationlevel: $opt:expr,)?
$(expectedduration: $duration:expr,)?
$(memoryrequirements: $mem:expr,)?
$(customhints: {$($key:expr => $value:expr),*$(,)?})?
}) => {
{
let mut hints = $crate::profiling::performance_hints::PerformanceHints::new();
$(hints = hints.with_complexity($complexity);)?
$(if $simd { hints = hints.simd_friendly(); })?
$(if $parallel { hints = hints.parallelizable(); })?
$(if $gpu { hints = hints.gpu_friendly(); })?
$(hints = hints.with_memory_pattern($memory);)?
$(hints = hints.with_cache_behavior($cache);)?
$(hints = hints.with_io_pattern($io);)?
$(hints = hints.with_optimization_level($opt_level);)?
$(hints = hints.with_expected_duration($std::time::Duration::from_secs(1));)?
$(hints = hints.with_memory_requirements($mem_req);)?
$($(hints = hints.with_custom_hint($key, $value);)*)?
$crate::profiling::performance_hints::global_registry()
.register($function_name, hints)
.unwrap_or_else(|e| eprintln!("Failed to register performance hints: {:?}", e));
}
};
}
pub struct PerformanceTracker {
function_name: String,
start_time: Instant,
}
impl PerformanceTracker {
pub fn new(functionname: &str) -> Self {
Self {
function_name: functionname.to_string(),
start_time: Instant::now(),
}
}
pub fn finish(self) {
let elapsed = self.start_time.elapsed();
let _ = global_registry().record_execution(&self.function_name, elapsed);
}
}
#[macro_export]
macro_rules! track_performance {
($function_name:expr, $code:block) => {{
let tracker =
$crate::profiling::performance_hints::PerformanceTracker::start($function_name);
let result = $code;
tracker.finish();
result
}};
}
#[cfg(test)]
mod tests {
use super::*;
use std::thread;
#[test]
fn test_performance_hints_creation() {
let hints = PerformanceHints::new()
.with_complexity(ComplexityClass::Quadratic)
.simd_friendly()
.parallelizable()
.with_memory_pattern(MemoryPattern::Sequential)
.with_cache_behavior(CacheBehavior::CacheFriendly);
assert_eq!(hints.complexity, ComplexityClass::Quadratic);
assert!(hints.simd_friendly);
assert!(hints.parallelizable);
assert_eq!(hints.memory_pattern, MemoryPattern::Sequential);
assert_eq!(hints.cache_behavior, CacheBehavior::CacheFriendly);
}
#[test]
fn test_registry_operations() {
let registry = PerformanceHintRegistry::new();
let hints = PerformanceHints::new()
.with_complexity(ComplexityClass::Linear)
.simd_friendly();
assert!(registry.register("test_function", hints.clone()).is_ok());
let retrieved = registry
.get_hint("test_function")
.expect("Operation failed");
assert!(retrieved.is_some());
assert_eq!(
retrieved.expect("Operation failed").complexity,
ComplexityClass::Linear
);
assert!(registry
.record_execution("test_function", Duration::from_millis(100))
.is_ok());
let stats = registry
.get_stats("test_function")
.expect("Operation failed");
assert!(stats.is_some());
assert_eq!(stats.expect("Operation failed").total_calls, 1);
}
#[test]
fn test_optimization_recommendations() {
let registry = PerformanceHintRegistry::new();
let hints = PerformanceHints::new()
.with_complexity(ComplexityClass::Quadratic)
.simd_friendly()
.parallelizable()
.gpu_friendly()
.with_memory_pattern(MemoryPattern::Random);
registry
.register("test_function", hints)
.expect("Operation failed");
let recommendations = registry
.get_optimization_recommendations("test_function")
.expect("Operation failed");
assert!(!recommendations.is_empty());
assert!(recommendations.contains(&OptimizationRecommendation::OptimizeMemoryLayout));
}
#[test]
fn test_performance_tracker() {
let tracker = PerformanceTracker::new("test_tracker");
thread::sleep(Duration::from_millis(10));
tracker.finish();
let stats = global_registry()
.get_stats("test_tracker")
.expect("Operation failed");
assert!(stats.is_some());
let stats = stats.expect("Operation failed");
assert_eq!(stats.total_calls, 1);
assert!(stats.average_duration >= Duration::from_millis(10));
}
#[test]
fn test_execution_stats_update() {
let mut stats = ExecutionStats::default();
stats.update(Duration::from_millis(100));
assert_eq!(stats.total_calls, 1);
assert_eq!(stats.average_duration, Duration::from_millis(100));
assert_eq!(stats.min_duration, Duration::from_millis(100));
assert_eq!(stats.max_duration, Duration::from_millis(100));
stats.update(Duration::from_millis(200));
assert_eq!(stats.total_calls, 2);
assert_eq!(stats.average_duration, Duration::from_millis(150));
assert_eq!(stats.min_duration, Duration::from_millis(100));
assert_eq!(stats.max_duration, Duration::from_millis(200));
}
#[test]
fn test_should_use_chunking() {
let hints = PerformanceHints::new().with_complexity(ComplexityClass::Quadratic);
assert!(hints.should_chunk(10000));
let linear_hints = PerformanceHints::new().with_complexity(ComplexityClass::Linear);
assert!(linear_hints.should_chunk(20000));
assert!(!linear_hints.should_chunk(1000));
}
}