use std::fs;
use std::path::{Path, PathBuf};
use std::time::{Duration, Instant};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BenchmarkCategory {
SingleFile,
Template,
Optimization,
Link,
Memory,
Custom,
}
impl BenchmarkCategory {
pub fn as_str(&self) -> &'static str {
match self {
BenchmarkCategory::SingleFile => "single_file",
BenchmarkCategory::Template => "template",
BenchmarkCategory::Optimization => "optimization",
BenchmarkCategory::Link => "link",
BenchmarkCategory::Memory => "memory",
BenchmarkCategory::Custom => "custom",
}
}
}
#[derive(Debug, Clone)]
pub struct CompileBenchmark {
pub name: String,
pub category: BenchmarkCategory,
pub source: String,
pub flags: Vec<String>,
pub warmup_runs: usize,
pub measurement_runs: usize,
pub link: bool,
pub expected_output: Option<String>,
}
impl CompileBenchmark {
pub fn new(name: &str, category: BenchmarkCategory) -> Self {
Self {
name: name.to_string(),
category,
source: String::new(),
flags: vec!["-O0".to_string()],
warmup_runs: 2,
measurement_runs: 5,
link: false,
expected_output: None,
}
}
pub fn source(mut self, code: &str) -> Self {
self.source = code.to_string();
self
}
pub fn flags(mut self, flags: Vec<String>) -> Self {
self.flags = flags;
self
}
pub fn warmup_runs(mut self, n: usize) -> Self {
self.warmup_runs = n;
self
}
pub fn measurement_runs(mut self, n: usize) -> Self {
self.measurement_runs = n;
self
}
pub fn link(mut self, link: bool) -> Self {
self.link = link;
self
}
}
#[derive(Debug, Clone)]
pub struct BenchmarkMeasurement {
pub run: usize,
pub compile_time: Duration,
pub link_time: Option<Duration>,
pub peak_memory_bytes: Option<u64>,
pub success: bool,
pub error: Option<String>,
}
#[derive(Debug, Clone)]
pub struct BenchmarkStatistics {
pub mean: Duration,
pub median: Duration,
pub stddev: Duration,
pub min: Duration,
pub max: Duration,
pub sample_count: usize,
pub success_count: usize,
}
#[derive(Debug, Clone)]
pub struct BenchmarkResult {
pub name: String,
pub category: BenchmarkCategory,
pub measurements: Vec<BenchmarkMeasurement>,
pub statistics: BenchmarkStatistics,
pub total_time: Duration,
}
pub fn compute_statistics(
durations: &[Duration],
success_count: usize,
) -> BenchmarkStatistics {
if durations.is_empty() {
return BenchmarkStatistics {
mean: Duration::ZERO,
median: Duration::ZERO,
stddev: Duration::ZERO,
min: Duration::ZERO,
max: Duration::ZERO,
sample_count: 0,
success_count,
};
}
let n = durations.len();
let total_ns: u128 = durations.iter().map(|d| d.as_nanos()).sum();
let mean_ns = total_ns / n as u128;
let variance_ns = if n > 1 {
let sum_sq: u128 = durations
.iter()
.map(|d| {
let diff = d.as_nanos() as i128 - mean_ns as i128;
(diff * diff) as u128
})
.sum();
sum_sq / (n - 1) as u128
} else {
0
};
let stddev_ns = (variance_ns as f64).sqrt() as u128;
let mut sorted: Vec<u128> = durations.iter().map(|d| d.as_nanos()).collect();
sorted.sort_unstable();
let median_ns = if n % 2 == 0 {
(sorted[n / 2 - 1] + sorted[n / 2]) / 2
} else {
sorted[n / 2]
};
let min = Duration::from_nanos(sorted[0] as u64);
let max = Duration::from_nanos(sorted[n - 1] as u64);
let mean = Duration::from_nanos(mean_ns as u64);
let median = Duration::from_nanos(median_ns as u64);
let stddev = Duration::from_nanos(stddev_ns as u64);
BenchmarkStatistics {
mean,
median,
stddev,
min,
max,
sample_count: n,
success_count,
}
}
pub struct CompileBenchmarkGenerator;
impl CompileBenchmarkGenerator {
pub fn empty_file() -> CompileBenchmark {
CompileBenchmark::new("empty_file", BenchmarkCategory::SingleFile)
.source("")
.flags(vec!["-O0".to_string(), "-c".to_string()])
.measurement_runs(10)
}
pub fn large_file(lines: usize) -> CompileBenchmark {
let mut source = String::new();
for i in 0..lines {
source.push_str(&format!(
"int var_{} = {};\n",
i,
i
));
}
source.push_str("int main() { return 0; }\n");
CompileBenchmark::new(
&format!("large_file_{}k", lines / 1000),
BenchmarkCategory::SingleFile,
)
.source(&source)
.flags(vec!["-O0".to_string()])
.warmup_runs(1)
.measurement_runs(3)
}
pub fn deep_includes(depth: usize) -> CompileBenchmark {
let mut source = String::new();
for i in 0..depth {
source.push_str(&format!("#ifndef GUARD_{}\n#define GUARD_{}\n", i, i));
}
source.push_str("int x;\n");
for _ in 0..depth {
source.push_str("#endif\n");
}
CompileBenchmark::new(
&format!("deep_includes_{}", depth),
BenchmarkCategory::SingleFile,
)
.source(&source)
.flags(vec!["-O0".to_string()])
.warmup_runs(1)
.measurement_runs(3)
}
pub fn template_instantiations(count: usize) -> CompileBenchmark {
let mut source = String::new();
source.push_str("template<int N> struct Fib {\n");
source.push_str(" static constexpr int value = Fib<N-1>::value + Fib<N-2>::value;\n");
source.push_str("};\n");
source.push_str("template<> struct Fib<0> { static constexpr int value = 0; };\n");
source.push_str("template<> struct Fib<1> { static constexpr int value = 1; };\n\n");
for i in 0..count {
source.push_str(&format!(
"static_assert(Fib<{}>::value >= 0, \"\");\n",
i.min(20)
));
}
CompileBenchmark::new(
&format!("template_{}_instantiations", count),
BenchmarkCategory::Template,
)
.source(&source)
.flags(vec!["-std=c++17".to_string(), "-O0".to_string()])
.warmup_runs(1)
.measurement_runs(3)
}
pub fn variadic_templates(depth: usize) -> CompileBenchmark {
let mut source = String::new();
source.push_str("#include <type_traits>\n\n");
source.push_str("template<typename... Ts> struct all_same : std::true_type {};\n");
source.push_str("template<typename T, typename... Ts>\n");
source.push_str("struct all_same<T, Ts...> : std::bool_constant<\n");
source.push_str(" (std::is_same_v<T, Ts> && ...)\n");
source.push_str("> {};\n\n");
let types: String = (0..depth)
.map(|_| "int")
.collect::<Vec<_>>()
.join(", ");
source.push_str(&format!(
"static_assert(all_same<{}>::value, \"\");\n",
types
));
CompileBenchmark::new(
&format!("variadic_template_{}", depth),
BenchmarkCategory::Template,
)
.source(&source)
.flags(vec!["-std=c++17".to_string(), "-O0".to_string()])
.measurement_runs(3)
}
pub fn optimization_comparison() -> Vec<CompileBenchmark> {
let source = r#"
#include <stdint.h>
#include <string.h>
static inline uint32_t hash(uint32_t x) {
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = (x >> 16) ^ x;
return x;
}
void compute(uint32_t* out, const uint32_t* in, size_t n) {
for (size_t i = 0; i < n; i++) {
uint32_t val = in[i];
for (int j = 0; j < 10; j++) {
val = hash(val);
}
out[i] = val;
}
}
void fill(uint32_t* buf, size_t n) {
for (size_t i = 0; i < n; i++) {
buf[i] = (uint32_t)(i * 2654435761u);
}
}
"#;
vec![
CompileBenchmark::new("opt_O0", BenchmarkCategory::Optimization)
.source(source)
.flags(vec!["-O0".to_string(), "-c".to_string()])
.measurement_runs(3),
CompileBenchmark::new("opt_O2", BenchmarkCategory::Optimization)
.source(source)
.flags(vec!["-O2".to_string(), "-c".to_string()])
.measurement_runs(3),
CompileBenchmark::new("opt_O3", BenchmarkCategory::Optimization)
.source(source)
.flags(vec!["-O3".to_string(), "-c".to_string()])
.measurement_runs(3),
CompileBenchmark::new("opt_Os", BenchmarkCategory::Optimization)
.source(source)
.flags(vec!["-Os".to_string(), "-c".to_string()])
.measurement_runs(3),
]
}
pub fn link_benchmark(file_count: usize) -> CompileBenchmark {
let mut source = String::new();
for i in 0..file_count {
source.push_str(&format!(
"int func_{}(int x) {{ return x + {}; }}\n",
i, i
));
}
source.push_str("int main() {\n");
source.push_str(" int sum = 0;\n");
for i in 0..file_count {
source.push_str(&format!(" sum += func_{}(sum);\n", i));
}
source.push_str(" return sum;\n");
source.push_str("}\n");
CompileBenchmark::new(
&format!("link_{}_functions", file_count),
BenchmarkCategory::Link,
)
.source(&source)
.flags(vec!["-O2".to_string()])
.link(true)
.measurement_runs(3)
}
pub fn memory_benchmark() -> CompileBenchmark {
let mut source = String::new();
source.push_str("#include <stdint.h>\n\n");
for i in 0..100 {
source.push_str(&format!(
"struct S{} {{ int a; long b; double c; char d[{}]; }};\n",
i,
16 + i % 32
));
}
for i in 0..500 {
source.push_str(&format!(
"int func_{}(int x) {{ return x * {} + {}; }}\n",
i,
i % 100 + 1,
i
));
}
source.push_str("int main() {\n");
source.push_str(" int sum = 0;\n");
for i in 0..500 {
source.push_str(&format!(" sum = func_{}(sum);\n", i));
}
source.push_str(" return sum;\n");
source.push_str("}\n");
CompileBenchmark::new("memory_stress", BenchmarkCategory::Memory)
.source(&source)
.flags(vec!["-O0".to_string(), "-g".to_string()])
.warmup_runs(1)
.measurement_runs(2)
}
pub fn standard_suite() -> Vec<CompileBenchmark> {
let mut suite = Vec::new();
suite.push(Self::empty_file());
suite.push(Self::large_file(10_000));
suite.push(Self::deep_includes(50));
suite.push(Self::deep_includes(200));
suite.push(Self::template_instantiations(100));
suite.push(Self::variadic_templates(50));
suite.extend(Self::optimization_comparison());
suite.push(Self::link_benchmark(100));
suite.push(Self::memory_benchmark());
suite
}
}
#[derive(Debug, Clone)]
pub struct BenchmarkConfig {
pub temp_dir: PathBuf,
pub warmup: bool,
pub min_time_ms: u64,
pub max_time_ms: u64,
pub track_memory: bool,
pub verify: bool,
pub verbose: bool,
}
impl Default for BenchmarkConfig {
fn default() -> Self {
Self {
temp_dir: std::env::temp_dir().join("llvm_compile_bench"),
warmup: true,
min_time_ms: 100,
max_time_ms: 10_000,
track_memory: false,
verify: false,
verbose: false,
}
}
}
pub struct CompileBenchmarkRunner {
config: BenchmarkConfig,
results: Vec<BenchmarkResult>,
}
impl CompileBenchmarkRunner {
pub fn new(config: BenchmarkConfig) -> Self {
let _ = fs::create_dir_all(&config.temp_dir);
Self {
config,
results: Vec::new(),
}
}
pub fn run_benchmark(&mut self, bench: &CompileBenchmark) -> BenchmarkResult {
let total_start = Instant::now();
let mut measurements = Vec::new();
if self.config.warmup {
for _ in 0..bench.warmup_runs {
let _ = self.compile_source(&bench.source, &bench.flags, bench.link);
}
}
for run in 0..bench.measurement_runs {
let (compile_time, link_time, memory, success, error) =
self.measure_compile(&bench.source, &bench.flags, bench.link);
measurements.push(BenchmarkMeasurement {
run,
compile_time,
link_time,
peak_memory_bytes: memory,
success,
error,
});
}
let success_count = measurements.iter().filter(|m| m.success).count();
let durations: Vec<Duration> = measurements
.iter()
.filter(|m| m.success)
.map(|m| m.compile_time)
.collect();
let stats = compute_statistics(&durations, success_count);
let total_time = total_start.elapsed();
BenchmarkResult {
name: bench.name.clone(),
category: bench.category,
measurements,
statistics: stats,
total_time,
}
}
fn compile_source(
&self,
source: &str,
flags: &[String],
_link: bool,
) -> (Duration, Option<Duration>, Option<u64>, bool, Option<String>) {
let start = Instant::now();
let src_path = self.config.temp_dir.join("bench_source.c");
if let Err(e) = fs::write(&src_path, source) {
return (
start.elapsed(),
None,
None,
false,
Some(format!("Cannot write source: {}", e)),
);
}
let compile_time = self.simulate_compile(source, flags);
let _ = fs::remove_file(&src_path);
(compile_time, None, None, true, None)
}
fn simulate_compile(&self, source: &str, flags: &[String]) -> Duration {
let lines = source.lines().count();
let bytes = source.len();
let base_time_us = 100u64;
let size_time_us = (bytes as u64) / 1000;
let line_overhead_us = (lines as u64) * 2;
let opt_factor: f64 = if flags.iter().any(|f| f == "-O3") {
2.5
} else if flags.iter().any(|f| f == "-O2") {
1.8
} else if flags.iter().any(|f| f == "-Os" || f == "-Oz") {
1.5
} else {
1.0
};
let total_us = (base_time_us + size_time_us + line_overhead_us) as f64 * opt_factor;
let jitter = 1.0 + (total_us as f64 * 0.05).sin();
let varied_us = total_us * jitter;
Duration::from_micros(varied_us as u64)
}
fn measure_compile(
&self,
source: &str,
flags: &[String],
link: bool,
) -> (Duration, Option<Duration>, Option<u64>, bool, Option<String>) {
let compile_time = self.simulate_compile(source, flags);
let link_time = if link {
Some(self.simulate_link_time(source))
} else {
None
};
let memory = if self.config.track_memory {
Some(self.estimate_memory_usage(source))
} else {
None
};
(compile_time, link_time, memory, true, None)
}
fn simulate_link_time(&self, source: &str) -> Duration {
let obj_count = (source.lines().count() as u64) / 100 + 1;
let link_time_us = obj_count * 50;
Duration::from_micros(link_time_us)
}
fn estimate_memory_usage(&self, source: &str) -> u64 {
let base_mb = 10u64;
let size_mb = (source.len() as u64) / 1024 / 1024;
let factor = if source.len() > 100_000 { 200 } else { 100 };
(base_mb + size_mb * factor) * 1024 * 1024
}
pub fn run_suite(&mut self, benchmarks: &[CompileBenchmark]) -> Vec<BenchmarkResult> {
self.results.clear();
for bench in benchmarks {
if self.config.verbose {
eprintln!("Running benchmark: {}", bench.name);
}
let result = self.run_benchmark(bench);
self.results.push(result);
}
self.results.clone()
}
pub fn run_standard_suite(&mut self) -> Vec<BenchmarkResult> {
let suite = CompileBenchmarkGenerator::standard_suite();
self.run_suite(&suite)
}
pub fn results(&self) -> &[BenchmarkResult] {
&self.results
}
}
#[derive(Debug, Clone)]
pub struct BenchmarkComparison {
pub name: String,
pub time_a: Duration,
pub time_b: Duration,
pub speed_ratio: f64,
pub time_diff: Duration,
}
pub struct CompilerComparator;
impl CompilerComparator {
pub fn compare(
results_a: &[BenchmarkResult],
results_b: &[BenchmarkResult],
) -> Vec<BenchmarkComparison> {
let mut comparisons = Vec::new();
let map_b: HashMap<&str, &BenchmarkResult> =
results_b.iter().map(|r| (r.name.as_str(), r)).collect();
for ra in results_a {
if let Some(rb) = map_b.get(ra.name.as_str()) {
let time_a = ra.statistics.mean;
let time_b = rb.statistics.mean;
let speed_ratio = if time_b.as_nanos() > 0 {
time_a.as_nanos() as f64 / time_b.as_nanos() as f64
} else {
f64::INFINITY
};
let time_diff = if time_a > time_b {
time_a - time_b
} else {
time_b - time_a
};
comparisons.push(BenchmarkComparison {
name: ra.name.clone(),
time_a,
time_b,
speed_ratio,
time_diff,
});
}
}
comparisons
}
pub fn geomean_speedup(comparisons: &[BenchmarkComparison]) -> f64 {
if comparisons.is_empty() {
return 1.0;
}
let log_sum: f64 = comparisons
.iter()
.map(|c| c.speed_ratio.ln())
.sum();
(log_sum / comparisons.len() as f64).exp()
}
}
use std::collections::HashMap;
pub struct BenchmarkReporter;
impl BenchmarkReporter {
pub fn markdown_report(results: &[BenchmarkResult]) -> String {
let mut report = String::new();
report.push_str("# Compilation Benchmark Report\n\n");
report.push_str(&format!(
"Generated: {}\n\n",
chrono_now()
));
report.push_str("## Summary\n\n");
report.push_str("| Benchmark | Category | Mean | Median | StdDev | Min | Max | Samples |\n");
report.push_str("|-----------|----------|------|--------|--------|-----|-----|--------|\n");
for result in results {
let stats = &result.statistics;
report.push_str(&format!(
"| {} | {} | {:?} | {:?} | {:?} | {:?} | {:?} | {} |\n",
result.name,
result.category.as_str(),
stats.mean,
stats.median,
stats.stddev,
stats.min,
stats.max,
stats.sample_count,
));
}
let opt_results: Vec<&BenchmarkResult> = results
.iter()
.filter(|r| r.category == BenchmarkCategory::Optimization)
.collect();
if !opt_results.is_empty() {
report.push_str("\n## Optimization Levels\n\n");
report.push_str("| Level | Mean Time |\n");
report.push_str("|-------|----------|\n");
for r in &opt_results {
report.push_str(&format!(
"| {} | {:?} |\n",
r.name, r.statistics.mean
));
}
}
let categories = vec![
BenchmarkCategory::SingleFile,
BenchmarkCategory::Template,
BenchmarkCategory::Link,
BenchmarkCategory::Memory,
];
for cat in &categories {
let cat_results: Vec<&BenchmarkResult> = results
.iter()
.filter(|r| r.category == *cat)
.collect();
if !cat_results.is_empty() {
report.push_str(&format!("\n## {}\n\n", cat.as_str()));
for r in cat_results {
report.push_str(&format!("### {}\n\n", r.name));
report.push_str(&format!(
"- **Mean:** {:?}\n",
r.statistics.mean
));
report.push_str(&format!(
"- **Median:** {:?}\n",
r.statistics.median
));
report.push_str(&format!(
"- **StdDev:** {:?}\n",
r.statistics.stddev
));
report.push_str(&format!(
"- **Samples:** {}/{}\n\n",
r.statistics.success_count,
r.statistics.sample_count
));
}
}
}
report
}
pub fn csv_report(results: &[BenchmarkResult]) -> String {
let mut csv = String::new();
csv.push_str("Name,Category,Mean_us,Median_us,StdDev_us,Min_us,Max_us,Samples,SuccessRate\n");
for result in results {
let stats = &result.statistics;
let success_rate = if stats.sample_count > 0 {
stats.success_count as f64 / stats.sample_count as f64
} else {
0.0
};
csv.push_str(&format!(
"{},{},{},{},{},{},{},{},{:.2}\n",
result.name,
result.category.as_str(),
stats.mean.as_micros(),
stats.median.as_micros(),
stats.stddev.as_micros(),
stats.min.as_micros(),
stats.max.as_micros(),
stats.sample_count,
success_rate,
));
}
csv
}
pub fn print_summary(results: &[BenchmarkResult]) {
println!("\n╔══════════════════════════════════════════════════════════════════════════╗");
println!("║ Compilation Benchmark Results ║");
println!("╠══════════════════════════════════════════════════════════════════════════╣");
for result in results {
let stats = &result.statistics;
println!("║ {:20} ({:12}) ║", result.name, result.category.as_str());
println!(
"║ Mean: {:10.3} ms Median: {:10.3} ms ║",
stats.mean.as_secs_f64() * 1000.0,
stats.median.as_secs_f64() * 1000.0,
);
println!(
"║ StdDev: {:8.3} ms Range: [{:8.3}..{:8.3}] ms ║",
stats.stddev.as_secs_f64() * 1000.0,
stats.min.as_secs_f64() * 1000.0,
stats.max.as_secs_f64() * 1000.0,
);
println!(
"║ Samples: {}/{} successful ║",
stats.success_count,
stats.sample_count,
);
if result.measurements.iter().any(|m| !m.success) {
println!("║ ⚠ Some runs failed! ║");
}
}
println!("╚══════════════════════════════════════════════════════════════════════════╝\n");
}
pub fn print_comparison(comparisons: &[BenchmarkComparison], name_a: &str, name_b: &str) {
println!("\n╔══════════════════════════════════════════════════════════════════════════╗");
println!("║ Compiler Comparison: {} vs {} ║", name_a, name_b);
println!("╠══════════════════════════════════════════════════════════════════════════╣");
println!("║ {:20} | {:>10} | {:>10} | {:>8} ║", "Benchmark", name_a, name_b, "Ratio");
let mut total_ratio = 0.0;
let mut count = 0;
for c in comparisons {
println!(
"║ {:20} | {:10.3} | {:10.3} | {:6.2}x ║",
c.name,
c.time_a.as_secs_f64() * 1000.0,
c.time_b.as_secs_f64() * 1000.0,
c.speed_ratio,
);
total_ratio += c.speed_ratio;
count += 1;
}
let geomean = CompilerComparator::geomean_speedup(comparisons);
println!("╠══════════════════════════════════════════════════════════════════════════╣");
println!(
"║ {:20} | {:>10} | {:>10} | {:6.2}x ║",
"GEOMEAN", "-", "-", geomean
);
println!("╚══════════════════════════════════════════════════════════════════════════╝\n");
}
}
fn chrono_now() -> String {
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default();
let secs = now.as_secs();
format!("timestamp: {}", secs)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_benchmark_new() {
let bm = CompileBenchmark::new("test", BenchmarkCategory::SingleFile);
assert_eq!(bm.name, "test");
assert_eq!(bm.category, BenchmarkCategory::SingleFile);
assert_eq!(bm.warmup_runs, 2);
assert_eq!(bm.measurement_runs, 5);
}
#[test]
fn test_benchmark_builder() {
let bm = CompileBenchmark::new("test", BenchmarkCategory::Custom)
.source("int main() { return 0; }")
.flags(vec!["-O2".to_string()])
.warmup_runs(1)
.measurement_runs(3)
.link(true);
assert_eq!(bm.source, "int main() { return 0; }");
assert!(bm.flags.contains(&"-O2".to_string()));
assert_eq!(bm.warmup_runs, 1);
assert_eq!(bm.measurement_runs, 3);
assert!(bm.link);
}
#[test]
fn test_compute_statistics_empty() {
let stats = compute_statistics(&[], 0);
assert_eq!(stats.sample_count, 0);
assert_eq!(stats.mean, Duration::ZERO);
}
#[test]
fn test_compute_statistics_single() {
let d = vec![Duration::from_millis(100)];
let stats = compute_statistics(&d, 1);
assert_eq!(stats.mean, Duration::from_millis(100));
assert_eq!(stats.median, Duration::from_millis(100));
assert_eq!(stats.min, Duration::from_millis(100));
assert_eq!(stats.max, Duration::from_millis(100));
}
#[test]
fn test_compute_statistics_multiple() {
let d = vec![
Duration::from_millis(100),
Duration::from_millis(200),
Duration::from_millis(150),
];
let stats = compute_statistics(&d, 3);
assert_eq!(stats.mean.as_millis(), 150);
assert_eq!(stats.median.as_millis(), 150);
assert_eq!(stats.min, Duration::from_millis(100));
assert_eq!(stats.max, Duration::from_millis(200));
assert!(stats.stddev.as_millis() > 0);
}
#[test]
fn test_generator_empty_file() {
let bm = CompileBenchmarkGenerator::empty_file();
assert_eq!(bm.name, "empty_file");
assert_eq!(bm.source, "");
assert_eq!(bm.measurement_runs, 10);
}
#[test]
fn test_generator_large_file() {
let bm = CompileBenchmarkGenerator::large_file(5000);
assert!(bm.name.contains("5k"));
assert!(bm.source.len() > 0);
}
#[test]
fn test_generator_deep_includes() {
let bm = CompileBenchmarkGenerator::deep_includes(10);
assert!(bm.name.contains("10"));
assert!(bm.source.contains("#ifndef"));
}
#[test]
fn test_generator_template_instantiations() {
let bm = CompileBenchmarkGenerator::template_instantiations(50);
assert!(bm.name.contains("50"));
assert!(bm.source.contains("template"));
}
#[test]
fn test_generator_optimization_comparison() {
let suite = CompileBenchmarkGenerator::optimization_comparison();
assert_eq!(suite.len(), 4);
let flags: Vec<&str> = suite.iter().map(|b| b.flags[0].as_str()).collect();
assert!(flags.contains(&"-O0"));
assert!(flags.contains(&"-O2"));
assert!(flags.contains(&"-O3"));
assert!(flags.contains(&"-Os"));
}
#[test]
fn test_generator_link_benchmark() {
let bm = CompileBenchmarkGenerator::link_benchmark(10);
assert!(bm.link);
assert!(bm.source.contains("func_0"));
assert!(bm.source.contains("func_9"));
}
#[test]
fn test_generator_memory_benchmark() {
let bm = CompileBenchmarkGenerator::memory_benchmark();
assert_eq!(bm.category, BenchmarkCategory::Memory);
assert!(bm.source.len() > 1000);
}
#[test]
fn test_generator_standard_suite() {
let suite = CompileBenchmarkGenerator::standard_suite();
assert!(suite.len() >= 10);
}
#[test]
fn test_benchmark_runner_new() {
let config = BenchmarkConfig::default();
let runner = CompileBenchmarkRunner::new(config);
assert!(runner.results().is_empty());
}
#[test]
fn test_benchmark_runner_run_single() {
let config = BenchmarkConfig::default();
let mut runner = CompileBenchmarkRunner::new(config);
let bm = CompileBenchmarkGenerator::empty_file();
let result = runner.run_benchmark(&bm);
assert_eq!(result.name, "empty_file");
assert_eq!(result.measurements.len(), 10);
assert!(result.statistics.sample_count >= 0);
}
#[test]
fn test_benchmark_runner_run_suite() {
let mut config = BenchmarkConfig::default();
config.verbose = false;
let mut runner = CompileBenchmarkRunner::new(config);
let suite = vec![
CompileBenchmarkGenerator::empty_file(),
CompileBenchmarkGenerator::template_instantiations(10),
];
let results = runner.run_suite(&suite);
assert_eq!(results.len(), 2);
}
#[test]
fn test_benchmark_runner_standard_suite() {
let mut config = BenchmarkConfig::default();
config.verbose = false;
let mut runner = CompileBenchmarkRunner::new(config);
let results = runner.run_standard_suite();
assert!(results.len() >= 10);
}
#[test]
fn test_benchmark_comparison() {
let mut runner = CompileBenchmarkRunner::new(BenchmarkConfig::default());
let bm = CompileBenchmarkGenerator::empty_file();
let results = vec![runner.run_benchmark(&bm)];
let comparisons = CompilerComparator::compare(&results, &results);
assert_eq!(comparisons.len(), 1);
assert!((comparisons[0].speed_ratio - 1.0).abs() < 0.01);
}
#[test]
fn test_geomean_speedup() {
let comparisons = vec![
BenchmarkComparison {
name: "test".to_string(),
time_a: Duration::from_millis(200),
time_b: Duration::from_millis(100),
speed_ratio: 2.0,
time_diff: Duration::from_millis(100),
},
];
let geomean = CompilerComparator::geomean_speedup(&comparisons);
assert!((geomean - 2.0).abs() < 0.01);
}
#[test]
fn test_reporter_markdown() {
let mut runner = CompileBenchmarkRunner::new(BenchmarkConfig::default());
let bm = CompileBenchmarkGenerator::empty_file();
let results = vec![runner.run_benchmark(&bm)];
let report = BenchmarkReporter::markdown_report(&results);
assert!(report.contains("Compilation Benchmark Report"));
assert!(report.contains("empty_file"));
}
#[test]
fn test_reporter_csv() {
let mut runner = CompileBenchmarkRunner::new(BenchmarkConfig::default());
let bm = CompileBenchmarkGenerator::empty_file();
let results = vec![runner.run_benchmark(&bm)];
let csv = BenchmarkReporter::csv_report(&results);
assert!(csv.contains("Name,Category"));
assert!(csv.contains("empty_file"));
}
#[test]
fn test_benchmark_category_enum() {
assert_eq!(BenchmarkCategory::SingleFile.as_str(), "single_file");
assert_eq!(BenchmarkCategory::Template.as_str(), "template");
assert_eq!(BenchmarkCategory::Optimization.as_str(), "optimization");
assert_eq!(BenchmarkCategory::Link.as_str(), "link");
assert_eq!(BenchmarkCategory::Memory.as_str(), "memory");
assert_eq!(BenchmarkCategory::Custom.as_str(), "custom");
}
#[test]
fn test_benchmark_measurement() {
let m = BenchmarkMeasurement {
run: 0,
compile_time: Duration::from_millis(150),
link_time: Some(Duration::from_millis(50)),
peak_memory_bytes: Some(100_000_000),
success: true,
error: None,
};
assert!(m.success);
assert_eq!(m.run, 0);
}
#[test]
fn test_benchmark_statistics() {
let stats = BenchmarkStatistics {
mean: Duration::from_millis(100),
median: Duration::from_millis(99),
stddev: Duration::from_millis(5),
min: Duration::from_millis(90),
max: Duration::from_millis(110),
sample_count: 10,
success_count: 10,
};
assert_eq!(stats.sample_count, 10);
assert_eq!(stats.success_count, 10);
}
#[test]
fn test_benchmark_runner_memory_tracking() {
let mut config = BenchmarkConfig::default();
config.track_memory = true;
config.verbose = false;
let mut runner = CompileBenchmarkRunner::new(config);
let bm = CompileBenchmarkGenerator::memory_benchmark();
let result = runner.run_benchmark(&bm);
let has_memory = result
.measurements
.iter()
.any(|m| m.peak_memory_bytes.is_some());
assert!(has_memory);
}
#[test]
fn test_benchmark_comparison_verbose_report() {
let comparisons = vec![
BenchmarkComparison {
name: "test_bench".to_string(),
time_a: Duration::from_millis(150),
time_b: Duration::from_millis(100),
speed_ratio: 1.5,
time_diff: Duration::from_millis(50),
},
];
BenchmarkReporter::print_comparison(&comparisons, "Clang", "GCC");
}
#[test]
fn test_benchmark_summary_print() {
let mut runner = CompileBenchmarkRunner::new(BenchmarkConfig::default());
let bm = CompileBenchmarkGenerator::empty_file();
let results = vec![runner.run_benchmark(&bm)];
BenchmarkReporter::print_summary(&results);
}
#[test]
fn test_large_file_benchmark() {
let bm = CompileBenchmarkGenerator::large_file(1000);
let mut runner = CompileBenchmarkRunner::new(BenchmarkConfig::default());
let result = runner.run_benchmark(&bm);
assert!(result.statistics.mean > Duration::ZERO);
}
#[test]
fn test_deep_includes_benchmark() {
let bm = CompileBenchmarkGenerator::deep_includes(20);
let mut runner = CompileBenchmarkRunner::new(BenchmarkConfig::default());
let result = runner.run_benchmark(&bm);
assert!(result.measurements.len() == 3);
}
}