1use crate::error::{IoError, Result};
7use scirs2_core::gpu::{GpuBuffer, GpuContext, GpuDataType, GpuDevice};
8use std::collections::{BTreeMap, HashMap, VecDeque};
9use std::sync::{Arc, Mutex};
10use std::time::{Duration, Instant};
11
12#[derive(Debug)]
14pub struct AdvancedGpuMemoryPool {
15 device: GpuDevice,
16 free_buffers: BTreeMap<usize, VecDeque<PooledBuffer>>,
17 allocated_buffers: HashMap<usize, BufferMetadata>,
18 allocation_stats: AllocationStats,
19 config: PoolConfig,
20 fragmentation_manager: FragmentationManager,
21 buffer_id_counter: usize,
22}
23
24impl AdvancedGpuMemoryPool {
25 pub fn new(device: GpuDevice, config: PoolConfig) -> Self {
27 Self {
28 device,
29 free_buffers: BTreeMap::new(),
30 allocated_buffers: HashMap::new(),
31 allocation_stats: AllocationStats::default(),
32 config,
33 fragmentation_manager: FragmentationManager::new(),
34 buffer_id_counter: 0,
35 }
36 }
37
38 pub fn allocate(&mut self, size: usize) -> Result<PooledBuffer> {
40 let aligned_size = self.align_size(size);
41
42 if let Some(buffer) = self.find_reusable_buffer(aligned_size) {
44 self.allocation_stats.cache_hits += 1;
45 return Ok(buffer);
46 }
47
48 self.allocation_stats.cache_misses += 1;
50 self.create_new_buffer(aligned_size)
51 }
52
53 pub fn deallocate(&mut self, mut buffer: PooledBuffer) -> Result<()> {
55 buffer.touch();
57 self.allocation_stats.total_deallocations += 1;
58
59 if buffer.metadata.size <= self.config.max_buffer_size
61 && self.get_total_pool_size() < self.config.max_pool_size
62 {
63 let size_bucket = self.get_size_bucket(buffer.metadata.size);
65 self.free_buffers
66 .entry(size_bucket)
67 .or_insert_with(VecDeque::new)
68 .push_back(buffer);
69 }
70 if self.fragmentation_manager.needs_compaction() {
74 self.compact_pool()?;
75 }
76
77 Ok(())
78 }
79
80 pub fn get_stats(&self) -> PoolStats {
82 PoolStats {
83 total_buffers: self.allocated_buffers.len(),
84 free_buffers: self.free_buffers.values().map(|v| v.len()).sum(),
85 total_pool_size: self.get_total_pool_size(),
86 fragmentation_ratio: self.fragmentation_manager.get_fragmentation_ratio(),
87 cache_hit_rate: self.allocation_stats.get_cache_hit_rate(),
88 allocation_stats: self.allocation_stats.clone(),
89 }
90 }
91
92 pub fn garbage_collect(&mut self) -> Result<usize> {
94 let mut freed_count = 0;
95 let now = Instant::now();
96
97 for buffers in self.free_buffers.values_mut() {
98 let original_len = buffers.len();
99 buffers.retain(|buffer| !buffer.is_expired(self.config.buffer_timeout));
100 freed_count += original_len - buffers.len();
101 }
102
103 self.fragmentation_manager.update_after_gc();
105
106 Ok(freed_count)
107 }
108
109 pub fn compact_pool(&mut self) -> Result<()> {
111 if !self.config.enable_compaction {
112 return Ok(());
113 }
114
115 for buffers in self.free_buffers.values_mut() {
117 let mut merged_buffers = VecDeque::new();
119
120 while let Some(buffer) = buffers.pop_front() {
121 merged_buffers.push_back(buffer);
123 }
124
125 *buffers = merged_buffers;
126 }
127
128 self.fragmentation_manager.reset_fragmentation();
129 Ok(())
130 }
131
132 pub fn clear(&mut self) {
134 self.free_buffers.clear();
135 self.allocation_stats.reset();
136 self.fragmentation_manager.reset_fragmentation();
137 }
138
139 fn find_reusable_buffer(&mut self, size: usize) -> Option<PooledBuffer> {
141 let size_bucket = self.get_size_bucket(size);
142
143 if let Some(buffers) = self.free_buffers.get_mut(&size_bucket) {
145 if let Some(mut buffer) = buffers.pop_front() {
146 buffer.touch();
147 return Some(buffer);
148 }
149 }
150
151 for (&bucket_size, buffers) in self.free_buffers.range_mut(size_bucket..) {
153 if bucket_size <= size * 2 {
154 if let Some(mut buffer) = buffers.pop_front() {
156 buffer.touch();
157 return Some(buffer);
158 }
159 }
160 }
161
162 None
163 }
164
165 fn create_new_buffer(&mut self, size: usize) -> Result<PooledBuffer> {
166 if size > self.config.max_buffer_size {
167 return Err(IoError::Other(format!(
168 "Buffer size {} exceeds maximum {}",
169 size, self.config.max_buffer_size
170 )));
171 }
172
173 let context = GpuContext::new(self.device.backend())
175 .map_err(|e| IoError::Other(format!("Failed to create GPU context: {}", e)))?;
176 let buffer: GpuBuffer<u8> = context.create_buffer(size);
177
178 let buffer_id = self.buffer_id_counter;
179 self.buffer_id_counter += 1;
180
181 let pooled_buffer = PooledBuffer::new(buffer, buffer_id, "memory_pool".to_string());
182
183 self.allocation_stats.total_allocations += 1;
185 self.allocation_stats.bytes_allocated += size;
186 self.allocated_buffers
187 .insert(buffer_id, pooled_buffer.metadata.clone());
188
189 Ok(pooled_buffer)
190 }
191
192 fn align_size(&self, size: usize) -> usize {
193 let alignment = self.config.alignment;
194 (size + alignment - 1) & !(alignment - 1)
195 }
196
197 fn get_size_bucket(&self, size: usize) -> usize {
198 if size <= self.config.min_buffer_size {
200 self.config.min_buffer_size
201 } else {
202 size.next_power_of_two()
203 }
204 }
205
206 fn get_total_pool_size(&self) -> usize {
207 self.free_buffers
208 .iter()
209 .map(|(&size, buffers)| size * buffers.len())
210 .sum()
211 }
212}
213
214#[derive(Debug, Clone)]
216pub struct PoolConfig {
217 pub max_pool_size: usize,
219 pub min_buffer_size: usize,
221 pub max_buffer_size: usize,
223 pub alignment: usize,
225 pub defragmentation_threshold: f64,
227 pub buffer_timeout: Duration,
229 pub enable_compaction: bool,
231 pub enable_prefetch: bool,
233}
234
235impl Default for PoolConfig {
236 fn default() -> Self {
237 Self {
238 max_pool_size: 1024 * 1024 * 1024, min_buffer_size: 4096, max_buffer_size: 64 * 1024 * 1024, alignment: 256, defragmentation_threshold: 0.3, buffer_timeout: Duration::from_secs(300), enable_compaction: true,
245 enable_prefetch: true,
246 }
247 }
248}
249
250#[derive(Debug, Clone)]
252pub struct BufferMetadata {
253 pub id: usize,
255 pub size: usize,
257 pub allocated_at: Instant,
259 pub access_count: usize,
261 pub last_access: Instant,
263 pub allocation_source: String,
265}
266
267pub struct PooledBuffer {
269 pub buffer: GpuBuffer<u8>,
271 pub metadata: BufferMetadata,
273 pub created_at: Instant,
275 pub last_used: Instant,
277 pub use_count: usize,
279}
280
281impl std::fmt::Debug for PooledBuffer {
282 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
283 f.debug_struct("PooledBuffer")
284 .field("buffer_size", &self.buffer.len())
285 .field("metadata", &self.metadata)
286 .field("created_at", &self.created_at)
287 .field("last_used", &self.last_used)
288 .field("use_count", &self.use_count)
289 .finish()
290 }
291}
292
293impl PooledBuffer {
294 fn new(buffer: GpuBuffer<u8>, id: usize, allocation_source: String) -> Self {
295 let now = Instant::now();
296 let size = buffer.len();
297
298 Self {
299 buffer,
300 metadata: BufferMetadata {
301 id,
302 size,
303 allocated_at: now,
304 access_count: 0,
305 last_access: now,
306 allocation_source,
307 },
308 created_at: now,
309 last_used: now,
310 use_count: 0,
311 }
312 }
313
314 fn touch(&mut self) {
315 self.last_used = Instant::now();
316 self.use_count += 1;
317 self.metadata.access_count += 1;
318 self.metadata.last_access = self.last_used;
319 }
320
321 fn is_expired(&self, timeout: Duration) -> bool {
322 self.last_used.elapsed() > timeout
323 }
324
325 pub fn get_utilization_efficiency(&self) -> f64 {
327 if self.use_count == 0 {
328 0.0
329 } else {
330 let age_seconds = self.created_at.elapsed().as_secs_f64();
331 self.use_count as f64 / age_seconds.max(1.0)
332 }
333 }
334}
335
336#[derive(Debug, Default, Clone)]
338pub struct AllocationStats {
339 pub total_allocations: usize,
341 pub total_deallocations: usize,
343 pub cache_hits: usize,
345 pub cache_misses: usize,
347 pub bytes_allocated: usize,
349 pub bytes_deallocated: usize,
351 pub peak_memory_usage: usize,
353 pub compaction_count: usize,
355}
356
357impl AllocationStats {
358 pub fn get_cache_hit_rate(&self) -> f64 {
360 let total_requests = self.cache_hits + self.cache_misses;
361 if total_requests == 0 {
362 0.0
363 } else {
364 self.cache_hits as f64 / total_requests as f64
365 }
366 }
367
368 pub fn reset(&mut self) {
370 *self = Self::default();
371 }
372}
373
374#[derive(Debug)]
376pub struct FragmentationManager {
377 internal_fragmentation: f64,
378 external_fragmentation: f64,
379 compaction_threshold: f64,
380 last_compaction: Instant,
381 fragmentation_history: VecDeque<f64>,
382}
383
384impl FragmentationManager {
385 pub fn new() -> Self {
387 Self {
388 internal_fragmentation: 0.0,
389 external_fragmentation: 0.0,
390 compaction_threshold: 0.3,
391 last_compaction: Instant::now(),
392 fragmentation_history: VecDeque::with_capacity(100),
393 }
394 }
395
396 pub fn needs_compaction(&self) -> bool {
398 self.external_fragmentation > self.compaction_threshold
399 && self.last_compaction.elapsed() > Duration::from_secs(60)
400 }
401
402 pub fn get_fragmentation_ratio(&self) -> f64 {
404 (self.internal_fragmentation + self.external_fragmentation) / 2.0
405 }
406
407 pub fn update_fragmentation(&mut self, internal: f64, external: f64) {
409 self.internal_fragmentation = internal;
410 self.external_fragmentation = external;
411
412 let avg_fragmentation = self.get_fragmentation_ratio();
413 self.fragmentation_history.push_back(avg_fragmentation);
414
415 if self.fragmentation_history.len() > 100 {
416 self.fragmentation_history.pop_front();
417 }
418 }
419
420 pub fn reset_fragmentation(&mut self) {
422 self.internal_fragmentation = 0.0;
423 self.external_fragmentation = 0.0;
424 self.last_compaction = Instant::now();
425 }
426
427 pub fn update_after_gc(&mut self) {
429 self.external_fragmentation *= 0.8;
431 }
432
433 pub fn get_trend(&self) -> FragmentationTrend {
435 if self.fragmentation_history.len() < 10 {
436 return FragmentationTrend::Stable;
437 }
438
439 let recent_avg = self.fragmentation_history.iter().rev().take(5).sum::<f64>() / 5.0;
440 let older_avg = self
441 .fragmentation_history
442 .iter()
443 .rev()
444 .skip(5)
445 .take(5)
446 .sum::<f64>()
447 / 5.0;
448
449 if recent_avg > older_avg * 1.1 {
450 FragmentationTrend::Increasing
451 } else if recent_avg < older_avg * 0.9 {
452 FragmentationTrend::Decreasing
453 } else {
454 FragmentationTrend::Stable
455 }
456 }
457}
458
459impl Default for FragmentationManager {
460 fn default() -> Self {
461 Self::new()
462 }
463}
464
465#[derive(Debug, Clone, Copy, PartialEq, Eq)]
467pub enum FragmentationTrend {
468 Increasing,
470 Stable,
472 Decreasing,
474}
475
476#[derive(Debug, Clone)]
478pub struct PoolStats {
479 pub total_buffers: usize,
481 pub free_buffers: usize,
483 pub total_pool_size: usize,
485 pub fragmentation_ratio: f64,
487 pub cache_hit_rate: f64,
489 pub allocation_stats: AllocationStats,
491}
492
493impl PoolStats {
494 pub fn get_efficiency_score(&self) -> f64 {
496 let utilization = if self.total_buffers == 0 {
497 0.0
498 } else {
499 (self.total_buffers - self.free_buffers) as f64 / self.total_buffers as f64
500 };
501
502 let fragmentation_penalty = 1.0 - self.fragmentation_ratio.min(1.0);
503 let cache_bonus = self.cache_hit_rate;
504
505 (utilization + fragmentation_penalty + cache_bonus) / 3.0
506 }
507}
508
509#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
511pub enum MemoryType {
512 Device,
514 Unified,
516 Pinned,
518 Mapped,
520}
521
522#[derive(Debug)]
524pub struct GpuMemoryPoolManager {
525 pools: HashMap<MemoryType, AdvancedGpuMemoryPool>,
526 device: GpuDevice,
527 global_stats: AllocationStats,
528}
529
530impl GpuMemoryPoolManager {
531 pub fn new(device: GpuDevice) -> Result<Self> {
533 let mut pools = HashMap::new();
534
535 for memory_type in [MemoryType::Device, MemoryType::Unified, MemoryType::Pinned] {
537 let config = PoolConfig::default();
538 let pool = AdvancedGpuMemoryPool::new(device.clone(), config);
539 pools.insert(memory_type, pool);
540 }
541
542 Ok(Self {
543 pools,
544 device,
545 global_stats: AllocationStats::default(),
546 })
547 }
548
549 pub fn create_pool(
551 &mut self,
552 total_size: usize,
553 memory_type: MemoryType,
554 ) -> Result<&mut AdvancedGpuMemoryPool> {
555 let mut config = PoolConfig::default();
556 config.max_pool_size = total_size;
557
558 let pool = AdvancedGpuMemoryPool::new(self.device.clone(), config);
559 self.pools.insert(memory_type, pool);
560
561 Ok(self.pools.get_mut(&memory_type).expect("Operation failed"))
562 }
563
564 pub fn allocate(&mut self, size: usize, memory_type: MemoryType) -> Result<PooledBuffer> {
566 let pool = self
567 .pools
568 .get_mut(&memory_type)
569 .ok_or_else(|| IoError::Other(format!("Memory pool {:?} not found", memory_type)))?;
570
571 let buffer = pool.allocate(size)?;
572 self.global_stats.total_allocations += 1;
573 self.global_stats.bytes_allocated += size;
574
575 Ok(buffer)
576 }
577
578 pub fn deallocate(&mut self, buffer: PooledBuffer, memory_type: MemoryType) -> Result<()> {
580 let pool = self
581 .pools
582 .get_mut(&memory_type)
583 .ok_or_else(|| IoError::Other(format!("Memory pool {:?} not found", memory_type)))?;
584
585 self.global_stats.total_deallocations += 1;
586 self.global_stats.bytes_deallocated += buffer.metadata.size;
587
588 pool.deallocate(buffer)
589 }
590
591 pub fn get_global_stats(&self) -> GlobalPoolStats {
593 let pool_stats: Vec<_> = self
594 .pools
595 .iter()
596 .map(|(&memory_type, pool)| (memory_type, pool.get_stats()))
597 .collect();
598
599 let total_buffers: usize = pool_stats
600 .iter()
601 .map(|(_, stats)| stats.total_buffers)
602 .sum();
603 let total_pool_size: usize = pool_stats
604 .iter()
605 .map(|(_, stats)| stats.total_pool_size)
606 .sum();
607 let avg_fragmentation: f64 = if pool_stats.is_empty() {
608 0.0
609 } else {
610 pool_stats
611 .iter()
612 .map(|(_, stats)| stats.fragmentation_ratio)
613 .sum::<f64>()
614 / pool_stats.len() as f64
615 };
616
617 GlobalPoolStats {
618 total_buffers,
619 total_pool_size,
620 pool_count: self.pools.len(),
621 average_fragmentation: avg_fragmentation,
622 global_allocation_stats: self.global_stats.clone(),
623 pool_stats,
624 }
625 }
626
627 pub fn garbage_collect_all(&mut self) -> Result<usize> {
629 let mut total_freed = 0;
630 for pool in self.pools.values_mut() {
631 total_freed += pool.garbage_collect()?;
632 }
633 Ok(total_freed)
634 }
635
636 pub fn get_pool_size(&self, memory_type: MemoryType) -> usize {
638 self.pools
639 .get(&memory_type)
640 .map(|pool| pool.get_total_pool_size())
641 .unwrap_or(0)
642 }
643}
644
645#[derive(Debug, Clone)]
647pub struct GlobalPoolStats {
648 pub total_buffers: usize,
650 pub total_pool_size: usize,
652 pub pool_count: usize,
654 pub average_fragmentation: f64,
656 pub global_allocation_stats: AllocationStats,
658 pub pool_stats: Vec<(MemoryType, PoolStats)>,
660}
661
662#[cfg(test)]
663mod tests {
664 use super::*;
665 use scirs2_core::gpu::{GpuBackend, GpuDevice};
666
667 fn create_test_device() -> GpuDevice {
668 GpuDevice::new(GpuBackend::Cpu, 0)
670 }
671
672 #[test]
673 fn test_pool_config_defaults() {
674 let config = PoolConfig::default();
675 assert_eq!(config.min_buffer_size, 4096);
676 assert_eq!(config.max_buffer_size, 64 * 1024 * 1024);
677 assert_eq!(config.alignment, 256);
678 }
679
680 #[test]
681 fn test_fragmentation_manager() {
682 let mut manager = FragmentationManager::new();
683 assert_eq!(manager.get_fragmentation_ratio(), 0.0);
684
685 manager.update_fragmentation(0.2, 0.3);
686 assert_eq!(manager.get_fragmentation_ratio(), 0.25);
687
688 assert!(!manager.needs_compaction()); }
690
691 #[test]
692 fn test_allocation_stats() {
693 let mut stats = AllocationStats::default();
694 stats.cache_hits = 8;
695 stats.cache_misses = 2;
696
697 assert_eq!(stats.get_cache_hit_rate(), 0.8);
698 }
699
700 #[test]
701 fn test_memory_pool_manager_creation() {
702 let device = create_test_device();
703 let manager = GpuMemoryPoolManager::new(device);
704 assert!(manager.is_ok());
705
706 let manager = manager.expect("Operation failed");
707 assert_eq!(manager.pools.len(), 3); }
709
710 #[test]
711 fn test_pool_stats_efficiency() {
712 let stats = PoolStats {
713 total_buffers: 10,
714 free_buffers: 2,
715 total_pool_size: 1024 * 1024,
716 fragmentation_ratio: 0.1,
717 cache_hit_rate: 0.9,
718 allocation_stats: AllocationStats::default(),
719 };
720
721 let efficiency = stats.get_efficiency_score();
722 assert!(efficiency > 0.8); }
724}