benchkit/
profiling.rs

1//! Memory allocation and performance profiling tools
2//!
3//! This module provides utilities for tracking memory allocation patterns
4//! and profiling resource usage during benchmarks.
5
6use crate ::prelude :: *;
7use std ::time ::Instant;
8
9/// Memory allocation tracking result
10#[ derive(Debug, Clone) ]
11pub struct AllocationResult
12{
13  /// Name of the operation
14  pub name: String,
15  /// Estimated total number of allocations
16  pub estimated_allocations: usize,
17  /// Timing benchmark result
18  pub timing_result: BenchmarkResult,
19  /// Allocations per operation
20  pub allocation_rate: f64,
21}
22
23impl AllocationResult
24{
25  /// Compare allocation efficiency with another result
26  #[ must_use ]
27  pub fn compare_allocations(&self, other: &AllocationResult) -> AllocationComparison
28  {
29  AllocationComparison
30  {
31   baseline: self.clone(),
32   current: other.clone(),
33   allocation_improvement: if other.allocation_rate > 0.0
34   {
35  (self.allocation_rate - other.allocation_rate) / other.allocation_rate * 100.0
36 }
37   else
38   {
39  0.0
40 },
41 }
42 }
43}
44
45/// Allocation comparison result
46#[ derive(Debug, Clone) ]
47pub struct AllocationComparison
48{
49  /// Baseline allocation result
50  pub baseline: AllocationResult,
51  /// Current allocation result being compared
52  pub current: AllocationResult,
53  /// Allocation improvement percentage (positive means baseline allocates less)
54  pub allocation_improvement: f64,
55}
56
57impl AllocationComparison
58{
59  /// Generate markdown report
60  #[ must_use ]
61  pub fn to_markdown( &self ) -> String
62  {
63  let mut output = String ::new();
64  
65  output.push_str("## Memory Allocation Comparison\n\n");
66  output.push_str("| Approach | Allocations/Op | Ops/sec | Memory Efficiency |\n");
67  output.push_str("|----------|----------------|---------|------------------|\n");
68  
69  output.push_str(&format!(
70   "| {} | {:.1} | {:.0} | Baseline |\n",
71   self.baseline.name,
72   self.baseline.allocation_rate,
73   self.baseline.timing_result.operations_per_second()
74 ));
75  
76  let efficiency = if self.allocation_improvement > 0.0
77  {
78   format!("{:.1}% fewer allocs", self.allocation_improvement)
79 }
80  else
81  {
82   format!("{:.1}% more allocs", -self.allocation_improvement)
83 };
84  
85  output.push_str(&format!(
86   "| {} | {:.1} | {:.0} | {} |\n",
87   self.current.name,
88   self.current.allocation_rate,
89   self.current.timing_result.operations_per_second(),
90   efficiency
91 ));
92  
93  if self.allocation_improvement > 50.0
94  {
95   output.push_str("\n**๐ŸŽ‰ Significant memory optimization achieved!**\n");
96 }
97  else if self.allocation_improvement > 10.0
98  {
99   output.push_str("\n**๐Ÿ‘ Good memory optimization**\n");
100 }
101  else if self.allocation_improvement < -20.0
102  {
103   output.push_str("\n**โš ๏ธ  Memory usage increased significantly**\n");
104 }
105  
106  output
107 }
108}
109
110/// Benchmark with estimated memory allocation tracking
111pub fn bench_with_allocation_tracking< F >(
112  name: &str,
113  mut operation: F,
114  estimated_allocs_per_call: usize,
115) -> AllocationResult
116where
117  F: FnMut() + Send,
118{
119  println!("๐Ÿง  Memory allocation tracking: {name}");
120  
121  // Run the timing benchmark
122  let timing_result = bench_function(name, ||
123  {
124  operation();
125 });
126  
127  // Calculate allocation metrics
128  let total_operations = timing_result.times.len();
129  let estimated_total_allocations = total_operations * estimated_allocs_per_call;
130  let allocation_rate = estimated_allocs_per_call as f64;
131  
132  println!("  ๐Ÿ“Š Est. allocations: {estimated_total_allocations} ({allocation_rate:.1}/op)");
133  
134  AllocationResult
135  {
136  name: name.to_string(),
137  estimated_allocations: estimated_total_allocations,
138  timing_result,
139  allocation_rate,
140 }
141}
142
143/// String interning benchmark helper
144pub fn bench_string_operations< F1, F2 >(
145  baseline_name: &str,
146  optimized_name: &str,
147  baseline_fn: F1,
148  optimized_fn: F2,
149  test_data: &[ &[ &str]],
150) -> AllocationComparison
151where
152  F1: Fn(&[ &str]) -> String + Send + Sync,
153  F2: Fn(&[ &str]) -> String + Send + Sync,
154{
155  println!("๐Ÿงต String operations comparison");
156  
157  // Benchmark baseline (typically more allocations)
158  let baseline_result = bench_with_allocation_tracking(
159  baseline_name,
160  ||
161  {
162   for slices in test_data
163   {
164  let _result = baseline_fn(slices);
165  std ::hint ::black_box(_result);
166 }
167 },
168  test_data.len() * 2, // Estimated: format!() + join() per operation
169 );
170  
171  // Benchmark optimized version (typically fewer allocations)
172  let optimized_result = bench_with_allocation_tracking(
173  optimized_name,
174  ||
175  {
176   for slices in test_data
177   {
178  let _result = optimized_fn(slices);
179  std ::hint ::black_box(_result);
180 }
181 },
182  test_data.len() / 10, // Estimated: cached lookups, fewer allocations
183 );
184  
185  optimized_result.compare_allocations(&baseline_result)
186}
187
188/// Memory usage pattern analysis
189#[ derive(Debug, Clone) ]
190pub struct MemoryProfile
191{
192  /// Name of the operation profiled
193  pub operation_name: String,
194  /// Peak estimated memory usage in megabytes
195  pub peak_estimated_usage_mb: f64,
196  /// Average memory usage in megabytes
197  pub average_usage_mb: f64,
198  /// Identified allocation hotspots
199  pub allocation_hotspots: Vec< String >,
200}
201
202impl MemoryProfile
203{
204  /// Analyze memory usage patterns (simplified estimation)
205  pub fn analyze< F >(name: &str, operation: F, iterations: usize) -> Self
206  where
207  F: Fn() + Send,
208  {
209  println!("๐Ÿ“ˆ Memory profiling: {name}");
210  
211  let start_time = Instant ::now();
212  
213  // Run operation multiple times to estimate pattern
214  for _ in 0..iterations
215  {
216   operation();
217 }
218  
219  let duration = start_time.elapsed();
220  
221  // Simplified memory estimation based on timing characteristics
222  let ops_per_sec = iterations as f64 / duration.as_secs_f64();
223  let estimated_memory_per_op = if ops_per_sec > 100_000.0
224  {
225   0.001 // Very fast = likely cached/minimal allocation
226 }
227  else if ops_per_sec > 10000.0
228  {
229   0.01 // Fast = some allocation
230 }
231  else
232  {
233   0.1 // Slow = likely heavy allocation
234 };
235  
236  let peak_usage = estimated_memory_per_op * iterations as f64;
237  let average_usage = peak_usage * 0.6; // Estimate average as 60% of peak
238  
239  let mut hotspots = Vec ::new();
240  if ops_per_sec < 1000.0
241  {
242   hotspots.push("Potential string allocation hotspot".to_string());
243 }
244  if peak_usage > 10.0
245  {
246   hotspots.push("High memory usage detected".to_string());
247 }
248  
249  println!("  ๐Ÿ“Š Est. peak memory: {peak_usage:.2} MB, avg: {average_usage:.2} MB");
250  
251  Self
252  {
253   operation_name: name.to_string(),
254   peak_estimated_usage_mb: peak_usage,
255   average_usage_mb: average_usage,
256   allocation_hotspots: hotspots,
257 }
258 }
259  
260  /// Generate markdown report
261  #[ must_use ]
262  pub fn to_markdown( &self ) -> String
263  {
264  let mut output = String ::new();
265  
266  output.push_str(&format!("## {} Memory Profile\n\n", self.operation_name));
267  output.push_str(&format!("- **Peak Usage** : {:.2} MB\n", self.peak_estimated_usage_mb));
268  output.push_str(&format!("- **Average Usage** : {:.2} MB\n", self.average_usage_mb));
269  
270  if !self.allocation_hotspots.is_empty()
271  {
272   output.push_str("\n**Potential Issues** : \n");
273   for hotspot in &self.allocation_hotspots
274   {
275  output.push_str(&format!("- โš ๏ธ  {}\n", hotspot));
276 }
277 }
278  else
279  {
280   output.push_str("\nโœ… **No memory issues detected**\n");
281 }
282  
283  output
284 }
285}
286