use crate ::prelude :: *;
use std ::time ::Instant;
#[ derive(Debug, Clone) ]
pub struct AllocationResult
{
pub name: String,
pub estimated_allocations: usize,
pub timing_result: BenchmarkResult,
pub allocation_rate: f64,
}
impl AllocationResult
{
#[ must_use ]
pub fn compare_allocations(&self, other: &AllocationResult) -> AllocationComparison
{
AllocationComparison
{
baseline: self.clone(),
current: other.clone(),
allocation_improvement: if other.allocation_rate > 0.0
{
(self.allocation_rate - other.allocation_rate) / other.allocation_rate * 100.0
}
else
{
0.0
},
}
}
}
#[ derive(Debug, Clone) ]
pub struct AllocationComparison
{
pub baseline: AllocationResult,
pub current: AllocationResult,
pub allocation_improvement: f64,
}
impl AllocationComparison
{
#[ must_use ]
pub fn to_markdown( &self ) -> String
{
let mut output = String ::new();
output.push_str("## Memory Allocation Comparison\n\n");
output.push_str("| Approach | Allocations/Op | Ops/sec | Memory Efficiency |\n");
output.push_str("|----------|----------------|---------|------------------|\n");
output.push_str(&format!(
"| {} | {:.1} | {:.0} | Baseline |\n",
self.baseline.name,
self.baseline.allocation_rate,
self.baseline.timing_result.operations_per_second()
));
let efficiency = if self.allocation_improvement > 0.0
{
format!("{:.1}% fewer allocs", self.allocation_improvement)
}
else
{
format!("{:.1}% more allocs", -self.allocation_improvement)
};
output.push_str(&format!(
"| {} | {:.1} | {:.0} | {} |\n",
self.current.name,
self.current.allocation_rate,
self.current.timing_result.operations_per_second(),
efficiency
));
if self.allocation_improvement > 50.0
{
output.push_str("\n**๐ Significant memory optimization achieved!**\n");
}
else if self.allocation_improvement > 10.0
{
output.push_str("\n**๐ Good memory optimization**\n");
}
else if self.allocation_improvement < -20.0
{
output.push_str("\n**โ ๏ธ Memory usage increased significantly**\n");
}
output
}
}
pub fn bench_with_allocation_tracking< F >(
name: &str,
mut operation: F,
estimated_allocs_per_call: usize,
) -> AllocationResult
where
F: FnMut() + Send,
{
println!("๐ง Memory allocation tracking: {name}");
let timing_result = bench_function(name, ||
{
operation();
});
let total_operations = timing_result.times.len();
let estimated_total_allocations = total_operations * estimated_allocs_per_call;
let allocation_rate = estimated_allocs_per_call as f64;
println!(" ๐ Est. allocations: {estimated_total_allocations} ({allocation_rate:.1}/op)");
AllocationResult
{
name: name.to_string(),
estimated_allocations: estimated_total_allocations,
timing_result,
allocation_rate,
}
}
pub fn bench_string_operations< F1, F2 >(
baseline_name: &str,
optimized_name: &str,
baseline_fn: F1,
optimized_fn: F2,
test_data: &[ &[ &str]],
) -> AllocationComparison
where
F1: Fn(&[ &str]) -> String + Send + Sync,
F2: Fn(&[ &str]) -> String + Send + Sync,
{
println!("๐งต String operations comparison");
let baseline_result = bench_with_allocation_tracking(
baseline_name,
||
{
for slices in test_data
{
let _result = baseline_fn(slices);
std ::hint ::black_box(_result);
}
},
test_data.len() * 2, );
let optimized_result = bench_with_allocation_tracking(
optimized_name,
||
{
for slices in test_data
{
let _result = optimized_fn(slices);
std ::hint ::black_box(_result);
}
},
test_data.len() / 10, );
optimized_result.compare_allocations(&baseline_result)
}
#[ derive(Debug, Clone) ]
pub struct MemoryProfile
{
pub operation_name: String,
pub peak_estimated_usage_mb: f64,
pub average_usage_mb: f64,
pub allocation_hotspots: Vec< String >,
}
impl MemoryProfile
{
pub fn analyze< F >(name: &str, operation: F, iterations: usize) -> Self
where
F: Fn() + Send,
{
println!("๐ Memory profiling: {name}");
let start_time = Instant ::now();
for _ in 0..iterations
{
operation();
}
let duration = start_time.elapsed();
let ops_per_sec = iterations as f64 / duration.as_secs_f64();
let estimated_memory_per_op = if ops_per_sec > 100_000.0
{
0.001 }
else if ops_per_sec > 10000.0
{
0.01 }
else
{
0.1 };
let peak_usage = estimated_memory_per_op * iterations as f64;
let average_usage = peak_usage * 0.6;
let mut hotspots = Vec ::new();
if ops_per_sec < 1000.0
{
hotspots.push("Potential string allocation hotspot".to_string());
}
if peak_usage > 10.0
{
hotspots.push("High memory usage detected".to_string());
}
println!(" ๐ Est. peak memory: {peak_usage:.2} MB, avg: {average_usage:.2} MB");
Self
{
operation_name: name.to_string(),
peak_estimated_usage_mb: peak_usage,
average_usage_mb: average_usage,
allocation_hotspots: hotspots,
}
}
#[ must_use ]
pub fn to_markdown( &self ) -> String
{
let mut output = String ::new();
output.push_str(&format!("## {} Memory Profile\n\n", self.operation_name));
output.push_str(&format!("- **Peak Usage** : {:.2} MB\n", self.peak_estimated_usage_mb));
output.push_str(&format!("- **Average Usage** : {:.2} MB\n", self.average_usage_mb));
if !self.allocation_hotspots.is_empty()
{
output.push_str("\n**Potential Issues** : \n");
for hotspot in &self.allocation_hotspots
{
output.push_str(&format!("- โ ๏ธ {}\n", hotspot));
}
}
else
{
output.push_str("\nโ
**No memory issues detected**\n");
}
output
}
}