use crate::{codecs::CodecType, error::Result, metadata::CompressionMetadata};
use serde::{Deserialize, Serialize};
use std::time::Duration;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BenchmarkResult {
pub codec: String,
pub original_size: usize,
pub compressed_size: usize,
pub compression_ratio: f64,
pub space_savings: f64,
pub compression_time: Duration,
pub decompression_time: Duration,
pub compression_throughput: f64,
pub decompression_throughput: f64,
}
impl BenchmarkResult {
pub fn from_metadata(metadata: CompressionMetadata, decompression_time: Duration) -> Self {
let decompression_throughput = if decompression_time.as_secs_f64() > 0.0 {
(metadata.original_size as f64 / decompression_time.as_secs_f64()) / 1_048_576.0
} else {
0.0
};
Self {
codec: metadata.codec,
original_size: metadata.original_size,
compressed_size: metadata.compressed_size,
compression_ratio: metadata.compression_ratio,
space_savings: metadata.space_savings,
compression_time: metadata.duration.unwrap_or(Duration::ZERO),
decompression_time,
compression_throughput: metadata.throughput.unwrap_or(0.0),
decompression_throughput,
}
}
pub fn format_summary(&self) -> String {
format!(
"{}: {:.2}x ratio ({:.1}% savings), compress: {:.2} MB/s, decompress: {:.2} MB/s",
self.codec,
self.compression_ratio,
self.space_savings,
self.compression_throughput,
self.decompression_throughput
)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BenchmarkReport {
pub results: Vec<BenchmarkResult>,
pub best_ratio: String,
pub best_compression_speed: String,
pub best_decompression_speed: String,
pub best_balanced: String,
}
impl BenchmarkReport {
pub fn new(results: Vec<BenchmarkResult>) -> Self {
let best_ratio = results
.iter()
.max_by(|a, b| {
a.compression_ratio
.partial_cmp(&b.compression_ratio)
.unwrap_or(std::cmp::Ordering::Equal)
})
.map(|r| r.codec.clone())
.unwrap_or_default();
let best_compression_speed = results
.iter()
.max_by(|a, b| {
a.compression_throughput
.partial_cmp(&b.compression_throughput)
.unwrap_or(std::cmp::Ordering::Equal)
})
.map(|r| r.codec.clone())
.unwrap_or_default();
let best_decompression_speed = results
.iter()
.max_by(|a, b| {
a.decompression_throughput
.partial_cmp(&b.decompression_throughput)
.unwrap_or(std::cmp::Ordering::Equal)
})
.map(|r| r.codec.clone())
.unwrap_or_default();
let best_balanced = results
.iter()
.max_by(|a, b| {
let score_a =
a.compression_ratio * (a.compression_throughput + a.decompression_throughput);
let score_b =
b.compression_ratio * (b.compression_throughput + b.decompression_throughput);
score_a
.partial_cmp(&score_b)
.unwrap_or(std::cmp::Ordering::Equal)
})
.map(|r| r.codec.clone())
.unwrap_or_default();
Self {
results,
best_ratio,
best_compression_speed,
best_decompression_speed,
best_balanced,
}
}
pub fn format_report(&self) -> String {
let mut report = String::new();
report.push_str("Compression Benchmark Report\n");
report.push_str("=============================\n\n");
for result in &self.results {
report.push_str(&result.format_summary());
report.push('\n');
}
report.push_str("\nSummary:\n");
report.push_str(&format!(" Best Ratio: {}\n", self.best_ratio));
report.push_str(&format!(
" Best Compression Speed: {}\n",
self.best_compression_speed
));
report.push_str(&format!(
" Best Decompression Speed: {}\n",
self.best_decompression_speed
));
report.push_str(&format!(" Best Balanced: {}\n", self.best_balanced));
report
}
}
pub struct Benchmarker {
_iterations: usize,
}
impl Benchmarker {
pub fn new(iterations: usize) -> Self {
Self {
_iterations: iterations,
}
}
pub fn benchmark(&self, _data: &[u8], _codecs: &[CodecType]) -> Result<BenchmarkReport> {
Ok(BenchmarkReport {
results: Vec::new(),
best_ratio: String::new(),
best_compression_speed: String::new(),
best_decompression_speed: String::new(),
best_balanced: String::new(),
})
}
}
impl Default for Benchmarker {
fn default() -> Self {
Self::new(3)
}
}