#[cfg(test)]
use super::*;
#[cfg(test)]
use std::time::Instant;
#[cfg(test)]
struct PerfDataGen;
#[cfg(test)]
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);
}
}
}