use crate::engine::array_ops::{ArrayOps, OptimizedArray};
use crate::engine::perf_alloc::StringBuilder;
use crate::engine::types::{PhpType, PhpValue, Val};
use crate::engine::vm::{execute_ex, Op, OpArray, Opcode};
use crate::vm::execute_data::ExecuteData;
use std::time::{Duration, Instant};
#[derive(Debug)]
pub struct BenchmarkResult {
pub name: String,
pub iterations: u64,
pub total_time: Duration,
pub avg_time: Duration,
pub ops_per_second: f64,
pub memory_used: usize,
}
impl BenchmarkResult {
fn new(name: &str, iterations: u64, total_time: Duration, memory_used: usize) -> Self {
let avg_time = total_time / iterations as u32;
let ops_per_second = iterations as f64 / total_time.as_secs_f64();
Self {
name: name.to_string(),
iterations,
total_time,
avg_time,
ops_per_second,
memory_used,
}
}
}
pub struct BenchmarkSuite {
results: Vec<BenchmarkResult>,
}
impl BenchmarkSuite {
pub fn new() -> Self {
Self {
results: Vec::new(),
}
}
pub fn run_all_benchmarks(&mut self) {
println!("Running Performance Benchmarks...\n");
self.benchmark_simple_arithmetic();
self.benchmark_string_operations();
self.benchmark_array_operations();
self.benchmark_function_calls();
self.benchmark_loop_operations();
self.benchmark_memory_operations();
self.benchmark_jit_performance();
self.benchmark_opcode_cache();
self.print_summary();
}
fn benchmark_simple_arithmetic(&mut self) {
println!("Benchmarking Simple Arithmetic Operations...");
let iterations = 10_000_000;
let start = Instant::now();
for i in 0..iterations {
let a = Val::new(PhpValue::Long(i as i64), PhpType::Long);
let b = Val::new(PhpValue::Long((i + 1) as i64), PhpType::Long);
let _result = crate::engine::operators::zval_add(&a, &b);
}
let elapsed = start.elapsed();
let result = BenchmarkResult::new("simple_arithmetic", iterations, elapsed, 0);
self.results.push(result);
println!(
" ✓ Completed: {:.2} ops/sec",
self.results.last().unwrap().ops_per_second
);
}
fn benchmark_string_operations(&mut self) {
println!("Benchmarking String Operations...");
let iterations = 1_000_000;
let start = Instant::now();
let base_string = "Hello World";
for _ in 0..iterations {
let mut builder = StringBuilder::with_capacity(base_string.len() * 2);
builder.push_str(base_string);
builder.push_str("!");
let _result = builder.into_string();
}
let elapsed = start.elapsed();
let result = BenchmarkResult::new("string_operations", iterations, elapsed, 0);
self.results.push(result);
println!(
" ✓ Completed: {:.2} ops/sec",
self.results.last().unwrap().ops_per_second
);
}
fn benchmark_array_operations(&mut self) {
println!("Benchmarking Array Operations...");
let iterations = 500_000;
let start = Instant::now();
for i in 0..iterations {
let mut array = OptimizedArray::with_capacity(100);
for j in 0..100 {
let val = Val::new(PhpValue::Long(j as i64), PhpType::Long);
array.push(val);
}
array.insert_string(
"test_key",
Val::new(PhpValue::Long(i as i64), PhpType::Long),
);
let _result = array.get("test_key");
let _result2 = array.get_index(50);
let _mapped = ArrayOps::map(&array, |val| {
if let PhpValue::Long(n) = val.value {
Val::new(PhpValue::Long(n * 2), PhpType::Long)
} else {
val.clone()
}
});
}
let elapsed = start.elapsed();
let result = BenchmarkResult::new("array_operations", iterations, elapsed, 0);
self.results.push(result);
println!(
" ✓ Completed: {:.2} ops/sec",
self.results.last().unwrap().ops_per_second
);
}
fn benchmark_function_calls(&mut self) {
println!("Benchmarking Function Calls...");
let iterations = 1_000_000;
let start = Instant::now();
let mut op_array = OpArray::new("test.php".to_string());
op_array.ops.push(Op::new(
Opcode::Add,
Val::new(PhpValue::Long(0), PhpType::Long), Val::new(PhpValue::Long(0), PhpType::Long), Val::new(PhpValue::Long(0), PhpType::Long), 0,
));
op_array.ops.push(Op::new(
Opcode::Return,
Val::new(PhpValue::Long(0), PhpType::Long), Val::new(PhpValue::Long(0), PhpType::Long),
Val::new(PhpValue::Long(0), PhpType::Long),
0,
));
for i in 0..iterations {
let mut execute_data = ExecuteData::new();
execute_data
.call_args
.push(Val::new(PhpValue::Long(i as i64), PhpType::Long));
execute_data
.call_args
.push(Val::new(PhpValue::Long((i + 1) as i64), PhpType::Long));
let _result = execute_ex(&mut execute_data, &op_array);
}
let elapsed = start.elapsed();
let result = BenchmarkResult::new("function_calls", iterations, elapsed, 0);
self.results.push(result);
println!(
" ✓ Completed: {:.2} ops/sec",
self.results.last().unwrap().ops_per_second
);
}
fn benchmark_loop_operations(&mut self) {
println!("Benchmarking Loop Operations...");
let iterations = 100_000;
let start = Instant::now();
for _ in 0..iterations {
let array = ArrayOps::range(0, 1000, 1);
let _filtered = ArrayOps::filter(&array, |val| {
if let PhpValue::Long(n) = val.value {
n % 2 == 0
} else {
false
}
});
let mut _sum = 0i64;
for bucket in &array.inner.ar_data {
if let PhpValue::Long(n) = bucket.val.value {
_sum += n;
}
}
}
let elapsed = start.elapsed();
let result = BenchmarkResult::new("loop_operations", iterations, elapsed, 0);
self.results.push(result);
println!(
" ✓ Completed: {:.2} ops/sec",
self.results.last().unwrap().ops_per_second
);
}
fn benchmark_memory_operations(&mut self) {
println!("Benchmarking Memory Operations...");
let iterations = 2_000_000;
let start = Instant::now();
for i in 0..iterations {
let size = 64 + (i % 256);
let mut vec = Vec::with_capacity(size);
vec.resize(size, 0);
let string = format!("test_string_{}", i);
let _hash = crate::engine::perf_alloc::fast_hash(string.as_bytes());
}
let elapsed = start.elapsed();
let result = BenchmarkResult::new("memory_operations", iterations as u64, elapsed, 0);
self.results.push(result);
println!(
" ✓ Completed: {:.2} ops/sec",
self.results.last().unwrap().ops_per_second
);
}
fn benchmark_jit_performance(&mut self) {
println!("Benchmarking JIT Performance...");
let iterations = 500_000;
let start = Instant::now();
for i in 0..iterations {
let func_name = if i % 3 == 0 {
"add"
} else if i % 3 == 1 {
"multiply"
} else {
"concat"
};
let _should_compile = crate::engine::jit::increment_execution_counter(func_name);
if i % 100 == 0 {
let a = Val::new(PhpValue::Long(i as i64), PhpType::Long);
let b = Val::new(PhpValue::Long((i + 1) as i64), PhpType::Long);
let _result = crate::engine::jit::try_inline_operation(Opcode::Add, &a, &b);
}
}
let elapsed = start.elapsed();
let result = BenchmarkResult::new("jit_performance", iterations, elapsed, 0);
self.results.push(result);
println!(
" ✓ Completed: {:.2} ops/sec",
self.results.last().unwrap().ops_per_second
);
}
fn benchmark_opcode_cache(&mut self) {
println!("Benchmarking Opcode Cache...");
let iterations = 100_000;
let start = Instant::now();
for i in 0..iterations {
let filename = format!("test_file_{}.php", i % 10);
let cache = crate::engine::opcode_cache::get_opcode_cache();
let _cached = cache.get(&filename);
if i % 1000 == 0 {
let ops = vec![Op::new(
Opcode::Nop,
Val::new(PhpValue::Long(0), PhpType::Long),
Val::new(PhpValue::Long(0), PhpType::Long),
Val::new(PhpValue::Long(0), PhpType::Long),
0,
)];
cache.store(
&filename,
ops,
crate::engine::opcode_cache::OptimizationLevel::Basic,
);
}
}
let elapsed = start.elapsed();
let result = BenchmarkResult::new("opcode_cache", iterations, elapsed, 0);
self.results.push(result);
println!(
" ✓ Completed: {:.2} ops/sec",
self.results.last().unwrap().ops_per_second
);
}
fn print_summary(&self) {
println!("\n{}", "=".repeat(60));
println!("PHPRS BENCHMARK SUMMARY (ops/sec)");
println!("{}", "=".repeat(60));
for result in &self.results {
println!("{:<20} | {:>12.2} ops/sec", result.name, result.ops_per_second);
}
println!("\nOPTIMIZATION STATISTICS:");
let jit = crate::engine::jit::get_jit_compiler();
let jit = jit.read().unwrap();
let jit_stats = jit.get_stats();
println!(" JIT Functions Compiled: {}", jit_stats.0);
println!(" JIT Total Executions: {}", jit_stats.1);
println!(" JIT Hits: {}", jit_stats.2);
let cache_stats = crate::engine::opcode_cache::get_opcode_cache().get_stats();
println!(" Cache Hits: {}", cache_stats.0);
println!(" Cache Misses: {}", cache_stats.1);
println!(" Cache Evictions: {}", cache_stats.2);
let func_opt = crate::engine::function_optimizer::get_function_optimizer();
let func_opt = func_opt.read().unwrap();
let func_stats = func_opt.get_stats();
println!(" Functions Optimized: {}", func_stats.0);
println!(" Functions Inlined: {}", func_stats.1);
println!(" Calls Saved: {}", func_stats.2);
let memory_stats = crate::engine::perf_alloc::get_memory_stats().get_stats();
println!(" Total Allocations: {}", memory_stats.0);
println!(" Peak Memory Usage: {} bytes", memory_stats.2);
println!("\n{}", "=".repeat(60));
}
pub fn export_results(&self) -> String {
use serde_json;
let mut results_map = serde_json::Map::new();
for result in &self.results {
let mut result_data = serde_json::Map::new();
result_data.insert(
"iterations".to_string(),
serde_json::Value::Number(serde_json::Number::from(result.iterations)),
);
result_data.insert(
"total_time_ms".to_string(),
serde_json::Value::Number(serde_json::Number::from(
result.total_time.as_millis() as i64
)),
);
result_data.insert(
"avg_time_ns".to_string(),
serde_json::Value::Number(serde_json::Number::from(
result.avg_time.as_nanos() as i64
)),
);
result_data.insert(
"ops_per_second".to_string(),
serde_json::Value::Number(
serde_json::Number::from_f64(result.ops_per_second).unwrap(),
),
);
result_data.insert(
"memory_used".to_string(),
serde_json::Value::Number(serde_json::Number::from(result.memory_used as i64)),
);
results_map.insert(result.name.clone(), serde_json::Value::Object(result_data));
}
serde_json::to_string_pretty(&serde_json::Value::Object(results_map)).unwrap()
}
}
impl Default for BenchmarkSuite {
fn default() -> Self {
Self::new()
}
}
pub fn run_performance_benchmarks() {
let mut suite = BenchmarkSuite::new();
suite.run_all_benchmarks();
let json_results = suite.export_results();
std::fs::write("benchmark_results.json", json_results).unwrap();
println!("\nBenchmark results exported to benchmark_results.json");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_benchmark_suite_creation() {
let suite = BenchmarkSuite::new();
assert!(suite.results.is_empty());
}
#[test]
fn test_benchmark_result_creation() {
let duration = Duration::from_millis(1000);
let result = BenchmarkResult::new("test", 1000, duration, 1024);
assert_eq!(result.name, "test");
assert_eq!(result.iterations, 1000);
assert_eq!(result.memory_used, 1024);
assert!(result.ops_per_second > 0.0);
}
#[test]
fn test_export_results_json_shape() {
let mut suite = BenchmarkSuite::new();
suite.results.push(BenchmarkResult::new(
"simple_arithmetic",
100,
Duration::from_millis(10),
0,
));
let json = suite.export_results();
assert!(json.contains("simple_arithmetic"));
assert!(json.contains("ops_per_second"));
assert!(!json.contains("speedup_vs_php8"));
}
}