1#![allow(dead_code)]
73pub mod core;
75pub mod export;
76pub mod memory;
77pub mod optimization;
78pub mod reporting;
79
80pub use core::{BenchmarkConfig, PerformanceMeasurement, SparseProfiler};
82
83pub use memory::{
85 analyze_sparse_memory, benchmark_cache_performance, CachePerformanceResult,
86 MemoryAccessPattern, MemoryAnalysis, MemoryBreakdown, MemoryTracker, MemoryUsageResult,
87};
88
89pub use reporting::{
91 MemoryStatistics, MetricStatistics, OperationStatistics, PerformanceReport, PerformanceSummary,
92 StatisticsCollector,
93};
94
95pub use optimization::{
97 AutoTuner, CacheInfo, CpuInfo, DistributionPattern, HardwareBenchmark, InputCharacteristics,
98 MemoryInfo, OperationType, OptimizationStrategy, SystemCapabilityReport, SystemInfo,
99 TuningResult,
100};
101
102pub use export::{
104 PerformanceExporter, PlotData, TensorBoardExporter, TrendAnalysis, TrendAnalyzer,
105 TrendDirection,
106};
107
108use crate::{SparseTensor, TorshError, TorshResult};
109
110pub fn default_profiler() -> SparseProfiler {
124 SparseProfiler::new(BenchmarkConfig::default())
125}
126
127pub fn fast_profiler() -> SparseProfiler {
141 SparseProfiler::new(BenchmarkConfig::fast())
142}
143
144pub fn thorough_profiler() -> SparseProfiler {
158 SparseProfiler::new(BenchmarkConfig::thorough())
159}
160
161pub fn memory_profiler() -> SparseProfiler {
175 SparseProfiler::new(BenchmarkConfig::memory_focused())
176}
177
178pub fn comprehensive_analysis(
203 sparse_tensors: &[&dyn SparseTensor],
204 operations: &[&str],
205 export_path: Option<&str>,
206) -> TorshResult<ComprehensiveAnalysisReport> {
207 let mut profiler = default_profiler();
208 let mut auto_tuner = AutoTuner::new();
209 let mut hardware_benchmark = HardwareBenchmark::new();
210 let _trend_analyzer = TrendAnalyzer::new();
211 let mut exporter = TensorBoardExporter::new();
212
213 let system_capabilities = hardware_benchmark.analyze_system_capabilities()?;
215
216 let mut memory_analyses = Vec::new();
218 for tensor in sparse_tensors {
219 let analysis = analyze_sparse_memory(*tensor)?;
220 memory_analyses.push(analysis);
221 }
222
223 let mut all_measurements = Vec::new();
225 for tensor in sparse_tensors {
226 let characteristics = infer_characteristics(*tensor);
228 let _tuning_result = auto_tuner.find_optimal_format(&characteristics)?;
229
230 for &operation in operations {
232 let measurements = match operation {
233 "matmul" => {
234 if sparse_tensors.len() >= 2 {
235 profiler.benchmark_sparse_matmul(*tensor, sparse_tensors[1])?
236 } else {
237 continue;
238 }
239 }
240 "format_conversion" => {
241 let shape = tensor.shape();
243 let dummy_dense = create_dummy_dense_matrix(shape.dims()[0], shape.dims()[1])?;
244 profiler.benchmark_format_conversion(&dummy_dense)?
245 }
246 "dense_to_sparse" => {
247 let shape = tensor.shape();
248 let dummy_dense = create_dummy_dense_matrix(shape.dims()[0], shape.dims()[1])?;
249 profiler.benchmark_dense_to_sparse(&dummy_dense, 0.01)?
250 }
251 _ => continue,
252 };
253 all_measurements.extend(measurements);
254 }
255 }
256
257 let mut report = PerformanceReport::new();
259 report.add_measurements(&profiler.measurements);
260
261 for analysis in &memory_analyses {
263 report.add_memory_analysis(analysis.clone());
264 }
265
266 report.add_metadata("analysis_type".to_string(), "comprehensive".to_string());
268 report.add_metadata(
269 "tensors_analyzed".to_string(),
270 sparse_tensors.len().to_string(),
271 );
272 report.add_metadata("operations_profiled".to_string(), operations.join(","));
273 report.add_metadata(
274 "memory_analyses_count".to_string(),
275 memory_analyses.len().to_string(),
276 );
277
278 if let Some(path) = export_path {
280 let csv_data = PerformanceExporter::to_csv(&report)?;
282 std::fs::write(format!("{}/performance_report.csv", path), csv_data)
283 .map_err(|e| TorshError::IoError(format!("Failed to write CSV report: {}", e)))?;
284
285 let json_data = PerformanceExporter::to_json(&report)?;
287 std::fs::write(format!("{}/performance_report.json", path), json_data)
288 .map_err(|e| TorshError::IoError(format!("Failed to write JSON report: {}", e)))?;
289
290 exporter.export_report(&report, path)?;
292
293 for (i, analysis) in memory_analyses.iter().enumerate() {
295 let analysis_path = format!("{}/memory_analysis_{}.txt", path, i);
296 let analysis_text = format!("{:#?}", analysis);
297 std::fs::write(&analysis_path, analysis_text).map_err(|e| {
298 TorshError::IoError(format!("Failed to write memory analysis: {}", e))
299 })?;
300 }
301 }
302
303 let auto_tuning_recommendations = auto_tuner.get_recommendations();
305 let hardware_recommendations = system_capabilities.recommendations.clone();
306 let performance_recommendations = report.get_recommendations();
307
308 let memory_budget = None; let operations_strings: Vec<String> = operations.iter().map(|s| s.to_string()).collect();
311 let memory_recommendations =
312 crate::performance_tools::memory::generate_memory_optimization_recommendations(
313 &memory_analyses,
314 memory_budget,
315 &operations_strings,
316 );
317
318 let mut all_recommendations = Vec::new();
320 all_recommendations.extend(auto_tuning_recommendations.clone());
321 all_recommendations.extend(hardware_recommendations.clone());
322 all_recommendations.extend(performance_recommendations.clone());
323 all_recommendations.extend(memory_recommendations.clone());
324
325 let comprehensive_report = ComprehensiveAnalysisReport {
327 performance_report: report,
328 memory_analyses,
329 system_capabilities,
330 auto_tuning_recommendations,
331 hardware_recommendations,
332 performance_recommendations,
333 export_path: export_path.map(|p| p.to_string()),
334 };
335
336 Ok(comprehensive_report)
337}
338
339#[derive(Debug, Clone)]
341pub struct ComprehensiveAnalysisReport {
342 pub performance_report: PerformanceReport,
344 pub memory_analyses: Vec<MemoryAnalysis>,
346 pub system_capabilities: SystemCapabilityReport,
348 pub auto_tuning_recommendations: Vec<String>,
350 pub hardware_recommendations: Vec<String>,
352 pub performance_recommendations: Vec<String>,
354 pub export_path: Option<String>,
356}
357
358impl ComprehensiveAnalysisReport {
359 pub fn all_recommendations(&self) -> Vec<String> {
361 let mut all_recs = Vec::new();
362 all_recs.extend(self.auto_tuning_recommendations.clone());
363 all_recs.extend(self.hardware_recommendations.clone());
364 all_recs.extend(self.performance_recommendations.clone());
365 all_recs
366 }
367
368 pub fn summary(&self) -> String {
370 format!(
371 "Performance Analysis Summary:\n\
372 - Total measurements: {}\n\
373 - Operations analyzed: {}\n\
374 - Memory analyses: {}\n\
375 - Recommendations: {}\n\
376 - Export path: {}",
377 self.performance_report.total_measurements,
378 self.performance_report.operation_count,
379 self.memory_analyses.len(),
380 self.all_recommendations().len(),
381 self.export_path.as_deref().unwrap_or("None")
382 )
383 }
384
385 pub fn top_recommendations(&self) -> Vec<String> {
389 let _all_recs = self.all_recommendations();
390
391 let mut prioritized = Vec::new();
393
394 prioritized.extend(self.hardware_recommendations.iter().take(3).cloned());
396
397 prioritized.extend(self.auto_tuning_recommendations.iter().take(2).cloned());
399
400 let remaining_slots = 5usize.saturating_sub(prioritized.len());
402 prioritized.extend(
403 self.performance_recommendations
404 .iter()
405 .take(remaining_slots)
406 .cloned(),
407 );
408
409 prioritized
410 }
411
412 pub fn bottleneck_analysis(&self) -> Vec<String> {
416 let mut bottlenecks = Vec::new();
417
418 if let Some((slowest_op, _)) = self
420 .performance_report
421 .operation_statistics
422 .iter()
423 .max_by_key(|(_, stats)| stats.max_time)
424 {
425 bottlenecks.push(format!(
426 "Slowest operation: {} (consider optimization)",
427 slowest_op
428 ));
429 }
430
431 let avg_compression_ratio: f32 = self
433 .memory_analyses
434 .iter()
435 .map(|analysis| analysis.compression_ratio)
436 .sum::<f32>()
437 / self.memory_analyses.len() as f32;
438
439 if avg_compression_ratio < 2.0 {
440 bottlenecks.push(
441 "Low compression ratio detected (consider different sparse format)".to_string(),
442 );
443 }
444
445 let high_overhead_count = self
447 .memory_analyses
448 .iter()
449 .filter(|analysis| analysis.overhead_per_nnz > 16.0) .count();
451
452 if high_overhead_count > 0 {
453 bottlenecks.push(format!(
454 "High memory overhead detected in {} tensor(s) (consider format optimization)",
455 high_overhead_count
456 ));
457 }
458
459 bottlenecks
460 }
461
462 pub fn optimization_priorities(&self) -> Vec<(String, String, String)> {
466 let mut priorities = Vec::new();
467
468 for rec in &self.hardware_recommendations {
470 priorities.push((rec.clone(), "High".to_string(), "Medium".to_string()));
471 }
472
473 for rec in &self.auto_tuning_recommendations {
475 priorities.push((rec.clone(), "Medium".to_string(), "Low".to_string()));
476 }
477
478 for rec in &self.performance_recommendations {
480 priorities.push((rec.clone(), "Medium".to_string(), "Medium".to_string()));
481 }
482
483 priorities
484 }
485}
486
487pub fn quick_performance_check(
500 sparse_tensor: &dyn SparseTensor,
501) -> TorshResult<QuickPerformanceResult> {
502 let mut profiler = fast_profiler();
503
504 let memory_analysis = analyze_sparse_memory(sparse_tensor)?;
506
507 let shape = sparse_tensor.shape();
509 let dummy_dense = create_dummy_dense_matrix(shape.dims()[0], shape.dims()[1])?;
510 let format_measurements = profiler.benchmark_format_conversion(&dummy_dense)?;
511
512 let mut report = PerformanceReport::new();
514 report.add_measurements(&format_measurements);
515 report.add_memory_analysis(memory_analysis.clone());
516 let summary = report.performance_summary();
517
518 let recommendations = generate_quick_recommendations(&memory_analysis, &summary);
520
521 Ok(QuickPerformanceResult {
522 memory_analysis,
523 performance_summary: summary,
524 format_conversion_time: format_measurements
525 .iter()
526 .find(|m| {
527 m.operation
528 .contains(&format!("{:?}", sparse_tensor.format()))
529 })
530 .map(|m| m.duration)
531 .unwrap_or_default(),
532 recommendations,
533 })
534}
535
536#[derive(Debug, Clone)]
538pub struct QuickPerformanceResult {
539 pub memory_analysis: MemoryAnalysis,
541 pub performance_summary: PerformanceSummary,
543 pub format_conversion_time: std::time::Duration,
545 pub recommendations: Vec<String>,
547}
548
549impl std::fmt::Display for QuickPerformanceResult {
550 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
551 writeln!(f, "=== Quick Performance Check ===")?;
552 writeln!(f, "Format: {:?}", self.memory_analysis.format)?;
553 writeln!(
554 f,
555 "Compression Ratio: {:.1}x",
556 self.memory_analysis.compression_ratio
557 )?;
558 writeln!(
559 f,
560 "Memory Efficiency: {}",
561 self.memory_analysis.memory_efficiency_rating()
562 )?;
563 writeln!(
564 f,
565 "Performance Grade: {}",
566 self.performance_summary.performance_grade()
567 )?;
568 writeln!(
569 f,
570 "Format Conversion: {:.3}ms",
571 self.format_conversion_time.as_secs_f64() * 1000.0
572 )?;
573 writeln!(f)?;
574 writeln!(f, "Recommendations:")?;
575 for rec in &self.recommendations {
576 writeln!(f, " • {}", rec)?;
577 }
578 Ok(())
579 }
580}
581
582fn infer_characteristics(sparse_tensor: &dyn SparseTensor) -> InputCharacteristics {
586 let shape = sparse_tensor.shape();
587 let nnz = sparse_tensor.nnz();
588 let total_elements = shape.dims()[0] * shape.dims()[1];
589 let sparsity = 1.0 - (nnz as f64 / total_elements as f64);
590
591 let distribution_pattern = match sparse_tensor.format() {
593 crate::SparseFormat::Csr => {
594 if sparsity > 0.95 {
595 DistributionPattern::Random
596 } else {
597 DistributionPattern::RowClustered
598 }
599 }
600 crate::SparseFormat::Csc => DistributionPattern::ColumnClustered,
601 crate::SparseFormat::Coo => DistributionPattern::Random,
602 _ => DistributionPattern::Random, };
604
605 let operation_types = vec![OperationType::MatrixVector, OperationType::ElementWise];
607
608 InputCharacteristics {
609 dimensions: (shape.dims()[0], shape.dims()[1]),
610 sparsity,
611 distribution_pattern,
612 operation_types,
613 memory_budget: None,
614 }
615}
616
617fn create_dummy_dense_matrix(rows: usize, cols: usize) -> TorshResult<crate::Tensor> {
619 use torsh_core::{DType, Shape};
620
621 let shape = Shape::new(vec![rows.min(100), cols.min(100)]); let device = torsh_core::DeviceType::Cpu;
624 let _dtype = DType::F32;
625
626 crate::Tensor::ones(shape.dims(), device)
629}
630
631fn generate_quick_recommendations(
633 memory_analysis: &MemoryAnalysis,
634 performance_summary: &PerformanceSummary,
635) -> Vec<String> {
636 let mut recommendations = Vec::new();
637
638 if memory_analysis.compression_ratio < 2.0 {
640 recommendations
641 .push("Consider using a denser format or different sparsity threshold".to_string());
642 } else if memory_analysis.compression_ratio > 10.0 {
643 recommendations
644 .push("Excellent compression achieved - current format is optimal".to_string());
645 }
646
647 match performance_summary.performance_grade().as_str() {
649 "A" => recommendations.push("Excellent performance - no optimization needed".to_string()),
650 "B" => recommendations.push("Good performance - minor optimizations possible".to_string()),
651 "C" => recommendations.push("Fair performance - consider format optimization".to_string()),
652 "D" | "F" => {
653 recommendations.push("Poor performance - significant optimization needed".to_string())
654 }
655 _ => {}
656 }
657
658 if performance_summary.consistency_score < 0.7 {
660 recommendations
661 .push("Performance inconsistency detected - investigate system load".to_string());
662 }
663
664 if recommendations.is_empty() {
665 recommendations.push("Performance appears optimal".to_string());
666 }
667
668 recommendations
669}
670
671#[cfg(test)]
672mod tests {
673 use super::*;
674 use crate::{CooTensor, SparseFormat};
675 use torsh_core::Shape;
676
677 fn create_test_sparse_tensor() -> CooTensor {
678 let row_indices = vec![0, 1, 2];
679 let col_indices = vec![0, 1, 2];
680 let values = vec![1.0, 2.0, 3.0];
681 let shape = Shape::new(vec![3, 3]);
682 CooTensor::new(row_indices, col_indices, values, shape).expect("Coo Tensor should succeed")
683 }
684
685 #[test]
686 fn test_default_profiler() {
687 let profiler = default_profiler();
688 assert_eq!(profiler.config.warmup_iterations, 3);
689 assert_eq!(profiler.config.measured_iterations, 10);
690 }
691
692 #[test]
693 fn test_fast_profiler() {
694 let profiler = fast_profiler();
695 assert_eq!(profiler.config.warmup_iterations, 1);
696 assert_eq!(profiler.config.measured_iterations, 3);
697 assert!(!profiler.config.collect_memory);
698 }
699
700 #[test]
701 fn test_thorough_profiler() {
702 let profiler = thorough_profiler();
703 assert_eq!(profiler.config.warmup_iterations, 5);
704 assert_eq!(profiler.config.measured_iterations, 20);
705 assert!(profiler.config.collect_memory);
706 }
707
708 #[test]
709 fn test_memory_profiler() {
710 let profiler = memory_profiler();
711 assert!(profiler.config.collect_memory);
712 assert!(profiler.config.gc_between_iterations);
713 }
714
715 #[test]
716 fn test_quick_performance_check() {
717 let tensor = create_test_sparse_tensor();
718 let result = quick_performance_check(&tensor);
719
720 assert!(result.is_ok());
721 let quick_result = result.expect("operation should succeed");
722
723 assert_eq!(quick_result.memory_analysis.format, SparseFormat::Coo);
724 assert_eq!(quick_result.memory_analysis.nnz, 3);
725 assert!(!quick_result.recommendations.is_empty());
726 }
727
728 #[test]
729 fn test_quick_performance_result_display() {
730 let tensor = create_test_sparse_tensor();
731 let result =
732 quick_performance_check(&tensor).expect("quick performance check should succeed");
733
734 let display_string = format!("{}", result);
735 assert!(display_string.contains("Quick Performance Check"));
736 assert!(display_string.contains("Format:"));
737 assert!(display_string.contains("Compression Ratio:"));
738 assert!(display_string.contains("Recommendations:"));
739 }
740
741 #[test]
742 fn test_infer_characteristics() {
743 let tensor = create_test_sparse_tensor();
744 let characteristics = infer_characteristics(&tensor);
745
746 assert_eq!(characteristics.dimensions, (3, 3));
747 assert!(characteristics.sparsity > 0.5); assert!(characteristics
749 .operation_types
750 .contains(&OperationType::MatrixVector));
751 }
752
753 #[test]
754 fn test_generate_quick_recommendations() {
755 let memory_analysis = MemoryAnalysis::new(SparseFormat::Coo, 100, (1000, 1000));
756 let performance_summary = PerformanceSummary {
757 total_operations: 10,
758 total_time: std::time::Duration::from_millis(100),
759 avg_throughput: 100.0,
760 avg_memory_efficiency: 0.8,
761 consistency_score: 0.9,
762 };
763
764 let recommendations =
765 generate_quick_recommendations(&memory_analysis, &performance_summary);
766 assert!(!recommendations.is_empty());
767 }
768
769 #[test]
770 fn test_comprehensive_analysis_report() {
771 use std::collections::HashMap;
772
773 let report = ComprehensiveAnalysisReport {
774 performance_report: PerformanceReport::new(),
775 memory_analyses: vec![MemoryAnalysis::new(
776 crate::SparseFormat::Coo,
777 100,
778 (1000, 1000),
779 )],
780 system_capabilities: SystemCapabilityReport {
781 system_info: SystemInfo::detect(),
782 capability_scores: HashMap::new(),
783 recommendations: vec!["Test recommendation".to_string()],
784 benchmark_timestamp: std::time::SystemTime::now(),
785 },
786 auto_tuning_recommendations: vec!["Auto-tune rec".to_string()],
787 hardware_recommendations: vec!["Hardware rec".to_string()],
788 performance_recommendations: vec!["Performance rec".to_string()],
789 export_path: Some(std::env::temp_dir().join("test").display().to_string()),
790 };
791
792 let all_recs = report.all_recommendations();
793 assert_eq!(all_recs.len(), 3);
794
795 let summary = report.summary();
796 assert!(summary.contains("Performance Analysis Summary"));
797 assert!(summary.contains("Total measurements: 0"));
798 assert!(summary.contains("Export path:"));
799 }
800
801 #[test]
802 fn test_module_re_exports() {
803 let _config = BenchmarkConfig::default();
805 let _profiler = SparseProfiler::new(_config);
806 let _tuner = AutoTuner::new();
807 let _analyzer = TrendAnalyzer::new();
808 let _exporter = TensorBoardExporter::new();
809
810 let _strategy = OptimizationStrategy::Speed;
812 let _pattern = DistributionPattern::Random;
813 let _operation = OperationType::MatrixVector;
814 let _trend = TrendDirection::Improving;
815 }
816
817 #[test]
818 fn test_convenience_functions() {
819 let _default = default_profiler();
821 let _fast = fast_profiler();
822 let _thorough = thorough_profiler();
823 let _memory = memory_profiler();
824
825 assert_ne!(
827 _fast.config.measured_iterations,
828 _thorough.config.measured_iterations
829 );
830 assert_ne!(_fast.config.collect_memory, _memory.config.collect_memory);
831 }
832}