pub struct BenchmarkSuite {
pub name: String,
pub results: Vec<BenchmarkResult>,
pub total_duration: Duration,
}Expand description
Collection of benchmark results
Fields§
§name: StringName of the benchmark suite
results: Vec<BenchmarkResult>Individual benchmark results
total_duration: DurationTotal time for the entire suite
Implementations§
Source§impl BenchmarkSuite
impl BenchmarkSuite
Sourcepub fn add_result(&mut self, result: BenchmarkResult)
pub fn add_result(&mut self, result: BenchmarkResult)
Add a benchmark result
Sourcepub fn successful_results(&self) -> Vec<&BenchmarkResult>
pub fn successful_results(&self) -> Vec<&BenchmarkResult>
Get successful results only
Examples found in repository?
examples/scikit_learn_benchmark.rs (line 66)
56fn analyze_toy_dataset_performance(suites: &[BenchmarkSuite]) {
57 if let Some(toy_suite) = suites.iter().find(|s| s.name == "Toy Datasets") {
58 println!("\n📊 TOY DATASET LOADING ANALYSIS");
59 println!("{}", "-".repeat(40));
60
61 let mut total_loading_time = Duration::ZERO;
62 let mut total_samples = 0;
63 let mut fastestdataset = ("", Duration::MAX);
64 let mut slowestdataset = ("", Duration::ZERO);
65
66 for result in toy_suite.successful_results() {
67 total_loading_time += result.duration;
68 total_samples += result.samples;
69
70 if result.duration < fastestdataset.1 {
71 fastestdataset = (&result.operation, result.duration);
72 }
73 if result.duration > slowestdataset.1 {
74 slowestdataset = (&result.operation, result.duration);
75 }
76
77 println!(
78 " {}: {} ({} samples, {:.1} samples/s)",
79 result.operation.replace("load_", ""),
80 result.formatted_duration(),
81 result.samples,
82 result.throughput
83 );
84 }
85
86 println!("\n Summary:");
87 println!(
88 " Total loading time: {:.2}s",
89 total_loading_time.as_secs_f64()
90 );
91 println!(" Total samples loaded: {total_samples}");
92 println!(
93 " Average throughput: {:.1} samples/s",
94 total_samples as f64 / total_loading_time.as_secs_f64()
95 );
96 println!(
97 " Fastest: {} ({})",
98 fastestdataset.0,
99 format_duration(fastestdataset.1)
100 );
101 println!(
102 " Slowest: {} ({})",
103 slowestdataset.0,
104 format_duration(slowestdataset.1)
105 );
106 }
107}
108
109#[allow(dead_code)]
110fn analyze_data_generation_performance(suites: &[BenchmarkSuite]) {
111 if let Some(gen_suite) = suites.iter().find(|s| s.name == "Data Generation") {
112 println!("\n🔬 DATA GENERATION ANALYSIS");
113 println!("{}", "-".repeat(40));
114
115 let mut classification_results = Vec::new();
116 let mut regression_results = Vec::new();
117 let mut clustering_results = Vec::new();
118
119 for result in gen_suite.successful_results() {
120 if result.operation.contains("classification") {
121 classification_results.push(result);
122 } else if result.operation.contains("regression") {
123 regression_results.push(result);
124 } else if result.operation.contains("blobs") {
125 clustering_results.push(result);
126 }
127 }
128
129 analyze_generation_type("Classification", &classification_results);
130 analyze_generation_type("Regression", ®ression_results);
131 analyze_generation_type("Clustering", &clustering_results);
132
133 // Performance scaling analysis
134 analyze_scaling_performance(gen_suite);
135 }
136}
137
138#[allow(dead_code)]
139fn analyze_generation_type(
140 gen_type: &str,
141 results: &[&scirs2_datasets::benchmarks::BenchmarkResult],
142) {
143 if results.is_empty() {
144 return;
145 }
146
147 println!("\n {gen_type} Generation:");
148
149 let total_samples: usize = results.iter().map(|r| r.samples).sum();
150 let total_duration: Duration = results.iter().map(|r| r.duration).sum();
151 let avg_throughput = total_samples as f64 / total_duration.as_secs_f64();
152
153 println!(" Configurations tested: {}", results.len());
154 println!(" Total samples generated: {total_samples}");
155 println!(" Average throughput: {avg_throughput:.1} samples/s");
156
157 // Find best and worst performance
158 let best = results
159 .iter()
160 .max_by(|a, b| a.throughput.partial_cmp(&b.throughput).unwrap());
161 let worst = results
162 .iter()
163 .min_by(|a, b| a.throughput.partial_cmp(&b.throughput).unwrap());
164
165 if let (Some(best), Some(worst)) = (best, worst) {
166 println!(
167 " Best: {} ({:.1} samples/s)",
168 best.operation.split('_').next_back().unwrap_or("unknown"),
169 best.throughput
170 );
171 println!(
172 " Worst: {} ({:.1} samples/s)",
173 worst.operation.split('_').next_back().unwrap_or("unknown"),
174 worst.throughput
175 );
176 }
177}
178
179#[allow(dead_code)]
180fn analyze_scaling_performance(suite: &BenchmarkSuite) {
181 println!("\n 📈 SCALING ANALYSIS:");
182
183 // Group results by sample size
184 let mut size_groups: HashMap<usize, Vec<_>> = HashMap::new();
185
186 for result in suite.successful_results() {
187 size_groups.entry(result.samples).or_default().push(result);
188 }
189
190 let mut sizes: Vec<_> = size_groups.keys().collect();
191 sizes.sort();
192
193 for &size in &sizes {
194 if let Some(results) = size_groups.get(size) {
195 let avg_throughput =
196 results.iter().map(|r| r.throughput).sum::<f64>() / results.len() as f64;
197 let avg_duration = results
198 .iter()
199 .map(|r| r.duration.as_secs_f64())
200 .sum::<f64>()
201 / results.len() as f64;
202
203 println!(" {size} samples: {avg_throughput:.1} samples/s (avg {avg_duration:.2}s)");
204 }
205 }
206
207 // Calculate scaling efficiency
208 if sizes.len() >= 2 {
209 let smallsize = sizes[0];
210 let largesize = sizes[sizes.len() - 1];
211
212 if let (Some(small_results), Some(large_results)) =
213 (size_groups.get(smallsize), size_groups.get(largesize))
214 {
215 let small_avg = small_results.iter().map(|r| r.throughput).sum::<f64>()
216 / small_results.len() as f64;
217 let large_avg = large_results.iter().map(|r| r.throughput).sum::<f64>()
218 / large_results.len() as f64;
219
220 let efficiency = large_avg / small_avg;
221 let size_ratio = *largesize as f64 / *smallsize as f64;
222
223 println!(" Scaling efficiency: {efficiency:.2}x (size increased {size_ratio:.1}x)");
224
225 if efficiency > 0.8 {
226 println!(" ✅ Good scaling performance");
227 } else if efficiency > 0.5 {
228 println!(" ⚠️ Moderate scaling performance");
229 } else {
230 println!(" ❌ Poor scaling performance");
231 }
232 }
233 }
234}
235
236#[allow(dead_code)]
237fn run_python_comparison_benchmarks() {
238 println!("\n🐍 PYTHON SCIKIT-LEARN COMPARISON");
239 println!("{}", "-".repeat(40));
240
241 // Check if Python and scikit-learn are available
242 let python_check = Command::new("python3")
243 .arg("-c")
244 .arg("import sklearn; print('scikit-learn', sklearn.__version__)")
245 .output();
246
247 match python_check {
248 Ok(output) if output.status.success() => {
249 let version = String::from_utf8_lossy(&output.stdout);
250 println!(" ✅ Found {}", version.trim());
251
252 // Run comparative benchmarks
253 run_sklearn_toy_dataset_comparison();
254 run_sklearn_generation_comparison();
255 }
256 _ => {
257 println!(" ❌ Python scikit-learn not available");
258 println!(" Install with: pip install scikit-learn");
259 println!(" Skipping Python comparison benchmarks");
260 }
261 }
262}
263
264#[allow(dead_code)]
265fn run_sklearn_toy_dataset_comparison() {
266 println!("\n 📊 Toy Dataset Loading Comparison:");
267
268 let datasets = vec![
269 (
270 "iris",
271 "from sklearn.datasets import load_iris; load_iris()",
272 ),
273 (
274 "boston",
275 "from sklearn.datasets import load_boston; load_boston()",
276 ),
277 (
278 "digits",
279 "from sklearn.datasets import load_digits; load_digits()",
280 ),
281 (
282 "wine",
283 "from sklearn.datasets import load_wine; load_wine()",
284 ),
285 (
286 "breast_cancer",
287 "from sklearn.datasets import load_breast_cancer; load_breast_cancer()",
288 ),
289 ];
290
291 for (name, python_code) in datasets {
292 // Time Python execution
293 let _start = Instant::now();
294 let python_result = Command::new("python3")
295 .arg("-c")
296 .arg(format!(
297 "import time; start=time.time(); {python_code}; print(f'{{:.4f}}', time.time()-start)"
298 ))
299 .output();
300
301 match python_result {
302 Ok(output) if output.status.success() => {
303 let python_time = String::from_utf8_lossy(&output.stdout)
304 .trim()
305 .parse::<f64>()
306 .unwrap_or(0.0);
307
308 // Time SciRS2 execution
309 let scirs2_start = Instant::now();
310 let _scirs2_result = match name {
311 "iris" => load_iris().map(|_| ()),
312 "boston" => load_boston().map(|_| ()),
313 "digits" => load_digits().map(|_| ()),
314 "wine" => load_wine(false).map(|_| ()),
315 "breast_cancer" => load_breast_cancer().map(|_| ()),
316 _ => Ok(()),
317 };
318 let scirs2_time = scirs2_start.elapsed().as_secs_f64();
319
320 let speedup = python_time / scirs2_time;
321 let status = if speedup > 1.2 {
322 "🚀 FASTER"
323 } else if speedup > 0.8 {
324 "≈ SIMILAR"
325 } else {
326 "🐌 SLOWER"
327 };
328
329 println!(
330 " {}: SciRS2 {:.2}ms vs sklearn {:.2}ms ({:.1}x {}",
331 name,
332 scirs2_time * 1000.0,
333 python_time * 1000.0,
334 speedup,
335 status
336 );
337 }
338 _ => {
339 println!(" {name}: Failed to benchmark Python version");
340 }
341 }
342 }
343}
344
345#[allow(dead_code)]
346fn run_sklearn_generation_comparison() {
347 println!("\n 🔬 Data Generation Comparison:");
348
349 let configs = vec![
350 (1000, 10, "classification"),
351 (5000, 20, "classification"),
352 (1000, 10, "regression"),
353 (5000, 20, "regression"),
354 ];
355
356 for (n_samples, n_features, gen_type) in configs {
357 let (python_code, scirs2_fn): (&str, Box<dyn Fn() -> Result<Dataset, Box<dyn std::error::Error>>>) = match gen_type {
358 "classification" => (
359 &format!("from sklearn.datasets import make_classification; make_classification(n_samples={n_samples}, n_features={n_features}, random_state=42)"),
360 Box::new(move || make_classification(n_samples, n_features, 3, 2, 4, Some(42)).map_err(|e| Box::new(e) as Box<dyn std::error::Error>))
361 ),
362 "regression" => (
363 &format!("from sklearn.datasets import make_regression; make_regression(n_samples={n_samples}, n_features={n_features}, random_state=42)"),
364 Box::new(move || make_regression(n_samples, n_features, 3, 0.1, Some(42)).map_err(|e| Box::new(e) as Box<dyn std::error::Error>))
365 ),
366 _ => continue,
367 };
368
369 // Time Python execution
370 let python_result = Command::new("python3")
371 .arg("-c")
372 .arg(format!(
373 "import time; start=time.time(); {python_code}; print(f'{{:.4f}}', time.time()-start)"
374 ))
375 .output();
376
377 match python_result {
378 Ok(output) if output.status.success() => {
379 let python_time = String::from_utf8_lossy(&output.stdout)
380 .trim()
381 .parse::<f64>()
382 .unwrap_or(0.0);
383
384 // Time SciRS2 execution
385 let scirs2_start = Instant::now();
386 let _scirs2_result = scirs2_fn();
387 let scirs2_time = scirs2_start.elapsed().as_secs_f64();
388
389 let speedup = python_time / scirs2_time;
390 let status = if speedup > 1.2 {
391 "🚀 FASTER"
392 } else if speedup > 0.8 {
393 "≈ SIMILAR"
394 } else {
395 "🐌 SLOWER"
396 };
397
398 println!(
399 " {} {}x{}: SciRS2 {:.2}ms vs sklearn {:.2}ms ({:.1}x {})",
400 gen_type,
401 n_samples,
402 n_features,
403 scirs2_time * 1000.0,
404 python_time * 1000.0,
405 speedup,
406 status
407 );
408 }
409 _ => {
410 println!(
411 " {gen_type} {n_samples}x{n_features}: Failed to benchmark Python version"
412 );
413 }
414 }
415 }
416}
417
418#[allow(dead_code)]
419fn generate_performance_report(suites: &[BenchmarkSuite]) {
420 println!("\n📋 PERFORMANCE SUMMARY REPORT");
421 println!("{}", "=".repeat(60));
422
423 let mut total_operations = 0;
424 let mut total_samples = 0;
425 let mut total_duration = Duration::ZERO;
426
427 for suite in suites {
428 total_operations += suite.results.len();
429 total_samples += suite.total_samples();
430 total_duration += suite.total_duration;
431 }
432
433 println!(" Total operations benchmarked: {total_operations}");
434 println!(" Total samples processed: {total_samples}");
435 println!(
436 " Total benchmark time: {:.2}s",
437 total_duration.as_secs_f64()
438 );
439 println!(
440 " Overall throughput: {:.1} samples/s",
441 total_samples as f64 / total_duration.as_secs_f64()
442 );
443
444 // Performance assessment
445 let avg_throughput = total_samples as f64 / total_duration.as_secs_f64();
446
447 println!("\n 🎯 PERFORMANCE ASSESSMENT:");
448 if avg_throughput > 50000.0 {
449 println!(" ⭐ EXCELLENT - High-performance implementation");
450 } else if avg_throughput > 10000.0 {
451 println!(" ✅ GOOD - Solid performance for scientific computing");
452 } else if avg_throughput > 1000.0 {
453 println!(" ⚠️ MODERATE - Acceptable for most use cases");
454 } else {
455 println!(" ❌ SLOW - May need optimization");
456 }
457
458 // Recommendations
459 println!("\n 💡 RECOMMENDATIONS:");
460
461 if let Some(gen_suite) = suites.iter().find(|s| s.name == "Data Generation") {
462 let successful = gen_suite.successful_results();
463 let failed = gen_suite.failed_results();
464
465 if !failed.is_empty() {
466 println!(
467 " • Fix {} failed data generation operations",
468 failed.len()
469 );
470 }
471
472 if !successful.is_empty() {
473 let avg_gen_throughput =
474 successful.iter().map(|r| r.throughput).sum::<f64>() / successful.len() as f64;
475 if avg_gen_throughput < 1000.0 {
476 println!(" • Consider optimizing data generation algorithms");
477 println!(" • Implement SIMD operations for numeric computations");
478 println!(" • Use parallel processing for large datasets");
479 }
480 }
481 }
482
483 println!(" • Consider GPU acceleration for large-scale operations");
484 println!(" • Implement streaming for memory-efficient processing");
485 println!(" • Add caching for frequently accessed datasets");
486}Sourcepub fn failed_results(&self) -> Vec<&BenchmarkResult>
pub fn failed_results(&self) -> Vec<&BenchmarkResult>
Get failed results only
Examples found in repository?
examples/scikit_learn_benchmark.rs (line 463)
419fn generate_performance_report(suites: &[BenchmarkSuite]) {
420 println!("\n📋 PERFORMANCE SUMMARY REPORT");
421 println!("{}", "=".repeat(60));
422
423 let mut total_operations = 0;
424 let mut total_samples = 0;
425 let mut total_duration = Duration::ZERO;
426
427 for suite in suites {
428 total_operations += suite.results.len();
429 total_samples += suite.total_samples();
430 total_duration += suite.total_duration;
431 }
432
433 println!(" Total operations benchmarked: {total_operations}");
434 println!(" Total samples processed: {total_samples}");
435 println!(
436 " Total benchmark time: {:.2}s",
437 total_duration.as_secs_f64()
438 );
439 println!(
440 " Overall throughput: {:.1} samples/s",
441 total_samples as f64 / total_duration.as_secs_f64()
442 );
443
444 // Performance assessment
445 let avg_throughput = total_samples as f64 / total_duration.as_secs_f64();
446
447 println!("\n 🎯 PERFORMANCE ASSESSMENT:");
448 if avg_throughput > 50000.0 {
449 println!(" ⭐ EXCELLENT - High-performance implementation");
450 } else if avg_throughput > 10000.0 {
451 println!(" ✅ GOOD - Solid performance for scientific computing");
452 } else if avg_throughput > 1000.0 {
453 println!(" ⚠️ MODERATE - Acceptable for most use cases");
454 } else {
455 println!(" ❌ SLOW - May need optimization");
456 }
457
458 // Recommendations
459 println!("\n 💡 RECOMMENDATIONS:");
460
461 if let Some(gen_suite) = suites.iter().find(|s| s.name == "Data Generation") {
462 let successful = gen_suite.successful_results();
463 let failed = gen_suite.failed_results();
464
465 if !failed.is_empty() {
466 println!(
467 " • Fix {} failed data generation operations",
468 failed.len()
469 );
470 }
471
472 if !successful.is_empty() {
473 let avg_gen_throughput =
474 successful.iter().map(|r| r.throughput).sum::<f64>() / successful.len() as f64;
475 if avg_gen_throughput < 1000.0 {
476 println!(" • Consider optimizing data generation algorithms");
477 println!(" • Implement SIMD operations for numeric computations");
478 println!(" • Use parallel processing for large datasets");
479 }
480 }
481 }
482
483 println!(" • Consider GPU acceleration for large-scale operations");
484 println!(" • Implement streaming for memory-efficient processing");
485 println!(" • Add caching for frequently accessed datasets");
486}Sourcepub fn average_throughput(&self) -> f64
pub fn average_throughput(&self) -> f64
Calculate average throughput
Sourcepub fn total_samples(&self) -> usize
pub fn total_samples(&self) -> usize
Get total samples processed
Examples found in repository?
examples/scikit_learn_benchmark.rs (line 429)
419fn generate_performance_report(suites: &[BenchmarkSuite]) {
420 println!("\n📋 PERFORMANCE SUMMARY REPORT");
421 println!("{}", "=".repeat(60));
422
423 let mut total_operations = 0;
424 let mut total_samples = 0;
425 let mut total_duration = Duration::ZERO;
426
427 for suite in suites {
428 total_operations += suite.results.len();
429 total_samples += suite.total_samples();
430 total_duration += suite.total_duration;
431 }
432
433 println!(" Total operations benchmarked: {total_operations}");
434 println!(" Total samples processed: {total_samples}");
435 println!(
436 " Total benchmark time: {:.2}s",
437 total_duration.as_secs_f64()
438 );
439 println!(
440 " Overall throughput: {:.1} samples/s",
441 total_samples as f64 / total_duration.as_secs_f64()
442 );
443
444 // Performance assessment
445 let avg_throughput = total_samples as f64 / total_duration.as_secs_f64();
446
447 println!("\n 🎯 PERFORMANCE ASSESSMENT:");
448 if avg_throughput > 50000.0 {
449 println!(" ⭐ EXCELLENT - High-performance implementation");
450 } else if avg_throughput > 10000.0 {
451 println!(" ✅ GOOD - Solid performance for scientific computing");
452 } else if avg_throughput > 1000.0 {
453 println!(" ⚠️ MODERATE - Acceptable for most use cases");
454 } else {
455 println!(" ❌ SLOW - May need optimization");
456 }
457
458 // Recommendations
459 println!("\n 💡 RECOMMENDATIONS:");
460
461 if let Some(gen_suite) = suites.iter().find(|s| s.name == "Data Generation") {
462 let successful = gen_suite.successful_results();
463 let failed = gen_suite.failed_results();
464
465 if !failed.is_empty() {
466 println!(
467 " • Fix {} failed data generation operations",
468 failed.len()
469 );
470 }
471
472 if !successful.is_empty() {
473 let avg_gen_throughput =
474 successful.iter().map(|r| r.throughput).sum::<f64>() / successful.len() as f64;
475 if avg_gen_throughput < 1000.0 {
476 println!(" • Consider optimizing data generation algorithms");
477 println!(" • Implement SIMD operations for numeric computations");
478 println!(" • Use parallel processing for large datasets");
479 }
480 }
481 }
482
483 println!(" • Consider GPU acceleration for large-scale operations");
484 println!(" • Implement streaming for memory-efficient processing");
485 println!(" • Add caching for frequently accessed datasets");
486}Sourcepub fn print_summary(&self)
pub fn print_summary(&self)
Print a summary report
Trait Implementations§
Source§impl Clone for BenchmarkSuite
impl Clone for BenchmarkSuite
Source§fn clone(&self) -> BenchmarkSuite
fn clone(&self) -> BenchmarkSuite
Returns a duplicate of the value. Read more
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
Performs copy-assignment from
source. Read moreAuto Trait Implementations§
impl Freeze for BenchmarkSuite
impl RefUnwindSafe for BenchmarkSuite
impl Send for BenchmarkSuite
impl Sync for BenchmarkSuite
impl Unpin for BenchmarkSuite
impl UnwindSafe for BenchmarkSuite
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
Converts
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
Converts
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> Pointable for T
impl<T> Pointable for T
Source§impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
Source§fn to_subset(&self) -> Option<SS>
fn to_subset(&self) -> Option<SS>
The inverse inclusion map: attempts to construct
self from the equivalent element of its
superset. Read moreSource§fn is_in_subset(&self) -> bool
fn is_in_subset(&self) -> bool
Checks if
self is actually part of its subset T (and can be converted to it).Source§fn to_subset_unchecked(&self) -> SS
fn to_subset_unchecked(&self) -> SS
Use with care! Same as
self.to_subset but without any property checks. Always succeeds.Source§fn from_subset(element: &SS) -> SP
fn from_subset(element: &SS) -> SP
The inclusion map: converts
self to the equivalent element of its superset.