1use super::config::PerformanceConfig;
4use super::meta_learning::MetaLearningResult;
5use super::TrainingMetrics;
6use crate::error::Result;
7use scirs2_core::ndarray::Array1;
8use scirs2_core::numeric::Float;
9use serde::{Deserialize, Serialize};
10use std::collections::{HashMap, VecDeque};
11use std::fmt::Debug;
12use std::time::{Duration, Instant};
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct PerformanceMetrics {
17 pub training_metrics: TrainingMetricsCollection,
19
20 pub inference_metrics: InferenceMetricsCollection,
22
23 pub memory_metrics: MemoryMetricsCollection,
25
26 pub timing_metrics: TimingMetricsCollection,
28
29 pub quality_metrics: QualityMetricsCollection,
31
32 pub resource_metrics: ResourceMetricsCollection,
34
35 pub optimization_metrics: OptimizationMetricsCollection,
37}
38
39pub struct TransformerPerformanceTracker<
41 T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static,
42> {
43 config: PerformanceConfig,
45
46 metrics: PerformanceMetrics,
48
49 loss_history: VecDeque<f64>,
51
52 step_timings: VecDeque<Duration>,
54
55 meta_results_history: VecDeque<MetaLearningResult<T>>,
57
58 training_history: VecDeque<TrainingMetrics>,
60
61 memory_samples: VecDeque<MemorySample>,
63
64 baselines: PerformanceBaselines,
66
67 alert_thresholds: AlertThresholds,
69
70 trends: PerformanceTrends,
72
73 profiling_data: ProfilingData,
75
76 session_start: Instant,
78}
79
80impl<T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static> Default
81 for TransformerPerformanceTracker<T>
82{
83 fn default() -> Self {
84 Self::new()
85 }
86}
87
88impl<T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static>
89 TransformerPerformanceTracker<T>
90{
91 pub fn new() -> Self {
93 let config = PerformanceConfig::default();
94
95 Self {
96 config,
97 metrics: PerformanceMetrics::new(),
98 loss_history: VecDeque::new(),
99 step_timings: VecDeque::new(),
100 meta_results_history: VecDeque::new(),
101 training_history: VecDeque::new(),
102 memory_samples: VecDeque::new(),
103 baselines: PerformanceBaselines::new(),
104 alert_thresholds: AlertThresholds::default(),
105 trends: PerformanceTrends::new(),
106 profiling_data: ProfilingData::new(),
107 session_start: Instant::now(),
108 }
109 }
110
111 pub fn with_config(config: PerformanceConfig) -> Self {
113 let mut tracker = Self::new();
114 tracker.config = config;
115 tracker
116 }
117
118 pub fn record_optimization_step(&mut self, duration: Duration, update: &Array1<T>) {
120 self.step_timings.push_back(duration);
122 if self.step_timings.len() > self.config.max_history_size {
123 self.step_timings.pop_front();
124 }
125
126 self.metrics.timing_metrics.record_step_time(duration);
128
129 let update_norm = self.compute_array_norm(update);
131 self.metrics
132 .optimization_metrics
133 .record_update_norm(update_norm);
134
135 self.trends.update_step_timing(duration);
137
138 self.check_performance_alerts(duration, update_norm);
140 }
141
142 pub fn record_training_epoch(&mut self, metrics: TrainingMetrics) {
144 self.training_history.push_back(metrics.clone());
145 if self.training_history.len() > self.config.max_history_size {
146 self.training_history.pop_front();
147 }
148
149 self.metrics.training_metrics.record_epoch(
151 metrics.loss,
152 metrics.training_time,
153 metrics.convergence_rate,
154 );
155
156 self.trends.update_training_loss(metrics.loss);
158 self.trends
159 .update_convergence_rate(metrics.convergence_rate);
160
161 self.check_convergence_alerts(&metrics);
163 }
164
165 pub fn record_meta_step(&mut self, result: MetaLearningResult<T>) {
167 self.meta_results_history.push_back(result.clone());
168 if self.meta_results_history.len() > 100 {
169 self.meta_results_history.pop_front();
170 }
171
172 self.metrics.training_metrics.record_meta_step(
174 result.meta_loss,
175 result.computation_time,
176 result.task_adaptations.len(),
177 );
178
179 self.trends.update_meta_loss(result.meta_loss);
181 self.trends
182 .update_adaptation_efficiency(result.task_adaptations.len() as f64);
183 }
184
185 pub fn record_inference(&mut self, input_size: usize, duration: Duration, output_quality: f64) {
187 self.metrics
188 .inference_metrics
189 .record_inference(input_size, duration, output_quality);
190
191 let throughput = input_size as f64 / duration.as_secs_f64();
193 self.metrics.inference_metrics.update_throughput(throughput);
194
195 self.trends.update_inference_latency(duration);
197 self.trends.update_inference_quality(output_quality);
198 }
199
200 pub fn record_memory_usage(&mut self, usage: MemoryUsage) {
202 let sample = MemorySample {
203 timestamp: std::time::SystemTime::now(),
204 usage,
205 };
206
207 self.memory_samples.push_back(sample.clone());
208 if self.memory_samples.len() > self.config.max_history_size {
209 self.memory_samples.pop_front();
210 }
211
212 self.metrics.memory_metrics.record_usage(usage);
214
215 self.metrics
220 .resource_metrics
221 .record_memory_usage(usage.total_memory as f64);
222
223 self.trends.update_memory_usage(usage.total_memory);
225
226 self.check_memory_alerts(&usage);
228 }
229
230 pub fn record_loss(&mut self, loss: f64) {
232 self.loss_history.push_back(loss);
233 if self.loss_history.len() > self.config.max_history_size {
234 self.loss_history.pop_front();
235 }
236
237 self.metrics.quality_metrics.record_loss(loss);
239
240 self.trends.update_loss(loss);
242 }
243
244 pub fn profile_operation<F, R>(&mut self, operation_name: &str, operation: F) -> Result<R>
246 where
247 F: FnOnce() -> Result<R>,
248 {
249 let start_time = Instant::now();
250 let result = operation();
251 let duration = start_time.elapsed();
252
253 self.profiling_data
255 .record_operation(operation_name.to_string(), duration);
256
257 self.metrics
259 .timing_metrics
260 .record_operation_time(operation_name, duration);
261
262 result
263 }
264
265 pub fn generate_report(&self) -> PerformanceReport {
267 let session_duration = self.session_start.elapsed();
268
269 PerformanceReport {
270 session_duration,
271 total_optimization_steps: self.step_timings.len(),
272 total_training_epochs: self.training_history.len(),
273 total_meta_steps: self.meta_results_history.len(),
274
275 average_step_time: self.calculate_average_step_time(),
277 average_loss: self.calculate_average_loss(),
278 current_convergence_rate: self.calculate_current_convergence_rate(),
279
280 peak_memory_usage: self.calculate_peak_memory_usage(),
282 average_memory_usage: self.calculate_average_memory_usage(),
283
284 best_loss: self.calculate_best_loss(),
286 loss_improvement: self.calculate_loss_improvement(),
287
288 loss_trend: self.trends.get_loss_trend(),
290 convergence_trend: self.trends.get_convergence_trend(),
291 memory_trend: self.trends.get_memory_trend(),
292
293 performance_alerts: self.get_recent_alerts(),
295
296 cpu_utilization: self.metrics.resource_metrics.average_cpu_utilization(),
299 memory_utilization: self.metrics.resource_metrics.average_memory_utilization(),
300
301 quality_score: self.calculate_overall_quality_score(),
303
304 recommendations: self.generate_recommendations(),
306 }
307 }
308
309 pub fn get_loss_history(&self) -> &VecDeque<f64> {
311 &self.loss_history
312 }
313
314 pub fn get_metrics(&self) -> &PerformanceMetrics {
316 &self.metrics
317 }
318
319 pub fn get_trends(&self) -> &PerformanceTrends {
321 &self.trends
322 }
323
324 pub fn set_baselines(&mut self, baselines: PerformanceBaselines) {
326 self.baselines = baselines;
327 }
328
329 pub fn reset(&mut self) {
331 self.metrics = PerformanceMetrics::new();
332 self.loss_history.clear();
333 self.step_timings.clear();
334 self.meta_results_history.clear();
335 self.training_history.clear();
336 self.memory_samples.clear();
337 self.trends.reset();
338 self.profiling_data.reset();
339 self.session_start = Instant::now();
340 }
341
342 fn compute_array_norm(&self, array: &Array1<T>) -> f64 {
344 let sum_squares: T = array
345 .iter()
346 .map(|&x| x * x)
347 .fold(T::zero(), |acc, x| acc + x);
348 sum_squares.sqrt().to_f64().unwrap_or(0.0)
349 }
350
351 fn calculate_average_step_time(&self) -> Duration {
352 if self.step_timings.is_empty() {
353 Duration::new(0, 0)
354 } else {
355 let total: Duration = self.step_timings.iter().sum();
356 total / self.step_timings.len() as u32
357 }
358 }
359
360 fn calculate_average_loss(&self) -> f64 {
361 if self.loss_history.is_empty() {
362 0.0
363 } else {
364 self.loss_history.iter().sum::<f64>() / self.loss_history.len() as f64
365 }
366 }
367
368 fn calculate_current_convergence_rate(&self) -> f64 {
369 if let Some(recent_training) = self.training_history.back() {
370 recent_training.convergence_rate
371 } else {
372 0.0
373 }
374 }
375
376 fn calculate_peak_memory_usage(&self) -> usize {
377 self.memory_samples
378 .iter()
379 .map(|sample| sample.usage.total_memory)
380 .max()
381 .unwrap_or(0)
382 }
383
384 fn calculate_average_memory_usage(&self) -> f64 {
385 if self.memory_samples.is_empty() {
386 0.0
387 } else {
388 let total: usize = self
389 .memory_samples
390 .iter()
391 .map(|s| s.usage.total_memory)
392 .sum();
393 total as f64 / self.memory_samples.len() as f64
394 }
395 }
396
397 fn calculate_best_loss(&self) -> f64 {
398 self.loss_history
399 .iter()
400 .fold(f64::INFINITY, |min, &loss| min.min(loss))
401 }
402
403 fn calculate_loss_improvement(&self) -> f64 {
404 if self.loss_history.len() < 2 {
405 return 0.0;
406 }
407
408 let (Some(&initial_loss), Some(&final_loss)) =
411 (self.loss_history.front(), self.loss_history.back())
412 else {
413 return 0.0;
414 };
415
416 if initial_loss > 0.0 {
417 (initial_loss - final_loss) / initial_loss
418 } else {
419 0.0
420 }
421 }
422
423 fn calculate_overall_quality_score(&self) -> f64 {
424 let loss_score = 1.0 - (self.calculate_average_loss() / 10.0).min(1.0);
426 let convergence_score = self.calculate_current_convergence_rate();
427 let stability_score = 1.0 - self.trends.get_loss_volatility().min(1.0);
428
429 (loss_score * 0.4 + convergence_score * 0.3 + stability_score * 0.3).clamp(0.0, 1.0)
430 }
431
432 fn generate_recommendations(&self) -> Vec<String> {
433 let mut recommendations = Vec::new();
434
435 if self.trends.get_loss_trend() > 0.0 {
437 recommendations
438 .push("Loss is increasing. Consider reducing learning rate.".to_string());
439 }
440
441 if self.calculate_peak_memory_usage() > 1024 * 1024 * 1024 {
443 recommendations.push(
445 "High memory usage detected. Consider enabling memory compression.".to_string(),
446 );
447 }
448
449 if self.calculate_average_step_time() > Duration::from_millis(100) {
451 recommendations.push(
452 "Slow optimization steps. Consider reducing model size or batch size.".to_string(),
453 );
454 }
455
456 if self.calculate_current_convergence_rate() < 0.1 {
458 recommendations.push(
459 "Low convergence rate. Consider adjusting meta-learning parameters.".to_string(),
460 );
461 }
462
463 recommendations
464 }
465
466 fn check_performance_alerts(&mut self, duration: Duration, update_norm: f64) {
467 if duration > self.alert_thresholds.max_step_time {
469 self.record_alert(AlertType::SlowStep, format!("Step took {:?}", duration));
470 }
471
472 if update_norm > self.alert_thresholds.max_gradient_norm {
474 self.record_alert(
475 AlertType::GradientExplosion,
476 format!("Update norm: {:.6}", update_norm),
477 );
478 }
479
480 if update_norm < self.alert_thresholds.min_gradient_norm {
482 self.record_alert(
483 AlertType::GradientVanishing,
484 format!("Update norm: {:.6}", update_norm),
485 );
486 }
487 }
488
489 fn check_convergence_alerts(&mut self, metrics: &TrainingMetrics) {
490 if metrics.convergence_rate < self.alert_thresholds.min_convergence_rate {
492 self.record_alert(
493 AlertType::TrainingStagnation,
494 format!("Convergence rate: {:.6}", metrics.convergence_rate),
495 );
496 }
497
498 if let Some(previous) = self.training_history.iter().rev().nth(1) {
500 if metrics.loss > previous.loss * 1.1 {
501 self.record_alert(
503 AlertType::LossIncrease,
504 format!(
505 "Loss increased from {:.6} to {:.6}",
506 previous.loss, metrics.loss
507 ),
508 );
509 }
510 }
511 }
512
513 fn check_memory_alerts(&mut self, usage: &MemoryUsage) {
514 if usage.total_memory > self.alert_thresholds.max_memory_usage {
516 self.record_alert(
517 AlertType::HighMemoryUsage,
518 format!("Memory usage: {} bytes", usage.total_memory),
519 );
520 }
521
522 if let Some(previous) = self.memory_samples.back() {
524 let memory_increase = usage
525 .total_memory
526 .saturating_sub(previous.usage.total_memory);
527 if memory_increase > 1024 * 1024 * 100 {
528 self.record_alert(
530 AlertType::PossibleMemoryLeak,
531 format!("Memory increased by {} bytes", memory_increase),
532 );
533 }
534 }
535 }
536
537 fn record_alert(&mut self, alert_type: AlertType, message: String) {
538 let alert = PerformanceAlert {
539 alert_type,
540 message,
541 timestamp: std::time::SystemTime::now(),
542 };
543
544 self.metrics.quality_metrics.record_alert(alert);
545 }
546
547 fn get_recent_alerts(&self) -> Vec<PerformanceAlert> {
548 self.metrics.quality_metrics.get_recent_alerts(10)
549 }
550}
551
552#[derive(Debug, Clone, Serialize, Deserialize)]
554pub struct TrainingMetricsCollection {
555 pub total_epochs: usize,
556 pub total_training_time: Duration,
557 pub average_loss: f64,
558 pub best_loss: f64,
559 pub convergence_rate: f64,
560 pub meta_steps: usize,
561 pub task_adaptations: usize,
562}
563
564#[derive(Debug, Clone, Serialize, Deserialize)]
565pub struct InferenceMetricsCollection {
566 pub total_inferences: usize,
567 pub average_latency: Duration,
568 pub peak_throughput: f64,
569 pub average_quality: f64,
570 pub cache_hit_rate: f64,
571}
572
573#[derive(Debug, Clone, Serialize, Deserialize)]
574pub struct MemoryMetricsCollection {
575 pub peak_usage: usize,
576 pub average_usage: f64,
577 pub total_allocations: usize,
578 pub compression_ratio: f64,
579 pub fragmentation_rate: f64,
580}
581
582#[derive(Debug, Clone, Serialize, Deserialize)]
583pub struct TimingMetricsCollection {
584 pub average_step_time: Duration,
585 pub total_computation_time: Duration,
586 pub operation_timings: HashMap<String, Duration>,
587 pub profiling_overhead: Duration,
588}
589
590#[derive(Debug, Clone, Serialize, Deserialize)]
591pub struct QualityMetricsCollection {
592 pub loss_statistics: LossStatistics,
593 pub convergence_statistics: ConvergenceStatistics,
594 pub stability_metrics: StabilityMetrics,
595 pub performance_alerts: VecDeque<PerformanceAlert>,
596}
597
598#[derive(Debug, Clone, Serialize, Deserialize)]
599pub struct ResourceMetricsCollection {
600 pub cpu_usage_history: VecDeque<f64>,
601 pub memory_usage_history: VecDeque<f64>,
602 pub disk_io_metrics: DiskIOMetrics,
603 pub network_metrics: NetworkMetrics,
604}
605
606#[derive(Debug, Clone, Serialize, Deserialize)]
607pub struct OptimizationMetricsCollection {
608 pub update_norm_history: VecDeque<f64>,
609 pub parameter_change_rate: f64,
610 pub optimization_efficiency: f64,
611 pub adaptive_learning_metrics: AdaptiveLearningMetrics,
612}
613
614#[derive(Debug, Clone)]
616pub struct MemorySample {
617 pub timestamp: std::time::SystemTime,
618 pub usage: MemoryUsage,
619}
620
621#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
622pub struct MemoryUsage {
623 pub total_memory: usize,
624 pub model_memory: usize,
625 pub cache_memory: usize,
626 pub temporary_memory: usize,
627}
628
629#[derive(Debug, Clone)]
630pub struct PerformanceBaselines {
631 pub baseline_loss: f64,
632 pub baseline_step_time: Duration,
633 pub baseline_memory_usage: usize,
634 pub baseline_convergence_rate: f64,
635}
636
637impl Default for PerformanceBaselines {
638 fn default() -> Self {
639 Self::new()
640 }
641}
642
643impl PerformanceBaselines {
644 pub fn new() -> Self {
645 Self {
646 baseline_loss: f64::INFINITY,
647 baseline_step_time: Duration::new(0, 0),
648 baseline_memory_usage: 0,
649 baseline_convergence_rate: 0.0,
650 }
651 }
652}
653
654#[derive(Debug, Clone)]
655pub struct AlertThresholds {
656 pub max_step_time: Duration,
657 pub min_convergence_rate: f64,
658 pub max_memory_usage: usize,
659 pub max_gradient_norm: f64,
660 pub min_gradient_norm: f64,
661}
662
663impl Default for AlertThresholds {
664 fn default() -> Self {
665 Self {
666 max_step_time: Duration::from_secs(1),
667 min_convergence_rate: 0.01,
668 max_memory_usage: 2 * 1024 * 1024 * 1024, max_gradient_norm: 10.0,
670 min_gradient_norm: 1e-8,
671 }
672 }
673}
674
675#[derive(Debug, Clone)]
676pub struct PerformanceTrends {
677 loss_trend: TrendAnalyzer,
678 convergence_trend: TrendAnalyzer,
679 memory_trend: TrendAnalyzer,
680 timing_trend: TrendAnalyzer,
681}
682
683impl Default for PerformanceTrends {
684 fn default() -> Self {
685 Self::new()
686 }
687}
688
689impl PerformanceTrends {
690 pub fn new() -> Self {
691 Self {
692 loss_trend: TrendAnalyzer::new(100),
693 convergence_trend: TrendAnalyzer::new(50),
694 memory_trend: TrendAnalyzer::new(100),
695 timing_trend: TrendAnalyzer::new(100),
696 }
697 }
698
699 pub fn update_loss(&mut self, loss: f64) {
700 self.loss_trend.add_sample(loss);
701 }
702
703 pub fn update_training_loss(&mut self, loss: f64) {
704 self.loss_trend.add_sample(loss);
705 }
706
707 pub fn update_meta_loss(&mut self, loss: f64) {
708 self.loss_trend.add_sample(loss);
709 }
710
711 pub fn update_convergence_rate(&mut self, rate: f64) {
712 self.convergence_trend.add_sample(rate);
713 }
714
715 pub fn update_adaptation_efficiency(&mut self, efficiency: f64) {
716 self.convergence_trend.add_sample(efficiency);
717 }
718
719 pub fn update_memory_usage(&mut self, usage: usize) {
720 self.memory_trend.add_sample(usage as f64);
721 }
722
723 pub fn update_step_timing(&mut self, duration: Duration) {
724 self.timing_trend.add_sample(duration.as_secs_f64());
725 }
726
727 pub fn update_inference_latency(&mut self, duration: Duration) {
728 self.timing_trend.add_sample(duration.as_secs_f64());
729 }
730
731 pub fn update_inference_quality(&mut self, _quality: f64) {
732 }
734
735 pub fn get_loss_trend(&self) -> f64 {
736 self.loss_trend.get_trend()
737 }
738
739 pub fn get_convergence_trend(&self) -> f64 {
740 self.convergence_trend.get_trend()
741 }
742
743 pub fn get_memory_trend(&self) -> f64 {
744 self.memory_trend.get_trend()
745 }
746
747 pub fn get_loss_volatility(&self) -> f64 {
748 self.loss_trend.get_volatility()
749 }
750
751 pub fn reset(&mut self) {
752 self.loss_trend.reset();
753 self.convergence_trend.reset();
754 self.memory_trend.reset();
755 self.timing_trend.reset();
756 }
757}
758
759#[derive(Debug, Clone)]
760pub struct TrendAnalyzer {
761 samples: VecDeque<f64>,
762 max_samples: usize,
763}
764
765impl TrendAnalyzer {
766 pub fn new(max_samples: usize) -> Self {
767 Self {
768 samples: VecDeque::new(),
769 max_samples,
770 }
771 }
772
773 pub fn add_sample(&mut self, value: f64) {
774 self.samples.push_back(value);
775 if self.samples.len() > self.max_samples {
776 self.samples.pop_front();
777 }
778 }
779
780 pub fn get_trend(&self) -> f64 {
781 if self.samples.len() < 2 {
782 return 0.0;
783 }
784
785 let n = self.samples.len() as f64;
787 let x_sum = (0..self.samples.len()).map(|i| i as f64).sum::<f64>();
788 let y_sum = self.samples.iter().sum::<f64>();
789 let xy_sum = self
790 .samples
791 .iter()
792 .enumerate()
793 .map(|(i, &y)| i as f64 * y)
794 .sum::<f64>();
795 let x_sq_sum = (0..self.samples.len())
796 .map(|i| (i as f64).powi(2))
797 .sum::<f64>();
798
799 let denominator = n * x_sq_sum - x_sum * x_sum;
800 if denominator.abs() < 1e-10 {
801 0.0
802 } else {
803 (n * xy_sum - x_sum * y_sum) / denominator
804 }
805 }
806
807 pub fn get_volatility(&self) -> f64 {
808 if self.samples.len() < 2 {
809 return 0.0;
810 }
811
812 let mean = self.samples.iter().sum::<f64>() / self.samples.len() as f64;
813 let variance = self
814 .samples
815 .iter()
816 .map(|&x| (x - mean).powi(2))
817 .sum::<f64>()
818 / self.samples.len() as f64;
819
820 variance.sqrt()
821 }
822
823 pub fn reset(&mut self) {
824 self.samples.clear();
825 }
826}
827
828#[derive(Debug, Clone)]
829pub struct ProfilingData {
830 operation_timings: HashMap<String, VecDeque<Duration>>,
831 total_operations: usize,
832}
833
834impl Default for ProfilingData {
835 fn default() -> Self {
836 Self::new()
837 }
838}
839
840impl ProfilingData {
841 pub fn new() -> Self {
842 Self {
843 operation_timings: HashMap::new(),
844 total_operations: 0,
845 }
846 }
847
848 pub fn record_operation(&mut self, operation: String, duration: Duration) {
849 let timings = self.operation_timings.entry(operation).or_default();
850 timings.push_back(duration);
851 if timings.len() > 1000 {
852 timings.pop_front();
853 }
854 self.total_operations += 1;
855 }
856
857 pub fn get_average_time(&self, operation: &str) -> Option<Duration> {
858 self.operation_timings.get(operation).map(|timings| {
859 let total: Duration = timings.iter().sum();
860 total / timings.len() as u32
861 })
862 }
863
864 pub fn reset(&mut self) {
865 self.operation_timings.clear();
866 self.total_operations = 0;
867 }
868}
869
870#[derive(Debug, Clone, Serialize, Deserialize)]
872pub struct PerformanceAlert {
873 pub alert_type: AlertType,
874 pub message: String,
875 pub timestamp: std::time::SystemTime,
876}
877
878#[derive(Debug, Clone, Serialize, Deserialize)]
879pub enum AlertType {
880 SlowStep,
881 GradientExplosion,
882 GradientVanishing,
883 TrainingStagnation,
884 LossIncrease,
885 HighMemoryUsage,
886 PossibleMemoryLeak,
887 ConvergenceFailure,
888}
889
890#[derive(Debug, Clone)]
892pub struct PerformanceReport {
893 pub session_duration: Duration,
894 pub total_optimization_steps: usize,
895 pub total_training_epochs: usize,
896 pub total_meta_steps: usize,
897 pub average_step_time: Duration,
898 pub average_loss: f64,
899 pub current_convergence_rate: f64,
900 pub peak_memory_usage: usize,
901 pub average_memory_usage: f64,
902 pub best_loss: f64,
903 pub loss_improvement: f64,
904 pub loss_trend: f64,
905 pub convergence_trend: f64,
906 pub memory_trend: f64,
907 pub performance_alerts: Vec<PerformanceAlert>,
908 pub cpu_utilization: Option<f64>,
913 pub memory_utilization: Option<f64>,
916 pub quality_score: f64,
917 pub recommendations: Vec<String>,
918}
919
920#[derive(Debug, Clone, Serialize, Deserialize)]
922pub struct LossStatistics {
923 pub mean: f64,
924 pub std_dev: f64,
925 pub min: f64,
926 pub max: f64,
927}
928
929#[derive(Debug, Clone, Serialize, Deserialize)]
930pub struct ConvergenceStatistics {
931 pub average_rate: f64,
932 pub best_rate: f64,
933 pub convergence_episodes: usize,
934}
935
936#[derive(Debug, Clone, Serialize, Deserialize)]
937pub struct StabilityMetrics {
938 pub loss_volatility: f64,
939 pub gradient_stability: f64,
940 pub convergence_stability: f64,
941}
942
943#[derive(Debug, Clone, Serialize, Deserialize)]
944pub struct DiskIOMetrics {
945 pub total_reads: usize,
946 pub total_writes: usize,
947 pub total_bytes_read: usize,
948 pub total_bytes_written: usize,
949}
950
951#[derive(Debug, Clone, Serialize, Deserialize)]
952pub struct NetworkMetrics {
953 pub total_requests: usize,
954 pub total_bytes_sent: usize,
955 pub total_bytes_received: usize,
956 pub average_latency: Duration,
957}
958
959#[derive(Debug, Clone, Serialize, Deserialize)]
960pub struct AdaptiveLearningMetrics {
961 pub learning_rate_adjustments: usize,
962 pub batch_size_adjustments: usize,
963 pub architecture_modifications: usize,
964}
965
966impl Default for PerformanceMetrics {
968 fn default() -> Self {
969 Self::new()
970 }
971}
972
973impl PerformanceMetrics {
974 pub fn new() -> Self {
975 Self {
976 training_metrics: TrainingMetricsCollection::new(),
977 inference_metrics: InferenceMetricsCollection::new(),
978 memory_metrics: MemoryMetricsCollection::new(),
979 timing_metrics: TimingMetricsCollection::new(),
980 quality_metrics: QualityMetricsCollection::new(),
981 resource_metrics: ResourceMetricsCollection::new(),
982 optimization_metrics: OptimizationMetricsCollection::new(),
983 }
984 }
985}
986
987impl Default for TrainingMetricsCollection {
988 fn default() -> Self {
989 Self::new()
990 }
991}
992
993impl TrainingMetricsCollection {
994 pub fn new() -> Self {
995 Self {
996 total_epochs: 0,
997 total_training_time: Duration::new(0, 0),
998 average_loss: 0.0,
999 best_loss: f64::INFINITY,
1000 convergence_rate: 0.0,
1001 meta_steps: 0,
1002 task_adaptations: 0,
1003 }
1004 }
1005
1006 pub fn record_epoch(&mut self, loss: f64, duration: Duration, convergence: f64) {
1007 self.total_epochs += 1;
1008 self.total_training_time += duration;
1009 self.average_loss =
1010 (self.average_loss * (self.total_epochs - 1) as f64 + loss) / self.total_epochs as f64;
1011 self.best_loss = self.best_loss.min(loss);
1012 self.convergence_rate = convergence;
1013 }
1014
1015 pub fn record_meta_step(&mut self, loss: f64, _duration: Duration, adaptations: usize) {
1016 self.meta_steps += 1;
1017 self.task_adaptations += adaptations;
1018 self.best_loss = self.best_loss.min(loss);
1019 }
1020}
1021
1022impl Default for InferenceMetricsCollection {
1023 fn default() -> Self {
1024 Self::new()
1025 }
1026}
1027
1028impl InferenceMetricsCollection {
1029 pub fn new() -> Self {
1030 Self {
1031 total_inferences: 0,
1032 average_latency: Duration::new(0, 0),
1033 peak_throughput: 0.0,
1034 average_quality: 0.0,
1035 cache_hit_rate: 0.0,
1036 }
1037 }
1038
1039 pub fn record_inference(&mut self, _input_size: usize, duration: Duration, quality: f64) {
1040 self.total_inferences += 1;
1041 self.average_latency = (self.average_latency * (self.total_inferences - 1) as u32
1042 + duration)
1043 / self.total_inferences as u32;
1044 self.average_quality = (self.average_quality * (self.total_inferences - 1) as f64
1045 + quality)
1046 / self.total_inferences as f64;
1047 }
1048
1049 pub fn update_throughput(&mut self, throughput: f64) {
1050 self.peak_throughput = self.peak_throughput.max(throughput);
1051 }
1052}
1053
1054impl Default for MemoryMetricsCollection {
1055 fn default() -> Self {
1056 Self::new()
1057 }
1058}
1059
1060impl MemoryMetricsCollection {
1061 pub fn new() -> Self {
1062 Self {
1063 peak_usage: 0,
1064 average_usage: 0.0,
1065 total_allocations: 0,
1066 compression_ratio: 1.0,
1067 fragmentation_rate: 0.0,
1068 }
1069 }
1070
1071 pub fn record_usage(&mut self, usage: MemoryUsage) {
1072 self.peak_usage = self.peak_usage.max(usage.total_memory);
1073 self.total_allocations += 1;
1074 self.average_usage = (self.average_usage * (self.total_allocations - 1) as f64
1075 + usage.total_memory as f64)
1076 / self.total_allocations as f64;
1077 }
1078}
1079
1080impl Default for TimingMetricsCollection {
1081 fn default() -> Self {
1082 Self::new()
1083 }
1084}
1085
1086impl TimingMetricsCollection {
1087 pub fn new() -> Self {
1088 Self {
1089 average_step_time: Duration::new(0, 0),
1090 total_computation_time: Duration::new(0, 0),
1091 operation_timings: HashMap::new(),
1092 profiling_overhead: Duration::new(0, 0),
1093 }
1094 }
1095
1096 pub fn record_step_time(&mut self, duration: Duration) {
1097 self.total_computation_time += duration;
1098 }
1100
1101 pub fn record_operation_time(&mut self, operation: &str, duration: Duration) {
1102 self.operation_timings
1103 .insert(operation.to_string(), duration);
1104 }
1105}
1106
1107impl Default for QualityMetricsCollection {
1108 fn default() -> Self {
1109 Self::new()
1110 }
1111}
1112
1113impl QualityMetricsCollection {
1114 pub fn new() -> Self {
1115 Self {
1116 loss_statistics: LossStatistics {
1117 mean: 0.0,
1118 std_dev: 0.0,
1119 min: f64::INFINITY,
1120 max: f64::NEG_INFINITY,
1121 },
1122 convergence_statistics: ConvergenceStatistics {
1123 average_rate: 0.0,
1124 best_rate: 0.0,
1125 convergence_episodes: 0,
1126 },
1127 stability_metrics: StabilityMetrics {
1128 loss_volatility: 0.0,
1129 gradient_stability: 0.0,
1130 convergence_stability: 0.0,
1131 },
1132 performance_alerts: VecDeque::new(),
1133 }
1134 }
1135
1136 pub fn record_loss(&mut self, loss: f64) {
1137 self.loss_statistics.min = self.loss_statistics.min.min(loss);
1138 self.loss_statistics.max = self.loss_statistics.max.max(loss);
1139 }
1140
1141 pub fn record_alert(&mut self, alert: PerformanceAlert) {
1142 self.performance_alerts.push_back(alert);
1143 if self.performance_alerts.len() > 100 {
1144 self.performance_alerts.pop_front();
1145 }
1146 }
1147
1148 pub fn get_recent_alerts(&self, count: usize) -> Vec<PerformanceAlert> {
1149 self.performance_alerts
1150 .iter()
1151 .rev()
1152 .take(count)
1153 .cloned()
1154 .collect()
1155 }
1156}
1157
1158impl Default for ResourceMetricsCollection {
1159 fn default() -> Self {
1160 Self::new()
1161 }
1162}
1163
1164impl ResourceMetricsCollection {
1165 pub fn new() -> Self {
1166 Self {
1167 cpu_usage_history: VecDeque::new(),
1168 memory_usage_history: VecDeque::new(),
1169 disk_io_metrics: DiskIOMetrics {
1170 total_reads: 0,
1171 total_writes: 0,
1172 total_bytes_read: 0,
1173 total_bytes_written: 0,
1174 },
1175 network_metrics: NetworkMetrics {
1176 total_requests: 0,
1177 total_bytes_sent: 0,
1178 total_bytes_received: 0,
1179 average_latency: Duration::new(0, 0),
1180 },
1181 }
1182 }
1183
1184 pub fn record_cpu_utilization(&mut self, fraction: f64) {
1195 if !fraction.is_finite() || !(0.0..=1.0).contains(&fraction) {
1196 return;
1197 }
1198 self.cpu_usage_history.push_back(fraction);
1199 while self.cpu_usage_history.len() > 1000 {
1200 self.cpu_usage_history.pop_front();
1201 }
1202 }
1203
1204 pub fn average_cpu_utilization(&self) -> Option<f64> {
1210 if self.cpu_usage_history.is_empty() {
1211 None
1212 } else {
1213 Some(self.cpu_usage_history.iter().sum::<f64>() / self.cpu_usage_history.len() as f64)
1214 }
1215 }
1216
1217 pub fn cpu_sample_count(&self) -> usize {
1219 self.cpu_usage_history.len()
1220 }
1221
1222 pub fn record_memory_usage(&mut self, bytes: f64) {
1225 if !bytes.is_finite() || bytes < 0.0 {
1226 return;
1227 }
1228 self.memory_usage_history.push_back(bytes);
1229 while self.memory_usage_history.len() > 1000 {
1230 self.memory_usage_history.pop_front();
1231 }
1232 }
1233
1234 pub fn memory_sample_count(&self) -> usize {
1236 self.memory_usage_history.len()
1237 }
1238
1239 pub fn average_memory_utilization(&self) -> Option<f64> {
1242 if self.memory_usage_history.is_empty() {
1243 None
1244 } else {
1245 Some(
1246 self.memory_usage_history.iter().sum::<f64>()
1247 / self.memory_usage_history.len() as f64,
1248 )
1249 }
1250 }
1251}
1252
1253impl Default for OptimizationMetricsCollection {
1254 fn default() -> Self {
1255 Self::new()
1256 }
1257}
1258
1259impl OptimizationMetricsCollection {
1260 pub fn new() -> Self {
1261 Self {
1262 update_norm_history: VecDeque::new(),
1263 parameter_change_rate: 0.0,
1264 optimization_efficiency: 0.0,
1265 adaptive_learning_metrics: AdaptiveLearningMetrics {
1266 learning_rate_adjustments: 0,
1267 batch_size_adjustments: 0,
1268 architecture_modifications: 0,
1269 },
1270 }
1271 }
1272
1273 pub fn record_update_norm(&mut self, norm: f64) {
1274 self.update_norm_history.push_back(norm);
1275 if self.update_norm_history.len() > 1000 {
1276 self.update_norm_history.pop_front();
1277 }
1278 }
1279}
1280
1281#[cfg(test)]
1282mod tests {
1283 use super::*;
1284
1285 #[test]
1286 fn test_performance_tracker_creation() {
1287 let tracker = TransformerPerformanceTracker::<f32>::new();
1288 assert_eq!(tracker.loss_history.len(), 0);
1289 assert_eq!(tracker.step_timings.len(), 0);
1290 }
1291
1292 #[test]
1293 fn test_record_loss() {
1294 let mut tracker = TransformerPerformanceTracker::<f32>::new();
1295 tracker.record_loss(1.5);
1296 tracker.record_loss(1.2);
1297 tracker.record_loss(0.9);
1298
1299 assert_eq!(tracker.loss_history.len(), 3);
1300 assert_eq!(tracker.calculate_best_loss(), 0.9);
1301 }
1302
1303 #[test]
1304 fn test_trend_analyzer() {
1305 let mut analyzer = TrendAnalyzer::new(10);
1306
1307 for i in 0..5 {
1308 analyzer.add_sample(i as f64);
1309 }
1310
1311 let trend = analyzer.get_trend();
1312 assert!(trend > 0.0); }
1314
1315 #[test]
1316 fn test_performance_report_generation() {
1317 let mut tracker = TransformerPerformanceTracker::<f32>::new();
1318 tracker.record_loss(2.0);
1319 tracker.record_loss(1.5);
1320 tracker.record_loss(1.0);
1321
1322 let report = tracker.generate_report();
1323 assert!(report.loss_improvement > 0.0);
1324 assert_eq!(report.best_loss, 1.0);
1325 }
1326
1327 #[test]
1328 fn test_profiling() {
1329 let mut tracker = TransformerPerformanceTracker::<f32>::new();
1330
1331 let result = tracker.profile_operation("test_op", || {
1332 std::thread::sleep(Duration::from_millis(10));
1333 Ok(42)
1334 });
1335
1336 assert!(result.is_ok());
1337 assert_eq!(result.expect("profile_operation should succeed"), 42);
1338 }
1339
1340 #[test]
1344 fn cpu_utilization_is_none_until_it_is_actually_sampled() {
1345 let mut metrics = ResourceMetricsCollection::new();
1346 assert_eq!(metrics.cpu_sample_count(), 0);
1347 assert_eq!(
1348 metrics.average_cpu_utilization(),
1349 None,
1350 "an unsampled CPU metric must not claim a value"
1351 );
1352
1353 metrics.record_cpu_utilization(0.25);
1354 metrics.record_cpu_utilization(0.75);
1355 assert_eq!(metrics.cpu_sample_count(), 2);
1356 let mean = metrics
1357 .average_cpu_utilization()
1358 .expect("two samples were recorded");
1359 assert!((mean - 0.5).abs() < 1e-12, "mean {mean}");
1360
1361 metrics.record_cpu_utilization(-0.1);
1363 metrics.record_cpu_utilization(1.5);
1364 metrics.record_cpu_utilization(f64::NAN);
1365 assert_eq!(
1366 metrics.cpu_sample_count(),
1367 2,
1368 "invalid samples must not enter the history"
1369 );
1370 }
1371
1372 #[test]
1374 fn a_report_reports_unmeasured_resources_as_none() {
1375 let mut tracker = TransformerPerformanceTracker::<f32>::new();
1376 tracker.record_loss(1.0);
1377 let report = tracker.generate_report();
1378 assert_eq!(
1379 report.cpu_utilization, None,
1380 "nothing sampled the CPU, so the report must say so"
1381 );
1382 assert_eq!(report.memory_utilization, None);
1383 }
1384
1385 #[test]
1388 fn recorded_memory_usage_reaches_the_report() {
1389 let mut tracker = TransformerPerformanceTracker::<f32>::new();
1390 tracker.record_loss(1.0);
1391 let sample = |total: usize| MemoryUsage {
1392 total_memory: total,
1393 model_memory: total / 2,
1394 cache_memory: total / 4,
1395 temporary_memory: total / 4,
1396 };
1397 tracker.record_memory_usage(sample(4096));
1398 tracker.record_memory_usage(sample(8192));
1399
1400 let report = tracker.generate_report();
1401 let mean = report
1402 .memory_utilization
1403 .expect("two memory samples were recorded");
1404 assert!((mean - 6144.0).abs() < 1e-9, "mean {mean}");
1405 assert_eq!(tracker.metrics.resource_metrics.memory_sample_count(), 2);
1406 }
1407}