scirs2_linalg/gpu/advanced/
memory.rs1use crate::error::{LinalgError, LinalgResult};
10use std::collections::HashMap;
11use std::time::Instant;
12
13#[derive(Debug)]
15pub struct GpuMemoryManager {
16 pub gpu_id: usize,
18 pub memory_pools: Vec<MemoryPool>,
20 pub allocation_strategy: MemoryAllocationStrategy,
22 pub garbage_collector: MemoryGarbageCollector,
24}
25
26#[derive(Debug)]
28pub struct MemoryPool {
29 pub size: usize,
31 pub free_blocks: Vec<MemoryBlock>,
33 pub allocated_blocks: Vec<MemoryBlock>,
35 pub pool_type: MemoryPoolType,
37}
38
39#[derive(Debug, Clone)]
41pub struct MemoryBlock {
42 pub start: usize,
44 pub size: usize,
46 pub in_use: bool,
48 pub allocated_at: Option<Instant>,
50}
51
52#[derive(Debug, Clone, PartialEq)]
54pub enum MemoryPoolType {
55 Global,
57 Shared,
59 Constant,
61 Texture,
63 Unified,
65}
66
67#[derive(Debug, Clone)]
69pub enum MemoryAllocationStrategy {
70 FirstFit,
72 BestFit,
74 WorstFit,
76 Buddy,
78 Segregated,
80 Predictive,
82}
83
84#[derive(Debug)]
86pub struct MemoryGarbageCollector {
87 pub strategy: GCStrategy,
89 pub threshold: f64,
91 pub auto_collect: bool,
93 pub stats: GCStats,
95}
96
97#[derive(Debug, Clone)]
99pub enum GCStrategy {
100 MarkAndSweep,
102 Generational,
104 Incremental,
106 Concurrent,
108}
109
110#[derive(Debug, Clone)]
112pub struct GCStats {
113 pub collections_performed: usize,
115 pub memory_reclaimed: usize,
117 pub total_gc_time_ms: f64,
119 pub avg_collection_time_ms: f64,
121}
122
123#[derive(Debug)]
125pub struct BandwidthPredictor {
126 pub models: Vec<BandwidthPredictionModel>,
128 pub history: std::collections::VecDeque<BandwidthMeasurement>,
130 pub accuracy: f64,
132}
133
134#[derive(Debug, Clone)]
136pub enum BandwidthPredictionModel {
137 LinearRegression,
139 NeuralNetwork,
141 TimeSeries,
143 Ensemble,
145}
146
147#[derive(Debug, Clone)]
149pub struct BandwidthMeasurement {
150 pub timestamp: Instant,
152 pub bandwidth_gbps: f64,
154 pub access_pattern: MemoryAccessPattern,
156 pub data_size: usize,
158}
159
160#[derive(Debug, Clone)]
162pub enum MemoryAccessPattern {
163 Sequential,
165 Random,
167 Strided(usize),
169 Coalesced,
171 Broadcast,
173}
174
175#[derive(Debug, Clone)]
177pub enum TensorCorePrecision {
178 FP16,
180 BF16,
182 FP32,
184 FP64,
186 TF32,
188 INT8,
190}
191
192impl GpuMemoryManager {
193 pub fn new(gpu_id: usize) -> LinalgResult<Self> {
195 Ok(Self {
196 gpu_id,
197 memory_pools: Vec::new(),
198 allocation_strategy: MemoryAllocationStrategy::BestFit,
199 garbage_collector: MemoryGarbageCollector::new(),
200 })
201 }
202
203 pub fn add_memory_pool(&mut self, pool: MemoryPool) {
205 self.memory_pools.push(pool);
206 }
207
208 pub fn allocate(
210 &mut self,
211 size: usize,
212 pool_type: MemoryPoolType,
213 ) -> LinalgResult<MemoryBlock> {
214 let pool_index = self
216 .memory_pools
217 .iter()
218 .position(|p| p.pool_type == pool_type)
219 .ok_or_else(|| {
220 LinalgError::ComputationError(format!("No pool found for type {:?}", pool_type))
221 })?;
222
223 let pool = &mut self.memory_pools[pool_index];
225 match self.allocation_strategy {
226 MemoryAllocationStrategy::FirstFit => Self::allocate_first_fit(pool, size),
227 MemoryAllocationStrategy::BestFit => Self::allocate_best_fit(pool, size),
228 MemoryAllocationStrategy::WorstFit => Self::allocate_worst_fit(pool, size),
229 MemoryAllocationStrategy::Buddy => Self::allocate_buddy(pool, size),
230 MemoryAllocationStrategy::Segregated => Self::allocate_segregated(pool, size),
231 MemoryAllocationStrategy::Predictive => Self::allocate_predictive(pool, size),
232 }
233 }
234
235 pub fn deallocate(
237 &mut self,
238 block: MemoryBlock,
239 pool_type: MemoryPoolType,
240 ) -> LinalgResult<()> {
241 let pool_index = self
242 .memory_pools
243 .iter()
244 .position(|p| p.pool_type == pool_type)
245 .ok_or_else(|| {
246 LinalgError::ComputationError(format!("No pool found for type {:?}", pool_type))
247 })?;
248
249 let pool = &mut self.memory_pools[pool_index];
250
251 pool.allocated_blocks.retain(|b| b.start != block.start);
253
254 let mut free_block = block;
256 free_block.in_use = false;
257 free_block.allocated_at = None;
258 pool.free_blocks.push(free_block);
259
260 Self::coalesce_free_blocks(pool);
262
263 Ok(())
264 }
265
266 pub fn collect_garbage(&mut self) -> LinalgResult<usize> {
268 let start_time = Instant::now();
269 let mut total_reclaimed = 0;
270
271 for pool in &mut self.memory_pools {
272 total_reclaimed += Self::collect_pool_garbage(pool)?;
273 }
274
275 let gc_time = start_time.elapsed().as_millis() as f64;
276
277 self.garbage_collector.stats.collections_performed += 1;
279 self.garbage_collector.stats.memory_reclaimed += total_reclaimed;
280 self.garbage_collector.stats.total_gc_time_ms += gc_time;
281 self.garbage_collector.stats.avg_collection_time_ms =
282 self.garbage_collector.stats.total_gc_time_ms
283 / self.garbage_collector.stats.collections_performed as f64;
284
285 Ok(total_reclaimed)
286 }
287
288 pub fn get_memory_stats(&self) -> MemoryStats {
290 let mut total_allocated = 0;
291 let mut total_free = 0;
292 let mut total_fragmented = 0;
293
294 for pool in &self.memory_pools {
295 total_allocated += pool.allocated_blocks.iter().map(|b| b.size).sum::<usize>();
296 total_free += pool.free_blocks.iter().map(|b| b.size).sum::<usize>();
297 total_fragmented += pool.free_blocks.len().saturating_sub(1);
298 }
299
300 MemoryStats {
301 total_allocated,
302 total_free,
303 fragmentation_count: total_fragmented,
304 pool_count: self.memory_pools.len(),
305 gc_stats: self.garbage_collector.stats.clone(),
306 }
307 }
308
309 fn allocate_first_fit(pool: &mut MemoryPool, size: usize) -> LinalgResult<MemoryBlock> {
311 for (i, block) in pool.free_blocks.iter().enumerate() {
312 if block.size >= size {
313 let mut allocated_block = block.clone();
314 allocated_block.size = size;
315 allocated_block.in_use = true;
316 allocated_block.allocated_at = Some(Instant::now());
317
318 if block.size > size {
320 let remaining_block = MemoryBlock {
321 start: block.start + size,
322 size: block.size - size,
323 in_use: false,
324 allocated_at: None,
325 };
326 pool.free_blocks[i] = remaining_block;
327 } else {
328 pool.free_blocks.remove(i);
329 }
330
331 pool.allocated_blocks.push(allocated_block.clone());
332 return Ok(allocated_block);
333 }
334 }
335
336 Err(LinalgError::ComputationError(
337 "No suitable block found".to_string(),
338 ))
339 }
340
341 fn allocate_best_fit(pool: &mut MemoryPool, size: usize) -> LinalgResult<MemoryBlock> {
342 let mut best_fit_index = None;
343 let mut best_fit_size = usize::MAX;
344
345 for (i, block) in pool.free_blocks.iter().enumerate() {
346 if block.size >= size && block.size < best_fit_size {
347 best_fit_index = Some(i);
348 best_fit_size = block.size;
349 }
350 }
351
352 if let Some(index) = best_fit_index {
353 let block = &pool.free_blocks[index];
354 let mut allocated_block = block.clone();
355 allocated_block.size = size;
356 allocated_block.in_use = true;
357 allocated_block.allocated_at = Some(Instant::now());
358
359 if block.size > size {
361 let remaining_block = MemoryBlock {
362 start: block.start + size,
363 size: block.size - size,
364 in_use: false,
365 allocated_at: None,
366 };
367 pool.free_blocks[index] = remaining_block;
368 } else {
369 pool.free_blocks.remove(index);
370 }
371
372 pool.allocated_blocks.push(allocated_block.clone());
373 Ok(allocated_block)
374 } else {
375 Err(LinalgError::ComputationError(
376 "No suitable block found".to_string(),
377 ))
378 }
379 }
380
381 fn allocate_worst_fit(pool: &mut MemoryPool, size: usize) -> LinalgResult<MemoryBlock> {
382 Self::allocate_first_fit(pool, size) }
385
386 fn allocate_buddy(pool: &mut MemoryPool, size: usize) -> LinalgResult<MemoryBlock> {
387 Self::allocate_first_fit(pool, size) }
390
391 fn allocate_segregated(pool: &mut MemoryPool, size: usize) -> LinalgResult<MemoryBlock> {
392 Self::allocate_first_fit(pool, size) }
395
396 fn allocate_predictive(pool: &mut MemoryPool, size: usize) -> LinalgResult<MemoryBlock> {
397 Self::allocate_best_fit(pool, size) }
400
401 fn coalesce_free_blocks(pool: &mut MemoryPool) {
402 pool.free_blocks.sort_by_key(|b| b.start);
404
405 let mut i = 0;
406 while i < pool.free_blocks.len().saturating_sub(1) {
407 let current_end = pool.free_blocks[i].start + pool.free_blocks[i].size;
408 let next_start = pool.free_blocks[i + 1].start;
409
410 if current_end == next_start {
412 pool.free_blocks[i].size += pool.free_blocks[i + 1].size;
413 pool.free_blocks.remove(i + 1);
414 } else {
415 i += 1;
416 }
417 }
418 }
419
420 fn collect_pool_garbage(pool: &mut MemoryPool) -> LinalgResult<usize> {
421 let before_count = pool.allocated_blocks.len();
422
423 pool.allocated_blocks.retain(|block| {
425 if let Some(allocated_at) = block.allocated_at {
426 allocated_at.elapsed().as_secs() < 300 } else {
428 true
429 }
430 });
431
432 let reclaimed_count = before_count - pool.allocated_blocks.len();
433 Ok(reclaimed_count * 1024) }
435}
436
437impl MemoryPool {
438 pub fn new(size: usize, pool_type: MemoryPoolType) -> Self {
440 let initial_block = MemoryBlock {
441 start: 0,
442 size,
443 in_use: false,
444 allocated_at: None,
445 };
446
447 Self {
448 size,
449 free_blocks: vec![initial_block],
450 allocated_blocks: Vec::new(),
451 pool_type,
452 }
453 }
454
455 pub fn utilization(&self) -> f64 {
457 let allocated_size: usize = self.allocated_blocks.iter().map(|b| b.size).sum();
458 if self.size == 0 {
459 0.0
460 } else {
461 (allocated_size as f64 / self.size as f64) * 100.0
462 }
463 }
464}
465
466impl MemoryGarbageCollector {
467 pub fn new() -> Self {
469 Self {
470 strategy: GCStrategy::MarkAndSweep,
471 threshold: 0.8, auto_collect: true,
473 stats: GCStats::new(),
474 }
475 }
476}
477
478impl GCStats {
479 pub fn new() -> Self {
480 Self {
481 collections_performed: 0,
482 memory_reclaimed: 0,
483 total_gc_time_ms: 0.0,
484 avg_collection_time_ms: 0.0,
485 }
486 }
487}
488
489#[derive(Debug, Clone)]
491pub struct MemoryStats {
492 pub total_allocated: usize,
494 pub total_free: usize,
496 pub fragmentation_count: usize,
498 pub pool_count: usize,
500 pub gc_stats: GCStats,
502}
503
504impl BandwidthPredictor {
505 pub fn new() -> Self {
507 Self {
508 models: vec![BandwidthPredictionModel::LinearRegression],
509 history: std::collections::VecDeque::new(),
510 accuracy: 0.85,
511 }
512 }
513
514 pub fn add_measurement(&mut self, measurement: BandwidthMeasurement) {
516 self.history.push_back(measurement);
517
518 if self.history.len() > 1000 {
520 self.history.pop_front();
521 }
522 }
523
524 pub fn predict_bandwidth(&self, data_size: usize, access_pattern: MemoryAccessPattern) -> f64 {
526 let base_bandwidth = match access_pattern {
528 MemoryAccessPattern::Sequential => 800.0, MemoryAccessPattern::Coalesced => 750.0,
530 MemoryAccessPattern::Strided(_) => 400.0,
531 MemoryAccessPattern::Random => 200.0,
532 MemoryAccessPattern::Broadcast => 600.0,
533 };
534
535 #[cfg(target_pointer_width = "32")]
537 let threshold = 256 * 1024 * 1024; #[cfg(target_pointer_width = "64")]
539 let threshold = 1024 * 1024 * 1024; let size_factor = if data_size > threshold { 0.9 } else { 1.0 };
542
543 base_bandwidth * size_factor
544 }
545}
546
547#[cfg(test)]
548mod tests {
549 use super::*;
550
551 #[test]
552 fn test_memory_pool_creation() {
553 let pool = MemoryPool::new(1024 * 1024, MemoryPoolType::Global);
554 assert_eq!(pool.size, 1024 * 1024);
555 assert_eq!(pool.free_blocks.len(), 1);
556 assert_eq!(pool.allocated_blocks.len(), 0);
557 }
558
559 #[test]
560 fn test_memory_manager_creation() {
561 let manager = GpuMemoryManager::new(0).expect("Operation failed");
562 assert_eq!(manager.gpu_id, 0);
563 assert_eq!(manager.memory_pools.len(), 0);
564 }
565
566 #[test]
567 fn test_bandwidth_predictor() {
568 let predictor = BandwidthPredictor::new();
569 let bandwidth = predictor.predict_bandwidth(1024, MemoryAccessPattern::Sequential);
570 assert!(bandwidth > 0.0);
571 }
572}