hft_benchmarks/
allocation.rs

1//! Memory allocation benchmarking utilities
2
3use jemallocator::Jemalloc;
4use crate::BenchmarkResults;
5
6#[global_allocator]
7static GLOBAL: Jemalloc = Jemalloc;
8
9const DEFAULT_ITERATIONS: usize = 10_000;
10const ALLOCATION_SIZES: [usize; 6] = [64, 128, 256, 512, 1024, 4096];
11
12pub fn benchmark_allocations() {
13    benchmark_allocations_with_iterations(DEFAULT_ITERATIONS)
14}
15
16pub fn benchmark_allocations_with_iterations(iterations: usize) {
17    println!("Benchmarking memory allocations ({iterations} iterations per size)...");
18    
19    for &size in &ALLOCATION_SIZES {
20        let mut results = BenchmarkResults::new(format!("allocation_{size}B"));
21        
22        for _ in 0..iterations {
23            let (_, elapsed) = crate::timing::time_function(|| vec![0u8; size]);
24            results.record(elapsed);
25        }
26        
27        println!("{}", results.analyze().summary());
28    }
29}
30
31pub struct SimpleObjectPool<T> {
32    objects: Vec<Box<T>>,
33}
34
35impl<T> Default for SimpleObjectPool<T> {
36    fn default() -> Self {
37        Self::new()
38    }
39}
40
41impl<T> SimpleObjectPool<T> {
42    pub fn new() -> Self {
43        Self {
44            objects: Vec::with_capacity(1000),
45        }
46    }
47    
48    pub fn get(&mut self) -> Box<T>
49    where
50        T: Default,
51    {
52        self.objects.pop().unwrap_or_else(|| Box::new(T::default()))
53    }
54    
55    pub fn put(&mut self, obj: Box<T>) {
56        if self.objects.len() < 1000 {
57            self.objects.push(obj);
58        }
59    }
60}
61
62/// Benchmark object pools vs direct allocation
63pub fn benchmark_object_pools() {
64    benchmark_object_pools_with_iterations(DEFAULT_ITERATIONS)
65}
66
67/// Benchmark object pools with custom iteration count
68pub fn benchmark_object_pools_with_iterations(iterations: usize) {
69    println!("Benchmarking object pools vs direct allocation...");
70    
71    let mut pool = SimpleObjectPool::<u64>::new();
72    let mut pool_results = BenchmarkResults::new("pool_allocation".to_string());
73    let mut direct_results = BenchmarkResults::new("direct_allocation".to_string());
74    
75    for _ in 0..iterations {
76        let (obj, elapsed) = crate::timing::time_function(|| pool.get());
77        pool.put(obj);
78        pool_results.record(elapsed);
79        
80        let (_, elapsed) = crate::timing::time_function(|| Box::new(0u64));
81        direct_results.record(elapsed);
82    }
83    
84    println!("Pool allocation: {}", pool_results.analyze().summary());
85    println!("Direct allocation: {}", direct_results.analyze().summary());
86}
87
88/// Benchmark allocation alignment impact
89pub fn benchmark_aligned_allocations() {
90    benchmark_aligned_allocations_with_iterations(DEFAULT_ITERATIONS / 2)
91}
92
93/// Benchmark aligned allocations with custom iteration count
94pub fn benchmark_aligned_allocations_with_iterations(iterations: usize) {
95    println!("Benchmarking aligned vs unaligned allocations...");
96    
97    let mut aligned_results = BenchmarkResults::new("aligned_allocation".to_string());
98    let mut unaligned_results = BenchmarkResults::new("unaligned_allocation".to_string());
99    
100    let aligned_layout = std::alloc::Layout::from_size_align(1024, 64).unwrap();
101    let unaligned_layout = std::alloc::Layout::from_size_align(1024, 8).unwrap();
102    
103    for _ in 0..iterations {
104        let (_, elapsed) = crate::timing::time_function(|| unsafe {
105            let ptr = std::alloc::alloc(aligned_layout);
106            if !ptr.is_null() {
107                std::alloc::dealloc(ptr, aligned_layout);
108            }
109        });
110        aligned_results.record(elapsed);
111        
112        let (_, elapsed) = crate::timing::time_function(|| unsafe {
113            let ptr = std::alloc::alloc(unaligned_layout);
114            if !ptr.is_null() {
115                std::alloc::dealloc(ptr, unaligned_layout);
116            }
117        });
118        unaligned_results.record(elapsed);
119    }
120    
121    println!("Aligned allocation: {}", aligned_results.analyze().summary());
122    println!("Unaligned allocation: {}", unaligned_results.analyze().summary());
123}
124
125#[cfg(test)]
126mod tests {
127    use super::*;
128    
129    #[test]
130    fn test_object_pool() {
131        let mut pool = SimpleObjectPool::<u64>::new();
132        
133        let obj1 = pool.get();
134        let obj2 = pool.get();
135        
136        pool.put(obj1);
137        pool.put(obj2);
138        
139        // Should reuse objects
140        let obj3 = pool.get();
141        let obj4 = pool.get();
142        
143        drop(obj3);
144        drop(obj4);
145    }
146    
147    #[test]
148    fn test_allocation_benchmarks() {
149        crate::quick_calibrate_tsc_frequency();
150        
151        // These tests just ensure benchmarks don't panic
152        // Actual performance will vary by system
153        benchmark_object_pools_with_iterations(10);
154        benchmark_allocations_with_iterations(10);
155        benchmark_aligned_allocations_with_iterations(10);
156    }
157    
158    #[test]
159    fn test_object_pool_reuse() {
160        let mut pool = SimpleObjectPool::<u64>::new();
161        
162        // Initially empty, should allocate
163        let obj1 = pool.get();
164        assert_eq!(*obj1, 0); // Default value
165        
166        // Return it to pool  
167        pool.put(obj1);
168        
169        // Pool should now have one object
170        assert_eq!(pool.objects.len(), 1);
171        
172        // Get should now reuse from pool instead of allocating
173        let obj2 = pool.get();
174        assert_eq!(*obj2, 0);
175        
176        // Pool should be empty again
177        assert_eq!(pool.objects.len(), 0);
178    }
179    
180    #[test]
181    fn test_pool_capacity_limit() {
182        let mut pool = SimpleObjectPool::<u64>::new();
183        
184        // Fill the pool beyond capacity
185        for _ in 0..1100 {
186            pool.put(Box::new(42));
187        }
188        
189        // Pool should not exceed capacity
190        assert!(pool.objects.len() <= 1000);
191    }
192}