#[cfg(test)]
use super::*;
#[cfg(test)]
use std::time::Instant;
struct PerfDataGen;
impl PerfDataGen {
pub fn sorted_sequence(size: usize) -> Vec<u32> {
(0..size as u32).collect()
}
pub fn small_range(size: usize) -> Vec<u32> {
(0..size).map(|i| (i % 1000) as u32).collect()
}
pub fn sparse_data(size: usize) -> Vec<u32> {
(0..size).map(|i| (i * 113 + 1000) as u32).collect()
}
pub fn nearly_identical(size: usize) -> Vec<u32> {
(0..size).map(|i| 42 + (i % 3) as u32).collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
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);
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;
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());
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);
}
_ => {
assert!(ratio <= 1.0, "Should not expand data");
}
}
assert!(memory_usage < original_size, "Should use less memory than original");
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();
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);
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);
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());
let expected_throughput = if data_size_mb < 0.1 {
15.0 } else if data_size_mb < 1.0 {
30.0 } else {
50.0 };
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 ===");
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();
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();
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());
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());
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);
assert!(memory_usage < original_size, "Should use less memory than original");
assert!(compression_ratio < 0.5, "Should achieve >50% compression for this pattern");
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; 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());
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());
assert!(ratio < 0.5, "Should maintain good compression for large datasets");
assert!(memory_mb < original_size_mb, "Should use less memory than original");
for i in (0..size).step_by(1000) {
assert_eq!(compressed.get(i), Some(data[i]), "Mismatch at index {}", i);
}
}
}