1use std::collections::HashMap;
7use std::sync::{Arc, Mutex, RwLock};
8use std::time::{Duration, Instant};
9
10#[derive(Debug, Clone)]
12pub struct PerformanceProfile {
13 pub function_profiles: HashMap<String, FunctionProfile>,
14 pub loop_profiles: HashMap<String, LoopProfile>,
15 pub memory_access_patterns: HashMap<String, MemoryAccessPattern>,
16 pub branch_predictions: HashMap<String, BranchProfile>,
17 pub cache_statistics: CacheStatistics,
18 pub instruction_mix: InstructionMix,
19 pub profiling_duration: Duration,
20 pub total_samples: u64,
21}
22
23#[derive(Debug, Clone)]
25pub struct FunctionProfile {
26 pub name: String,
27 pub total_time: Duration,
28 pub self_time: Duration,
29 pub call_count: u64,
30 pub avg_time_per_call: Duration,
31 pub max_time_per_call: Duration,
32 pub min_time_per_call: Duration,
33 pub cpu_cycles: u64,
34 pub cache_misses: u64,
35 pub branch_misses: u64,
36 pub hotness_score: f64,
37 pub optimization_opportunities: Vec<OptimizationOpportunity>,
38}
39
40#[derive(Debug, Clone)]
42pub struct LoopProfile {
43 pub loop_id: String,
44 pub location: String,
45 pub iteration_count: u64,
46 pub total_time: Duration,
47 pub avg_time_per_iteration: Duration,
48 pub vectorization_efficiency: f64,
49 pub dependency_chains: Vec<DependencyChain>,
50 pub memory_access_stride: i64,
51 pub loop_carried_dependencies: u32,
52 pub optimization_potential: f64,
53}
54
55#[derive(Debug, Clone)]
57pub struct MemoryAccessPattern {
58 pub function_name: String,
59 pub access_type: MemoryAccessType,
60 pub access_frequency: u64,
61 pub cache_hit_rate: f64,
62 pub average_latency: Duration,
63 pub stride_pattern: StridePattern,
64 pub prefetch_effectiveness: f64,
65 pub numa_locality: f64,
66}
67
68#[derive(Debug, Clone)]
70pub struct BranchProfile {
71 pub branch_id: String,
72 pub location: String,
73 pub taken_count: u64,
74 pub not_taken_count: u64,
75 pub prediction_accuracy: f64,
76 pub misprediction_penalty: Duration,
77 pub branch_type: BranchType,
78}
79
80#[derive(Debug, Clone)]
82pub struct CacheStatistics {
83 pub l1_hit_rate: f64,
84 pub l2_hit_rate: f64,
85 pub l3_hit_rate: f64,
86 pub tlb_hit_rate: f64,
87 pub cache_line_utilization: f64,
88 pub false_sharing_incidents: u64,
89 pub prefetch_accuracy: f64,
90}
91
92#[derive(Debug, Clone)]
94pub struct InstructionMix {
95 pub integer_ops: u64,
96 pub floating_point_ops: u64,
97 pub vector_ops: u64,
98 pub memory_ops: u64,
99 pub branch_ops: u64,
100 pub simd_utilization: f64,
101 pub parallel_efficiency: f64,
102}
103
104#[derive(Debug, Clone)]
106pub struct OptimizationOpportunity {
107 pub opportunity_type: OptimizationType,
108 pub description: String,
109 pub potential_speedup: f64,
110 pub implementation_effort: ImplementationEffort,
111 pub confidence: f64,
112 pub code_location: String,
113 pub suggested_actions: Vec<String>,
114}
115
116#[derive(Debug, Clone)]
118pub struct DependencyChain {
119 pub chain_id: String,
120 pub length: u32,
121 pub critical_path_time: Duration,
122 pub parallelization_potential: f64,
123}
124
125#[derive(Debug, Clone, PartialEq)]
127pub enum MemoryAccessType {
128 Sequential,
129 Random,
130 Strided,
131 Gather,
132 Scatter,
133}
134
135#[derive(Debug, Clone)]
137pub struct StridePattern {
138 pub primary_stride: i64,
139 pub secondary_stride: Option<i64>,
140 pub regularity: f64,
141 pub predictability: f64,
142}
143
144#[derive(Debug, Clone, PartialEq)]
146pub enum BranchType {
147 Conditional,
148 Indirect,
149 Return,
150 Call,
151 Loop,
152}
153
154#[derive(Debug, Clone, PartialEq)]
156pub enum OptimizationType {
157 Vectorization,
158 LoopUnrolling,
159 FunctionInlining,
160 MemoryPrefetching,
161 BranchElimination,
162 CacheOptimization,
163 Parallelization,
164 AlgorithmicImprovement,
165}
166
167#[derive(Debug, Clone, PartialEq)]
169pub enum ImplementationEffort {
170 Low,
171 Medium,
172 High,
173 VeryHigh,
174}
175
176pub struct ProfileGuidedOptimizer {
178 profiles: Arc<RwLock<HashMap<String, PerformanceProfile>>>,
179 optimization_rules: Vec<OptimizationRule>,
180 #[allow(dead_code)]
181 performance_targets: PerformanceTargets,
182 profiler_config: ProfilerConfig,
183 optimization_history: Arc<Mutex<Vec<OptimizationApplication>>>,
184}
185
186#[derive(Debug, Clone)]
188pub struct OptimizationRule {
189 pub name: String,
190 pub trigger_condition: TriggerCondition,
191 pub optimization_type: OptimizationType,
192 pub implementation: String,
193 pub expected_benefit: f64,
194 pub risk_level: RiskLevel,
195}
196
197#[derive(Debug, Clone)]
199pub struct TriggerCondition {
200 pub min_hotness_score: f64,
201 pub min_call_frequency: u64,
202 pub max_cache_miss_rate: f64,
203 pub min_loop_iterations: u64,
204 pub function_name_patterns: Vec<String>,
205}
206
207#[derive(Debug, Clone)]
209pub struct PerformanceTargets {
210 pub target_speedup: f64,
211 pub max_memory_increase: f64,
212 pub max_compilation_time: Duration,
213 pub stability_requirement: f64,
214}
215
216#[derive(Debug, Clone)]
218pub struct ProfilerConfig {
219 pub sampling_frequency: u64,
220 pub enable_instruction_profiling: bool,
221 pub enable_memory_profiling: bool,
222 pub enable_cache_profiling: bool,
223 pub enable_branch_profiling: bool,
224 pub profiling_duration: Duration,
225}
226
227#[derive(Debug, Clone)]
229pub struct OptimizationApplication {
230 pub timestamp: Instant,
231 pub rule_name: String,
232 pub target_function: String,
233 pub optimization_type: OptimizationType,
234 pub measured_speedup: Option<f64>,
235 pub success: bool,
236 pub notes: String,
237}
238
239#[derive(Debug, Clone, PartialEq)]
241pub enum RiskLevel {
242 Low,
243 Medium,
244 High,
245 Experimental,
246}
247
248impl ProfileGuidedOptimizer {
249 pub fn new(config: ProfilerConfig, targets: PerformanceTargets) -> Self {
251 let mut optimizer = Self {
252 profiles: Arc::new(RwLock::new(HashMap::new())),
253 optimization_rules: Vec::new(),
254 performance_targets: targets,
255 profiler_config: config,
256 optimization_history: Arc::new(Mutex::new(Vec::new())),
257 };
258
259 optimizer.initialize_default_rules();
260 optimizer
261 }
262
263 fn initialize_default_rules(&mut self) {
265 self.optimization_rules = vec![
266 OptimizationRule {
267 name: "Hot Function Inlining".to_string(),
268 trigger_condition: TriggerCondition {
269 min_hotness_score: 0.8,
270 min_call_frequency: 1000,
271 max_cache_miss_rate: 1.0,
272 min_loop_iterations: 0,
273 function_name_patterns: vec![".*_hot.*".to_string()],
274 },
275 optimization_type: OptimizationType::FunctionInlining,
276 implementation: "#[inline(always)]".to_string(),
277 expected_benefit: 1.15,
278 risk_level: RiskLevel::Low,
279 },
280 OptimizationRule {
281 name: "Loop Vectorization".to_string(),
282 trigger_condition: TriggerCondition {
283 min_hotness_score: 0.6,
284 min_call_frequency: 0,
285 max_cache_miss_rate: 1.0,
286 min_loop_iterations: 100,
287 function_name_patterns: vec![".*_vectorizable.*".to_string()],
288 },
289 optimization_type: OptimizationType::Vectorization,
290 implementation: "SIMD optimization".to_string(),
291 expected_benefit: 2.0,
292 risk_level: RiskLevel::Medium,
293 },
294 OptimizationRule {
295 name: "Memory Prefetching".to_string(),
296 trigger_condition: TriggerCondition {
297 min_hotness_score: 0.5,
298 min_call_frequency: 0,
299 max_cache_miss_rate: 0.1,
300 min_loop_iterations: 0,
301 function_name_patterns: vec![".*_memory_intensive.*".to_string()],
302 },
303 optimization_type: OptimizationType::MemoryPrefetching,
304 implementation: "Software prefetching".to_string(),
305 expected_benefit: 1.3,
306 risk_level: RiskLevel::Medium,
307 },
308 OptimizationRule {
309 name: "Loop Unrolling".to_string(),
310 trigger_condition: TriggerCondition {
311 min_hotness_score: 0.7,
312 min_call_frequency: 0,
313 max_cache_miss_rate: 1.0,
314 min_loop_iterations: 10,
315 function_name_patterns: vec![".*_tight_loop.*".to_string()],
316 },
317 optimization_type: OptimizationType::LoopUnrolling,
318 implementation: "Unroll factor 4".to_string(),
319 expected_benefit: 1.25,
320 risk_level: RiskLevel::Low,
321 },
322 ];
323 }
324
325 pub fn add_optimization_rule(&mut self, rule: OptimizationRule) {
327 self.optimization_rules.push(rule);
328 }
329
330 pub fn collect_profile(&self, program_name: &str) -> Result<PerformanceProfile, ProfileError> {
332 let mock_profile = PerformanceProfile {
334 function_profiles: self.generate_mock_function_profiles(),
335 loop_profiles: self.generate_mock_loop_profiles(),
336 memory_access_patterns: self.generate_mock_memory_patterns(),
337 branch_predictions: self.generate_mock_branch_profiles(),
338 cache_statistics: CacheStatistics {
339 l1_hit_rate: 0.95,
340 l2_hit_rate: 0.85,
341 l3_hit_rate: 0.70,
342 tlb_hit_rate: 0.98,
343 cache_line_utilization: 0.75,
344 false_sharing_incidents: 5,
345 prefetch_accuracy: 0.80,
346 },
347 instruction_mix: InstructionMix {
348 integer_ops: 1_000_000,
349 floating_point_ops: 500_000,
350 vector_ops: 100_000,
351 memory_ops: 200_000,
352 branch_ops: 150_000,
353 simd_utilization: 0.60,
354 parallel_efficiency: 0.75,
355 },
356 profiling_duration: self.profiler_config.profiling_duration,
357 total_samples: 1_000_000,
358 };
359
360 self.profiles
361 .write()
362 .expect("operation should succeed")
363 .insert(program_name.to_string(), mock_profile.clone());
364 Ok(mock_profile)
365 }
366
367 fn generate_mock_function_profiles(&self) -> HashMap<String, FunctionProfile> {
369 let mut profiles = HashMap::new();
370
371 profiles.insert(
372 "matrix_multiply".to_string(),
373 FunctionProfile {
374 name: "matrix_multiply".to_string(),
375 total_time: Duration::from_millis(500),
376 self_time: Duration::from_millis(450),
377 call_count: 1000,
378 avg_time_per_call: Duration::from_micros(500),
379 max_time_per_call: Duration::from_millis(2),
380 min_time_per_call: Duration::from_micros(100),
381 cpu_cycles: 1_000_000,
382 cache_misses: 5000,
383 branch_misses: 100,
384 hotness_score: 0.9,
385 optimization_opportunities: vec![OptimizationOpportunity {
386 opportunity_type: OptimizationType::Vectorization,
387 description: "Loop can be vectorized for SIMD".to_string(),
388 potential_speedup: 2.5,
389 implementation_effort: ImplementationEffort::Medium,
390 confidence: 0.85,
391 code_location: "matrix_multiply.rs:45".to_string(),
392 suggested_actions: vec![
393 "Use SIMD intrinsics".to_string(),
394 "Enable auto-vectorization".to_string(),
395 ],
396 }],
397 },
398 );
399
400 profiles.insert(
401 "activation_function".to_string(),
402 FunctionProfile {
403 name: "activation_function".to_string(),
404 total_time: Duration::from_millis(200),
405 self_time: Duration::from_millis(180),
406 call_count: 10_000,
407 avg_time_per_call: Duration::from_micros(20),
408 max_time_per_call: Duration::from_micros(100),
409 min_time_per_call: Duration::from_micros(5),
410 cpu_cycles: 400_000,
411 cache_misses: 1000,
412 branch_misses: 50,
413 hotness_score: 0.7,
414 optimization_opportunities: vec![OptimizationOpportunity {
415 opportunity_type: OptimizationType::FunctionInlining,
416 description: "Small function called frequently".to_string(),
417 potential_speedup: 1.15,
418 implementation_effort: ImplementationEffort::Low,
419 confidence: 0.95,
420 code_location: "activation.rs:12".to_string(),
421 suggested_actions: vec!["Add inline attribute".to_string()],
422 }],
423 },
424 );
425
426 profiles
427 }
428
429 fn generate_mock_loop_profiles(&self) -> HashMap<String, LoopProfile> {
431 let mut profiles = HashMap::new();
432
433 profiles.insert(
434 "training_loop".to_string(),
435 LoopProfile {
436 loop_id: "training_loop".to_string(),
437 location: "train.rs:100".to_string(),
438 iteration_count: 1000,
439 total_time: Duration::from_millis(1000),
440 avg_time_per_iteration: Duration::from_millis(1),
441 vectorization_efficiency: 0.4,
442 dependency_chains: vec![DependencyChain {
443 chain_id: "weight_update".to_string(),
444 length: 3,
445 critical_path_time: Duration::from_micros(100),
446 parallelization_potential: 0.8,
447 }],
448 memory_access_stride: 8,
449 loop_carried_dependencies: 1,
450 optimization_potential: 0.6,
451 },
452 );
453
454 profiles
455 }
456
457 fn generate_mock_memory_patterns(&self) -> HashMap<String, MemoryAccessPattern> {
459 let mut patterns = HashMap::new();
460
461 patterns.insert(
462 "data_loading".to_string(),
463 MemoryAccessPattern {
464 function_name: "data_loading".to_string(),
465 access_type: MemoryAccessType::Sequential,
466 access_frequency: 10_000,
467 cache_hit_rate: 0.85,
468 average_latency: Duration::from_nanos(50),
469 stride_pattern: StridePattern {
470 primary_stride: 8,
471 secondary_stride: None,
472 regularity: 0.95,
473 predictability: 0.90,
474 },
475 prefetch_effectiveness: 0.75,
476 numa_locality: 0.80,
477 },
478 );
479
480 patterns
481 }
482
483 fn generate_mock_branch_profiles(&self) -> HashMap<String, BranchProfile> {
485 let mut profiles = HashMap::new();
486
487 profiles.insert(
488 "convergence_check".to_string(),
489 BranchProfile {
490 branch_id: "convergence_check".to_string(),
491 location: "optimizer.rs:200".to_string(),
492 taken_count: 950,
493 not_taken_count: 50,
494 prediction_accuracy: 0.95,
495 misprediction_penalty: Duration::from_nanos(20),
496 branch_type: BranchType::Conditional,
497 },
498 );
499
500 profiles
501 }
502
503 pub fn analyze_and_recommend(
505 &self,
506 program_name: &str,
507 ) -> Result<Vec<OptimizationRecommendation>, ProfileError> {
508 let profiles = self.profiles.read().expect("operation should succeed");
509 let profile = profiles
510 .get(program_name)
511 .ok_or(ProfileError::ProfileNotFound)?;
512
513 let mut recommendations = Vec::new();
514
515 for func_profile in profile.function_profiles.values() {
517 for rule in &self.optimization_rules {
518 if self.matches_trigger_condition(&rule.trigger_condition, func_profile) {
519 recommendations.push(OptimizationRecommendation {
520 rule_name: rule.name.clone(),
521 target_function: func_profile.name.clone(),
522 optimization_type: rule.optimization_type.clone(),
523 expected_speedup: rule.expected_benefit,
524 risk_level: rule.risk_level.clone(),
525 implementation: rule.implementation.clone(),
526 priority: self.calculate_priority(func_profile, rule),
527 estimated_effort: ImplementationEffort::Medium,
528 confidence: 0.8,
529 });
530 }
531 }
532 }
533
534 recommendations.sort_by(|a, b| {
536 b.priority
537 .partial_cmp(&a.priority)
538 .unwrap_or(std::cmp::Ordering::Equal)
539 });
540
541 Ok(recommendations)
542 }
543
544 fn matches_trigger_condition(
546 &self,
547 condition: &TriggerCondition,
548 profile: &FunctionProfile,
549 ) -> bool {
550 profile.hotness_score >= condition.min_hotness_score
551 && profile.call_count >= condition.min_call_frequency
552 && (profile.cache_misses as f64 / profile.call_count as f64)
553 <= condition.max_cache_miss_rate
554 }
555
556 fn calculate_priority(&self, profile: &FunctionProfile, rule: &OptimizationRule) -> f64 {
558 let hotness_factor = profile.hotness_score;
559 let benefit_factor = rule.expected_benefit - 1.0; let risk_factor = match rule.risk_level {
561 RiskLevel::Low => 1.0,
562 RiskLevel::Medium => 0.8,
563 RiskLevel::High => 0.6,
564 RiskLevel::Experimental => 0.4,
565 };
566
567 hotness_factor * benefit_factor * risk_factor
568 }
569
570 pub fn apply_optimization(
572 &self,
573 recommendation: &OptimizationRecommendation,
574 ) -> Result<(), ProfileError> {
575 let application = OptimizationApplication {
577 timestamp: Instant::now(),
578 rule_name: recommendation.rule_name.clone(),
579 target_function: recommendation.target_function.clone(),
580 optimization_type: recommendation.optimization_type.clone(),
581 measured_speedup: Some(recommendation.expected_speedup * 0.9), success: true,
583 notes: format!(
584 "Applied {} to {}",
585 recommendation.implementation, recommendation.target_function
586 ),
587 };
588
589 self.optimization_history
590 .lock()
591 .expect("operation should succeed")
592 .push(application);
593 Ok(())
594 }
595
596 pub fn get_optimization_history(&self) -> Vec<OptimizationApplication> {
598 self.optimization_history
599 .lock()
600 .expect("operation should succeed")
601 .clone()
602 }
603
604 pub fn calculate_performance_gain(&self) -> f64 {
606 let history = self
607 .optimization_history
608 .lock()
609 .expect("operation should succeed");
610 let successful_optimizations: Vec<_> = history
611 .iter()
612 .filter(|app| app.success && app.measured_speedup.is_some())
613 .collect();
614
615 if successful_optimizations.is_empty() {
616 return 1.0;
617 }
618
619 successful_optimizations
621 .iter()
622 .map(|app| app.measured_speedup.expect("operation should succeed"))
623 .fold(1.0, |acc, speedup| acc * speedup)
624 }
625
626 pub fn generate_report(&self, program_name: &str) -> Result<OptimizationReport, ProfileError> {
628 let profiles = self.profiles.read().expect("operation should succeed");
629 let profile = profiles
630 .get(program_name)
631 .ok_or(ProfileError::ProfileNotFound)?;
632
633 let recommendations = self.analyze_and_recommend(program_name)?;
634 let history = self.get_optimization_history();
635 let performance_gain = self.calculate_performance_gain();
636
637 let potential_further_gains = recommendations
638 .iter()
639 .map(|r| r.expected_speedup - 1.0)
640 .sum::<f64>();
641
642 Ok(OptimizationReport {
643 program_name: program_name.to_string(),
644 profile_summary: ProfileSummary {
645 total_functions: profile.function_profiles.len(),
646 hot_functions: profile
647 .function_profiles
648 .values()
649 .filter(|f| f.hotness_score > 0.5)
650 .count(),
651 total_loops: profile.loop_profiles.len(),
652 vectorizable_loops: profile
653 .loop_profiles
654 .values()
655 .filter(|l| l.vectorization_efficiency < 0.5)
656 .count(),
657 cache_efficiency: profile.cache_statistics.l1_hit_rate,
658 simd_utilization: profile.instruction_mix.simd_utilization,
659 },
660 recommendations,
661 applied_optimizations: history,
662 overall_performance_gain: performance_gain,
663 potential_further_gains,
664 report_timestamp: Instant::now(),
665 })
666 }
667
668 pub fn reset(&self) {
670 self.profiles
671 .write()
672 .expect("operation should succeed")
673 .clear();
674 self.optimization_history
675 .lock()
676 .expect("operation should succeed")
677 .clear();
678 }
679}
680
681#[derive(Debug, Clone)]
683pub struct OptimizationRecommendation {
684 pub rule_name: String,
685 pub target_function: String,
686 pub optimization_type: OptimizationType,
687 pub expected_speedup: f64,
688 pub risk_level: RiskLevel,
689 pub implementation: String,
690 pub priority: f64,
691 pub estimated_effort: ImplementationEffort,
692 pub confidence: f64,
693}
694
695#[derive(Debug, Clone)]
697pub struct ProfileSummary {
698 pub total_functions: usize,
699 pub hot_functions: usize,
700 pub total_loops: usize,
701 pub vectorizable_loops: usize,
702 pub cache_efficiency: f64,
703 pub simd_utilization: f64,
704}
705
706#[derive(Debug, Clone)]
708pub struct OptimizationReport {
709 pub program_name: String,
710 pub profile_summary: ProfileSummary,
711 pub recommendations: Vec<OptimizationRecommendation>,
712 pub applied_optimizations: Vec<OptimizationApplication>,
713 pub overall_performance_gain: f64,
714 pub potential_further_gains: f64,
715 pub report_timestamp: Instant,
716}
717
718#[derive(Debug, thiserror::Error)]
720pub enum ProfileError {
721 #[error("Profile not found")]
722 ProfileNotFound,
723 #[error("Profiling failed: {0}")]
724 ProfilingFailed(String),
725 #[error("Optimization failed: {0}")]
726 OptimizationFailed(String),
727 #[error("Invalid configuration: {0}")]
728 InvalidConfiguration(String),
729}
730
731impl Default for ProfilerConfig {
732 fn default() -> Self {
733 Self {
734 sampling_frequency: 1000,
735 enable_instruction_profiling: true,
736 enable_memory_profiling: true,
737 enable_cache_profiling: true,
738 enable_branch_profiling: true,
739 profiling_duration: Duration::from_secs(10),
740 }
741 }
742}
743
744impl Default for PerformanceTargets {
745 fn default() -> Self {
746 Self {
747 target_speedup: 1.5,
748 max_memory_increase: 0.1,
749 max_compilation_time: Duration::from_secs(60),
750 stability_requirement: 0.95,
751 }
752 }
753}
754
755#[allow(non_snake_case)]
756#[cfg(test)]
757mod tests {
758 use super::*;
759
760 #[test]
761 fn test_optimizer_creation() {
762 let config = ProfilerConfig::default();
763 let targets = PerformanceTargets::default();
764 let optimizer = ProfileGuidedOptimizer::new(config, targets);
765
766 assert!(!optimizer.optimization_rules.is_empty());
767 }
768
769 #[test]
770 fn test_profile_collection() {
771 let config = ProfilerConfig::default();
772 let targets = PerformanceTargets::default();
773 let optimizer = ProfileGuidedOptimizer::new(config, targets);
774
775 let profile = optimizer
776 .collect_profile("test_program")
777 .expect("operation should succeed");
778 assert!(!profile.function_profiles.is_empty());
779 assert!(profile.total_samples > 0);
780 }
781
782 #[test]
783 fn test_optimization_recommendations() {
784 let config = ProfilerConfig::default();
785 let targets = PerformanceTargets::default();
786 let optimizer = ProfileGuidedOptimizer::new(config, targets);
787
788 optimizer
789 .collect_profile("test_program")
790 .expect("operation should succeed");
791 let recommendations = optimizer
792 .analyze_and_recommend("test_program")
793 .expect("operation should succeed");
794
795 assert!(!recommendations.is_empty());
796 assert!(recommendations[0].priority > 0.0);
797 }
798
799 #[test]
800 fn test_optimization_application() {
801 let config = ProfilerConfig::default();
802 let targets = PerformanceTargets::default();
803 let optimizer = ProfileGuidedOptimizer::new(config, targets);
804
805 optimizer
806 .collect_profile("test_program")
807 .expect("operation should succeed");
808 let recommendations = optimizer
809 .analyze_and_recommend("test_program")
810 .expect("operation should succeed");
811
812 if let Some(recommendation) = recommendations.first() {
813 assert!(optimizer.apply_optimization(recommendation).is_ok());
814
815 let history = optimizer.get_optimization_history();
816 assert!(!history.is_empty());
817 assert!(history[0].success);
818 }
819 }
820
821 #[test]
822 fn test_performance_gain_calculation() {
823 let config = ProfilerConfig::default();
824 let targets = PerformanceTargets::default();
825 let optimizer = ProfileGuidedOptimizer::new(config, targets);
826
827 optimizer
828 .collect_profile("test_program")
829 .expect("operation should succeed");
830 let recommendations = optimizer
831 .analyze_and_recommend("test_program")
832 .expect("operation should succeed");
833
834 for recommendation in recommendations.iter().take(2) {
835 optimizer
836 .apply_optimization(recommendation)
837 .expect("operation should succeed");
838 }
839
840 let gain = optimizer.calculate_performance_gain();
841 assert!(gain >= 1.0);
842 }
843
844 #[test]
845 fn test_optimization_report() {
846 let config = ProfilerConfig::default();
847 let targets = PerformanceTargets::default();
848 let optimizer = ProfileGuidedOptimizer::new(config, targets);
849
850 optimizer
851 .collect_profile("test_program")
852 .expect("operation should succeed");
853 let report = optimizer
854 .generate_report("test_program")
855 .expect("operation should succeed");
856
857 assert_eq!(report.program_name, "test_program");
858 assert!(report.profile_summary.total_functions > 0);
859 assert!(!report.recommendations.is_empty());
860 }
861
862 #[test]
863 fn test_custom_optimization_rule() {
864 let config = ProfilerConfig::default();
865 let targets = PerformanceTargets::default();
866 let mut optimizer = ProfileGuidedOptimizer::new(config, targets);
867
868 let custom_rule = OptimizationRule {
869 name: "Custom Parallel".to_string(),
870 trigger_condition: TriggerCondition {
871 min_hotness_score: 0.9,
872 min_call_frequency: 1000,
873 max_cache_miss_rate: 0.05,
874 min_loop_iterations: 1000,
875 function_name_patterns: vec!["parallel_*".to_string()],
876 },
877 optimization_type: OptimizationType::Parallelization,
878 implementation: "Use rayon parallel iterator".to_string(),
879 expected_benefit: 3.0,
880 risk_level: RiskLevel::Medium,
881 };
882
883 let initial_rules = optimizer.optimization_rules.len();
884 optimizer.add_optimization_rule(custom_rule);
885 assert_eq!(optimizer.optimization_rules.len(), initial_rules + 1);
886 }
887
888 #[test]
889 fn test_trigger_condition_matching() {
890 let config = ProfilerConfig::default();
891 let targets = PerformanceTargets::default();
892 let optimizer = ProfileGuidedOptimizer::new(config, targets);
893
894 let condition = TriggerCondition {
895 min_hotness_score: 0.5,
896 min_call_frequency: 100,
897 max_cache_miss_rate: 0.1,
898 min_loop_iterations: 0,
899 function_name_patterns: vec![],
900 };
901
902 let profile = FunctionProfile {
903 name: "test_function".to_string(),
904 total_time: Duration::from_millis(100),
905 self_time: Duration::from_millis(90),
906 call_count: 1000,
907 avg_time_per_call: Duration::from_micros(100),
908 max_time_per_call: Duration::from_millis(1),
909 min_time_per_call: Duration::from_micros(50),
910 cpu_cycles: 200_000,
911 cache_misses: 50, branch_misses: 10,
913 hotness_score: 0.8,
914 optimization_opportunities: vec![],
915 };
916
917 assert!(optimizer.matches_trigger_condition(&condition, &profile));
918 }
919
920 #[test]
921 fn test_priority_calculation() {
922 let config = ProfilerConfig::default();
923 let targets = PerformanceTargets::default();
924 let optimizer = ProfileGuidedOptimizer::new(config, targets);
925
926 let profile = FunctionProfile {
927 name: "test_function".to_string(),
928 total_time: Duration::from_millis(100),
929 self_time: Duration::from_millis(90),
930 call_count: 1000,
931 avg_time_per_call: Duration::from_micros(100),
932 max_time_per_call: Duration::from_millis(1),
933 min_time_per_call: Duration::from_micros(50),
934 cpu_cycles: 200_000,
935 cache_misses: 50,
936 branch_misses: 10,
937 hotness_score: 0.8,
938 optimization_opportunities: vec![],
939 };
940
941 let rule = OptimizationRule {
942 name: "Test Rule".to_string(),
943 trigger_condition: TriggerCondition {
944 min_hotness_score: 0.5,
945 min_call_frequency: 100,
946 max_cache_miss_rate: 0.1,
947 min_loop_iterations: 0,
948 function_name_patterns: vec![],
949 },
950 optimization_type: OptimizationType::FunctionInlining,
951 implementation: "inline".to_string(),
952 expected_benefit: 1.5,
953 risk_level: RiskLevel::Low,
954 };
955
956 let priority = optimizer.calculate_priority(&profile, &rule);
957 assert!(priority > 0.0);
958 }
959}