Skip to main content

torsh_benches/benchmarks/
data_loading.rs

1//! Data Loading and Preprocessing Benchmarks
2//!
3//! This module contains comprehensive benchmarks for testing the performance of data loading
4//! operations, preprocessing pipelines, and various data handling strategies in ToRSh.
5//!
6//! The benchmarks cover a wide range of data loading scenarios including:
7//! - Single-threaded and multi-threaded data loading
8//! - Different batch sizes and sampling strategies
9//! - Memory usage during data loading operations
10//! - Transform pipeline performance
11//! - Prefetching and caching strategies
12//! - Distributed data loading scenarios
13
14use crate::Benchmarkable;
15use std::time::{Duration, Instant};
16use torsh_core::TensorElement;
17use torsh_data::{
18    BatchingSampler, DataLoader, Dataset, DistributedSampler, RandomSampler, Sampler,
19    SequentialSampler, TensorDataset,
20};
21use torsh_tensor::prelude::{rand, Tensor};
22
23// Helper functions for data loading benchmarks
24
25/// Create a simple dataloader with basic configuration
26pub fn simple_dataloader<T: TensorElement>(
27    dataset: TensorDataset<T>,
28    batch_size: usize,
29    _shuffle: bool,
30) -> torsh_core::error::Result<
31    DataLoader<
32        TensorDataset<T>,
33        BatchingSampler<SequentialSampler>,
34        torsh_data::collate::DefaultCollate,
35    >,
36> {
37    DataLoader::builder(dataset)
38        .batch_size(batch_size)
39        .drop_last(false)
40        .build()
41}
42
43/// Create a simple random dataloader with seed
44pub fn simple_random_dataloader<T: TensorElement>(
45    dataset: TensorDataset<T>,
46    batch_size: usize,
47    _seed: Option<u64>,
48) -> torsh_core::error::Result<
49    DataLoader<
50        TensorDataset<T>,
51        BatchingSampler<SequentialSampler>,
52        torsh_data::collate::DefaultCollate,
53    >,
54> {
55    // Simplified version - actual implementation would use RandomSampler
56    DataLoader::builder(dataset)
57        .batch_size(batch_size)
58        .drop_last(true)
59        .build()
60}
61
62// ================================================================================================
63// Core Data Loading Benchmarks
64// ================================================================================================
65
66/// DataLoader throughput benchmarks
67///
68/// Tests the basic throughput of data loading operations with fixed-size tensors.
69/// Measures how quickly batches can be loaded and processed from a dataset.
70pub struct DataLoaderThroughputBench;
71
72impl Benchmarkable for DataLoaderThroughputBench {
73    type Input = TensorDataset<f32>;
74    type Output = Vec<Vec<Tensor<f32>>>;
75
76    fn setup(&mut self, size: usize) -> Self::Input {
77        // Create a dataset with multiple tensors
78        let num_samples = size * 10; // Scale samples with size
79        let mut tensors = Vec::new();
80        for _ in 0..num_samples {
81            tensors.push(rand::<f32>(&[32, 32]).expect("tensor creation should succeed"));
82            // Fixed size tensors
83        }
84
85        TensorDataset::new(tensors)
86    }
87
88    fn run(&mut self, input: &Self::Input) -> Self::Output {
89        // Simplified: just sample directly from dataset for benchmarking
90        let mut results = Vec::new();
91        let batch_size = 32;
92        let num_batches = std::cmp::min(10, input.len() / batch_size);
93
94        for batch_idx in 0..num_batches {
95            let mut batch = Vec::new();
96            for i in 0..batch_size {
97                let idx = batch_idx * batch_size + i;
98                if idx < input.len() {
99                    if let Ok(item) = input.get(idx) {
100                        batch.extend(item);
101                    }
102                }
103            }
104            if !batch.is_empty() {
105                results.push(batch);
106            }
107        }
108        results
109    }
110
111    fn bytes_accessed(&self, size: usize) -> usize {
112        let num_samples = size * 10;
113        num_samples * 32 * 32 * std::mem::size_of::<f32>()
114    }
115}
116
117/// Multi-worker DataLoader benchmarks
118///
119/// Tests the performance improvement gained from using multiple worker threads
120/// for data loading operations. Compares throughput with different worker counts.
121pub struct MultiWorkerDataLoaderBench {
122    num_workers: usize,
123}
124
125impl MultiWorkerDataLoaderBench {
126    pub fn new(num_workers: usize) -> Self {
127        Self { num_workers }
128    }
129
130    /// Get the number of workers used in this benchmark
131    pub fn worker_count(&self) -> usize {
132        self.num_workers
133    }
134}
135
136impl Benchmarkable for MultiWorkerDataLoaderBench {
137    type Input = TensorDataset<f32>;
138    type Output = Vec<Vec<Tensor<f32>>>;
139
140    fn setup(&mut self, size: usize) -> Self::Input {
141        let num_samples = size * 20;
142        let mut tensors = Vec::new();
143        for _ in 0..num_samples {
144            tensors.push(rand::<f32>(&[64, 64]).expect("tensor creation should succeed"));
145        }
146        TensorDataset::new(tensors)
147    }
148
149    fn run(&mut self, input: &Self::Input) -> Self::Output {
150        let dataloader = simple_random_dataloader(input.clone(), 16, Some(42))
151            .expect("dataloader creation should succeed");
152        dataloader
153            .iter()
154            .take(10)
155            .map(|batch| batch.expect("batch should succeed"))
156            .collect() // Take first 10 batches
157    }
158
159    fn bytes_accessed(&self, _size: usize) -> usize {
160        let _num_samples = _size * 20;
161        10 * 16 * 64 * 64 * std::mem::size_of::<f32>() // 10 batches * 16 batch_size
162    }
163}
164
165// ================================================================================================
166// Batch Size and Scaling Benchmarks
167// ================================================================================================
168
169/// Batch size scaling benchmarks
170///
171/// Tests how performance scales with different batch sizes. Measures the
172/// impact of batch size on memory usage, throughput, and processing efficiency.
173pub struct BatchSizeScalingBench {
174    batch_size: usize,
175}
176
177impl BatchSizeScalingBench {
178    pub fn new(batch_size: usize) -> Self {
179        Self { batch_size }
180    }
181
182    /// Get the batch size used in this benchmark
183    pub fn get_batch_size(&self) -> usize {
184        self.batch_size
185    }
186
187    /// Calculate effective throughput based on batch processing
188    pub fn calculate_throughput(&self, processing_time: Duration, num_batches: usize) -> f64 {
189        let samples_processed = num_batches * self.batch_size;
190        samples_processed as f64 / processing_time.as_secs_f64()
191    }
192}
193
194impl Benchmarkable for BatchSizeScalingBench {
195    type Input = TensorDataset<f32>;
196    type Output = Vec<Vec<Tensor<f32>>>;
197
198    fn setup(&mut self, size: usize) -> Self::Input {
199        let num_samples = std::cmp::max(self.batch_size * 8, size * 10);
200        let mut tensors = Vec::new();
201        for _ in 0..num_samples {
202            tensors.push(rand::<f32>(&[32, 32]).expect("tensor creation should succeed"));
203        }
204        TensorDataset::new(tensors)
205    }
206
207    fn run(&mut self, input: &Self::Input) -> Self::Output {
208        let dataloader = DataLoader::builder(input.clone())
209            .batch_size(self.batch_size)
210            .drop_last(true)
211            .build()
212            .expect("dataloader creation should succeed");
213
214        dataloader
215            .iter()
216            .take(5)
217            .map(|batch| batch.expect("batch should succeed"))
218            .collect() // Take first 5 batches
219    }
220
221    fn bytes_accessed(&self, _size: usize) -> usize {
222        5 * self.batch_size * 32 * 32 * std::mem::size_of::<f32>()
223    }
224}
225
226// ================================================================================================
227// Transform and Preprocessing Benchmarks
228// ================================================================================================
229
230/// Transform pipeline benchmarks
231///
232/// Tests the performance of data transformation pipelines that are commonly
233/// used in deep learning preprocessing (normalization, augmentation, etc.).
234pub struct TransformPipelineBench;
235
236impl TransformPipelineBench {
237    /// Calculate the number of transform operations performed
238    pub fn calculate_transform_ops(
239        &self,
240        num_samples: usize,
241        transforms_per_sample: usize,
242    ) -> usize {
243        num_samples * transforms_per_sample
244    }
245
246    /// Estimate FLOPS for common transform operations
247    pub fn estimate_transform_flops(&self, tensor_size: usize, num_transforms: usize) -> usize {
248        // Approximation: normalization (2 ops per element), resize, etc.
249        tensor_size * num_transforms * 3
250    }
251}
252
253impl Benchmarkable for TransformPipelineBench {
254    type Input = TensorDataset<f32>;
255    type Output = Vec<Vec<Tensor<f32>>>;
256
257    fn setup(&mut self, size: usize) -> Self::Input {
258        let num_samples = size * 10;
259        let mut tensors = Vec::new();
260        for _ in 0..num_samples {
261            tensors.push(rand::<f32>(&[32, 32]).expect("tensor creation should succeed"));
262        }
263        TensorDataset::new(tensors)
264    }
265
266    fn run(&mut self, input: &Self::Input) -> Self::Output {
267        // For this benchmark, we'll just use a simple dataloader
268        // Transform integration would be done at the dataset level
269        let dataloader = simple_dataloader(input.clone(), 16, false)
270            .expect("dataloader creation should succeed");
271        dataloader
272            .iter()
273            .take(8)
274            .map(|batch| batch.expect("batch should succeed"))
275            .collect()
276    }
277
278    fn flops(&self, size: usize) -> usize {
279        let _num_samples = size * 10;
280        8 * 16 * 32 * 32 * 3 // 8 batches * 16 samples * operations per pixel * 3 channels approx
281    }
282
283    fn bytes_accessed(&self, _size: usize) -> usize {
284        8 * 16 * 32 * 32 * std::mem::size_of::<f32>()
285    }
286}
287
288// ================================================================================================
289// Sampling Strategy Benchmarks
290// ================================================================================================
291
292/// Sampling strategy benchmarks
293///
294/// Tests the performance and characteristics of different sampling strategies
295/// including sequential, random, and weighted sampling approaches.
296pub struct SamplingStrategyBench;
297
298/// Results from sampling strategy analysis
299#[derive(Debug, Clone)]
300pub struct SamplingResults {
301    pub sequential_samples: Vec<usize>,
302    pub random_samples: Vec<usize>,
303    pub sequential_time: Duration,
304    pub random_time: Duration,
305    pub sample_distribution_variance: f64,
306}
307
308impl SamplingStrategyBench {
309    /// Analyze the distribution of samples
310    pub fn analyze_sample_distribution(&self, samples: &[usize]) -> f64 {
311        if samples.is_empty() {
312            return 0.0;
313        }
314
315        let mean = samples.iter().sum::<usize>() as f64 / samples.len() as f64;
316        let variance = samples
317            .iter()
318            .map(|&x| {
319                let diff = x as f64 - mean;
320                diff * diff
321            })
322            .sum::<f64>()
323            / samples.len() as f64;
324
325        variance
326    }
327
328    /// Generate comprehensive sampling results
329    pub fn generate_sampling_results(&self, dataset_size: usize) -> SamplingResults {
330        let start_sequential = Instant::now();
331        let sequential_sampler = SequentialSampler::new(dataset_size);
332        let sequential_samples: Vec<usize> = sequential_sampler.iter().take(100).collect();
333        let sequential_time = start_sequential.elapsed();
334
335        let start_random = Instant::now();
336        let random_sampler = RandomSampler::new(dataset_size, None, false);
337        let random_samples: Vec<usize> = random_sampler.iter().take(100).collect();
338        let random_time = start_random.elapsed();
339
340        let sample_distribution_variance = self.analyze_sample_distribution(&random_samples);
341
342        SamplingResults {
343            sequential_samples,
344            random_samples,
345            sequential_time,
346            random_time,
347            sample_distribution_variance,
348        }
349    }
350}
351
352impl Benchmarkable for SamplingStrategyBench {
353    type Input = TensorDataset<f32>;
354    type Output = Vec<usize>;
355
356    fn setup(&mut self, size: usize) -> Self::Input {
357        let num_samples = size * 100;
358        let mut tensors = Vec::new();
359        for _ in 0..num_samples {
360            tensors.push(rand::<f32>(&[16, 16]).expect("tensor creation should succeed"));
361        }
362        TensorDataset::new(tensors)
363    }
364
365    fn run(&mut self, input: &Self::Input) -> Self::Output {
366        // Test different sampling strategies
367        let sequential_sampler = SequentialSampler::new(input.len());
368        let random_sampler = RandomSampler::new(input.len(), None, false);
369
370        // Collect samples from both samplers
371        let mut samples = Vec::new();
372
373        // Sequential sampling
374        samples.extend(sequential_sampler.iter().take(100));
375
376        // Random sampling
377        samples.extend(random_sampler.iter().take(100));
378
379        samples
380    }
381
382    fn bytes_accessed(&self, _size: usize) -> usize {
383        200 * 16 * 16 * std::mem::size_of::<f32>() // 200 samples
384    }
385}
386
387// ================================================================================================
388// Dataset Combination Benchmarks
389// ================================================================================================
390
391/// Concat dataset benchmarks (simplified)
392///
393/// Tests the performance of concatenating multiple datasets and accessing
394/// data across dataset boundaries.
395pub struct ConcatDatasetBench;
396
397impl ConcatDatasetBench {
398    /// Calculate the overhead of dataset concatenation
399    pub fn calculate_concat_overhead(
400        &self,
401        individual_access_time: Duration,
402        concat_access_time: Duration,
403    ) -> f64 {
404        concat_access_time.as_secs_f64() / individual_access_time.as_secs_f64() - 1.0
405    }
406}
407
408impl Benchmarkable for ConcatDatasetBench {
409    type Input = TensorDataset<f32>;
410    type Output = Vec<Tensor<f32>>;
411
412    fn setup(&mut self, size: usize) -> Self::Input {
413        // Simplified: just use a single dataset
414        let num_samples = size * 10;
415        let mut tensors = Vec::new();
416        for _ in 0..num_samples {
417            tensors.push(rand::<f32>(&[16, 16]).expect("tensor creation should succeed"));
418        }
419        TensorDataset::new(tensors)
420    }
421
422    fn run(&mut self, input: &Self::Input) -> Self::Output {
423        // Just return first 10 samples directly without dataloader for now
424        let mut results = Vec::new();
425        for i in 0..std::cmp::min(10, input.len()) {
426            if let Ok(item) = input.get(i) {
427                results.extend(item);
428            }
429        }
430        results
431    }
432
433    fn bytes_accessed(&self, _size: usize) -> usize {
434        10 * 16 * 16 * std::mem::size_of::<f32>()
435    }
436}
437
438// ================================================================================================
439// Distributed Data Loading Benchmarks
440// ================================================================================================
441
442/// Distributed sampler benchmarks
443///
444/// Tests the performance of distributed data loading scenarios where data
445/// needs to be partitioned across multiple processes or machines.
446pub struct DistributedSamplerBench;
447
448/// Configuration for distributed sampling
449#[derive(Debug, Clone)]
450pub struct DistributedConfig {
451    pub num_replicas: usize,
452    pub rank: usize,
453    pub epoch: usize,
454    pub drop_last: bool,
455}
456
457impl Default for DistributedConfig {
458    fn default() -> Self {
459        Self {
460            num_replicas: 4,
461            rank: 0,
462            epoch: 0,
463            drop_last: false,
464        }
465    }
466}
467
468impl DistributedSamplerBench {
469    /// Calculate the expected samples per replica
470    pub fn samples_per_replica(&self, total_samples: usize, config: &DistributedConfig) -> usize {
471        if config.drop_last {
472            total_samples / config.num_replicas
473        } else {
474            (total_samples + config.num_replicas - 1) / config.num_replicas
475        }
476    }
477
478    /// Validate distributed sampling correctness
479    pub fn validate_distribution(
480        &self,
481        samples: &[usize],
482        config: &DistributedConfig,
483        total_samples: usize,
484    ) -> bool {
485        // Check that samples are within valid range and properly distributed
486        samples.iter().all(|&sample| sample < total_samples)
487            && samples.len() <= self.samples_per_replica(total_samples, config)
488    }
489}
490
491impl Benchmarkable for DistributedSamplerBench {
492    type Input = TensorDataset<f32>;
493    type Output = Vec<usize>;
494
495    fn setup(&mut self, size: usize) -> Self::Input {
496        let num_samples = size * 100;
497        let mut tensors = Vec::new();
498        for _ in 0..num_samples {
499            tensors.push(rand::<f32>(&[8, 8]).expect("tensor creation should succeed"));
500        }
501        TensorDataset::new(tensors)
502    }
503
504    fn run(&mut self, input: &Self::Input) -> Self::Output {
505        // Simulate distributed training with 4 processes
506        let num_replicas = 4;
507        let rank = 0;
508        let _epoch = 0;
509
510        let distributed_sampler = DistributedSampler::new(input.len(), num_replicas, rank, false);
511
512        distributed_sampler.iter().take(50).collect()
513    }
514
515    fn bytes_accessed(&self, _size: usize) -> usize {
516        50 * 8 * 8 * std::mem::size_of::<f32>()
517    }
518}
519
520// ================================================================================================
521// Memory and Performance Benchmarks
522// ================================================================================================
523
524/// Memory usage during data loading benchmarks
525///
526/// Monitors memory consumption patterns during data loading operations
527/// to identify memory leaks or excessive memory usage.
528pub struct DataLoadingMemoryBench;
529
530/// Memory usage statistics during data loading
531#[derive(Debug, Clone)]
532pub struct MemoryUsageStats {
533    pub initial_memory: usize,
534    pub peak_memory: usize,
535    pub final_memory: usize,
536    pub memory_growth: usize,
537    pub batches_processed: usize,
538    pub avg_memory_per_batch: f64,
539}
540
541impl DataLoadingMemoryBench {
542    /// Calculate memory efficiency metrics
543    pub fn calculate_memory_efficiency(
544        &self,
545        stats: &MemoryUsageStats,
546        theoretical_memory: usize,
547    ) -> f64 {
548        if theoretical_memory == 0 {
549            return 0.0;
550        }
551        stats.peak_memory as f64 / theoretical_memory as f64
552    }
553
554    /// Generate detailed memory usage statistics
555    pub fn generate_memory_stats(&self, batches_processed: usize) -> MemoryUsageStats {
556        // Mock memory statistics for benchmarking
557        let initial_memory = 1024 * 1024; // 1MB
558        let peak_memory = initial_memory + (batches_processed * 512 * 1024); // +512KB per batch
559        let final_memory = initial_memory + (batches_processed * 64 * 1024); // +64KB retained
560
561        MemoryUsageStats {
562            initial_memory,
563            peak_memory,
564            final_memory,
565            memory_growth: final_memory - initial_memory,
566            batches_processed,
567            avg_memory_per_batch: (peak_memory - initial_memory) as f64 / batches_processed as f64,
568        }
569    }
570}
571
572impl Benchmarkable for DataLoadingMemoryBench {
573    type Input = TensorDataset<f32>;
574    type Output = usize; // Return total batches processed
575
576    fn setup(&mut self, size: usize) -> Self::Input {
577        let num_samples = size * 50;
578        let mut tensors = Vec::new();
579        for _ in 0..num_samples {
580            tensors.push(rand::<f32>(&[64, 64]).expect("tensor creation should succeed"));
581        }
582        TensorDataset::new(tensors)
583    }
584
585    fn run(&mut self, input: &Self::Input) -> Self::Output {
586        let dataloader = simple_random_dataloader(input.clone(), 32, Some(42))
587            .expect("dataloader creation should succeed");
588
589        let mut count = 0;
590        for _batch in dataloader.iter().take(20) {
591            count += 1;
592            // Simulate processing time
593            std::thread::sleep(std::time::Duration::from_micros(100));
594        }
595
596        count
597    }
598
599    fn bytes_accessed(&self, _size: usize) -> usize {
600        20 * 32 * 64 * 64 * std::mem::size_of::<f32>() // 20 batches
601    }
602}
603
604// ================================================================================================
605// Advanced Data Loading Features
606// ================================================================================================
607
608/// Prefetching performance benchmarks
609///
610/// Tests the performance impact of prefetching strategies where batches
611/// are prepared in advance to reduce waiting time during training.
612pub struct PrefetchingBench {
613    prefetch_factor: usize,
614}
615
616impl PrefetchingBench {
617    pub fn new(prefetch_factor: usize) -> Self {
618        Self { prefetch_factor }
619    }
620
621    /// Calculate the theoretical speedup from prefetching
622    pub fn theoretical_speedup(&self, processing_time: Duration, loading_time: Duration) -> f64 {
623        let total_time = processing_time + loading_time;
624        let prefetched_time = processing_time.max(loading_time);
625        total_time.as_secs_f64() / prefetched_time.as_secs_f64()
626    }
627
628    /// Get the prefetch factor used in this benchmark
629    pub fn get_prefetch_factor(&self) -> usize {
630        self.prefetch_factor
631    }
632}
633
634impl Benchmarkable for PrefetchingBench {
635    type Input = TensorDataset<f32>;
636    type Output = Vec<Vec<Tensor<f32>>>;
637
638    fn setup(&mut self, size: usize) -> Self::Input {
639        let num_samples = size * 30;
640        let mut tensors = Vec::new();
641        for _ in 0..num_samples {
642            tensors.push(rand::<f32>(&[32, 32]).expect("tensor creation should succeed"));
643        }
644        TensorDataset::new(tensors)
645    }
646
647    fn run(&mut self, input: &Self::Input) -> Self::Output {
648        // Note: The simple API doesn't expose prefetch_factor,
649        // so this is a simplified version of the benchmark
650        let dataloader = simple_dataloader(input.clone(), 16, false)
651            .expect("dataloader creation should succeed");
652        dataloader
653            .iter()
654            .take(15)
655            .map(|batch| batch.expect("batch should succeed"))
656            .collect()
657    }
658
659    fn bytes_accessed(&self, _size: usize) -> usize {
660        15 * 16 * 32 * 32 * std::mem::size_of::<f32>()
661    }
662}
663
664// ================================================================================================
665// Comprehensive Test Suite
666// ================================================================================================
667
668#[cfg(test)]
669mod tests {
670    use super::*;
671    use approx::assert_relative_eq;
672
673    #[test]
674    fn test_dataloader_throughput_bench() {
675        let mut bench = DataLoaderThroughputBench;
676        let input = bench.setup(10);
677        assert!(!input.is_empty());
678
679        let output = bench.run(&input);
680        assert!(!output.is_empty());
681
682        let bytes = bench.bytes_accessed(10);
683        assert!(bytes > 0);
684    }
685
686    #[test]
687    fn test_multi_worker_bench() {
688        let mut bench = MultiWorkerDataLoaderBench::new(4);
689        assert_eq!(bench.worker_count(), 4);
690
691        let input = bench.setup(5);
692        let output = bench.run(&input);
693        assert!(!output.is_empty());
694    }
695
696    #[test]
697    fn test_batch_size_scaling_bench() {
698        let mut bench = BatchSizeScalingBench::new(32);
699        assert_eq!(bench.get_batch_size(), 32);
700
701        let input = bench.setup(10);
702        let output = bench.run(&input);
703        assert!(!output.is_empty());
704
705        let throughput = bench.calculate_throughput(Duration::from_secs(1), 10);
706        assert!(throughput > 0.0);
707    }
708
709    #[test]
710    fn test_transform_pipeline_bench() {
711        let mut bench = TransformPipelineBench;
712        let input = bench.setup(8);
713        let output = bench.run(&input);
714        assert!(!output.is_empty());
715
716        let ops = bench.calculate_transform_ops(100, 3);
717        assert_eq!(ops, 300);
718
719        let flops = bench.estimate_transform_flops(1024, 5);
720        assert_eq!(flops, 1024 * 5 * 3);
721    }
722
723    #[test]
724    fn test_sampling_strategy_bench() {
725        let mut bench = SamplingStrategyBench;
726        let input = bench.setup(5);
727        let output = bench.run(&input);
728        // Note: Samplers may return fewer samples than requested based on implementation
729        assert!(output.len() > 0); // At least some samples were collected
730        assert!(output.len() <= 200); // But not more than the theoretical maximum
731
732        let samples = vec![1, 2, 3, 4, 5];
733        let variance = bench.analyze_sample_distribution(&samples);
734        assert!(variance >= 0.0);
735
736        let results = bench.generate_sampling_results(1000);
737        assert_eq!(results.sequential_samples.len(), 100);
738        assert_eq!(results.random_samples.len(), 100);
739    }
740
741    #[test]
742    fn test_concat_dataset_bench() {
743        let mut bench = ConcatDatasetBench;
744        let input = bench.setup(8);
745        let output = bench.run(&input);
746        assert!(!output.is_empty());
747
748        let overhead =
749            bench.calculate_concat_overhead(Duration::from_millis(10), Duration::from_millis(15));
750        assert_relative_eq!(overhead, 0.5, epsilon = 1e-10);
751    }
752
753    #[test]
754    fn test_distributed_sampler_bench() {
755        let mut bench = DistributedSamplerBench;
756        let input = bench.setup(5);
757        let output = bench.run(&input);
758        assert!(!output.is_empty());
759
760        let config = DistributedConfig::default();
761        let samples_per_replica = bench.samples_per_replica(1000, &config);
762        assert_eq!(samples_per_replica, 250);
763
764        let valid = bench.validate_distribution(&output, &config, input.len());
765        assert!(valid);
766    }
767
768    #[test]
769    fn test_data_loading_memory_bench() {
770        let mut bench = DataLoadingMemoryBench;
771        let input = bench.setup(5);
772        let output = bench.run(&input);
773        assert!(output > 0);
774
775        let stats = bench.generate_memory_stats(10);
776        assert_eq!(stats.batches_processed, 10);
777        assert!(stats.peak_memory > stats.initial_memory);
778
779        let efficiency = bench.calculate_memory_efficiency(&stats, 10 * 1024 * 1024);
780        assert!(efficiency > 0.0);
781    }
782
783    #[test]
784    fn test_prefetching_bench() {
785        let mut bench = PrefetchingBench::new(2);
786        assert_eq!(bench.get_prefetch_factor(), 2);
787
788        let input = bench.setup(5);
789        let output = bench.run(&input);
790        assert!(!output.is_empty());
791
792        let speedup =
793            bench.theoretical_speedup(Duration::from_millis(100), Duration::from_millis(50));
794        assert!(speedup > 1.0);
795    }
796
797    #[test]
798    fn test_helper_functions() {
799        let tensors = vec![
800            rand::<f32>(&[4, 4]).expect("operation should succeed"),
801            rand::<f32>(&[4, 4]).expect("operation should succeed"),
802        ];
803        let dataset = TensorDataset::new(tensors);
804
805        let dataloader = simple_dataloader(dataset.clone(), 1, false);
806        assert!(dataloader.is_ok());
807
808        let random_dataloader = simple_random_dataloader(dataset, 1, Some(42));
809        assert!(random_dataloader.is_ok());
810    }
811
812    #[test]
813    fn test_distributed_config() {
814        let config = DistributedConfig::default();
815        assert_eq!(config.num_replicas, 4);
816        assert_eq!(config.rank, 0);
817        assert_eq!(config.epoch, 0);
818        assert!(!config.drop_last);
819
820        let custom_config = DistributedConfig {
821            num_replicas: 8,
822            rank: 2,
823            epoch: 5,
824            drop_last: true,
825        };
826        assert_eq!(custom_config.num_replicas, 8);
827        assert_eq!(custom_config.rank, 2);
828    }
829
830    #[test]
831    fn test_sampling_results_analysis() {
832        let bench = SamplingStrategyBench;
833
834        // Test empty samples
835        let empty_variance = bench.analyze_sample_distribution(&[]);
836        assert_eq!(empty_variance, 0.0);
837
838        // Test uniform samples
839        let uniform_samples = vec![5, 5, 5, 5, 5];
840        let uniform_variance = bench.analyze_sample_distribution(&uniform_samples);
841        assert_eq!(uniform_variance, 0.0);
842
843        // Test varied samples
844        let varied_samples = vec![1, 3, 5, 7, 9];
845        let varied_variance = bench.analyze_sample_distribution(&varied_samples);
846        assert!(varied_variance > 0.0);
847    }
848
849    #[test]
850    fn test_memory_usage_stats() {
851        let bench = DataLoadingMemoryBench;
852        let stats = bench.generate_memory_stats(5);
853
854        assert!(stats.peak_memory >= stats.initial_memory);
855        assert!(stats.final_memory >= stats.initial_memory);
856        assert_eq!(stats.batches_processed, 5);
857        assert_eq!(
858            stats.memory_growth,
859            stats.final_memory - stats.initial_memory
860        );
861        assert!(stats.avg_memory_per_batch > 0.0);
862
863        let efficiency = bench.calculate_memory_efficiency(&stats, 0);
864        assert_eq!(efficiency, 0.0);
865    }
866}