1use super::{
7 cache::{AccessPattern, CacheOptimizer},
8 pools::{MemoryPool, MemoryPoolStats},
9 streams::MultiStreamMemoryManager,
10 tracking::{global_monitor_arc, PerformanceMonitor},
11 views::{MemoryAliasDetector, StridedView},
12};
13use crate::Device;
14#[cfg(feature = "gpu")]
15use crate::TensorError;
16use std::collections::HashMap;
17use std::sync::{Arc, RwLock};
18
19pub struct MemoryManager {
21 pools: Arc<RwLock<HashMap<Device, Arc<MemoryPool>>>>,
22 multi_stream_managers: Arc<RwLock<HashMap<usize, Arc<MultiStreamMemoryManager>>>>,
23 alias_detector: Arc<MemoryAliasDetector>,
24 cache_optimizer: Arc<CacheOptimizer>,
25 performance_monitor: Arc<PerformanceMonitor>,
26 default_pool_size: usize,
27}
28
29impl MemoryManager {
30 pub fn new() -> Self {
32 Self {
33 pools: Arc::new(RwLock::new(HashMap::new())),
34 multi_stream_managers: Arc::new(RwLock::new(HashMap::new())),
35 alias_detector: Arc::new(MemoryAliasDetector::new()),
36 cache_optimizer: Arc::new(CacheOptimizer::new()),
37 performance_monitor: global_monitor_arc(),
38 default_pool_size: 512 * 1024 * 1024, }
40 }
41
42 pub fn with_pool_size(pool_size: usize) -> Self {
44 let mut manager = Self::new();
45 manager.default_pool_size = pool_size;
46 manager
47 }
48
49 #[cfg(feature = "gpu")]
51 pub fn get_pool(&self, device: Device) -> crate::Result<Arc<MemoryPool>> {
52 let pools = self.pools.read().map_err(|_| {
53 TensorError::invalid_operation_simple("manager read lock poisoned".to_string())
54 })?;
55
56 if let Some(pool) = pools.get(&device) {
57 return Ok(Arc::clone(pool));
58 }
59
60 drop(pools);
61
62 let pool = match device {
64 Device::Gpu(device_id) => MemoryPool::new(device_id, self.default_pool_size)?,
65 Device::Cpu => {
66 return Err(TensorError::unsupported_operation_simple(
67 "CPU memory pools not yet implemented".to_string(),
68 ))
69 }
70 #[cfg(feature = "rocm")]
71 Device::Rocm(_) => {
72 return Err(TensorError::unsupported_operation_simple(
73 "ROCm memory pools not yet implemented".to_string(),
74 ))
75 }
76 };
77 let pool = Arc::new(pool);
78
79 let mut pools = self.pools.write().map_err(|_| {
80 TensorError::invalid_operation_simple("manager write lock poisoned".to_string())
81 })?;
82 pools.insert(device, Arc::clone(&pool));
83
84 Ok(pool)
85 }
86
87 #[cfg(feature = "gpu")]
89 pub fn get_multi_stream_manager(
90 &self,
91 device_id: usize,
92 num_streams: usize,
93 ) -> crate::Result<Arc<MultiStreamMemoryManager>> {
94 let managers = self.multi_stream_managers.read().map_err(|_| {
95 TensorError::invalid_operation_simple("manager read lock poisoned".to_string())
96 })?;
97
98 if let Some(manager) = managers.get(&device_id) {
99 return Ok(Arc::clone(manager));
100 }
101
102 drop(managers);
103
104 let stream_pool_size = self.default_pool_size / num_streams;
106 let manager = MultiStreamMemoryManager::new(device_id, num_streams, stream_pool_size)?;
107 let manager = Arc::new(manager);
108
109 let mut managers = self.multi_stream_managers.write().map_err(|_| {
110 TensorError::invalid_operation_simple("manager write lock poisoned".to_string())
111 })?;
112 managers.insert(device_id, Arc::clone(&manager));
113
114 Ok(manager)
115 }
116
117 pub fn check_memory_alias(&self, buffer_id: usize, offset: usize, size: usize) -> bool {
119 self.alias_detector.check_alias(buffer_id, offset, size)
120 }
121
122 pub fn register_memory_view(&self, buffer_id: usize, offset: usize, size: usize) {
124 self.alias_detector.register_view(buffer_id, offset, size);
125 }
126
127 pub fn unregister_memory_view(&self, buffer_id: usize, offset: usize, size: usize) {
129 self.alias_detector.unregister_view(buffer_id, offset, size);
130 }
131
132 pub fn get_optimal_access_pattern(&self, dims: &[usize], element_size: usize) -> AccessPattern {
134 self.cache_optimizer
135 .optimize_access_pattern(dims, element_size)
136 }
137
138 pub fn get_optimal_alignment(&self, data_size: usize) -> usize {
140 self.cache_optimizer.get_optimal_alignment(data_size)
141 }
142
143 pub fn record_memory_operation(&self, operation: &str, size: usize) {
145 self.performance_monitor.record_allocation(operation, size);
146 }
147
148 pub fn get_memory_statistics(&self) -> MemoryStatistics {
150 let mut stats = MemoryStatistics::new();
151
152 let pools = self
154 .pools
155 .read()
156 .unwrap_or_else(|poisoned| poisoned.into_inner());
157 for (device, pool) in pools.iter() {
158 let pool_stats = pool.stats();
159 stats.add_device_stats(*device, pool_stats);
160 }
161
162 stats.total_allocations = self.performance_monitor.get_allocation_stats().0;
164 stats.total_deallocations = self.performance_monitor.get_allocation_stats().1;
165 stats.current_memory_tracked = self.performance_monitor.get_current_memory();
166 stats.peak_memory_tracked = self.performance_monitor.get_peak_memory();
167
168 let (alias_buffers, alias_views) = self.alias_detector.get_alias_statistics();
170 stats.active_alias_buffers = alias_buffers;
171 stats.active_alias_views = alias_views;
172
173 stats
174 }
175
176 pub fn generate_report(&self) -> String {
178 let mut report = String::new();
179 report.push_str("=== Memory Manager Report ===\n\n");
180
181 report.push_str("Performance Monitoring:\n");
183 let perf_report = self.performance_monitor.generate_report();
184 report.push_str(&perf_report);
185 report.push('\n');
186
187 report.push_str("Memory Pools:\n");
189 let pools = self
190 .pools
191 .read()
192 .unwrap_or_else(|poisoned| poisoned.into_inner());
193 for (device, pool) in pools.iter() {
194 let stats = pool.stats();
195 report.push_str(&format!(" Device {:?}:\n", device));
196 report.push_str(&format!(" Allocated: {} bytes\n", stats.total_allocated));
197 report.push_str(&format!(" Free: {} bytes\n", stats.total_free));
198 report.push_str(&format!(
199 " Fragmentation: {:.2}%\n",
200 stats.fragmentation_ratio * 100.0
201 ));
202 report.push_str(&format!(
203 " Memory Pressure: {:.2}%\n",
204 stats.memory_pressure * 100.0
205 ));
206 }
207 report.push('\n');
208
209 report.push_str("Multi-Stream Managers:\n");
211 let managers = self
212 .multi_stream_managers
213 .read()
214 .unwrap_or_else(|poisoned| poisoned.into_inner());
215 for (device_id, manager) in managers.iter() {
216 report.push_str(&format!(" Device {}:\n", device_id));
217 report.push_str(&format!(" Streams: {}\n", manager.num_streams()));
218
219 let (total_allocated, total_free) = manager.total_memory_usage();
220 report.push_str(&format!(" Total Allocated: {} bytes\n", total_allocated));
221 report.push_str(&format!(" Total Free: {} bytes\n", total_free));
222 }
223 report.push('\n');
224
225 let (alias_buffers, alias_views) = self.alias_detector.get_alias_statistics();
227 report.push_str("Memory Aliasing:\n");
228 report.push_str(&format!(" Active Buffers: {}\n", alias_buffers));
229 report.push_str(&format!(" Active Views: {}\n", alias_views));
230
231 report
232 }
233
234 pub fn optimize_tensor_layout(
236 &self,
237 shape: &[usize],
238 element_size: usize,
239 ) -> TensorLayoutOptimization {
240 let access_pattern = self
241 .cache_optimizer
242 .optimize_access_pattern(shape, element_size);
243 let optimal_alignment = self
244 .cache_optimizer
245 .get_optimal_alignment(shape.iter().product::<usize>() * element_size);
246
247 TensorLayoutOptimization {
248 access_pattern,
249 alignment: optimal_alignment,
250 block_size: self
251 .cache_optimizer
252 .get_optimal_block_size(element_size, shape.iter().product()),
253 }
254 }
255
256 pub fn create_strided_view(
258 &self,
259 offset: usize,
260 shape: Vec<usize>,
261 strides: Vec<usize>,
262 element_size: usize,
263 ) -> StridedView {
264 StridedView::new(offset, shape, strides, element_size)
265 }
266
267 pub fn set_default_pool_size(&mut self, size: usize) {
269 self.default_pool_size = size;
270 }
271
272 pub fn default_pool_size(&self) -> usize {
274 self.default_pool_size
275 }
276
277 pub fn clear(&self) {
279 let mut pools = self
280 .pools
281 .write()
282 .unwrap_or_else(|poisoned| poisoned.into_inner());
283 pools.clear();
284
285 let mut managers = self
286 .multi_stream_managers
287 .write()
288 .unwrap_or_else(|poisoned| poisoned.into_inner());
289 managers.clear();
290
291 self.performance_monitor.clear();
292 }
293
294 pub fn cache_optimizer(&self) -> &CacheOptimizer {
296 &self.cache_optimizer
297 }
298
299 pub fn performance_monitor(&self) -> &PerformanceMonitor {
301 &self.performance_monitor
302 }
303
304 pub fn alias_detector(&self) -> &MemoryAliasDetector {
306 &self.alias_detector
307 }
308}
309
310impl Default for MemoryManager {
311 fn default() -> Self {
312 Self::new()
313 }
314}
315
316#[derive(Debug, Clone)]
318pub struct MemoryStatistics {
319 pub device_stats: HashMap<Device, MemoryPoolStats>,
320 pub total_allocations: usize,
321 pub total_deallocations: usize,
322 pub current_memory_tracked: usize,
323 pub peak_memory_tracked: usize,
324 pub active_alias_buffers: usize,
325 pub active_alias_views: usize,
326}
327
328impl MemoryStatistics {
329 fn new() -> Self {
330 Self {
331 device_stats: HashMap::new(),
332 total_allocations: 0,
333 total_deallocations: 0,
334 current_memory_tracked: 0,
335 peak_memory_tracked: 0,
336 active_alias_buffers: 0,
337 active_alias_views: 0,
338 }
339 }
340
341 fn add_device_stats(&mut self, device: Device, stats: MemoryPoolStats) {
342 self.device_stats.insert(device, stats);
343 }
344
345 pub fn total_allocated(&self) -> usize {
347 self.device_stats.values().map(|s| s.total_allocated).sum()
348 }
349
350 pub fn total_free(&self) -> usize {
352 self.device_stats.values().map(|s| s.total_free).sum()
353 }
354
355 pub fn average_fragmentation(&self) -> f32 {
357 if self.device_stats.is_empty() {
358 return 0.0;
359 }
360
361 let total_fragmentation: f32 = self
362 .device_stats
363 .values()
364 .map(|s| s.fragmentation_ratio)
365 .sum();
366
367 total_fragmentation / self.device_stats.len() as f32
368 }
369
370 pub fn max_memory_pressure(&self) -> f32 {
372 self.device_stats
373 .values()
374 .map(|s| s.memory_pressure)
375 .fold(0.0, f32::max)
376 }
377}
378
379#[derive(Debug, Clone)]
381pub struct TensorLayoutOptimization {
382 pub access_pattern: AccessPattern,
383 pub alignment: usize,
384 pub block_size: usize,
385}
386
387static GLOBAL_MEMORY_MANAGER: std::sync::OnceLock<Arc<MemoryManager>> = std::sync::OnceLock::new();
389
390pub fn global_memory_manager() -> &'static MemoryManager {
392 GLOBAL_MEMORY_MANAGER.get_or_init(|| Arc::new(MemoryManager::new()))
393}
394
395pub fn global_memory_manager_arc() -> Arc<MemoryManager> {
397 GLOBAL_MEMORY_MANAGER
398 .get_or_init(|| Arc::new(MemoryManager::new()))
399 .clone()
400}
401
402#[cfg(test)]
403mod tests {
404 use super::*;
405 use crate::Device;
406
407 #[test]
408 fn test_memory_manager_creation() {
409 let manager = MemoryManager::new();
410 assert_eq!(manager.default_pool_size(), 512 * 1024 * 1024);
411
412 let custom_manager = MemoryManager::with_pool_size(1024 * 1024);
413 assert_eq!(custom_manager.default_pool_size(), 1024 * 1024);
414 }
415
416 #[test]
417 fn test_memory_statistics() {
418 let mut stats = MemoryStatistics::new();
419 assert_eq!(stats.total_allocated(), 0);
420 assert_eq!(stats.total_free(), 0);
421 assert_eq!(stats.average_fragmentation(), 0.0);
422 assert_eq!(stats.max_memory_pressure(), 0.0);
423
424 let device_stats = crate::memory::pools::MemoryPoolStats {
426 total_allocated: 1000,
427 total_free: 2000,
428 blocks_allocated: 10,
429 blocks_free: 5,
430 fragmentation_ratio: 0.1,
431 peak_allocated: 1500,
432 allocation_count: 100,
433 deallocation_count: 90,
434 defragmentation_count: 2,
435 largest_free_block: 1000,
436 average_block_size: 200.0,
437 memory_pressure: 0.3,
438 };
439
440 stats.add_device_stats(Device::Cpu, device_stats.clone());
441
442 assert_eq!(stats.total_allocated(), 1000);
443 assert_eq!(stats.total_free(), 2000);
444 assert_eq!(stats.average_fragmentation(), 0.1);
445 assert_eq!(stats.max_memory_pressure(), 0.3);
446 }
447
448 #[test]
449 fn test_tensor_layout_optimization() {
450 let manager = MemoryManager::new();
451 let optimization = manager.optimize_tensor_layout(&[100, 100], 4);
452
453 assert!(optimization.alignment > 0);
455 assert!(optimization.block_size > 0);
456 matches!(
458 optimization.access_pattern,
459 AccessPattern::Sequential | AccessPattern::Blocked { .. } | AccessPattern::Tiled { .. }
460 );
461 }
462
463 #[test]
464 fn test_strided_view_creation() {
465 let manager = MemoryManager::new();
466 let view = manager.create_strided_view(0, vec![2, 3], vec![12, 4], 4);
467
468 assert_eq!(view.offset, 0);
469 assert_eq!(view.shape, vec![2, 3]);
470 assert_eq!(view.strides, vec![12, 4]);
471 assert_eq!(view.element_size, 4);
472 }
473
474 #[test]
475 fn test_memory_alias_operations() {
476 let manager = MemoryManager::new();
477
478 manager.register_memory_view(0, 0, 100);
480
481 assert!(manager.check_memory_alias(0, 50, 100)); assert!(!manager.check_memory_alias(0, 100, 50)); manager.unregister_memory_view(0, 0, 100);
487 assert!(!manager.check_memory_alias(0, 50, 100)); }
489
490 #[test]
491 fn test_memory_operation_recording() {
492 let manager = MemoryManager::new();
493
494 let initial_stats = manager.get_memory_statistics();
496 let initial_allocations = initial_stats.total_allocations;
497 let initial_memory = initial_stats.current_memory_tracked;
498
499 manager.record_memory_operation("test_alloc", 1024);
500
501 let stats = manager.get_memory_statistics();
503 assert_eq!(stats.total_allocations, initial_allocations + 1);
504 assert_eq!(stats.current_memory_tracked, initial_memory + 1024);
505 }
506
507 #[test]
508 fn test_optimal_access_pattern() {
509 let manager = MemoryManager::new();
510
511 let pattern = manager.get_optimal_access_pattern(&[10, 10], 4);
513 matches!(pattern, AccessPattern::Sequential);
514
515 let pattern = manager.get_optimal_access_pattern(&[1000, 1000], 4);
517 matches!(
518 pattern,
519 AccessPattern::Blocked { .. } | AccessPattern::Tiled { .. }
520 );
521 }
522
523 #[test]
524 fn test_optimal_alignment() {
525 let manager = MemoryManager::new();
526
527 let small_alignment = manager.get_optimal_alignment(32);
528 let large_alignment = manager.get_optimal_alignment(8192);
529
530 assert!(small_alignment > 0);
531 assert!(large_alignment > 0);
532 assert!(large_alignment >= small_alignment); }
534
535 #[test]
536 fn test_manager_clear() {
537 let manager = MemoryManager::new();
538
539 manager.record_memory_operation("test", 1024);
540 manager.register_memory_view(0, 0, 100);
541
542 let stats_before = manager.get_memory_statistics();
543 assert!(stats_before.total_allocations > 0);
544
545 manager.clear();
546
547 let stats_after = manager.get_memory_statistics();
548 assert_eq!(stats_after.total_allocations, 0);
549 assert_eq!(stats_after.current_memory_tracked, 0);
550 }
551
552 #[test]
553 fn test_global_memory_manager() {
554 let manager1 = global_memory_manager();
555 let manager2 = global_memory_manager();
556
557 assert!(std::ptr::eq(manager1, manager2));
559
560 let initial_stats = manager1.get_memory_statistics();
562 let initial_tracked = initial_stats.current_memory_tracked;
563
564 manager1.record_memory_operation("global_test", 512);
565 let final_stats = manager2.get_memory_statistics();
566 let final_tracked = final_stats.current_memory_tracked;
567
568 assert!(final_tracked >= initial_tracked + 512);
571 }
572
573 #[test]
574 fn test_report_generation() {
575 let manager = MemoryManager::new();
576 manager.record_memory_operation("test_op", 1024);
577
578 let report = manager.generate_report();
579 assert!(report.contains("Memory Manager Report"));
580 assert!(report.contains("Performance Monitoring"));
581 assert!(report.contains("Memory Pools"));
582 assert!(report.contains("Memory Aliasing"));
583 }
584
585 #[test]
586 fn test_set_default_pool_size() {
587 let mut manager = MemoryManager::new();
588 let original_size = manager.default_pool_size();
589
590 manager.set_default_pool_size(1024 * 1024);
591 assert_eq!(manager.default_pool_size(), 1024 * 1024);
592 assert_ne!(manager.default_pool_size(), original_size);
593 }
594
595 #[cfg(feature = "gpu")]
615 mod gpu_tests {
616 use super::*;
617
618 const TEST_POOL_SIZE: usize = 4 * 1024 * 1024;
620
621 #[test]
622 fn get_pool_returns_same_arc_on_cache_hit() {
623 let manager = MemoryManager::with_pool_size(TEST_POOL_SIZE);
624
625 let pool1 = match manager.get_pool(Device::Gpu(0)) {
626 Ok(pool) => pool,
627 Err(_) => return, };
629 let pool2 = manager
630 .get_pool(Device::Gpu(0))
631 .expect("test: second get_pool call should hit the cache and succeed");
632
633 assert!(
634 Arc::ptr_eq(&pool1, &pool2),
635 "get_pool should return a handle to the same pool on a cache hit"
636 );
637 }
638
639 #[test]
640 fn get_multi_stream_manager_returns_same_arc_on_cache_hit() {
641 let manager = MemoryManager::with_pool_size(TEST_POOL_SIZE);
642
643 let stream_manager1 = match manager.get_multi_stream_manager(0, 2) {
644 Ok(stream_manager) => stream_manager,
645 Err(_) => return, };
647 let stream_manager2 = manager.get_multi_stream_manager(0, 2).expect(
648 "test: second get_multi_stream_manager call should hit the cache and succeed",
649 );
650
651 assert!(
652 Arc::ptr_eq(&stream_manager1, &stream_manager2),
653 "get_multi_stream_manager should return a handle to the same manager on a cache hit"
654 );
655 }
656 }
657}