1use crate::gpu::{GpuBuffer, GpuDataType, GpuError};
7use std::collections::{BTreeMap, HashMap, VecDeque};
8use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
9use std::sync::{Arc, Mutex};
10use std::time::{Duration, Instant};
11use thiserror::Error;
12
13#[derive(Error, Debug)]
14pub enum MemoryError {
15 #[error("Out of memory: {0}")]
17 OutOfMemory(String),
18
19 #[error("Invalid allocation size: {0}")]
21 InvalidSize(usize),
22
23 #[error("Buffer not found: {0}")]
25 BufferNotFound(u64),
26
27 #[error("Pool is full")]
29 PoolFull,
30
31 #[error("Fragmentation threshold exceeded: {0:.2}%")]
33 FragmentationExceeded(f64),
34
35 #[error("GPU error: {0}")]
37 GpuError(#[from] GpuError),
38}
39
40pub type MemoryResult<T> = Result<T, MemoryError>;
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
45pub struct BufferHandle(u64);
46
47impl BufferHandle {
48 fn new() -> Self {
50 static COUNTER: AtomicU64 = AtomicU64::new(1);
51 Self(COUNTER.fetch_add(1, Ordering::Relaxed))
52 }
53
54 pub fn raw(&self) -> u64 {
56 self.0
57 }
58}
59
60#[derive(Debug, Clone)]
62struct BufferMetadata {
63 handle: BufferHandle,
64 size: usize,
65 allocated_at: Instant,
66 last_used: Instant,
67 use_count: usize,
68 is_pinned: bool,
69}
70
71impl BufferMetadata {
72 fn new(handle: BufferHandle, size: usize) -> Self {
73 let now = Instant::now();
74 Self {
75 handle,
76 size,
77 allocated_at: now,
78 last_used: now,
79 use_count: 0,
80 is_pinned: false,
81 }
82 }
83
84 fn mark_used(&mut self) {
85 self.last_used = Instant::now();
86 self.use_count += 1;
87 }
88
89 fn age(&self) -> Duration {
90 self.allocated_at.elapsed()
91 }
92
93 fn idle_time(&self) -> Duration {
94 self.last_used.elapsed()
95 }
96}
97
98#[derive(Debug)]
100pub struct BufferPool<T: GpuDataType> {
101 pools: Arc<Mutex<BTreeMap<usize, VecDeque<(BufferHandle, Arc<GpuBuffer<T>>)>>>>,
103 active_buffers: Arc<Mutex<HashMap<BufferHandle, BufferMetadata>>>,
105 total_allocated: Arc<AtomicUsize>,
107 total_reused: Arc<AtomicUsize>,
108 max_pool_size: usize,
109 eviction_policy: EvictionPolicy,
110}
111
112#[derive(Debug, Clone, Copy, PartialEq, Eq)]
114pub enum EvictionPolicy {
115 Lru,
117 Lfu,
119 Fifo,
121}
122
123impl<T: GpuDataType> BufferPool<T> {
124 pub fn new() -> Self {
126 Self::with_capacity_and_policy(1024, EvictionPolicy::Lru)
127 }
128
129 pub fn with_capacity(max_pool_size: usize) -> Self {
131 Self::with_capacity_and_policy(max_pool_size, EvictionPolicy::Lru)
132 }
133
134 pub fn with_capacity_and_policy(max_pool_size: usize, eviction_policy: EvictionPolicy) -> Self {
136 Self {
137 pools: Arc::new(Mutex::new(BTreeMap::new())),
138 active_buffers: Arc::new(Mutex::new(HashMap::new())),
139 total_allocated: Arc::new(AtomicUsize::new(0)),
140 total_reused: Arc::new(AtomicUsize::new(0)),
141 max_pool_size,
142 eviction_policy,
143 }
144 }
145
146 pub fn allocate(&self, size: usize) -> MemoryResult<(BufferHandle, Arc<GpuBuffer<T>>)> {
148 if size == 0 {
149 return Err(MemoryError::InvalidSize(size));
150 }
151
152 let mut pools = self.pools.lock().map_err(|_| {
154 MemoryError::GpuError(GpuError::Other("Failed to lock pools".to_string()))
155 })?;
156
157 let reusable = pools
159 .range_mut(size..)
160 .next()
161 .and_then(|(_, buffers)| buffers.pop_front());
162
163 if let Some((handle, buffer)) = reusable {
164 self.total_reused.fetch_add(1, Ordering::Relaxed);
165
166 let mut active = self.active_buffers.lock().map_err(|_| {
167 MemoryError::GpuError(GpuError::Other("Failed to lock active buffers".to_string()))
168 })?;
169
170 if let Some(metadata) = active.get_mut(&handle) {
171 metadata.mark_used();
172 } else {
173 let metadata = BufferMetadata::new(handle, size);
174 active.insert(handle, metadata);
175 }
176
177 return Ok((handle, buffer));
178 }
179
180 drop(pools);
181
182 self.allocate_new_buffer(size)
186 }
187
188 fn allocate_new_buffer(&self, size: usize) -> MemoryResult<(BufferHandle, Arc<GpuBuffer<T>>)> {
190 self.total_allocated.fetch_add(1, Ordering::Relaxed);
191
192 let handle = BufferHandle::new();
195
196 Err(MemoryError::GpuError(GpuError::Other(
200 "Buffer allocation requires GPU backend context".to_string(),
201 )))
202 }
203
204 pub fn deallocate(&self, handle: BufferHandle, buffer: Arc<GpuBuffer<T>>) -> MemoryResult<()> {
206 let mut active = self.active_buffers.lock().map_err(|_| {
207 MemoryError::GpuError(GpuError::Other("Failed to lock active buffers".to_string()))
208 })?;
209
210 let metadata = active
211 .remove(&handle)
212 .ok_or(MemoryError::BufferNotFound(handle.raw()))?;
213
214 drop(active);
215
216 let mut pools = self.pools.lock().map_err(|_| {
217 MemoryError::GpuError(GpuError::Other("Failed to lock pools".to_string()))
218 })?;
219
220 let pool = pools.entry(metadata.size).or_insert_with(VecDeque::new);
221
222 if pool.len() >= self.max_pool_size {
223 self.evict_buffer(pool)?;
225 }
226
227 pool.push_back((handle, buffer));
228 Ok(())
229 }
230
231 fn evict_buffer(
233 &self,
234 pool: &mut VecDeque<(BufferHandle, Arc<GpuBuffer<T>>)>,
235 ) -> MemoryResult<()> {
236 match self.eviction_policy {
237 EvictionPolicy::Lru | EvictionPolicy::Lfu | EvictionPolicy::Fifo => {
238 pool.pop_front();
240 Ok(())
241 }
242 }
243 }
244
245 pub fn statistics(&self) -> BufferPoolStatistics {
247 let pools = self.pools.lock().expect("Failed to lock pools");
248 let active = self
249 .active_buffers
250 .lock()
251 .expect("Failed to lock active buffers");
252
253 let total_pooled: usize = pools.values().map(|v| v.len()).sum();
254 let total_active = active.len();
255
256 BufferPoolStatistics {
257 total_allocated: self.total_allocated.load(Ordering::Relaxed),
258 total_reused: self.total_reused.load(Ordering::Relaxed),
259 pooled_buffers: total_pooled,
260 active_buffers: total_active,
261 pool_size_distribution: pools
262 .iter()
263 .map(|(size, buffers)| (*size, buffers.len()))
264 .collect(),
265 }
266 }
267
268 pub fn clear(&self) -> MemoryResult<()> {
270 let mut pools = self.pools.lock().map_err(|_| {
271 MemoryError::GpuError(GpuError::Other("Failed to lock pools".to_string()))
272 })?;
273 pools.clear();
274 Ok(())
275 }
276
277 pub fn pooled_count(&self) -> MemoryResult<usize> {
279 let pools = self.pools.lock().map_err(|_| {
280 MemoryError::GpuError(GpuError::Other("Failed to lock pools".to_string()))
281 })?;
282 Ok(pools.values().map(|v| v.len()).sum())
283 }
284}
285
286impl<T: GpuDataType> Default for BufferPool<T> {
287 fn default() -> Self {
288 Self::new()
289 }
290}
291
292#[derive(Debug, Clone)]
294pub struct BufferPoolStatistics {
295 pub total_allocated: usize,
296 pub total_reused: usize,
297 pub pooled_buffers: usize,
298 pub active_buffers: usize,
299 pub pool_size_distribution: Vec<(usize, usize)>,
300}
301
302#[derive(Debug)]
304pub struct BufferAllocator {
305 size_classes: Vec<usize>,
307 allocated_bytes: Arc<AtomicUsize>,
309 freed_bytes: Arc<AtomicUsize>,
310 peak_usage: Arc<AtomicUsize>,
311 fragmentation_threshold: f64,
313}
314
315impl BufferAllocator {
316 pub fn new() -> Self {
318 Self::with_size_classes(Self::default_size_classes())
319 }
320
321 pub fn with_size_classes(size_classes: Vec<usize>) -> Self {
323 Self {
324 size_classes,
325 allocated_bytes: Arc::new(AtomicUsize::new(0)),
326 freed_bytes: Arc::new(AtomicUsize::new(0)),
327 peak_usage: Arc::new(AtomicUsize::new(0)),
328 fragmentation_threshold: 0.3, }
330 }
331
332 fn default_size_classes() -> Vec<usize> {
334 let mut classes = Vec::new();
335 let mut size = 256;
336 while size <= 1024 * 1024 * 1024 {
337 classes.push(size);
338 size *= 2;
339 }
340 classes
341 }
342
343 pub fn size_class_for(&self, size: usize) -> usize {
345 self.size_classes
346 .iter()
347 .find(|&&class_size| class_size >= size)
348 .copied()
349 .unwrap_or_else(|| {
350 size.next_power_of_two()
352 })
353 }
354
355 pub fn record_allocation(&self, size: usize) {
357 let new_allocated = self.allocated_bytes.fetch_add(size, Ordering::Relaxed) + size;
358
359 let mut peak = self.peak_usage.load(Ordering::Relaxed);
361 while new_allocated > peak {
362 match self.peak_usage.compare_exchange_weak(
363 peak,
364 new_allocated,
365 Ordering::Relaxed,
366 Ordering::Relaxed,
367 ) {
368 Ok(_) => break,
369 Err(current) => peak = current,
370 }
371 }
372 }
373
374 pub fn record_deallocation(&self, size: usize) {
376 self.freed_bytes.fetch_add(size, Ordering::Relaxed);
377 }
378
379 pub fn allocated_bytes(&self) -> usize {
381 self.allocated_bytes.load(Ordering::Relaxed)
382 }
383
384 pub fn freed_bytes(&self) -> usize {
386 self.freed_bytes.load(Ordering::Relaxed)
387 }
388
389 pub fn current_usage(&self) -> usize {
391 self.allocated_bytes() - self.freed_bytes()
392 }
393
394 pub fn peak_usage(&self) -> usize {
396 self.peak_usage.load(Ordering::Relaxed)
397 }
398
399 pub fn fragmentation_ratio(&self) -> f64 {
401 let allocated = self.allocated_bytes() as f64;
402 let current = self.current_usage() as f64;
403
404 if allocated == 0.0 {
405 return 0.0;
406 }
407
408 (allocated - current) / allocated
409 }
410
411 pub fn needs_defragmentation(&self) -> bool {
413 self.fragmentation_ratio() > self.fragmentation_threshold
414 }
415
416 pub fn statistics(&self) -> AllocatorStatistics {
418 AllocatorStatistics {
419 allocated_bytes: self.allocated_bytes(),
420 freed_bytes: self.freed_bytes(),
421 current_usage: self.current_usage(),
422 peak_usage: self.peak_usage(),
423 fragmentation_ratio: self.fragmentation_ratio(),
424 }
425 }
426}
427
428impl Default for BufferAllocator {
429 fn default() -> Self {
430 Self::new()
431 }
432}
433
434#[derive(Debug, Clone)]
436pub struct AllocatorStatistics {
437 pub allocated_bytes: usize,
438 pub freed_bytes: usize,
439 pub current_usage: usize,
440 pub peak_usage: usize,
441 pub fragmentation_ratio: f64,
442}
443
444#[derive(Debug)]
446pub struct MemoryArena<T: GpuDataType> {
447 buffers: Arc<Mutex<Vec<Arc<GpuBuffer<T>>>>>,
449 current_offset: Arc<AtomicUsize>,
451 buffer_size: usize,
453 total_allocated: Arc<AtomicUsize>,
455}
456
457impl<T: GpuDataType> MemoryArena<T> {
458 pub fn new(buffer_size: usize) -> Self {
460 Self {
461 buffers: Arc::new(Mutex::new(Vec::new())),
462 current_offset: Arc::new(AtomicUsize::new(0)),
463 buffer_size,
464 total_allocated: Arc::new(AtomicUsize::new(0)),
465 }
466 }
467
468 pub fn allocate_temp(&self, size: usize) -> MemoryResult<ArenaAllocation> {
470 if size > self.buffer_size {
471 return Err(MemoryError::InvalidSize(size));
472 }
473
474 let offset = self.current_offset.fetch_add(size, Ordering::Relaxed);
475
476 if offset + size > self.buffer_size {
478 self.current_offset.store(size, Ordering::Relaxed);
481 self.total_allocated
482 .fetch_add(self.buffer_size, Ordering::Relaxed);
483
484 Ok(ArenaAllocation {
485 buffer_index: 0, offset: 0,
487 size,
488 })
489 } else {
490 Ok(ArenaAllocation {
491 buffer_index: 0,
492 offset,
493 size,
494 })
495 }
496 }
497
498 pub fn reset(&self) {
500 self.current_offset.store(0, Ordering::Relaxed);
501 }
502
503 pub fn total_allocated(&self) -> usize {
505 self.total_allocated.load(Ordering::Relaxed)
506 }
507
508 pub fn current_usage(&self) -> usize {
510 self.current_offset.load(Ordering::Relaxed)
511 }
512}
513
514#[derive(Debug, Clone, Copy)]
516pub struct ArenaAllocation {
517 buffer_index: usize,
518 offset: usize,
519 size: usize,
520}
521
522impl ArenaAllocation {
523 pub fn offset(&self) -> usize {
525 self.offset
526 }
527
528 pub fn size(&self) -> usize {
530 self.size
531 }
532
533 pub fn buffer_index(&self) -> usize {
535 self.buffer_index
536 }
537}
538
539#[cfg(test)]
540mod tests {
541 use super::*;
542
543 fn test_buffer_handle_creation() {
544 let handle1 = BufferHandle::new();
545 let handle2 = BufferHandle::new();
546 assert_ne!(handle1, handle2);
547 }
548
549 #[test]
550 fn test_buffer_metadata() {
551 let handle = BufferHandle::new();
552 let mut metadata = BufferMetadata::new(handle, 1024);
553
554 assert_eq!(metadata.handle, handle);
555 assert_eq!(metadata.size, 1024);
556 assert_eq!(metadata.use_count, 0);
557 assert!(!metadata.is_pinned);
558
559 metadata.mark_used();
560 assert_eq!(metadata.use_count, 1);
561 }
562
563 #[test]
564 fn test_buffer_allocator_size_classes() {
565 let allocator = BufferAllocator::new();
566
567 assert_eq!(allocator.size_class_for(100), 256);
568 assert_eq!(allocator.size_class_for(256), 256);
569 assert_eq!(allocator.size_class_for(257), 512);
570 assert_eq!(allocator.size_class_for(1000), 1024);
571 }
572
573 #[test]
574 fn test_buffer_allocator_statistics() {
575 let allocator = BufferAllocator::new();
576
577 allocator.record_allocation(1024);
578 allocator.record_allocation(2048);
579
580 assert_eq!(allocator.allocated_bytes(), 3072);
581 assert_eq!(allocator.current_usage(), 3072);
582 assert_eq!(allocator.peak_usage(), 3072);
583
584 allocator.record_deallocation(1024);
585 assert_eq!(allocator.current_usage(), 2048);
586 assert_eq!(allocator.freed_bytes(), 1024);
587 }
588
589 #[test]
590 fn test_buffer_allocator_fragmentation() {
591 let allocator = BufferAllocator::new();
592
593 allocator.record_allocation(10000);
594 allocator.record_deallocation(3000);
595
596 let frag = allocator.fragmentation_ratio();
597 assert!(frag > 0.0 && frag < 1.0);
598 }
599
600 #[test]
601 fn test_memory_arena() {
602 let arena = MemoryArena::<f32>::new(4096);
603
604 let alloc1 = arena.allocate_temp(1024).expect("Failed to allocate");
605 assert_eq!(alloc1.size(), 1024);
606 assert_eq!(alloc1.offset(), 0);
607
608 let alloc2 = arena.allocate_temp(512).expect("Failed to allocate");
609 assert_eq!(alloc2.size(), 512);
610 assert_eq!(alloc2.offset(), 1024);
611
612 arena.reset();
613 assert_eq!(arena.current_usage(), 0);
614 }
615
616 #[test]
617 fn test_memory_arena_overflow() {
618 let arena = MemoryArena::<f32>::new(1024);
619
620 let result = arena.allocate_temp(2048);
621 assert!(result.is_err());
622 }
623
624 fn test_eviction_policy() {
625 let pool = BufferPool::<f32>::with_capacity_and_policy(10, EvictionPolicy::Lru);
626 let stats = pool.statistics();
627
628 assert_eq!(stats.pooled_buffers, 0);
629 assert_eq!(stats.active_buffers, 0);
630 }
631}