1use crate::benchmarking::{BenchmarkConfig, BenchmarkResult, BenchmarkRunner, BenchmarkSuite};
7use crate::error::{CoreError, CoreResult, ErrorContext};
8use std::time::Duration;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum BenchmarkCategory {
13 Computation,
15 Memory,
17 InputOutput,
19 Parallel,
21 Simd,
23 Algorithmic,
25}
26
27#[derive(Debug, Clone)]
29pub struct PerformanceTarget {
30 pub category: BenchmarkCategory,
32 pub target_time: Duration,
34 pub target_throughput: Option<f64>,
36 pub target_memory: Option<usize>,
38 pub scaling_factor: f64,
40}
41
42impl PerformanceTarget {
43 pub fn new(category: BenchmarkCategory, target_time: Duration) -> Self {
45 Self {
46 category,
47 target_time,
48 target_throughput: None,
49 target_memory: None,
50 scaling_factor: 1.0,
51 }
52 }
53
54 pub fn with_throughput(mut self, throughput: f64) -> Self {
56 self.target_throughput = Some(throughput);
57 self
58 }
59
60 pub fn with_memory(mut self, memory: usize) -> Self {
62 self.target_memory = Some(memory);
63 self
64 }
65
66 pub fn with_scaling_factor(mut self, factor: f64) -> Self {
68 self.scaling_factor = factor;
69 self
70 }
71
72 pub fn is_met_by(&self, result: &BenchmarkResult, input_scale: f64) -> bool {
74 let scaled_target_time = Duration::from_nanos(
75 (self.target_time.as_nanos() as f64 * input_scale.powf(self.scaling_factor)) as u64,
76 );
77
78 if result.statistics.mean_execution_time > scaled_target_time {
80 return false;
81 }
82
83 if let Some(target_throughput) = self.target_throughput {
85 let actual_throughput = 1.0 / result.statistics.mean_execution_time.as_secs_f64();
86 if actual_throughput < target_throughput {
87 return false;
88 }
89 }
90
91 if let Some(target_memory) = self.target_memory {
93 if result.statistics.mean_memory_usage > target_memory {
94 return false;
95 }
96 }
97
98 true
99 }
100}
101
102#[derive(Debug, Clone)]
104pub struct PerformanceBenchmarkResult {
105 pub benchmark_result: BenchmarkResult,
107 pub target: PerformanceTarget,
109 pub input_scale: f64,
111 pub target_met: bool,
113 pub performance_ratio: f64,
115}
116
117impl PerformanceBenchmarkResult {
118 pub fn new(
120 benchmark_result: BenchmarkResult,
121 target: PerformanceTarget,
122 input_scale: f64,
123 ) -> Self {
124 let target_met = target.is_met_by(&benchmark_result, input_scale);
125
126 let scaled_target_time = Duration::from_nanos(
127 (target.target_time.as_nanos() as f64 * input_scale.powf(target.scaling_factor)) as u64,
128 );
129
130 let performance_ratio = benchmark_result
131 .statistics
132 .mean_execution_time
133 .as_secs_f64()
134 / scaled_target_time.as_secs_f64();
135
136 Self {
137 benchmark_result,
138 target,
139 input_scale,
140 target_met,
141 performance_ratio,
142 }
143 }
144
145 pub fn performance_grade(&self) -> PerformanceGrade {
147 if self.performance_ratio <= 0.5 {
148 PerformanceGrade::Excellent
149 } else if self.performance_ratio <= 0.8 {
150 PerformanceGrade::Good
151 } else if self.performance_ratio <= 1.0 {
152 PerformanceGrade::Acceptable
153 } else if self.performance_ratio <= 1.5 {
154 PerformanceGrade::Poor
155 } else {
156 PerformanceGrade::Unacceptable
157 }
158 }
159}
160
161#[derive(Debug, Clone, Copy, PartialEq, Eq)]
163pub enum PerformanceGrade {
164 Excellent,
166 Good,
168 Acceptable,
170 Poor,
172 Unacceptable,
174}
175
176pub struct PerformanceBenchmarker {
178 runner: BenchmarkRunner,
179}
180
181impl PerformanceBenchmarker {
182 pub fn new(config: BenchmarkConfig) -> Self {
184 Self {
185 runner: BenchmarkRunner::new(config),
186 }
187 }
188
189 pub fn run_with_target<F, T>(
191 &self,
192 name: &str,
193 target: PerformanceTarget,
194 input_scale: f64,
195 benchmark_fn: F,
196 ) -> CoreResult<PerformanceBenchmarkResult>
197 where
198 F: FnMut() -> CoreResult<T>,
199 {
200 let benchmark_result = self.runner.run(name, benchmark_fn)?;
201 Ok(PerformanceBenchmarkResult::new(
202 benchmark_result,
203 target,
204 input_scale,
205 ))
206 }
207
208 pub fn benchmark_computation<F, T>(
210 &self,
211 name: &str,
212 computation_fn: F,
213 expected_complexity: f64,
214 ) -> CoreResult<PerformanceBenchmarkResult>
215 where
216 F: FnMut() -> CoreResult<T>,
217 {
218 let target =
219 PerformanceTarget::new(BenchmarkCategory::Computation, Duration::from_millis(100))
220 .with_scaling_factor(expected_complexity);
221
222 self.run_with_target(name, target, 1.0, computation_fn)
223 }
224
225 pub fn benchmark_memory_access<F, T>(
227 &self,
228 name: &str,
229 memory_fn: F,
230 data_size: usize,
231 ) -> CoreResult<PerformanceBenchmarkResult>
232 where
233 F: FnMut() -> CoreResult<T>,
234 {
235 let target = PerformanceTarget::new(
236 BenchmarkCategory::Memory,
237 Duration::from_micros(10),
238 )
239 .with_memory(data_size * 2) .with_scaling_factor(1.0); let scale = data_size as f64 / 1024.0; self.run_with_target(name, target, scale, memory_fn)
244 }
245
246 pub fn benchmark_algorithm_scaling<F, T>(
248 &self,
249 name: &str,
250 algorithm_fn: F,
251 input_sizes: Vec<usize>,
252 expected_complexity: f64,
253 ) -> CoreResult<Vec<PerformanceBenchmarkResult>>
254 where
255 F: Fn(usize) -> CoreResult<T> + Clone,
256 {
257 let mut results = Vec::new();
258 let base_target =
259 PerformanceTarget::new(BenchmarkCategory::Algorithmic, Duration::from_millis(10))
260 .with_scaling_factor(expected_complexity);
261
262 for size in &input_sizes {
263 let size_name = format!("{}(n={})", name, size);
264 let algorithm_clone = algorithm_fn.clone();
265
266 let benchmark_result = self.runner.run(&size_name, || algorithm_clone(*size))?;
267 let scale = *size as f64 / input_sizes[0] as f64;
268 let performance_result =
269 PerformanceBenchmarkResult::new(benchmark_result, base_target.clone(), scale);
270
271 results.push(performance_result);
272 }
273
274 Ok(results)
275 }
276
277 #[cfg(feature = "simd")]
279 pub fn benchmark_simd<F, G, T>(
280 &self,
281 name: &str,
282 simd_fn: F,
283 scalar_fn: G,
284 data_size: usize,
285 ) -> CoreResult<(PerformanceBenchmarkResult, PerformanceBenchmarkResult, f64)>
286 where
287 F: FnMut() -> CoreResult<T>,
288 G: FnMut() -> CoreResult<T>,
289 {
290 let simd_target =
292 PerformanceTarget::new(BenchmarkCategory::Simd, Duration::from_micros(100));
293 let simd_result = self.run_with_target(
294 &format!("{}_simd", name),
295 simd_target,
296 data_size as f64 / 1000.0,
297 simd_fn,
298 )?;
299
300 let scalar_target =
302 PerformanceTarget::new(BenchmarkCategory::Computation, Duration::from_millis(1));
303 let scalar_result = self.run_with_target(
304 &format!("{}_scalar", name),
305 scalar_target,
306 data_size as f64 / 1000.0,
307 scalar_fn,
308 )?;
309
310 let speedup = scalar_result
312 .benchmark_result
313 .statistics
314 .mean_execution_time
315 .as_secs_f64()
316 / simd_result
317 .benchmark_result
318 .statistics
319 .mean_execution_time
320 .as_secs_f64();
321
322 Ok((simd_result, scalar_result, speedup))
323 }
324
325 #[cfg(feature = "parallel")]
327 pub fn benchmark_parallel<F, G, T>(
328 &self,
329 name: &str,
330 parallel_fn: F,
331 sequential_fn: G,
332 thread_count: usize,
333 ) -> CoreResult<(PerformanceBenchmarkResult, PerformanceBenchmarkResult, f64)>
334 where
335 F: FnMut() -> CoreResult<T>,
336 G: FnMut() -> CoreResult<T>,
337 {
338 let parallel_target =
340 PerformanceTarget::new(BenchmarkCategory::Parallel, Duration::from_millis(100));
341 let parallel_result = self.run_with_target(
342 &format!("{}_parallel", name),
343 parallel_target,
344 1.0,
345 parallel_fn,
346 )?;
347
348 let sequential_target =
350 PerformanceTarget::new(BenchmarkCategory::Computation, Duration::from_millis(500));
351 let sequential_result = self.run_with_target(
352 &format!("{}_sequential", name),
353 sequential_target,
354 1.0,
355 sequential_fn,
356 )?;
357
358 let theoretical_speedup = thread_count as f64;
360 let actual_speedup = sequential_result
361 .benchmark_result
362 .statistics
363 .mean_execution_time
364 .as_secs_f64()
365 / parallel_result
366 .benchmark_result
367 .statistics
368 .mean_execution_time
369 .as_secs_f64();
370 let efficiency = actual_speedup / theoretical_speedup;
371
372 Ok((parallel_result, sequential_result, efficiency))
373 }
374}
375
376pub struct StandardBenchmarks;
378
379impl StandardBenchmarks {
380 pub fn create_computation_suite(config: BenchmarkConfig) -> BenchmarkSuite {
382 let mut suite = BenchmarkSuite::new("computation_performance", config);
383
384 suite.add_benchmark(|runner| {
386 runner.run("arithmetic_operations", || {
387 let mut sum = 0.0f64;
388 for i in 0..10000 {
389 sum += (i as f64).sin().cos().sqrt();
390 }
391 Ok(sum)
392 })
393 });
394
395 suite.add_benchmark(|runner| {
397 runner.run("vector_operations", || {
398 let a: Vec<f64> = (0..10000).map(|i| i as f64).collect();
399 let b: Vec<f64> = (0..10000).map(|i| (i as f64) * 2.0).collect();
400 let result: Vec<f64> = a.iter().zip(b.iter()).map(|(x, y)| x + y).collect();
401 Ok(result.iter().sum::<f64>())
402 })
403 });
404
405 suite.add_benchmark(|runner| {
407 runner.run("matrix_multiplication", || {
408 let size = 100;
409 let a: Vec<Vec<f64>> = (0..size)
410 .map(|i| (0..size).map(|j| (i * j) as f64).collect())
411 .collect();
412 let b: Vec<Vec<f64>> = (0..size)
413 .map(|i| (0..size).map(|j| (i + j) as f64).collect())
414 .collect();
415
416 let mut c = vec![vec![0.0; size]; size];
417 for i in 0..size {
418 for j in 0..size {
419 #[allow(clippy::needless_range_loop)]
420 for k in 0..size {
421 c[i][j] += a[i][k] * b[k][j];
422 }
423 }
424 }
425
426 Ok(c[0][0])
427 })
428 });
429
430 suite
431 }
432
433 pub fn create_memory_suite(config: BenchmarkConfig) -> BenchmarkSuite {
435 let mut suite = BenchmarkSuite::new("memory_performance", config);
436
437 suite.add_benchmark(|runner| {
439 runner.run("memory_allocation", || {
440 let mut vectors = Vec::new();
441 for i in 0..1000 {
442 vectors.push(vec![i as f64; 1000]);
443 }
444 Ok(vectors.len())
445 })
446 });
447
448 suite.add_benchmark(|runner| {
450 runner.run("sequential_access", || {
451 let data: Vec<f64> = (0..1000000).map(|i| i as f64).collect();
452 let sum: f64 = data.iter().sum();
453 Ok(sum)
454 })
455 });
456
457 suite.add_benchmark(|runner| {
459 runner.run("random_access", || {
460 let data: Vec<f64> = (0..100000).map(|i| i as f64).collect();
461 let mut sum = 0.0;
462 for i in (0..data.len()).step_by(1000) {
463 sum += data[i];
464 }
465 Ok(sum)
466 })
467 });
468
469 suite
470 }
471
472 pub fn create_io_suite(config: BenchmarkConfig) -> BenchmarkSuite {
474 let mut suite = BenchmarkSuite::new("io_performance", config);
475
476 suite.add_benchmark(|runner| {
478 runner.run_with_setup(
479 "file_io",
480 || {
481 use tempfile::NamedTempFile;
482 let temp_file = NamedTempFile::new().map_err(|e| {
483 CoreError::IoError(ErrorContext::new(format!(
484 "Failed to create temp file: {}",
485 e
486 )))
487 })?;
488 Ok(temp_file)
489 },
490 |temp_file| {
491 use std::io::Write;
492 let data = vec![42u8; 10000];
493 temp_file
494 .write_all(&data)
495 .map_err(|e| CoreError::IoError(ErrorContext::new(format!("{e}"))))?;
496 temp_file
497 .flush()
498 .map_err(|e| CoreError::IoError(ErrorContext::new(format!("{e}"))))?;
499 Ok(data.len())
500 },
501 |temp_file| {
502 drop(temp_file);
503 Ok(())
504 },
505 )
506 });
507
508 suite
509 }
510
511 pub fn create_comprehensive_suite(config: BenchmarkConfig) -> BenchmarkSuite {
513 let mut suite = BenchmarkSuite::new("comprehensive_performance", config.clone());
514
515 let comp_suite = Self::create_computation_suite(config.clone());
517 suite.add_benchmark(|runner| {
519 runner.run("comprehensive_computation", || {
520 let mut result = 0.0;
522 for i in 0..1000 {
523 result += (i as f64).sin();
524 }
525 Ok(result)
526 })
527 });
528
529 suite.add_benchmark(|runner| {
531 runner.run("comprehensive_memory", || {
532 let data: Vec<f64> = (0..10000).map(|i| i as f64).collect();
533 Ok(data.iter().sum::<f64>())
534 })
535 });
536
537 suite
538 }
539}
540
541#[cfg(test)]
542mod tests {
543 use super::*;
544
545 #[test]
546 fn test_performance_target() {
547 let target =
548 PerformanceTarget::new(BenchmarkCategory::Computation, Duration::from_millis(100))
549 .with_throughput(1000.0)
550 .with_memory(1024)
551 .with_scaling_factor(2.0);
552
553 assert_eq!(target.category, BenchmarkCategory::Computation);
554 assert_eq!(target.target_time, Duration::from_millis(100));
555 assert_eq!(target.target_throughput, Some(1000.0));
556 assert_eq!(target.target_memory, Some(1024));
557 assert_eq!(target.scaling_factor, 2.0);
558 }
559
560 #[test]
561 fn test_performance_grade() {
562 let config = BenchmarkConfig::default();
563 let mut result = crate::benchmarking::BenchmarkResult::new("test".to_string(), config);
564
565 result.add_measurement(crate::benchmarking::BenchmarkMeasurement::new(
567 Duration::from_millis(50),
568 ));
569 result.finalize().expect("Operation failed");
570
571 let target =
572 PerformanceTarget::new(BenchmarkCategory::Computation, Duration::from_millis(100));
573
574 let perf_result = PerformanceBenchmarkResult::new(result, target, 1.0);
575 assert_eq!(perf_result.performance_grade(), PerformanceGrade::Excellent);
577 }
578
579 #[test]
580 fn test_performance_benchmarker() {
581 let config = BenchmarkConfig::new()
582 .with_warmup_iterations(1)
583 .with_measurement_iterations(5);
584 let benchmarker = PerformanceBenchmarker::new(config);
585
586 let result = benchmarker
587 .benchmark_computation(
588 "test_computation",
589 || {
590 std::thread::sleep(Duration::from_micros(100));
591 Ok(42)
592 },
593 1.0,
594 )
595 .expect("Operation failed");
596
597 assert!(result.benchmark_result.statistics.mean_execution_time > Duration::from_micros(50));
598 assert_eq!(result.target.category, BenchmarkCategory::Computation);
599 }
600}