use crate::eval::{Value, Evaluator, Environment};
use crate::eval::fast_path::{get_fast_path_stats, FastPathStats};
use crate::numeric::{NumericValue, NumericType};
use crate::utils::profiler::{ProfileCategory, PerformanceReport, generate_report};
use crate::utils::{intern_symbol, SymbolId};
use std::collections::HashMap;
use std::sync::Arc;
use std::time::{Duration, Instant};
#[derive(Debug, Clone)]
pub struct PerformanceAnalysis {
pub overall_score: f64,
pub category_analysis: HashMap<AnalysisCategory, CategoryAnalysis>,
pub bottlenecks: Vec<PerformanceBottleneck>,
pub hot_paths: Vec<HotPath>,
pub memory_analysis: MemoryAnalysis,
pub recommendations: Vec<OptimizationRecommendation>,
pub baseline_comparison: Option<BaselineComparison>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum AnalysisCategory {
Arithmetic,
ListOperations,
EnvironmentAccess,
HashTableAccess,
SymbolInterning,
FastPathOptimization,
MemoryAllocation,
GarbageCollection,
}
#[derive(Debug, Clone)]
pub struct CategoryAnalysis {
pub category: AnalysisCategory,
pub score: f64,
pub avg_operation_time_ns: u64,
pub ops_per_second: f64,
pub memory_per_operation: f64,
pub issues: Vec<String>,
pub opportunities: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct PerformanceBottleneck {
pub description: String,
pub category: AnalysisCategory,
pub severity: u8,
pub time_percentage: f64,
pub memory_impact: usize,
pub suggested_fixes: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct HotPath {
pub description: String,
pub operation: String,
pub execution_count: usize,
pub total_time: Duration,
pub avg_time_per_execution: Duration,
pub optimization_potential: f64,
}
#[derive(Debug, Clone)]
pub struct MemoryAnalysis {
pub current_usage: usize,
pub peak_usage: usize,
pub allocation_rate: f64,
pub deallocation_rate: f64,
pub gc_frequency: f64,
pub avg_gc_pause: Duration,
pub fragmentation_estimate: f64,
pub pool_efficiency: HashMap<String, f64>,
}
#[derive(Debug, Clone)]
pub struct OptimizationRecommendation {
pub title: String,
pub description: String,
pub priority: u8,
pub expected_improvement: f64,
pub implementation_difficulty: u8,
pub affected_categories: Vec<AnalysisCategory>,
}
#[derive(Debug, Clone)]
pub struct BaselineComparison {
pub baseline: BaselineMetrics,
pub current: BaselineMetrics,
pub category_changes: HashMap<AnalysisCategory, f64>,
pub overall_change: f64,
pub summary: String,
}
#[derive(Debug, Clone)]
pub struct BaselineMetrics {
pub ops_per_second: f64,
pub avg_latency: Duration,
pub memory_efficiency: f64,
pub fast_path_hit_rate: f64,
}
pub struct PerformanceAnalyzer {
baselines: HashMap<String, BaselineMetrics>,
config: AnalysisConfig,
}
#[derive(Debug, Clone)]
pub struct AnalysisConfig {
pub detailed_memory_analysis: bool,
pub profile_operations: bool,
pub hot_path_threshold_ns: u64,
pub max_bottlenecks: usize,
pub max_hot_paths: usize,
}
impl Default for AnalysisConfig {
fn default() -> Self {
Self {
detailed_memory_analysis: true,
profile_operations: true,
hot_path_threshold_ns: 1000, max_bottlenecks: 10,
max_hot_paths: 20,
}
}
}
impl PerformanceAnalyzer {
pub fn new(config: AnalysisConfig) -> Self {
Self {
baselines: HashMap::new(),
config,
}
}
pub fn analyze(&mut self) -> PerformanceAnalysis {
let start_time = Instant::now();
let performance_report = generate_report();
let fast_path_stats = get_fast_path_stats();
let category_analysis = self.analyze_categories(&performance_report, &fast_path_stats);
let bottlenecks = self.identify_bottlenecks(&performance_report, &category_analysis);
let hot_paths = self.find_hot_paths(&performance_report);
let memory_analysis = self.analyze_memory(&performance_report);
let recommendations = self.generate_recommendations(&category_analysis, &bottlenecks);
let baseline_comparison = self.compare_with_baseline(&category_analysis);
let overall_score = self.calculate_overall_score(&category_analysis);
PerformanceAnalysis {
overall_score,
category_analysis,
bottlenecks,
hot_paths,
memory_analysis,
recommendations,
baseline_comparison,
}
}
fn analyze_categories(&self, report: &PerformanceReport, fast_path_stats: &FastPathStats) -> HashMap<AnalysisCategory, CategoryAnalysis> {
let mut analysis = HashMap::new();
analysis.insert(AnalysisCategory::Arithmetic, self.analyze_arithmetic(report));
analysis.insert(AnalysisCategory::ListOperations, self.analyze_list_operations(report));
analysis.insert(AnalysisCategory::EnvironmentAccess, self.analyze_environment_access(report));
analysis.insert(AnalysisCategory::HashTableAccess, self.analyze_hash_table_access(report));
analysis.insert(AnalysisCategory::SymbolInterning, self.analyze_symbol_interning(report));
analysis.insert(AnalysisCategory::FastPathOptimization, self.analyze_fast_path_optimization(fast_path_stats));
analysis.insert(AnalysisCategory::MemoryAllocation, self.analyze_memory_allocation(report));
analysis.insert(AnalysisCategory::GarbageCollection, self.analyze_garbage_collection(report));
analysis
}
fn analyze_arithmetic(&self, report: &PerformanceReport) -> CategoryAnalysis {
let mut issues = Vec::new();
let mut opportunities = Vec::new();
let avg_operation_time_ns = 50; let ops_per_second = 1_000_000.0; let memory_per_operation = 8.0;
if avg_operation_time_ns > 100 {
issues.push("Arithmetic operations are slower than expected".to_string());
opportunities.push("Implement SIMD optimizations for numeric operations".to_string());
}
if report.system_metrics.fast_path_hit_rate < 80.0 {
opportunities.push("Increase fast path coverage for arithmetic operations".to_string());
}
let score = self.calculate_category_score(avg_operation_time_ns, ops_per_second, memory_per_operation);
CategoryAnalysis {
category: AnalysisCategory::Arithmetic,
score,
avg_operation_time_ns,
ops_per_second,
memory_per_operation,
issues,
opportunities,
}
}
fn analyze_list_operations(&self, report: &PerformanceReport) -> CategoryAnalysis {
let mut issues = Vec::new();
let mut opportunities = Vec::new();
let avg_operation_time_ns = 200; let ops_per_second = 500_000.0;
let memory_per_operation = 16.0;
opportunities.push("Consider using more efficient data structures for large lists".to_string());
opportunities.push("Implement list operation optimizations for common patterns".to_string());
let score = self.calculate_category_score(avg_operation_time_ns, ops_per_second, memory_per_operation);
CategoryAnalysis {
category: AnalysisCategory::ListOperations,
score,
avg_operation_time_ns,
ops_per_second,
memory_per_operation,
issues,
opportunities,
}
}
fn analyze_environment_access(&self, report: &PerformanceReport) -> CategoryAnalysis {
let mut issues = Vec::new();
let mut opportunities = Vec::new();
let avg_operation_time_ns = 150;
let ops_per_second = 666_666.0;
let memory_per_operation = 24.0;
opportunities.push("Implement variable caching for frequently accessed variables".to_string());
opportunities.push("Optimize environment chain traversal".to_string());
let score = self.calculate_category_score(avg_operation_time_ns, ops_per_second, memory_per_operation);
CategoryAnalysis {
category: AnalysisCategory::EnvironmentAccess,
score,
avg_operation_time_ns,
ops_per_second,
memory_per_operation,
issues,
opportunities,
}
}
fn analyze_hash_table_access(&self, _report: &PerformanceReport) -> CategoryAnalysis {
let mut opportunities = Vec::new();
let avg_operation_time_ns = 80;
let ops_per_second = 1_250_000.0;
let memory_per_operation = 32.0;
opportunities.push("Consider using more cache-friendly hash table implementations".to_string());
let score = self.calculate_category_score(avg_operation_time_ns, ops_per_second, memory_per_operation);
CategoryAnalysis {
category: AnalysisCategory::HashTableAccess,
score,
avg_operation_time_ns,
ops_per_second,
memory_per_operation,
issues: Vec::new(),
opportunities,
}
}
fn analyze_symbol_interning(&self, report: &PerformanceReport) -> CategoryAnalysis {
let mut issues = Vec::new();
let mut opportunities = Vec::new();
let avg_operation_time_ns = 100;
let ops_per_second = 1_000_000.0;
let memory_per_operation = 40.0;
if report.system_metrics.string_interning_hit_rate < 70.0 {
issues.push("String interning hit rate is below optimal".to_string());
opportunities.push("Pre-intern more common symbols".to_string());
}
let score = self.calculate_category_score(avg_operation_time_ns, ops_per_second, memory_per_operation);
CategoryAnalysis {
category: AnalysisCategory::SymbolInterning,
score,
avg_operation_time_ns,
ops_per_second,
memory_per_operation,
issues,
opportunities,
}
}
fn analyze_fast_path_optimization(&self, fast_path_stats: &FastPathStats) -> CategoryAnalysis {
let mut issues = Vec::new();
let mut opportunities = Vec::new();
let avg_operation_time_ns = 30; let ops_per_second = 3_333_333.0;
let memory_per_operation = 4.0;
if fast_path_stats.hit_rate < 80.0 {
issues.push(format!("Fast path hit rate is {:.1}%, should be >80%", fast_path_stats.hit_rate));
opportunities.push("Add more operations to fast path optimization".to_string());
}
if fast_path_stats.total_fast_path_calls < fast_path_stats.total_regular_calls {
opportunities.push("Identify more operations that can benefit from fast path optimization".to_string());
}
let score = if fast_path_stats.hit_rate > 90.0 { 95.0 }
else if fast_path_stats.hit_rate > 80.0 { 85.0 }
else if fast_path_stats.hit_rate > 70.0 { 70.0 }
else { 50.0 };
CategoryAnalysis {
category: AnalysisCategory::FastPathOptimization,
score,
avg_operation_time_ns,
ops_per_second,
memory_per_operation,
issues,
opportunities,
}
}
fn analyze_memory_allocation(&self, report: &PerformanceReport) -> CategoryAnalysis {
let mut issues = Vec::new();
let mut opportunities = Vec::new();
let avg_operation_time_ns = 1000; let ops_per_second = 1_000_000.0;
let memory_per_operation = 0.0;
if report.system_metrics.memory_pool_efficiency < 0.7 {
issues.push("Memory pool efficiency is below optimal".to_string());
opportunities.push("Tune memory pool sizes for better efficiency".to_string());
}
if report.system_metrics.peak_memory_usage > 100 * 1024 * 1024 { issues.push("High memory usage detected".to_string());
opportunities.push("Implement more aggressive memory management".to_string());
}
let score = if report.system_metrics.memory_pool_efficiency > 0.8 { 90.0 }
else if report.system_metrics.memory_pool_efficiency > 0.6 { 75.0 }
else { 60.0 };
CategoryAnalysis {
category: AnalysisCategory::MemoryAllocation,
score,
avg_operation_time_ns,
ops_per_second,
memory_per_operation,
issues,
opportunities,
}
}
fn analyze_garbage_collection(&self, report: &PerformanceReport) -> CategoryAnalysis {
let mut issues = Vec::new();
let mut opportunities = Vec::new();
let avg_operation_time_ns = 50_000; let ops_per_second = 20.0; let memory_per_operation = -1000.0;
let gc_overhead = report.system_metrics.gc_time.as_secs_f64() / report.system_metrics.total_cpu_time.as_secs_f64();
if gc_overhead > 0.1 { issues.push("Garbage collection overhead is high".to_string());
opportunities.push("Tune GC parameters for better performance".to_string());
}
if report.system_metrics.gc_count > 100 {
opportunities.push("Consider reducing allocation pressure to minimize GC frequency".to_string());
}
let score = if gc_overhead < 0.05 { 95.0 }
else if gc_overhead < 0.1 { 80.0 }
else { 60.0 };
CategoryAnalysis {
category: AnalysisCategory::GarbageCollection,
score,
avg_operation_time_ns,
ops_per_second,
memory_per_operation,
issues,
opportunities,
}
}
fn calculate_category_score(&self, avg_time_ns: u64, ops_per_sec: f64, memory_per_op: f64) -> f64 {
let time_score = if avg_time_ns < 50 { 100.0 }
else if avg_time_ns < 100 { 90.0 }
else if avg_time_ns < 500 { 75.0 }
else if avg_time_ns < 1000 { 60.0 }
else { 40.0 };
let throughput_score = if ops_per_sec > 1_000_000.0 { 100.0 }
else if ops_per_sec > 500_000.0 { 85.0 }
else if ops_per_sec > 100_000.0 { 70.0 }
else { 50.0 };
let memory_score = if memory_per_op < 10.0 { 100.0 }
else if memory_per_op < 25.0 { 85.0 }
else if memory_per_op < 50.0 { 70.0 }
else { 50.0 };
time_score * 0.4 + throughput_score * 0.4 + memory_score * 0.2
}
fn identify_bottlenecks(&self, report: &PerformanceReport, category_analysis: &HashMap<AnalysisCategory, CategoryAnalysis>) -> Vec<PerformanceBottleneck> {
let mut bottlenecks = Vec::new();
for (category, analysis) in category_analysis {
if analysis.score < 70.0 {
let severity = if analysis.score < 50.0 { 8 }
else if analysis.score < 60.0 { 6 }
else { 4 };
let bottleneck = PerformanceBottleneck {
description: format!("Low performance in {category:?} operations"),
category: category.clone(),
severity,
time_percentage: analysis.avg_operation_time_ns as f64 / 10_000.0, memory_impact: (analysis.memory_per_operation * 1000.0) as usize,
suggested_fixes: analysis.opportunities.clone(),
};
bottlenecks.push(bottleneck);
}
}
bottlenecks.sort_by_key(|b| std::cmp::Reverse(b.severity));
bottlenecks.truncate(self.config.max_bottlenecks);
bottlenecks
}
fn find_hot_paths(&self, report: &PerformanceReport) -> Vec<HotPath> {
let mut hot_paths = Vec::new();
let mut operation_stats: HashMap<String, (usize, Duration)> = HashMap::new();
for entry in &report.recent_entries {
let stats = operation_stats.entry(entry.operation.clone()).or_insert((0, Duration::ZERO));
stats.0 += 1;
stats.1 += entry.duration;
}
for (operation, (count, total_time)) in operation_stats {
if total_time.as_nanos() > self.config.hot_path_threshold_ns as u128 {
let avg_time = total_time / count as u32;
let optimization_potential = if avg_time.as_nanos() > 1000 { 90.0 }
else if avg_time.as_nanos() > 500 { 70.0 }
else { 40.0 };
let hot_path = HotPath {
description: format!("Frequent execution of {operation}"),
operation,
execution_count: count,
total_time,
avg_time_per_execution: avg_time,
optimization_potential,
};
hot_paths.push(hot_path);
}
}
hot_paths.sort_by_key(|h| std::cmp::Reverse(h.total_time));
hot_paths.truncate(self.config.max_hot_paths);
hot_paths
}
fn analyze_memory(&self, report: &PerformanceReport) -> MemoryAnalysis {
let current_usage = report.system_metrics.current_memory_usage;
let peak_usage = report.system_metrics.peak_memory_usage;
let allocation_rate = current_usage as f64 / 10.0; let deallocation_rate = allocation_rate * 0.9; let gc_frequency = report.system_metrics.gc_count as f64 / 60.0; let avg_gc_pause = if report.system_metrics.gc_count > 0 {
report.system_metrics.gc_time / report.system_metrics.gc_count as u32
} else {
Duration::ZERO
};
let fragmentation_estimate = if peak_usage > 0 {
((peak_usage - current_usage) as f64 / peak_usage as f64) * 100.0
} else {
0.0
};
let mut pool_efficiency = HashMap::new();
pool_efficiency.insert("small_objects".to_string(), report.system_metrics.memory_pool_efficiency);
pool_efficiency.insert("large_objects".to_string(), report.system_metrics.memory_pool_efficiency * 0.8);
MemoryAnalysis {
current_usage,
peak_usage,
allocation_rate,
deallocation_rate,
gc_frequency,
avg_gc_pause,
fragmentation_estimate,
pool_efficiency,
}
}
fn generate_recommendations(&self, category_analysis: &HashMap<AnalysisCategory, CategoryAnalysis>, bottlenecks: &[PerformanceBottleneck]) -> Vec<OptimizationRecommendation> {
let mut recommendations = Vec::new();
for bottleneck in bottlenecks {
if bottleneck.severity >= 6 {
for (i, fix) in bottleneck.suggested_fixes.iter().enumerate() {
let rec = OptimizationRecommendation {
title: format!("Fix bottleneck in {:?}", bottleneck.category),
description: fix.clone(),
priority: bottleneck.severity,
expected_improvement: 20.0 + (bottleneck.severity as f64 * 2.0),
implementation_difficulty: 5 + (i as u8),
affected_categories: vec![bottleneck.category.clone()],
};
recommendations.push(rec);
}
}
}
for (category, analysis) in category_analysis {
if analysis.score < 85.0 {
for opportunity in &analysis.opportunities {
let rec = OptimizationRecommendation {
title: format!("Optimize {category:?} performance"),
description: opportunity.clone(),
priority: if analysis.score < 70.0 { 7 } else { 5 },
expected_improvement: (100.0 - analysis.score) * 0.3,
implementation_difficulty: 4,
affected_categories: vec![category.clone()],
};
recommendations.push(rec);
}
}
}
recommendations.sort_by(|a, b| {
b.priority.cmp(&a.priority)
.then(b.expected_improvement.partial_cmp(&a.expected_improvement).unwrap_or(std::cmp::Ordering::Equal))
});
recommendations.dedup_by(|a, b| a.title == b.title);
recommendations.truncate(15); recommendations
}
fn compare_with_baseline(&self, category_analysis: &HashMap<AnalysisCategory, CategoryAnalysis>) -> Option<BaselineComparison> {
None
}
fn calculate_overall_score(&self, category_analysis: &HashMap<AnalysisCategory, CategoryAnalysis>) -> f64 {
if category_analysis.is_empty() {
return 0.0;
}
let weights: HashMap<AnalysisCategory, f64> = vec![
(AnalysisCategory::Arithmetic, 0.20),
(AnalysisCategory::ListOperations, 0.15),
(AnalysisCategory::EnvironmentAccess, 0.15),
(AnalysisCategory::FastPathOptimization, 0.15),
(AnalysisCategory::MemoryAllocation, 0.10),
(AnalysisCategory::GarbageCollection, 0.10),
(AnalysisCategory::HashTableAccess, 0.08),
(AnalysisCategory::SymbolInterning, 0.07),
].into_iter().collect();
let mut weighted_sum = 0.0;
let mut total_weight = 0.0;
for (category, analysis) in category_analysis {
if let Some(&weight) = weights.get(category) {
weighted_sum += analysis.score * weight;
total_weight += weight;
}
}
if total_weight > 0.0 {
weighted_sum / total_weight
} else {
0.0
}
}
pub fn record_baseline(&mut self, name: String, metrics: BaselineMetrics) {
self.baselines.insert(name, metrics);
}
}
impl PerformanceAnalysis {
pub fn format_report(&self) -> String {
let mut report = String::new();
report.push_str("=== Lambdust Performance Analysis Report ===\n\n");
report.push_str(&format!("Overall Performance Score: {:.1}/100\n\n", self.overall_score));
report.push_str("=== Performance by Category ===\n");
let mut categories: Vec<_> = self.category_analysis.iter().collect();
categories.sort_by_key(|(_, analysis)| std::cmp::Reverse((analysis.score * 100.0) as u32));
for (category, analysis) in categories {
report.push_str(&format!("{:?}: {:.1}/100 ({:.0} ops/sec, {:.1} ns avg)\n",
category, analysis.score, analysis.ops_per_second, analysis.avg_operation_time_ns));
for issue in &analysis.issues {
report.push_str(&format!(" ⚠ {issue}\n"));
}
}
report.push('\n');
if !self.bottlenecks.is_empty() {
report.push_str("=== Performance Bottlenecks ===\n");
for (i, bottleneck) in self.bottlenecks.iter().take(5).enumerate() {
report.push_str(&format!("{}. {} (Severity: {}/10)\n",
i + 1, bottleneck.description, bottleneck.severity));
if !bottleneck.suggested_fixes.is_empty() {
report.push_str(&format!(" Fix: {}\n", bottleneck.suggested_fixes[0]));
}
}
report.push('\n');
}
if !self.hot_paths.is_empty() {
report.push_str("=== Performance Hot Paths ===\n");
for (i, hot_path) in self.hot_paths.iter().take(5).enumerate() {
report.push_str(&format!("{}. {} ({} executions, {:.2}ms total)\n",
i + 1, hot_path.description, hot_path.execution_count,
hot_path.total_time.as_secs_f64() * 1000.0));
}
report.push('\n');
}
report.push_str("=== Memory Analysis ===\n");
report.push_str(&format!("Current Usage: {:.2} MB\n", self.memory_analysis.current_usage as f64 / 1024.0 / 1024.0));
report.push_str(&format!("Peak Usage: {:.2} MB\n", self.memory_analysis.peak_usage as f64 / 1024.0 / 1024.0));
report.push_str(&format!("GC Frequency: {:.1} collections/sec\n", self.memory_analysis.gc_frequency));
report.push_str(&format!("Average GC Pause: {:.2}ms\n", self.memory_analysis.avg_gc_pause.as_secs_f64() * 1000.0));
report.push('\n');
if !self.recommendations.is_empty() {
report.push_str("=== Top Optimization Recommendations ===\n");
for (i, rec) in self.recommendations.iter().take(5).enumerate() {
report.push_str(&format!("{}. {} (Priority: {}/10, Expected improvement: {:.1}%)\n",
i + 1, rec.title, rec.priority, rec.expected_improvement));
report.push_str(&format!(" {}\n", rec.description));
}
report.push('\n');
}
report
}
pub fn to_json(&self) -> Result<String, Box<dyn std::error::Error>> {
Ok(format!(r#"{{"overall_score": {}, "bottleneck_count": {}, "recommendation_count": {}}}"#,
self.overall_score, self.bottlenecks.len(), self.recommendations.len()))
}
}