1use anyhow::{anyhow, Result};
15use serde::{Deserialize, Serialize};
16use std::sync::Arc;
17use std::time::{Duration, Instant};
18use tokio::sync::RwLock;
19use tracing::info;
20
21use scirs2_core::metrics::{Counter, Gauge, MetricsRegistry};
23
24use crate::ast::Document;
25use crate::distributed_cache::{CacheConfig, GraphQLQueryCache};
26use crate::federation::{EnhancedFederationConfig, EnhancedFederationManager};
27use crate::hybrid_optimizer::{HybridOptimizerConfig, HybridQueryOptimizer};
28use crate::ml_optimizer::{MLOptimizerConfig, MLQueryOptimizer};
29use crate::performance::PerformanceTracker;
30use crate::quantum_optimizer::{QuantumOptimizerConfig, QuantumQueryOptimizer};
31use crate::system_monitor;
32
33#[derive(Debug, Clone)]
35pub struct BenchmarkConfig {
36 pub test_duration: Duration,
37 pub warmup_duration: Duration,
38 pub concurrent_users: usize,
39 pub queries_per_user: usize,
40 pub enable_detailed_metrics: bool,
41 pub test_scenarios: Vec<TestScenario>,
42 pub optimization_strategies: Vec<OptimizationStrategy>,
43 pub cache_configurations: Vec<CacheConfig>,
44 pub federation_configurations: Vec<EnhancedFederationConfig>,
45}
46
47impl Default for BenchmarkConfig {
48 fn default() -> Self {
49 Self {
50 test_duration: Duration::from_secs(60),
51 warmup_duration: Duration::from_secs(10),
52 concurrent_users: 10,
53 queries_per_user: 100,
54 enable_detailed_metrics: true,
55 test_scenarios: vec![
56 TestScenario::SimpleQuery,
57 TestScenario::ComplexQuery,
58 TestScenario::DeepNestedQuery,
59 TestScenario::FederatedQuery,
60 TestScenario::AggregationQuery,
61 ],
62 optimization_strategies: vec![
63 OptimizationStrategy::None,
64 OptimizationStrategy::ML,
65 OptimizationStrategy::Quantum,
66 OptimizationStrategy::Hybrid,
67 ],
68 cache_configurations: vec![CacheConfig::default()],
69 federation_configurations: vec![EnhancedFederationConfig::default()],
70 }
71 }
72}
73
74#[derive(Debug, Clone)]
76pub enum TestScenario {
77 SimpleQuery,
78 ComplexQuery,
79 DeepNestedQuery,
80 FederatedQuery,
81 AggregationQuery,
82 SubscriptionQuery,
83 BulkOperations,
84 StressTest,
85}
86
87#[derive(Debug, Clone)]
89pub enum OptimizationStrategy {
90 None,
91 ML,
92 Quantum,
93 Hybrid,
94 Federation,
95 Cache,
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct BenchmarkResult {
101 pub scenario: String,
102 pub strategy: String,
103 pub configuration: String,
104 pub total_requests: u64,
105 pub successful_requests: u64,
106 pub failed_requests: u64,
107 pub average_response_time: Duration,
108 pub min_response_time: Duration,
109 pub max_response_time: Duration,
110 pub p50_response_time: Duration,
111 pub p95_response_time: Duration,
112 pub p99_response_time: Duration,
113 pub requests_per_second: f64,
114 pub throughput_mbps: f64,
115 pub memory_usage_mb: f64,
116 pub cpu_usage_percent: f64,
117 pub cache_hit_rate: f64,
118 pub error_rate: f64,
119 pub detailed_metrics: Option<DetailedMetrics>,
120}
121
122#[derive(Debug, Clone, Serialize, Deserialize)]
124pub struct DetailedMetrics {
125 pub response_times: Vec<Duration>,
126 pub memory_samples: Vec<f64>,
127 pub cpu_samples: Vec<f64>,
128 pub network_bytes_sent: u64,
129 pub network_bytes_received: u64,
130 pub database_queries: u64,
131 pub cache_operations: CacheOperationMetrics,
132 pub optimization_metrics: OptimizationMetrics,
133}
134
135#[derive(Debug, Clone, Serialize, Deserialize)]
137pub struct CacheOperationMetrics {
138 pub hits: u64,
139 pub misses: u64,
140 pub sets: u64,
141 pub deletes: u64,
142 pub invalidations: u64,
143 pub average_lookup_time: Duration,
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize)]
148pub struct OptimizationMetrics {
149 pub optimization_attempts: u64,
150 pub successful_optimizations: u64,
151 pub average_optimization_time: Duration,
152 pub performance_improvement: f64,
153 pub strategy_selection_accuracy: f64,
154}
155
156#[derive(Debug, Clone, Serialize, Deserialize)]
158pub struct BenchmarkReport {
159 pub config: BenchmarkConfigSummary,
160 pub results: Vec<BenchmarkResult>,
161 pub summary: BenchmarkSummary,
162 pub recommendations: Vec<PerformanceRecommendation>,
163 pub generated_at: std::time::SystemTime,
164}
165
166#[derive(Debug, Clone, Serialize, Deserialize)]
168pub struct BenchmarkConfigSummary {
169 pub test_duration: Duration,
170 pub concurrent_users: usize,
171 pub total_queries: usize,
172 pub scenarios_tested: usize,
173 pub strategies_tested: usize,
174}
175
176#[derive(Debug, Clone, Serialize, Deserialize)]
178pub struct BenchmarkSummary {
179 pub best_strategy: String,
180 pub best_average_response_time: Duration,
181 pub best_throughput: f64,
182 pub best_cache_hit_rate: f64,
183 pub overall_performance_improvement: f64,
184 pub strategy_rankings: Vec<StrategyRanking>,
185}
186
187#[derive(Debug, Clone, Serialize, Deserialize)]
189pub struct StrategyRanking {
190 pub strategy: String,
191 pub score: f64,
192 pub response_time_rank: usize,
193 pub throughput_rank: usize,
194 pub reliability_rank: usize,
195}
196
197#[derive(Debug, Clone, Serialize, Deserialize)]
199pub struct PerformanceRecommendation {
200 pub category: String,
201 pub recommendation: String,
202 pub impact: String,
203 pub priority: String,
204}
205
206pub struct PerformanceBenchmarkSuite {
208 config: BenchmarkConfig,
209 performance_tracker: Arc<PerformanceTracker>,
210 ml_optimizer: Option<Arc<MLQueryOptimizer>>,
211 quantum_optimizer: Option<Arc<QuantumQueryOptimizer>>,
212 hybrid_optimizer: Option<Arc<HybridQueryOptimizer>>,
213 cache: Option<Arc<GraphQLQueryCache>>,
214 #[allow(dead_code)]
215 federation_manager: Option<Arc<EnhancedFederationManager>>,
216 results: Arc<RwLock<Vec<BenchmarkResult>>>,
217 metrics_registry: Arc<MetricsRegistry>,
219}
220
221impl PerformanceBenchmarkSuite {
222 pub fn new(config: BenchmarkConfig) -> Self {
224 let performance_tracker = Arc::new(PerformanceTracker::new());
225 let metrics_registry = Arc::new(MetricsRegistry::new());
226
227 let _ = metrics_registry.register(
229 "total_requests".to_string(),
230 Counter::new("Total benchmark requests processed".to_string()),
231 );
232 let _ = metrics_registry.register(
233 "successful_requests".to_string(),
234 Counter::new("Successful benchmark requests".to_string()),
235 );
236 let _ = metrics_registry.register(
237 "failed_requests".to_string(),
238 Counter::new("Failed benchmark requests".to_string()),
239 );
240 let _ = metrics_registry.register(
241 "active_benchmarks".to_string(),
242 Gauge::new("Currently active benchmark runs".to_string()),
243 );
244 let _ = metrics_registry.register(
245 "memory_usage_mb".to_string(),
246 Gauge::new("Memory usage during benchmarking (MB)".to_string()),
247 );
248 let _ = metrics_registry.register(
249 "cpu_usage_percent".to_string(),
250 Gauge::new("CPU usage during benchmarking (%)".to_string()),
251 );
252
253 Self {
254 config,
255 performance_tracker,
256 ml_optimizer: None,
257 quantum_optimizer: None,
258 hybrid_optimizer: None,
259 cache: None,
260 federation_manager: None,
261 results: Arc::new(RwLock::new(Vec::new())),
262 metrics_registry,
263 }
264 }
265
266 pub fn metrics(&self) -> &MetricsRegistry {
268 &self.metrics_registry
269 }
270
271 pub async fn initialize_optimizers(&mut self) -> Result<()> {
273 info!("Initializing optimization strategies for benchmarking");
274
275 let ml_config = MLOptimizerConfig::default();
277 self.ml_optimizer = Some(Arc::new(MLQueryOptimizer::new(
278 ml_config,
279 self.performance_tracker.clone(),
280 )));
281
282 let quantum_config = QuantumOptimizerConfig::default();
284 self.quantum_optimizer = Some(Arc::new(QuantumQueryOptimizer::new(quantum_config)));
285
286 let hybrid_config = HybridOptimizerConfig::default();
288 self.hybrid_optimizer = Some(Arc::new(HybridQueryOptimizer::new(
289 hybrid_config,
290 self.performance_tracker.clone(),
291 )));
292
293 if !self.config.cache_configurations.is_empty() {
295 let cache_config = self.config.cache_configurations[0].clone();
296 self.cache = Some(Arc::new(GraphQLQueryCache::new(cache_config).await?));
297 }
298
299 info!("Optimization strategies initialized");
300 Ok(())
301 }
302
303 pub async fn run_benchmarks(&mut self) -> Result<BenchmarkReport> {
305 info!("Starting comprehensive performance benchmark suite");
306
307 self.initialize_optimizers().await?;
308
309 let start_time = Instant::now();
310
311 info!("Running warmup period");
313 self.run_warmup().await?;
314
315 for scenario in &self.config.test_scenarios.clone() {
317 for strategy in &self.config.optimization_strategies.clone() {
318 let result = self.run_single_benchmark(scenario, strategy).await?;
319 self.results.write().await.push(result);
320 }
321 }
322
323 let report = self.generate_report().await?;
325
326 let total_time = start_time.elapsed();
327 info!("Benchmark suite completed in {:?}", total_time);
328
329 Ok(report)
330 }
331
332 async fn run_warmup(&self) -> Result<()> {
334 let warmup_queries = self.generate_test_queries(&TestScenario::SimpleQuery, 50);
335
336 let warmup_start = Instant::now();
337 while warmup_start.elapsed() < self.config.warmup_duration {
338 for query in &warmup_queries {
339 let _ = self
341 .execute_query_with_strategy(query, &OptimizationStrategy::None)
342 .await;
343
344 tokio::time::sleep(Duration::from_millis(10)).await;
346 }
347 }
348
349 Ok(())
350 }
351
352 async fn run_single_benchmark(
354 &self,
355 scenario: &TestScenario,
356 strategy: &OptimizationStrategy,
357 ) -> Result<BenchmarkResult> {
358 info!("Running benchmark: {:?} with {:?}", scenario, strategy);
359
360 let queries = self.generate_test_queries(scenario, self.config.queries_per_user);
361 let mut response_times = Vec::new();
362 let mut successful_requests = 0;
363 let mut failed_requests = 0;
364 let mut _total_bytes_sent = 0u64;
365 let mut _total_bytes_received = 0u64;
366
367 let test_start = Instant::now();
368
369 let mut tasks = Vec::new();
371 for _user_id in 0..self.config.concurrent_users {
372 let queries_clone = queries.clone();
373 let strategy_clone = strategy.clone();
374
375 let task = {
376 let suite = self;
377 async move {
378 let mut user_response_times = Vec::new();
379 let mut user_successful = 0;
380 let mut user_failed = 0;
381
382 for query in queries_clone {
383 let start = Instant::now();
384 match suite
385 .execute_query_with_strategy(&query, &strategy_clone)
386 .await
387 {
388 Ok(_) => {
389 user_successful += 1;
390 user_response_times.push(start.elapsed());
391 }
392 Err(_) => {
393 user_failed += 1;
394 }
395 }
396
397 tokio::time::sleep(Duration::from_millis(10)).await;
399 }
400
401 (user_response_times, user_successful, user_failed)
402 }
403 };
404
405 tasks.push(task);
406 }
407
408 let results = futures::future::join_all(tasks).await;
410
411 for (user_times, user_successful, user_failed) in results {
413 response_times.extend(user_times);
414 successful_requests += user_successful;
415 failed_requests += user_failed;
416 }
417
418 let total_time = test_start.elapsed();
419
420 let result = self
422 .calculate_benchmark_result(
423 scenario,
424 strategy,
425 &response_times,
426 successful_requests,
427 failed_requests,
428 total_time,
429 )
430 .await?;
431
432 Ok(result)
433 }
434
435 async fn execute_query_with_strategy(
437 &self,
438 query: &Document,
439 strategy: &OptimizationStrategy,
440 ) -> Result<serde_json::Value> {
441 match strategy {
442 OptimizationStrategy::None => {
443 Ok(serde_json::json!({"data": {"result": "basic"}}))
445 }
446 OptimizationStrategy::ML => {
447 if let Some(ml_optimizer) = &self.ml_optimizer {
448 let _prediction = ml_optimizer.predict_performance(query).await?;
449 Ok(serde_json::json!({"data": {"result": "ml_optimized"}}))
451 } else {
452 Err(anyhow!("ML optimizer not initialized"))
453 }
454 }
455 OptimizationStrategy::Quantum => {
456 if let Some(_quantum_optimizer) = &self.quantum_optimizer {
457 Ok(serde_json::json!({"data": {"result": "quantum_optimized"}}))
459 } else {
460 Err(anyhow!("Quantum optimizer not initialized"))
461 }
462 }
463 OptimizationStrategy::Hybrid => {
464 if let Some(hybrid_optimizer) = &self.hybrid_optimizer {
465 let _result = hybrid_optimizer.optimize_query(query).await?;
466 Ok(serde_json::json!({"data": {"result": "hybrid_optimized"}}))
467 } else {
468 Err(anyhow!("Hybrid optimizer not initialized"))
469 }
470 }
471 OptimizationStrategy::Federation => {
472 Ok(serde_json::json!({"data": {"result": "federated"}}))
474 }
475 OptimizationStrategy::Cache => {
476 if let Some(cache) = &self.cache {
477 let _stats = cache.get_stats().await?;
479 Ok(serde_json::json!({"data": {"result": "cached"}}))
480 } else {
481 Err(anyhow!("Cache not initialized"))
482 }
483 }
484 }
485 }
486
487 fn generate_test_queries(&self, scenario: &TestScenario, count: usize) -> Vec<Document> {
489 let mut queries = Vec::new();
490
491 for i in 0..count {
492 let query = match scenario {
493 TestScenario::SimpleQuery => self.create_simple_query(i),
494 TestScenario::ComplexQuery => self.create_complex_query(i),
495 TestScenario::DeepNestedQuery => self.create_deep_nested_query(i),
496 TestScenario::FederatedQuery => self.create_federated_query(i),
497 TestScenario::AggregationQuery => self.create_aggregation_query(i),
498 TestScenario::SubscriptionQuery => self.create_subscription_query(i),
499 TestScenario::BulkOperations => self.create_bulk_operation_query(i),
500 TestScenario::StressTest => self.create_stress_test_query(i),
501 };
502 queries.push(query);
503 }
504
505 queries
506 }
507
508 fn create_simple_query(&self, id: usize) -> Document {
510 use crate::ast::*;
511
512 Document {
513 definitions: vec![Definition::Operation(OperationDefinition {
514 operation_type: OperationType::Query,
515 name: Some(format!("SimpleQuery{id}")),
516 variable_definitions: vec![],
517 directives: vec![],
518 selection_set: SelectionSet {
519 selections: vec![Selection::Field(Field {
520 alias: None,
521 name: "user".to_string(),
522 arguments: vec![],
523 directives: vec![],
524 selection_set: Some(SelectionSet {
525 selections: vec![
526 Selection::Field(Field {
527 alias: None,
528 name: "id".to_string(),
529 arguments: vec![],
530 directives: vec![],
531 selection_set: None,
532 }),
533 Selection::Field(Field {
534 alias: None,
535 name: "name".to_string(),
536 arguments: vec![],
537 directives: vec![],
538 selection_set: None,
539 }),
540 ],
541 }),
542 })],
543 },
544 })],
545 }
546 }
547
548 fn create_complex_query(&self, id: usize) -> Document {
550 self.create_simple_query(id) }
553
554 fn create_deep_nested_query(&self, id: usize) -> Document {
556 self.create_simple_query(id)
557 }
558 fn create_federated_query(&self, id: usize) -> Document {
559 self.create_simple_query(id)
560 }
561 fn create_aggregation_query(&self, id: usize) -> Document {
562 self.create_simple_query(id)
563 }
564 fn create_subscription_query(&self, id: usize) -> Document {
565 self.create_simple_query(id)
566 }
567 fn create_bulk_operation_query(&self, id: usize) -> Document {
568 self.create_simple_query(id)
569 }
570 fn create_stress_test_query(&self, id: usize) -> Document {
571 self.create_simple_query(id)
572 }
573
574 async fn calculate_benchmark_result(
576 &self,
577 scenario: &TestScenario,
578 strategy: &OptimizationStrategy,
579 response_times: &[Duration],
580 successful_requests: u64,
581 failed_requests: u64,
582 total_time: Duration,
583 ) -> Result<BenchmarkResult> {
584 if response_times.is_empty() {
585 let memory_usage_mb = system_monitor::get_current_memory_usage_mb().await;
587 let cpu_usage_percent = system_monitor::get_current_cpu_usage_percent().await;
588
589 return Ok(BenchmarkResult {
590 scenario: format!("{scenario:?}"),
591 strategy: format!("{strategy:?}"),
592 configuration: "default".to_string(),
593 total_requests: successful_requests + failed_requests,
594 successful_requests,
595 failed_requests,
596 average_response_time: Duration::from_millis(0),
597 min_response_time: Duration::from_millis(0),
598 max_response_time: Duration::from_millis(0),
599 p50_response_time: Duration::from_millis(0),
600 p95_response_time: Duration::from_millis(0),
601 p99_response_time: Duration::from_millis(0),
602 requests_per_second: 0.0,
603 throughput_mbps: 0.0,
604 memory_usage_mb,
605 cpu_usage_percent,
606 cache_hit_rate: 0.0,
607 error_rate: 100.0,
608 detailed_metrics: None,
609 });
610 }
611
612 let mut sorted_times = response_times.to_vec();
613 sorted_times.sort();
614
615 let total_requests = successful_requests + failed_requests;
616 let average_response_time =
617 response_times.iter().sum::<Duration>() / response_times.len() as u32;
618 let min_response_time = *sorted_times.first().unwrap_or(&Duration::from_millis(0));
619 let max_response_time = *sorted_times.last().unwrap_or(&Duration::from_millis(0));
620
621 let p50_index = (sorted_times.len() as f64 * 0.5) as usize;
622 let p95_index = (sorted_times.len() as f64 * 0.95) as usize;
623 let p99_index = (sorted_times.len() as f64 * 0.99) as usize;
624
625 let p50_response_time = sorted_times
626 .get(p50_index)
627 .copied()
628 .unwrap_or(Duration::from_millis(0));
629 let p95_response_time = sorted_times
630 .get(p95_index)
631 .copied()
632 .unwrap_or(Duration::from_millis(0));
633 let p99_response_time = sorted_times
634 .get(p99_index)
635 .copied()
636 .unwrap_or(Duration::from_millis(0));
637
638 let requests_per_second = if total_time.as_secs_f64() > 0.0 {
639 successful_requests as f64 / total_time.as_secs_f64()
640 } else {
641 0.0
642 };
643
644 let error_rate = if total_requests > 0 {
645 (failed_requests as f64 / total_requests as f64) * 100.0
646 } else {
647 0.0
648 };
649
650 let cache_hit_rate = if let Some(cache) = &self.cache {
652 match cache.get_stats().await {
653 Ok(stats) if (stats.hits + stats.misses) > 0 => {
654 (stats.hits as f64 / (stats.hits + stats.misses) as f64) * 100.0
655 }
656 _ => 0.0,
657 }
658 } else {
659 0.0
660 };
661
662 let memory_usage_mb = system_monitor::get_current_memory_usage_mb().await;
664 let cpu_usage_percent = system_monitor::get_current_cpu_usage_percent().await;
665
666 let avg_response_size_bytes = 2048.0; let throughput_mbps =
669 system_monitor::calculate_throughput_mbps(requests_per_second, avg_response_size_bytes);
670
671 Ok(BenchmarkResult {
672 scenario: format!("{scenario:?}"),
673 strategy: format!("{strategy:?}"),
674 configuration: "default".to_string(),
675 total_requests,
676 successful_requests,
677 failed_requests,
678 average_response_time,
679 min_response_time,
680 max_response_time,
681 p50_response_time,
682 p95_response_time,
683 p99_response_time,
684 requests_per_second,
685 throughput_mbps,
686 memory_usage_mb,
687 cpu_usage_percent,
688 cache_hit_rate,
689 error_rate,
690 detailed_metrics: None, })
692 }
693
694 async fn generate_report(&self) -> Result<BenchmarkReport> {
696 let results = self.results.read().await.clone();
697
698 let best_result = results
700 .iter()
701 .min_by(|a, b| a.average_response_time.cmp(&b.average_response_time))
702 .cloned();
703
704 let best_strategy = best_result
705 .as_ref()
706 .map(|r| r.strategy.clone())
707 .unwrap_or_else(|| "None".to_string());
708
709 let mut strategy_rankings = Vec::new();
711 let strategies: std::collections::HashSet<String> =
712 results.iter().map(|r| r.strategy.clone()).collect();
713
714 for strategy in strategies {
715 let strategy_results: Vec<_> =
716 results.iter().filter(|r| r.strategy == strategy).collect();
717
718 if !strategy_results.is_empty() {
719 let avg_response_time = strategy_results
720 .iter()
721 .map(|r| r.average_response_time.as_millis() as f64)
722 .sum::<f64>()
723 / strategy_results.len() as f64;
724
725 let avg_throughput = strategy_results
726 .iter()
727 .map(|r| r.requests_per_second)
728 .sum::<f64>()
729 / strategy_results.len() as f64;
730
731 let avg_error_rate = strategy_results.iter().map(|r| r.error_rate).sum::<f64>()
732 / strategy_results.len() as f64;
733
734 let score = avg_response_time + (100.0 - avg_throughput) + avg_error_rate;
736
737 strategy_rankings.push(StrategyRanking {
738 strategy: strategy.clone(),
739 score,
740 response_time_rank: 0, throughput_rank: 0,
742 reliability_rank: 0,
743 });
744 }
745 }
746
747 strategy_rankings.sort_by(|a, b| {
749 a.score
750 .partial_cmp(&b.score)
751 .unwrap_or(std::cmp::Ordering::Equal)
752 });
753
754 let recommendations = self.generate_recommendations(&results).await;
756
757 Ok(BenchmarkReport {
758 config: BenchmarkConfigSummary {
759 test_duration: self.config.test_duration,
760 concurrent_users: self.config.concurrent_users,
761 total_queries: self.config.queries_per_user * self.config.concurrent_users,
762 scenarios_tested: self.config.test_scenarios.len(),
763 strategies_tested: self.config.optimization_strategies.len(),
764 },
765 results,
766 summary: BenchmarkSummary {
767 best_strategy: best_strategy.clone(),
768 best_average_response_time: best_result
769 .as_ref()
770 .map(|r| r.average_response_time)
771 .unwrap_or(Duration::from_millis(0)),
772 best_throughput: best_result
773 .as_ref()
774 .map(|r| r.requests_per_second)
775 .unwrap_or(0.0),
776 best_cache_hit_rate: best_result
777 .as_ref()
778 .map(|r| r.cache_hit_rate)
779 .unwrap_or(0.0),
780 overall_performance_improvement: 0.0, strategy_rankings,
782 },
783 recommendations,
784 generated_at: std::time::SystemTime::now(),
785 })
786 }
787
788 async fn generate_recommendations(
790 &self,
791 results: &[BenchmarkResult],
792 ) -> Vec<PerformanceRecommendation> {
793 let mut recommendations = Vec::new();
794
795 let avg_error_rate =
797 results.iter().map(|r| r.error_rate).sum::<f64>() / results.len() as f64;
798 if avg_error_rate > 5.0 {
799 recommendations.push(PerformanceRecommendation {
800 category: "Reliability".to_string(),
801 recommendation: "High error rate detected. Consider implementing better error handling and retry mechanisms.".to_string(),
802 impact: "High".to_string(),
803 priority: "Critical".to_string(),
804 });
805 }
806
807 let avg_response_time = results
808 .iter()
809 .map(|r| r.average_response_time.as_millis() as f64)
810 .sum::<f64>()
811 / results.len() as f64;
812 if avg_response_time > 1000.0 {
813 recommendations.push(PerformanceRecommendation {
814 category: "Performance".to_string(),
815 recommendation:
816 "Response times are high. Consider implementing hybrid optimization strategies."
817 .to_string(),
818 impact: "Medium".to_string(),
819 priority: "High".to_string(),
820 });
821 }
822
823 let cache_results: Vec<_> = results.iter().filter(|r| r.cache_hit_rate > 0.0).collect();
824 if !cache_results.is_empty() {
825 let avg_cache_hit_rate = cache_results.iter().map(|r| r.cache_hit_rate).sum::<f64>()
826 / cache_results.len() as f64;
827
828 if avg_cache_hit_rate < 50.0 {
829 recommendations.push(PerformanceRecommendation {
830 category: "Caching".to_string(),
831 recommendation: "Low cache hit rate. Consider optimizing cache configuration and TTL settings.".to_string(),
832 impact: "Medium".to_string(),
833 priority: "Medium".to_string(),
834 });
835 }
836 }
837
838 recommendations
839 }
840
841 pub async fn export_results(&self, format: ExportFormat, path: &str) -> Result<()> {
843 let report = self.generate_report().await?;
844
845 match format {
846 ExportFormat::Json => {
847 let json = serde_json::to_string_pretty(&report)?;
848 tokio::fs::write(path, json).await?;
849 }
850 ExportFormat::Csv => {
851 let csv = self.results_to_csv(&report.results)?;
852 tokio::fs::write(path, csv).await?;
853 }
854 ExportFormat::Html => {
855 let html = self.results_to_html(&report)?;
856 tokio::fs::write(path, html).await?;
857 }
858 }
859
860 info!("Benchmark results exported to: {}", path);
861 Ok(())
862 }
863
864 fn results_to_csv(&self, results: &[BenchmarkResult]) -> Result<String> {
866 let mut csv = String::new();
867 csv.push_str("Scenario,Strategy,Configuration,TotalRequests,SuccessfulRequests,FailedRequests,AvgResponseTime,MinResponseTime,MaxResponseTime,RequestsPerSecond,ErrorRate,CacheHitRate\n");
868
869 for result in results {
870 csv.push_str(&format!(
871 "{},{},{},{},{},{},{},{},{},{:.2},{:.2},{:.2}\n",
872 result.scenario,
873 result.strategy,
874 result.configuration,
875 result.total_requests,
876 result.successful_requests,
877 result.failed_requests,
878 result.average_response_time.as_millis(),
879 result.min_response_time.as_millis(),
880 result.max_response_time.as_millis(),
881 result.requests_per_second,
882 result.error_rate,
883 result.cache_hit_rate,
884 ));
885 }
886
887 Ok(csv)
888 }
889
890 fn results_to_html(&self, report: &BenchmarkReport) -> Result<String> {
892 let mut html = String::new();
893 html.push_str("<!DOCTYPE html><html><head><title>Benchmark Report</title></head><body>");
894 html.push_str("<h1>Performance Benchmark Report</h1>");
895 html.push_str(&format!("<p>Generated at: {:?}</p>", report.generated_at));
896 html.push_str(&format!(
897 "<p>Best Strategy: {}</p>",
898 report.summary.best_strategy
899 ));
900 html.push_str("<table border='1'><tr><th>Scenario</th><th>Strategy</th><th>Avg Response Time</th><th>Requests/sec</th><th>Error Rate</th></tr>");
901
902 for result in &report.results {
903 html.push_str(&format!(
904 "<tr><td>{}</td><td>{}</td><td>{}ms</td><td>{:.2}</td><td>{:.2}%</td></tr>",
905 result.scenario,
906 result.strategy,
907 result.average_response_time.as_millis(),
908 result.requests_per_second,
909 result.error_rate,
910 ));
911 }
912
913 html.push_str("</table></body></html>");
914 Ok(html)
915 }
916}
917
918#[derive(Debug, Clone)]
920pub enum ExportFormat {
921 Json,
922 Csv,
923 Html,
924}
925
926#[cfg(test)]
927mod tests {
928 use super::*;
929
930 #[tokio::test]
931 async fn test_benchmark_suite_creation() {
932 let config = BenchmarkConfig::default();
933 let mut suite = PerformanceBenchmarkSuite::new(config);
934
935 assert!(suite.initialize_optimizers().await.is_ok());
936 }
937
938 #[tokio::test]
939 async fn test_query_generation() {
940 let config = BenchmarkConfig::default();
941 let suite = PerformanceBenchmarkSuite::new(config);
942
943 let queries = suite.generate_test_queries(&TestScenario::SimpleQuery, 5);
944 assert_eq!(queries.len(), 5);
945 }
946
947 #[test]
948 fn test_csv_export() {
949 let config = BenchmarkConfig::default();
950 let suite = PerformanceBenchmarkSuite::new(config);
951
952 let results = vec![BenchmarkResult {
953 scenario: "SimpleQuery".to_string(),
954 strategy: "ML".to_string(),
955 configuration: "default".to_string(),
956 total_requests: 100,
957 successful_requests: 95,
958 failed_requests: 5,
959 average_response_time: Duration::from_millis(150),
960 min_response_time: Duration::from_millis(50),
961 max_response_time: Duration::from_millis(300),
962 p50_response_time: Duration::from_millis(140),
963 p95_response_time: Duration::from_millis(250),
964 p99_response_time: Duration::from_millis(290),
965 requests_per_second: 50.0,
966 throughput_mbps: 1.5,
967 memory_usage_mb: 128.0,
968 cpu_usage_percent: 25.0,
969 cache_hit_rate: 75.0,
970 error_rate: 5.0,
971 detailed_metrics: None,
972 }];
973
974 let csv = suite.results_to_csv(&results).expect("should succeed");
975 assert!(csv.contains("SimpleQuery"));
976 assert!(csv.contains("ML"));
977 assert!(csv.contains("95"));
978 }
979}