Skip to main content

torsh_autograd/
buffer_optimization.rs

1//! Temporary buffer allocation optimization for autograd operations
2//!
3//! This module provides efficient management of temporary buffers used during
4//! gradient computations, including buffer pools, allocation patterns optimization,
5//! memory alignment, and cache-friendly allocation strategies.
6
7use crate::error_handling::{AutogradError, AutogradResult};
8use std::alloc::{alloc, dealloc, Layout};
9use std::collections::{HashMap, VecDeque};
10use std::ptr::NonNull;
11use std::sync::{Arc, Mutex, RwLock};
12use std::time::{Duration, Instant};
13use torsh_core::sync::{MutexExt, RwLockExt};
14
15/// Buffer size categories for efficient pooling
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
17pub enum BufferSizeCategory {
18    Tiny = 0,   // < 1KB
19    Small = 1,  // 1KB - 16KB
20    Medium = 2, // 16KB - 256KB
21    Large = 3,  // 256KB - 4MB
22    Huge = 4,   // > 4MB
23}
24
25impl BufferSizeCategory {
26    /// Get the size category for a given buffer size
27    pub fn from_size(size: usize) -> Self {
28        if size < 1024 {
29            BufferSizeCategory::Tiny
30        } else if size < 16 * 1024 {
31            BufferSizeCategory::Small
32        } else if size < 256 * 1024 {
33            BufferSizeCategory::Medium
34        } else if size < 4 * 1024 * 1024 {
35            BufferSizeCategory::Large
36        } else {
37            BufferSizeCategory::Huge
38        }
39    }
40
41    /// Get the maximum size for this category
42    pub fn max_size(&self) -> usize {
43        match self {
44            BufferSizeCategory::Tiny => 1024,
45            BufferSizeCategory::Small => 16 * 1024,
46            BufferSizeCategory::Medium => 256 * 1024,
47            BufferSizeCategory::Large => 4 * 1024 * 1024,
48            BufferSizeCategory::Huge => usize::MAX,
49        }
50    }
51
52    /// Get the minimum size for this category
53    pub fn min_size(&self) -> usize {
54        match self {
55            BufferSizeCategory::Tiny => 0,
56            BufferSizeCategory::Small => 1024,
57            BufferSizeCategory::Medium => 16 * 1024,
58            BufferSizeCategory::Large => 256 * 1024,
59            BufferSizeCategory::Huge => 4 * 1024 * 1024,
60        }
61    }
62}
63
64/// Allocation strategy for buffer management
65#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66pub enum AllocationStrategy {
67    /// Pool-based allocation with size categories
68    Pooled,
69    /// Direct allocation for each request
70    Direct,
71    /// Hybrid approach: pooled for small buffers, direct for large
72    Hybrid,
73    /// Memory-mapped allocation for very large buffers
74    MemoryMapped,
75    /// Stack-based allocation for very small buffers
76    StackBased,
77}
78
79/// Buffer allocation alignment requirements
80#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
81pub enum BufferAlignment {
82    /// No specific alignment requirements
83    None,
84    /// 8-byte alignment for basic types
85    Align8 = 8,
86    /// 16-byte alignment for SIMD operations
87    Align16 = 16,
88    /// 32-byte alignment for AVX operations
89    Align32 = 32,
90    /// 64-byte alignment for cache line optimization
91    Align64 = 64,
92    /// Page-aligned for memory-mapped operations
93    PageAligned = 4096,
94}
95
96/// Temporary buffer metadata
97#[derive(Debug, Clone)]
98pub struct BufferMetadata {
99    pub size: usize,
100    pub alignment: BufferAlignment,
101    pub category: BufferSizeCategory,
102    pub allocation_time: Instant,
103    pub last_access_time: Instant,
104    pub access_count: usize,
105    pub allocation_location: String,
106    pub is_active: bool,
107}
108
109/// Managed temporary buffer
110#[derive(Debug)]
111pub struct TempBuffer {
112    ptr: NonNull<u8>,
113    metadata: BufferMetadata,
114    layout: Layout,
115}
116
117impl TempBuffer {
118    /// Create a new temporary buffer
119    pub fn new(size: usize, alignment: BufferAlignment, location: &str) -> AutogradResult<Self> {
120        let align = match alignment {
121            BufferAlignment::None => 1,
122            BufferAlignment::Align8 => 8,
123            BufferAlignment::Align16 => 16,
124            BufferAlignment::Align32 => 32,
125            BufferAlignment::Align64 => 64,
126            BufferAlignment::PageAligned => 4096,
127        };
128
129        let layout = Layout::from_size_align(size, align).map_err(|e| {
130            AutogradError::gradient_computation(
131                "buffer_allocation",
132                format!("Invalid buffer layout: {}", e),
133            )
134        })?;
135
136        let ptr = unsafe { alloc(layout) };
137        if ptr.is_null() {
138            return Err(AutogradError::gradient_computation(
139                "buffer_allocation",
140                "Failed to allocate buffer memory",
141            ));
142        }
143
144        let metadata = BufferMetadata {
145            size,
146            alignment,
147            category: BufferSizeCategory::from_size(size),
148            allocation_time: Instant::now(),
149            last_access_time: Instant::now(),
150            access_count: 0,
151            allocation_location: location.to_string(),
152            is_active: true,
153        };
154
155        Ok(Self {
156            ptr: NonNull::new(ptr).expect("memory allocation returned null pointer"),
157            metadata,
158            layout,
159        })
160    }
161
162    /// Get a raw pointer to the buffer data
163    pub fn as_ptr(&self) -> *mut u8 {
164        self.ptr.as_ptr()
165    }
166
167    /// Get a mutable slice view of the buffer
168    pub fn as_mut_slice(&mut self) -> &mut [u8] {
169        self.metadata.last_access_time = Instant::now();
170        self.metadata.access_count += 1;
171        unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.metadata.size) }
172    }
173
174    /// Get buffer size
175    pub fn size(&self) -> usize {
176        self.metadata.size
177    }
178
179    /// Get buffer metadata
180    pub fn metadata(&self) -> &BufferMetadata {
181        &self.metadata
182    }
183
184    /// Check if buffer is suitable for reuse
185    pub fn is_reusable(&self, required_size: usize, required_alignment: BufferAlignment) -> bool {
186        self.metadata.size >= required_size
187            && self.metadata.alignment as usize >= required_alignment as usize
188    }
189
190    /// Mark buffer as accessed
191    pub fn mark_accessed(&mut self) {
192        self.metadata.last_access_time = Instant::now();
193        self.metadata.access_count += 1;
194    }
195}
196
197unsafe impl Send for TempBuffer {}
198unsafe impl Sync for TempBuffer {}
199
200impl Drop for TempBuffer {
201    fn drop(&mut self) {
202        unsafe {
203            dealloc(self.ptr.as_ptr(), self.layout);
204        }
205    }
206}
207
208/// Buffer pool for efficient reuse of temporary buffers
209#[derive(Debug)]
210pub struct BufferPool {
211    pools: HashMap<BufferSizeCategory, VecDeque<TempBuffer>>,
212    max_pool_size: usize,
213    max_idle_time: Duration,
214    allocation_stats: AllocationStats,
215}
216
217impl BufferPool {
218    /// Create a new buffer pool
219    pub fn new(max_pool_size: usize, max_idle_time: Duration) -> Self {
220        Self {
221            pools: HashMap::new(),
222            max_pool_size,
223            max_idle_time,
224            allocation_stats: AllocationStats::default(),
225        }
226    }
227
228    /// Get a buffer from the pool or allocate a new one
229    pub fn get_buffer(
230        &mut self,
231        size: usize,
232        alignment: BufferAlignment,
233        location: &str,
234    ) -> AutogradResult<TempBuffer> {
235        let category = BufferSizeCategory::from_size(size);
236
237        // Try to reuse a buffer from the pool
238        if let Some(pool) = self.pools.get_mut(&category) {
239            // Find a suitable buffer
240            for i in 0..pool.len() {
241                if pool[i].is_reusable(size, alignment) {
242                    let mut buffer = pool.remove(i).expect("index i is within pool bounds");
243                    buffer.mark_accessed();
244                    self.allocation_stats.pool_hits += 1;
245                    return Ok(buffer);
246                }
247            }
248        }
249
250        // No suitable buffer found, allocate a new one
251        let buffer = TempBuffer::new(size, alignment, location)?;
252        self.allocation_stats.new_allocations += 1;
253        self.allocation_stats.total_allocated += size;
254        Ok(buffer)
255    }
256
257    /// Return a buffer to the pool
258    pub fn return_buffer(&mut self, mut buffer: TempBuffer) {
259        buffer.metadata.is_active = false;
260        let category = buffer.metadata.category;
261
262        // Clean up old buffers before adding new one
263        self.cleanup_idle_buffers();
264
265        // Add to pool if there's space
266        let pool = self.pools.entry(category).or_insert_with(VecDeque::new);
267        if pool.len() < self.max_pool_size {
268            pool.push_back(buffer);
269        } else {
270            // Pool is full, drop the buffer
271            self.allocation_stats.buffers_dropped += 1;
272        }
273    }
274
275    /// Clean up buffers that have been idle too long
276    pub fn cleanup_idle_buffers(&mut self) {
277        let now = Instant::now();
278        let max_idle = self.max_idle_time;
279
280        for pool in self.pools.values_mut() {
281            pool.retain(|buffer| {
282                let idle_time = now.duration_since(buffer.metadata.last_access_time);
283                if idle_time > max_idle {
284                    self.allocation_stats.buffers_cleaned += 1;
285                    false
286                } else {
287                    true
288                }
289            });
290        }
291    }
292
293    /// Get allocation statistics
294    pub fn get_stats(&self) -> AllocationStats {
295        self.allocation_stats.clone()
296    }
297
298    /// Get pool utilization statistics
299    pub fn get_pool_utilization(&self) -> HashMap<BufferSizeCategory, (usize, usize)> {
300        let mut utilization = HashMap::new();
301        for (&category, pool) in &self.pools {
302            utilization.insert(category, (pool.len(), self.max_pool_size));
303        }
304        utilization
305    }
306}
307
308/// Statistics about buffer allocation patterns
309#[derive(Debug, Clone, Default)]
310pub struct AllocationStats {
311    pub new_allocations: usize,
312    pub pool_hits: usize,
313    pub buffers_dropped: usize,
314    pub buffers_cleaned: usize,
315    pub total_allocated: usize,
316    pub peak_pool_size: usize,
317}
318
319impl AllocationStats {
320    /// Calculate pool hit rate
321    pub fn hit_rate(&self) -> f64 {
322        let total_requests = self.new_allocations + self.pool_hits;
323        if total_requests == 0 {
324            0.0
325        } else {
326            self.pool_hits as f64 / total_requests as f64
327        }
328    }
329
330    /// Get average allocation size
331    pub fn average_allocation_size(&self) -> f64 {
332        if self.new_allocations == 0 {
333            0.0
334        } else {
335            self.total_allocated as f64 / self.new_allocations as f64
336        }
337    }
338}
339
340/// Optimized buffer allocator with multiple strategies
341#[derive(Debug)]
342pub struct OptimizedBufferAllocator {
343    strategy: AllocationStrategy,
344    pool: Option<Arc<Mutex<BufferPool>>>,
345    allocation_patterns: AllocationPatternAnalyzer,
346    cache_optimization: CacheOptimizer,
347}
348
349impl OptimizedBufferAllocator {
350    /// Create a new optimized buffer allocator
351    pub fn new(strategy: AllocationStrategy) -> Self {
352        let pool = match strategy {
353            AllocationStrategy::Pooled | AllocationStrategy::Hybrid => {
354                Some(Arc::new(Mutex::new(BufferPool::new(
355                    1000,                     // max pool size
356                    Duration::from_secs(300), // 5 minutes idle time
357                ))))
358            }
359            _ => None,
360        };
361
362        Self {
363            strategy,
364            pool,
365            allocation_patterns: AllocationPatternAnalyzer::new(),
366            cache_optimization: CacheOptimizer::new(),
367        }
368    }
369
370    /// Allocate a temporary buffer with optimization
371    pub fn allocate(
372        &mut self,
373        size: usize,
374        alignment: BufferAlignment,
375        location: &str,
376    ) -> AutogradResult<TempBuffer> {
377        // Record allocation pattern
378        self.allocation_patterns
379            .record_allocation(size, alignment, location);
380
381        // Apply cache optimization suggestions
382        let optimized_size = self.cache_optimization.optimize_size(size);
383        let optimized_alignment = self.cache_optimization.optimize_alignment(alignment);
384
385        match self.strategy {
386            AllocationStrategy::Pooled => {
387                if let Some(ref pool) = self.pool {
388                    pool.lock_or_recover()
389                        .get_buffer(optimized_size, optimized_alignment, location)
390                } else {
391                    TempBuffer::new(optimized_size, optimized_alignment, location)
392                }
393            }
394            AllocationStrategy::Direct => {
395                TempBuffer::new(optimized_size, optimized_alignment, location)
396            }
397            AllocationStrategy::Hybrid => {
398                let category = BufferSizeCategory::from_size(optimized_size);
399                if matches!(
400                    category,
401                    BufferSizeCategory::Tiny
402                        | BufferSizeCategory::Small
403                        | BufferSizeCategory::Medium
404                ) {
405                    // Use pool for smaller buffers
406                    if let Some(ref pool) = self.pool {
407                        pool.lock_or_recover().get_buffer(
408                            optimized_size,
409                            optimized_alignment,
410                            location,
411                        )
412                    } else {
413                        TempBuffer::new(optimized_size, optimized_alignment, location)
414                    }
415                } else {
416                    // Direct allocation for larger buffers
417                    TempBuffer::new(optimized_size, optimized_alignment, location)
418                }
419            }
420            AllocationStrategy::MemoryMapped => {
421                // For very large buffers, consider memory mapping
422                if optimized_size > 16 * 1024 * 1024 {
423                    // Would implement memory mapping here
424                    TempBuffer::new(optimized_size, optimized_alignment, location)
425                } else {
426                    TempBuffer::new(optimized_size, optimized_alignment, location)
427                }
428            }
429            AllocationStrategy::StackBased => {
430                // For very small buffers, could use stack allocation
431                TempBuffer::new(optimized_size, optimized_alignment, location)
432            }
433        }
434    }
435
436    /// Deallocate a temporary buffer
437    pub fn deallocate(&mut self, buffer: TempBuffer) {
438        match self.strategy {
439            AllocationStrategy::Pooled | AllocationStrategy::Hybrid => {
440                if let Some(ref pool) = self.pool {
441                    pool.lock_or_recover().return_buffer(buffer);
442                }
443                // If no pool, buffer will be dropped automatically
444            }
445            _ => {
446                // Direct deallocation - buffer will be dropped automatically
447            }
448        }
449    }
450
451    /// Get allocation statistics
452    pub fn get_allocation_stats(&self) -> Option<AllocationStats> {
453        self.pool
454            .as_ref()
455            .map(|pool| pool.lock_or_recover().get_stats())
456    }
457
458    /// Get allocation pattern analysis
459    pub fn get_pattern_analysis(&self) -> &AllocationPatternAnalyzer {
460        &self.allocation_patterns
461    }
462
463    /// Perform maintenance (cleanup, optimization)
464    pub fn perform_maintenance(&mut self) {
465        if let Some(ref pool) = self.pool {
466            pool.lock_or_recover().cleanup_idle_buffers();
467        }
468        self.allocation_patterns.analyze_patterns();
469        self.cache_optimization
470            .update_optimizations(&self.allocation_patterns);
471    }
472}
473
474/// Analyzes allocation patterns to suggest optimizations
475#[derive(Debug)]
476pub struct AllocationPatternAnalyzer {
477    allocation_history: VecDeque<AllocationRecord>,
478    size_frequency: HashMap<usize, usize>,
479    alignment_frequency: HashMap<BufferAlignment, usize>,
480    location_frequency: HashMap<String, usize>,
481    max_history_size: usize,
482}
483
484#[derive(Debug, Clone)]
485struct AllocationRecord {
486    size: usize,
487    alignment: BufferAlignment,
488    location: String,
489    timestamp: Instant,
490}
491
492impl AllocationPatternAnalyzer {
493    /// Create a new allocation pattern analyzer
494    pub fn new() -> Self {
495        Self {
496            allocation_history: VecDeque::new(),
497            size_frequency: HashMap::new(),
498            alignment_frequency: HashMap::new(),
499            location_frequency: HashMap::new(),
500            max_history_size: 10000,
501        }
502    }
503
504    /// Record an allocation
505    pub fn record_allocation(&mut self, size: usize, alignment: BufferAlignment, location: &str) {
506        let record = AllocationRecord {
507            size,
508            alignment,
509            location: location.to_string(),
510            timestamp: Instant::now(),
511        };
512
513        self.allocation_history.push_back(record);
514
515        // Update frequency counters
516        *self.size_frequency.entry(size).or_insert(0) += 1;
517        *self.alignment_frequency.entry(alignment).or_insert(0) += 1;
518        *self
519            .location_frequency
520            .entry(location.to_string())
521            .or_insert(0) += 1;
522
523        // Limit history size
524        if self.allocation_history.len() > self.max_history_size {
525            if let Some(old_record) = self.allocation_history.pop_front() {
526                // Decrement frequency counters for removed record
527                if let Some(count) = self.size_frequency.get_mut(&old_record.size) {
528                    *count = count.saturating_sub(1);
529                    if *count == 0 {
530                        self.size_frequency.remove(&old_record.size);
531                    }
532                }
533                if let Some(count) = self.alignment_frequency.get_mut(&old_record.alignment) {
534                    *count = count.saturating_sub(1);
535                    if *count == 0 {
536                        self.alignment_frequency.remove(&old_record.alignment);
537                    }
538                }
539                if let Some(count) = self.location_frequency.get_mut(&old_record.location) {
540                    *count = count.saturating_sub(1);
541                    if *count == 0 {
542                        self.location_frequency.remove(&old_record.location);
543                    }
544                }
545            }
546        }
547    }
548
549    /// Analyze patterns and suggest optimizations
550    pub fn analyze_patterns(&self) -> PatternAnalysis {
551        let most_common_sizes: Vec<_> = {
552            let mut sizes: Vec<_> = self.size_frequency.iter().collect();
553            sizes.sort_by(|a, b| b.1.cmp(a.1));
554            sizes
555                .into_iter()
556                .take(10)
557                .map(|(&size, &count)| (size, count))
558                .collect()
559        };
560
561        let most_common_alignments: Vec<_> = {
562            let mut alignments: Vec<_> = self.alignment_frequency.iter().collect();
563            alignments.sort_by(|a, b| b.1.cmp(a.1));
564            alignments
565                .into_iter()
566                .take(5)
567                .map(|(&alignment, &count)| (alignment, count))
568                .collect()
569        };
570
571        let hottest_locations: Vec<_> = {
572            let mut locations: Vec<_> = self.location_frequency.iter().collect();
573            locations.sort_by(|a, b| b.1.cmp(a.1));
574            locations
575                .into_iter()
576                .take(10)
577                .map(|(location, &count)| (location.clone(), count))
578                .collect()
579        };
580
581        PatternAnalysis {
582            total_allocations: self.allocation_history.len(),
583            most_common_sizes,
584            most_common_alignments,
585            hottest_locations,
586        }
587    }
588
589    /// Get current allocation rate (allocations per second)
590    pub fn get_allocation_rate(&self) -> f64 {
591        if self.allocation_history.is_empty() {
592            return 0.0;
593        }
594
595        let now = Instant::now();
596        let cutoff = now - Duration::from_secs(60); // Last minute
597
598        let recent_allocations = self
599            .allocation_history
600            .iter()
601            .rev()
602            .take_while(|record| record.timestamp > cutoff)
603            .count();
604
605        recent_allocations as f64 / 60.0
606    }
607}
608
609/// Results of allocation pattern analysis
610#[derive(Debug)]
611pub struct PatternAnalysis {
612    pub total_allocations: usize,
613    pub most_common_sizes: Vec<(usize, usize)>,
614    pub most_common_alignments: Vec<(BufferAlignment, usize)>,
615    pub hottest_locations: Vec<(String, usize)>,
616}
617
618/// Cache-aware optimization for buffer allocation
619#[derive(Debug)]
620pub struct CacheOptimizer {
621    cache_line_size: usize,
622    preferred_alignments: HashMap<usize, BufferAlignment>,
623    size_adjustments: HashMap<usize, usize>,
624}
625
626impl CacheOptimizer {
627    /// Create a new cache optimizer
628    pub fn new() -> Self {
629        Self {
630            cache_line_size: 64, // Typical cache line size
631            preferred_alignments: HashMap::new(),
632            size_adjustments: HashMap::new(),
633        }
634    }
635
636    /// Optimize buffer size for cache efficiency
637    pub fn optimize_size(&self, size: usize) -> usize {
638        // Check if we have a specific optimization for this size
639        if let Some(&adjusted_size) = self.size_adjustments.get(&size) {
640            return adjusted_size;
641        }
642
643        // Round up to cache line boundary for larger buffers
644        if size > self.cache_line_size {
645            let cache_lines = (size + self.cache_line_size - 1) / self.cache_line_size;
646            cache_lines * self.cache_line_size
647        } else {
648            // For smaller buffers, round to power of 2
649            size.next_power_of_two()
650        }
651    }
652
653    /// Optimize alignment for cache efficiency
654    pub fn optimize_alignment(&self, alignment: BufferAlignment) -> BufferAlignment {
655        match alignment {
656            BufferAlignment::None => BufferAlignment::Align8,
657            BufferAlignment::Align8 => BufferAlignment::Align16,
658            other => other,
659        }
660    }
661
662    /// Update optimizations based on allocation patterns
663    pub fn update_optimizations(&mut self, analyzer: &AllocationPatternAnalyzer) {
664        let analysis = analyzer.analyze_patterns();
665
666        // Update preferred alignments based on common patterns
667        for (alignment, count) in analysis.most_common_alignments {
668            if count > 100 {
669                // Threshold for frequent usage
670                for (size, _) in &analysis.most_common_sizes {
671                    self.preferred_alignments.insert(*size, alignment);
672                }
673            }
674        }
675    }
676}
677
678/// Global optimized buffer allocator
679static GLOBAL_ALLOCATOR: std::sync::OnceLock<Arc<RwLock<OptimizedBufferAllocator>>> =
680    std::sync::OnceLock::new();
681
682/// Get the global buffer allocator
683pub fn get_global_allocator() -> &'static Arc<RwLock<OptimizedBufferAllocator>> {
684    GLOBAL_ALLOCATOR.get_or_init(|| {
685        Arc::new(RwLock::new(OptimizedBufferAllocator::new(
686            AllocationStrategy::Hybrid,
687        )))
688    })
689}
690
691/// Convenience functions for global buffer allocation
692pub fn allocate_temp_buffer(
693    size: usize,
694    alignment: BufferAlignment,
695    location: &str,
696) -> AutogradResult<TempBuffer> {
697    get_global_allocator()
698        .write_or_recover()
699        .allocate(size, alignment, location)
700}
701
702pub fn deallocate_temp_buffer(buffer: TempBuffer) {
703    get_global_allocator().write_or_recover().deallocate(buffer);
704}
705
706pub fn get_global_allocation_stats() -> Option<AllocationStats> {
707    get_global_allocator()
708        .read_or_recover()
709        .get_allocation_stats()
710}
711
712pub fn perform_global_maintenance() {
713    get_global_allocator()
714        .write_or_recover()
715        .perform_maintenance();
716}
717
718/// RAII wrapper for automatic buffer cleanup
719#[derive(Debug)]
720pub struct AutoBuffer {
721    buffer: Option<TempBuffer>,
722}
723
724impl AutoBuffer {
725    /// Create a new auto-managed buffer
726    pub fn new(size: usize, alignment: BufferAlignment, location: &str) -> AutogradResult<Self> {
727        let buffer = allocate_temp_buffer(size, alignment, location)?;
728        Ok(Self {
729            buffer: Some(buffer),
730        })
731    }
732
733    /// Get a mutable slice view of the buffer
734    pub fn as_mut_slice(&mut self) -> Option<&mut [u8]> {
735        self.buffer.as_mut().map(|b| b.as_mut_slice())
736    }
737
738    /// Get buffer size
739    pub fn size(&self) -> Option<usize> {
740        self.buffer.as_ref().map(|b| b.size())
741    }
742
743    /// Take ownership of the inner buffer
744    pub fn take(mut self) -> Option<TempBuffer> {
745        self.buffer.take()
746    }
747}
748
749impl Drop for AutoBuffer {
750    fn drop(&mut self) {
751        if let Some(buffer) = self.buffer.take() {
752            deallocate_temp_buffer(buffer);
753        }
754    }
755}
756
757/// Macro for convenient temporary buffer allocation
758#[macro_export]
759macro_rules! temp_buffer {
760    ($size:expr) => {
761        $crate::buffer_optimization::AutoBuffer::new(
762            $size,
763            $crate::buffer_optimization::BufferAlignment::Align16,
764            concat!(file!(), ":", line!()),
765        )
766    };
767    ($size:expr, $align:expr) => {
768        $crate::buffer_optimization::AutoBuffer::new($size, $align, concat!(file!(), ":", line!()))
769    };
770}
771
772#[cfg(test)]
773mod tests {
774    use super::*;
775
776    #[test]
777    fn test_buffer_size_category() {
778        assert_eq!(BufferSizeCategory::from_size(512), BufferSizeCategory::Tiny);
779        assert_eq!(
780            BufferSizeCategory::from_size(8192),
781            BufferSizeCategory::Small
782        );
783        assert_eq!(
784            BufferSizeCategory::from_size(128 * 1024),
785            BufferSizeCategory::Medium
786        );
787        assert_eq!(
788            BufferSizeCategory::from_size(2 * 1024 * 1024),
789            BufferSizeCategory::Large
790        );
791        assert_eq!(
792            BufferSizeCategory::from_size(8 * 1024 * 1024),
793            BufferSizeCategory::Huge
794        );
795    }
796
797    #[test]
798    fn test_temp_buffer_creation() {
799        let buffer = TempBuffer::new(1024, BufferAlignment::Align16, "test").unwrap();
800        assert_eq!(buffer.size(), 1024);
801        assert_eq!(buffer.metadata().alignment, BufferAlignment::Align16);
802    }
803
804    #[test]
805    fn test_buffer_pool() {
806        let mut pool = BufferPool::new(10, Duration::from_secs(60));
807
808        // Allocate a buffer
809        let buffer1 = pool
810            .get_buffer(1024, BufferAlignment::Align16, "test1")
811            .unwrap();
812        assert_eq!(buffer1.size(), 1024);
813
814        // Return it to the pool
815        pool.return_buffer(buffer1);
816
817        // Get another buffer of the same size (should reuse)
818        let buffer2 = pool
819            .get_buffer(1024, BufferAlignment::Align16, "test2")
820            .unwrap();
821        assert_eq!(buffer2.size(), 1024);
822
823        let stats = pool.get_stats();
824        assert_eq!(stats.pool_hits, 1);
825        assert_eq!(stats.new_allocations, 1);
826    }
827
828    #[test]
829    fn test_optimized_allocator() {
830        let mut allocator = OptimizedBufferAllocator::new(AllocationStrategy::Pooled);
831
832        let buffer1 = allocator
833            .allocate(1024, BufferAlignment::Align16, "test1")
834            .unwrap();
835        assert_eq!(buffer1.size(), 1024);
836
837        allocator.deallocate(buffer1);
838
839        let buffer2 = allocator
840            .allocate(1024, BufferAlignment::Align16, "test2")
841            .unwrap();
842        assert_eq!(buffer2.size(), 1024);
843
844        if let Some(stats) = allocator.get_allocation_stats() {
845            assert!(stats.total_allocated > 0);
846        }
847    }
848
849    #[test]
850    fn test_allocation_pattern_analyzer() {
851        let mut analyzer = AllocationPatternAnalyzer::new();
852
853        // Record some allocations
854        analyzer.record_allocation(1024, BufferAlignment::Align16, "test1");
855        analyzer.record_allocation(1024, BufferAlignment::Align16, "test2");
856        analyzer.record_allocation(2048, BufferAlignment::Align32, "test3");
857
858        let analysis = analyzer.analyze_patterns();
859        assert_eq!(analysis.total_allocations, 3);
860        assert!(!analysis.most_common_sizes.is_empty());
861        assert!(!analysis.most_common_alignments.is_empty());
862
863        let rate = analyzer.get_allocation_rate();
864        assert!(rate >= 0.0);
865    }
866
867    #[test]
868    fn test_cache_optimizer() {
869        let optimizer = CacheOptimizer::new();
870
871        // Test size optimization
872        let optimized_size = optimizer.optimize_size(100);
873        assert!(optimized_size >= 100);
874
875        // Test alignment optimization
876        let optimized_alignment = optimizer.optimize_alignment(BufferAlignment::None);
877        assert_ne!(optimized_alignment, BufferAlignment::None);
878    }
879
880    #[test]
881    fn test_auto_buffer() {
882        let mut auto_buffer = AutoBuffer::new(1024, BufferAlignment::Align16, "test").unwrap();
883        assert_eq!(auto_buffer.size(), Some(1024));
884
885        if let Some(slice) = auto_buffer.as_mut_slice() {
886            assert_eq!(slice.len(), 1024);
887            slice[0] = 42;
888            assert_eq!(slice[0], 42);
889        }
890    }
891
892    #[test]
893    fn test_global_allocator() {
894        let buffer = allocate_temp_buffer(1024, BufferAlignment::Align16, "test").unwrap();
895        assert_eq!(buffer.size(), 1024);
896
897        deallocate_temp_buffer(buffer);
898
899        if let Some(stats) = get_global_allocation_stats() {
900            assert!(stats.total_allocated > 0);
901        }
902
903        perform_global_maintenance();
904    }
905}