zipora 3.1.2

High-performance Rust implementation providing advanced data structures and compression algorithms with memory safety guarantees. Features LRU page cache, sophisticated caching layer, fiber-based concurrency, real-time compression, secure memory pools, SIMD optimizations, and complete C FFI for migration from C++.
//! Performance validation tests for IntVec<T>
//!
//! These tests validate the performance targets and ensure the implementation
//! meets the compression ratio and speed requirements.
//!
//! **IMPORTANT**: These tests only run in release mode (`cargo test --release`).
//! Debug mode has no optimizations, making timing measurements meaningless.

#[cfg(test)]
use super::*;
#[cfg(test)]
use std::time::Instant;

/// Performance test data generator
struct PerfDataGen;

impl PerfDataGen {
    /// Generate sorted sequence - should achieve excellent compression
    pub fn sorted_sequence(size: usize) -> Vec<u32> {
        (0..size as u32).collect()
    }

    /// Generate small range data - should compress very well
    pub fn small_range(size: usize) -> Vec<u32> {
        (0..size).map(|i| (i % 1000) as u32).collect()
    }

    /// Generate sparse data with larger gaps
    pub fn sparse_data(size: usize) -> Vec<u32> {
        (0..size).map(|i| (i * 113 + 1000) as u32).collect()
    }

    /// Generate nearly identical values
    pub fn nearly_identical(size: usize) -> Vec<u32> {
        (0..size).map(|i| 42 + (i % 3) as u32).collect()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    /// Skip performance tests in debug mode — timing measurements are meaningless
    /// without optimizations. Run with `cargo test --release`.
    fn is_release_build() -> bool {
        !cfg!(debug_assertions)
    }

    #[test]
    fn test_compression_performance() {
        if !is_release_build() {
            println!("Skipping performance test in debug mode");
            return;
        }
        let sizes = vec![1000, 10000, 100000];
        
        for size in sizes {
            println!("\n=== Testing compression performance for {} elements ===", size);
            
            // Test different data patterns
            let test_cases = vec![
                ("sorted", PerfDataGen::sorted_sequence(size)),
                ("small_range", PerfDataGen::small_range(size)),
                ("sparse", PerfDataGen::sparse_data(size)),
                ("nearly_identical", PerfDataGen::nearly_identical(size)),
            ];

            for (pattern, data) in test_cases {
                let original_size = data.len() * 4; // u32 = 4 bytes
                
                let start = Instant::now();
                let compressed = IntVec::<u32>::from_slice(&data).unwrap();
                let compression_time = start.elapsed();
                
                let ratio = compressed.compression_ratio();
                let memory_usage = compressed.memory_usage();
                let stats = compressed.stats();
                
                println!("{} pattern ({} elements):", pattern, size);
                println!("  Original size: {} bytes", original_size);
                println!("  Compressed size: {} bytes", stats.compressed_size);
                println!("  Memory usage: {} bytes", memory_usage);
                println!("  Compression ratio: {:.3}", ratio);
                println!("  Space savings: {:.1}%", (1.0 - ratio) * 100.0);
                println!("  Compression time: {:?}", compression_time);
                println!("  Throughput: {:.1} MB/s", 
                        (original_size as f64 / 1_048_576.0) / compression_time.as_secs_f64());
                
                // Validate compression targets
                match pattern {
                    "sorted" | "nearly_identical" => {
                        assert!(ratio < 0.2, "Pattern '{}' should achieve >80% compression, got {:.3}", pattern, ratio);
                    }
                    "small_range" => {
                        assert!(ratio < 0.4, "Pattern '{}' should achieve >60% compression, got {:.3}", pattern, ratio);
                    }
                    _ => {
                        // Other patterns should still provide some compression
                        assert!(ratio <= 1.0, "Should not expand data");
                    }
                }
                
                // Validate performance targets
                assert!(memory_usage < original_size, "Should use less memory than original");
                
                // Verify correctness
                for (i, &expected) in data.iter().enumerate() {
                    assert_eq!(compressed.get(i), Some(expected), 
                             "Mismatch at index {} for pattern '{}'", i, pattern);
                }
            }
        }
    }

    #[test]
    fn test_random_access_performance() {
        if !is_release_build() {
            println!("Skipping performance test in debug mode");
            return;
        }
        let size = 100000;
        let data = PerfDataGen::small_range(size);
        let compressed = IntVec::<u32>::from_slice(&data).unwrap();
        
        // Generate random indices
        let indices: Vec<usize> = (0..10000).map(|i| (i * 97) % size).collect();
        
        println!("\n=== Testing random access performance ===");
        println!("Dataset size: {} elements", size);
        println!("Number of accesses: {}", indices.len());
        
        let start = Instant::now();
        for &index in &indices {
            let _value = compressed.get(index);
        }
        let access_time = start.elapsed();
        
        let access_per_sec = indices.len() as f64 / access_time.as_secs_f64();
        
        println!("Total time: {:?}", access_time);
        println!("Access rate: {:.0} accesses/sec", access_per_sec);
        println!("Average access time: {:.1} ns", access_time.as_nanos() as f64 / indices.len() as f64);
        
        // Validate performance - should be very fast (millions of accesses per second)
        assert!(access_per_sec > 1_000_000.0, 
               "Random access should exceed 1M accesses/sec, got {:.0}", access_per_sec);
    }

    #[test]
    fn test_sequential_access_performance() {
        if !is_release_build() {
            println!("Skipping performance test in debug mode");
            return;
        }
        let size = 100000;
        let data = PerfDataGen::small_range(size);
        let compressed = IntVec::<u32>::from_slice(&data).unwrap();
        
        println!("\n=== Testing sequential access performance ===");
        println!("Dataset size: {} elements", size);
        
        let start = Instant::now();
        for i in 0..size {
            let _value = compressed.get(i);
        }
        let access_time = start.elapsed();
        
        let throughput = size as f64 / access_time.as_secs_f64();
        
        println!("Total time: {:?}", access_time);
        println!("Throughput: {:.0} accesses/sec", throughput);
        println!("Average access time: {:.1} ns", access_time.as_nanos() as f64 / size as f64);
        
        // Sequential access should be even faster than random access
        assert!(throughput > 2_000_000.0, 
               "Sequential access should exceed 2M accesses/sec, got {:.0}", throughput);
    }

    #[test]
    fn test_construction_performance() {
        if !is_release_build() {
            println!("Skipping performance test in debug mode");
            return;
        }

        let sizes = vec![10000, 100000, 1000000];

        println!("\n=== Testing construction performance ===");

        for size in sizes {
            let data = PerfDataGen::small_range(size);
            let data_size_mb = (data.len() * 4) as f64 / 1_048_576.0;

            let start = Instant::now();
            let compressed = IntVec::<u32>::from_slice(&data).unwrap();
            let construction_time = start.elapsed();

            let throughput_mb_s = data_size_mb / construction_time.as_secs_f64();

            println!("Size: {} elements ({:.1} MB)", size, data_size_mb);
            println!("  Construction time: {:?}", construction_time);
            println!("  Throughput: {:.1} MB/s", throughput_mb_s);
            println!("  Compression ratio: {:.3}", compressed.compression_ratio());

            // Construction throughput: strategy analysis + bit-packing compression.
            // Small datasets are dominated by per-element analysis overhead.
            // Thresholds are conservative minimums for loaded machines.
            let expected_throughput = if data_size_mb < 0.1 {
                15.0   // Small datasets (40KB): analysis overhead dominates
            } else if data_size_mb < 1.0 {
                30.0   // Medium datasets: better amortization
            } else {
                50.0   // Large datasets: compression throughput dominates
            };

            assert!(throughput_mb_s > expected_throughput,
                   "Construction should exceed {:.0} MB/s for {:.1} MB dataset, got {:.1}",
                   expected_throughput, data_size_mb, throughput_mb_s);
        }
    }

    #[test]
    fn test_integer_type_performance() {
        if !is_release_build() {
            println!("Skipping performance test in debug mode");
            return;
        }
        let size = 50000;
        
        println!("\n=== Testing performance across integer types ===");
        
        // Use small ranges for all types to create fair compression test conditions
        // u8: 0-99 (needs 7 bits instead of 8)
        // u32: 0-999 (needs 10 bits instead of 32)  
        // u64: 0-999 (needs 10 bits instead of 64)
        
        // Test u8 - use small range for fair comparison
        let u8_data: Vec<u8> = (0..size).map(|i| (i % 100) as u8).collect();
        let start = Instant::now();
        let u8_compressed = IntVec::<u8>::from_slice(&u8_data).unwrap();
        let u8_time = start.elapsed();
        
        // Test u32 
        let u32_data: Vec<u32> = (0..size).map(|i| (i % 1000) as u32).collect();
        let start = Instant::now();
        let u32_compressed = IntVec::<u32>::from_slice(&u32_data).unwrap();
        let u32_time = start.elapsed();
        
        // Test u64
        let u64_data: Vec<u64> = (0..size).map(|i| (i % 1000) as u64).collect();
        let start = Instant::now();
        let u64_compressed = IntVec::<u64>::from_slice(&u64_data).unwrap();
        let u64_time = start.elapsed();
        
        println!("u8:  time={:?}, ratio={:.3}, memory={} bytes", 
                u8_time, u8_compressed.compression_ratio(), u8_compressed.memory_usage());
        println!("u32: time={:?}, ratio={:.3}, memory={} bytes", 
                u32_time, u32_compressed.compression_ratio(), u32_compressed.memory_usage());
        println!("u64: time={:?}, ratio={:.3}, memory={} bytes", 
                u64_time, u64_compressed.compression_ratio(), u64_compressed.memory_usage());
        
        // Compression expectations based on type constraints:
        // u8 with range 0-99: needs 7 bits vs 8 bits = ~12.5% theoretical savings, but overhead limits actual compression
        // u32 with range 0-999: needs 10 bits vs 32 bits = ~69% theoretical savings
        // u64 with range 0-999: needs 10 bits vs 64 bits = ~84% theoretical savings
        assert!(u8_compressed.compression_ratio() < 0.9, "u8 should achieve some compression, got {:.3}", u8_compressed.compression_ratio());
        assert!(u32_compressed.compression_ratio() < 0.5, "u32 should achieve good compression, got {:.3}", u32_compressed.compression_ratio());
        assert!(u64_compressed.compression_ratio() < 0.5, "u64 should achieve good compression, got {:.3}", u64_compressed.compression_ratio());
        
        // Verify correctness
        for i in 0..1000 {
            assert_eq!(u8_compressed.get(i), Some(u8_data[i]));
            assert_eq!(u32_compressed.get(i), Some(u32_data[i]));
            assert_eq!(u64_compressed.get(i), Some(u64_data[i]));
        }
    }

    #[test]
    fn test_memory_efficiency() {
        if !is_release_build() {
            println!("Skipping performance test in debug mode");
            return;
        }
        let size = 100000;
        let data = PerfDataGen::small_range(size);
        let original_size = data.len() * 4;
        
        let compressed = IntVec::<u32>::from_slice(&data).unwrap();
        let memory_usage = compressed.memory_usage();
        let compression_ratio = compressed.compression_ratio();
        
        println!("\n=== Memory efficiency analysis ===");
        println!("Original size: {} bytes ({:.1} MB)", original_size, original_size as f64 / 1_048_576.0);
        println!("Memory usage: {} bytes ({:.1} MB)", memory_usage, memory_usage as f64 / 1_048_576.0);
        println!("Compression ratio: {:.3}", compression_ratio);
        println!("Space savings: {:.1}%", (1.0 - compression_ratio) * 100.0);
        
        // Validate memory efficiency targets
        assert!(memory_usage < original_size, "Should use less memory than original");
        assert!(compression_ratio < 0.5, "Should achieve >50% compression for this pattern");
        
        // Memory usage should be close to compressed size (minimal overhead)
        let stats = compressed.stats();
        let overhead = memory_usage as f64 / stats.compressed_size as f64;
        println!("Memory overhead factor: {:.2}x", overhead);
        assert!(overhead < 2.0, "Memory overhead should be reasonable, got {:.2}x", overhead);
    }

    #[test]
    fn test_stress_large_dataset() {
        if !is_release_build() {
            println!("Skipping performance test in debug mode");
            return;
        }
        let size = 1_000_000; // 1M elements
        let data = PerfDataGen::small_range(size);
        let original_size_mb = (data.len() * 4) as f64 / 1_048_576.0;
        
        println!("\n=== Stress test with large dataset ===");
        println!("Dataset size: {} elements ({:.1} MB)", size, original_size_mb);
        
        let start = Instant::now();
        let compressed = IntVec::<u32>::from_slice(&data).unwrap();
        let construction_time = start.elapsed();
        
        let ratio = compressed.compression_ratio();
        let memory_mb = compressed.memory_usage() as f64 / 1_048_576.0;
        
        println!("Construction time: {:?}", construction_time);
        println!("Compression ratio: {:.3}", ratio);
        println!("Memory usage: {:.1} MB", memory_mb);
        println!("Throughput: {:.1} MB/s", original_size_mb / construction_time.as_secs_f64());
        
        // Test random access on large dataset
        let test_indices: Vec<usize> = (0..10000).map(|i| (i * 997) % size).collect();
        let start = Instant::now();
        for &idx in &test_indices {
            let _value = compressed.get(idx);
        }
        let access_time = start.elapsed();
        
        println!("Random access time (10K accesses): {:?}", access_time);
        println!("Random access rate: {:.0} accesses/sec", 
                test_indices.len() as f64 / access_time.as_secs_f64());
        
        // Validate large dataset performance
        assert!(ratio < 0.5, "Should maintain good compression for large datasets");
        assert!(memory_mb < original_size_mb, "Should use less memory than original");
        
        // Verify correctness on sample
        for i in (0..size).step_by(1000) {
            assert_eq!(compressed.get(i), Some(data[i]), "Mismatch at index {}", i);
        }
    }
}