1use crate::error::Result;
10use crate::storage::page::{PageId, PAGE_SIZE};
11use parking_lot::RwLock;
12use std::collections::VecDeque;
13use std::sync::atomic::{AtomicU64, Ordering};
14use std::sync::Arc;
15
16#[derive(Debug, Clone)]
18pub struct MemoryOptimizationConfig {
19 pub enable_prefetch: bool,
21 pub prefetch_pages: usize,
23 pub enable_memory_pool: bool,
25 pub pool_size: usize,
27 pub readahead_strategy: ReadaheadStrategy,
29}
30
31impl Default for MemoryOptimizationConfig {
32 fn default() -> Self {
33 Self {
34 enable_prefetch: true,
35 prefetch_pages: 8,
36 enable_memory_pool: true,
37 pool_size: 256,
38 readahead_strategy: ReadaheadStrategy::Adaptive,
39 }
40 }
41}
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub enum ReadaheadStrategy {
46 None,
48 Fixed,
50 Adaptive,
52 Aggressive,
54}
55
56#[derive(Debug)]
58struct AccessPatternDetector {
59 recent_accesses: RwLock<VecDeque<PageId>>,
61 sequential_count: AtomicU64,
63 random_count: AtomicU64,
65 history_size: usize,
67}
68
69impl AccessPatternDetector {
70 fn new(history_size: usize) -> Self {
72 Self {
73 recent_accesses: RwLock::new(VecDeque::with_capacity(history_size)),
74 sequential_count: AtomicU64::new(0),
75 random_count: AtomicU64::new(0),
76 history_size,
77 }
78 }
79
80 fn record_access(&self, page_id: PageId) {
82 let mut accesses = self.recent_accesses.write();
83
84 if let Some(&last_page) = accesses.back() {
86 if page_id == last_page + 1 || page_id == last_page.saturating_sub(1) {
87 self.sequential_count.fetch_add(1, Ordering::Relaxed);
88 } else {
89 self.random_count.fetch_add(1, Ordering::Relaxed);
90 }
91 }
92
93 accesses.push_back(page_id);
94
95 while accesses.len() > self.history_size {
97 accesses.pop_front();
98 }
99 }
100
101 fn is_sequential(&self) -> bool {
103 let sequential = self.sequential_count.load(Ordering::Relaxed);
104 let random = self.random_count.load(Ordering::Relaxed);
105 let total = sequential + random;
106
107 if total == 0 {
108 false
109 } else {
110 (sequential as f64 / total as f64) > 0.7
112 }
113 }
114
115 fn recommended_prefetch_distance(&self) -> usize {
117 if self.is_sequential() {
118 16
120 } else {
121 4
123 }
124 }
125
126 fn reset(&self) {
128 self.recent_accesses.write().clear();
129 self.sequential_count.store(0, Ordering::Relaxed);
130 self.random_count.store(0, Ordering::Relaxed);
131 }
132}
133
134#[derive(Debug)]
136struct MemoryPool {
137 buffers: RwLock<Vec<Vec<u8>>>,
139 capacity: usize,
141 allocations: AtomicU64,
143 pool_hits: AtomicU64,
145 pool_misses: AtomicU64,
147}
148
149impl MemoryPool {
150 fn new(capacity: usize) -> Self {
152 let mut buffers = Vec::with_capacity(capacity);
153
154 for _ in 0..capacity {
156 let buffer = vec![0; PAGE_SIZE];
157 buffers.push(buffer);
158 }
159
160 Self {
161 buffers: RwLock::new(buffers),
162 capacity,
163 allocations: AtomicU64::new(0),
164 pool_hits: AtomicU64::new(0),
165 pool_misses: AtomicU64::new(0),
166 }
167 }
168
169 fn acquire(&self) -> Vec<u8> {
171 self.allocations.fetch_add(1, Ordering::Relaxed);
172
173 let mut buffers = self.buffers.write();
174
175 if let Some(buffer) = buffers.pop() {
176 self.pool_hits.fetch_add(1, Ordering::Relaxed);
177 buffer
178 } else {
179 self.pool_misses.fetch_add(1, Ordering::Relaxed);
180 vec![0; PAGE_SIZE]
181 }
182 }
183
184 fn release(&self, mut buffer: Vec<u8>) {
186 buffer.fill(0);
188
189 let mut buffers = self.buffers.write();
190
191 if buffers.len() < self.capacity {
192 buffers.push(buffer);
193 }
194 }
196
197 fn hit_rate(&self) -> f64 {
199 let hits = self.pool_hits.load(Ordering::Relaxed) as f64;
200 let total = self.allocations.load(Ordering::Relaxed) as f64;
201
202 if total == 0.0 {
203 0.0
204 } else {
205 hits / total
206 }
207 }
208
209 fn stats(&self) -> MemoryPoolStats {
211 MemoryPoolStats {
212 capacity: self.capacity,
213 available: self.buffers.read().len(),
214 allocations: self.allocations.load(Ordering::Relaxed),
215 hits: self.pool_hits.load(Ordering::Relaxed),
216 misses: self.pool_misses.load(Ordering::Relaxed),
217 hit_rate: self.hit_rate(),
218 }
219 }
220}
221
222#[derive(Debug, Clone)]
224pub struct MemoryPoolStats {
225 pub capacity: usize,
227 pub available: usize,
229 pub allocations: u64,
231 pub hits: u64,
233 pub misses: u64,
235 pub hit_rate: f64,
237}
238
239pub struct MemoryOptimizer {
241 config: MemoryOptimizationConfig,
243 pattern_detector: Arc<AccessPatternDetector>,
245 memory_pool: Option<Arc<MemoryPool>>,
247 stats: MemoryOptimizerStats,
249}
250
251#[derive(Debug, Default)]
253pub struct MemoryOptimizerStats {
254 pub prefetch_requests: AtomicU64,
256 pub prefetch_hits: AtomicU64,
258 pub sequential_accesses: AtomicU64,
260 pub random_accesses: AtomicU64,
262}
263
264impl MemoryOptimizerStats {
265 pub fn prefetch_hit_rate(&self) -> f64 {
267 let requests = self.prefetch_requests.load(Ordering::Relaxed) as f64;
268 if requests == 0.0 {
269 0.0
270 } else {
271 let hits = self.prefetch_hits.load(Ordering::Relaxed) as f64;
272 hits / requests
273 }
274 }
275
276 pub fn sequential_percentage(&self) -> f64 {
278 let sequential = self.sequential_accesses.load(Ordering::Relaxed) as f64;
279 let random = self.random_accesses.load(Ordering::Relaxed) as f64;
280 let total = sequential + random;
281
282 if total == 0.0 {
283 0.0
284 } else {
285 (sequential / total) * 100.0
286 }
287 }
288}
289
290impl MemoryOptimizer {
291 pub fn new(config: MemoryOptimizationConfig) -> Self {
293 let pattern_detector = Arc::new(AccessPatternDetector::new(100));
294
295 let memory_pool = if config.enable_memory_pool {
296 Some(Arc::new(MemoryPool::new(config.pool_size)))
297 } else {
298 None
299 };
300
301 Self {
302 config,
303 pattern_detector,
304 memory_pool,
305 stats: MemoryOptimizerStats::default(),
306 }
307 }
308
309 pub fn record_access(&self, page_id: PageId) {
311 self.pattern_detector.record_access(page_id);
312
313 if self.pattern_detector.is_sequential() {
314 self.stats
315 .sequential_accesses
316 .fetch_add(1, Ordering::Relaxed);
317 } else {
318 self.stats.random_accesses.fetch_add(1, Ordering::Relaxed);
319 }
320 }
321
322 pub fn get_prefetch_recommendations(&self, current_page: PageId) -> Vec<PageId> {
324 if !self.config.enable_prefetch {
325 return Vec::new();
326 }
327
328 let prefetch_distance = match self.config.readahead_strategy {
329 ReadaheadStrategy::None => return Vec::new(),
330 ReadaheadStrategy::Fixed => self.config.prefetch_pages,
331 ReadaheadStrategy::Adaptive => self.pattern_detector.recommended_prefetch_distance(),
332 ReadaheadStrategy::Aggressive => 32, };
334
335 let mut recommendations = Vec::with_capacity(prefetch_distance);
337 for i in 1..=prefetch_distance {
338 recommendations.push(current_page + i as u64);
339 }
340
341 self.stats
342 .prefetch_requests
343 .fetch_add(prefetch_distance as u64, Ordering::Relaxed);
344
345 recommendations
346 }
347
348 pub fn acquire_buffer(&self) -> Vec<u8> {
350 if let Some(ref pool) = self.memory_pool {
351 pool.acquire()
352 } else {
353 vec![0; PAGE_SIZE]
354 }
355 }
356
357 pub fn release_buffer(&self, buffer: Vec<u8>) {
359 if let Some(ref pool) = self.memory_pool {
360 pool.release(buffer);
361 }
362 }
364
365 pub fn pool_stats(&self) -> Option<MemoryPoolStats> {
367 self.memory_pool.as_ref().map(|pool| pool.stats())
368 }
369
370 pub fn stats(&self) -> &MemoryOptimizerStats {
372 &self.stats
373 }
374
375 pub fn is_sequential_access(&self) -> bool {
377 self.pattern_detector.is_sequential()
378 }
379
380 pub fn reset_pattern_detection(&self) {
382 self.pattern_detector.reset();
383 self.stats.sequential_accesses.store(0, Ordering::Relaxed);
384 self.stats.random_accesses.store(0, Ordering::Relaxed);
385 }
386
387 pub fn recommended_readahead(&self) -> usize {
389 self.pattern_detector.recommended_prefetch_distance()
390 }
391}
392
393impl Default for MemoryOptimizer {
394 fn default() -> Self {
395 Self::new(MemoryOptimizationConfig::default())
396 }
397}
398
399#[cfg(test)]
400mod tests {
401 use super::*;
402
403 #[test]
404 fn test_memory_optimizer_creation() {
405 let config = MemoryOptimizationConfig::default();
406 let optimizer = MemoryOptimizer::new(config);
407
408 assert!(!optimizer.is_sequential_access());
409 }
410
411 #[test]
412 fn test_sequential_access_detection() {
413 let optimizer = MemoryOptimizer::default();
414
415 for page_id in 0..20 {
417 optimizer.record_access(page_id);
418 }
419
420 assert!(optimizer.is_sequential_access());
421 assert!(optimizer.stats().sequential_percentage() > 50.0);
422 }
423
424 #[test]
425 fn test_random_access_detection() {
426 let optimizer = MemoryOptimizer::default();
427
428 let random_pages = [5, 100, 3, 200, 7, 150, 9, 250];
430 for &page_id in &random_pages {
431 optimizer.record_access(page_id);
432 }
433
434 assert!(!optimizer.is_sequential_access());
435 }
436
437 #[test]
438 fn test_prefetch_recommendations() {
439 let config = MemoryOptimizationConfig {
440 enable_prefetch: true,
441 prefetch_pages: 4,
442 readahead_strategy: ReadaheadStrategy::Fixed,
443 ..Default::default()
444 };
445 let optimizer = MemoryOptimizer::new(config);
446
447 let recommendations = optimizer.get_prefetch_recommendations(100);
448
449 assert_eq!(recommendations.len(), 4);
450 assert_eq!(recommendations[0], 101);
451 assert_eq!(recommendations[1], 102);
452 assert_eq!(recommendations[2], 103);
453 assert_eq!(recommendations[3], 104);
454 }
455
456 #[test]
457 fn test_adaptive_prefetch() {
458 let config = MemoryOptimizationConfig {
459 readahead_strategy: ReadaheadStrategy::Adaptive,
460 ..Default::default()
461 };
462 let optimizer = MemoryOptimizer::new(config);
463
464 for page_id in 0..10 {
466 optimizer.record_access(page_id);
467 }
468
469 let recommendations = optimizer.get_prefetch_recommendations(10);
470
471 assert!(recommendations.len() >= 4);
473 }
474
475 #[test]
476 fn test_memory_pool_acquire_release() {
477 let config = MemoryOptimizationConfig {
478 enable_memory_pool: true,
479 pool_size: 10,
480 ..Default::default()
481 };
482 let optimizer = MemoryOptimizer::new(config);
483
484 let buffer1 = optimizer.acquire_buffer();
486 assert_eq!(buffer1.len(), PAGE_SIZE);
487
488 optimizer.release_buffer(buffer1);
490
491 let buffer2 = optimizer.acquire_buffer();
493 assert_eq!(buffer2.len(), PAGE_SIZE);
494
495 let stats = optimizer.pool_stats().unwrap();
497 assert!(stats.allocations >= 2);
498 assert!(stats.hit_rate > 0.0);
499 }
500
501 #[test]
502 fn test_memory_pool_statistics() {
503 let config = MemoryOptimizationConfig {
504 enable_memory_pool: true,
505 pool_size: 5,
506 ..Default::default()
507 };
508 let optimizer = MemoryOptimizer::new(config);
509
510 for _ in 0..10 {
512 let buffer = optimizer.acquire_buffer();
513 optimizer.release_buffer(buffer);
514 }
515
516 let stats = optimizer.pool_stats().unwrap();
517 assert_eq!(stats.capacity, 5);
518 assert!(stats.hits > 0);
519 assert!(stats.hit_rate > 0.0);
520 }
521
522 #[test]
523 fn test_disabled_prefetch() {
524 let config = MemoryOptimizationConfig {
525 enable_prefetch: false,
526 ..Default::default()
527 };
528 let optimizer = MemoryOptimizer::new(config);
529
530 let recommendations = optimizer.get_prefetch_recommendations(10);
531 assert!(recommendations.is_empty());
532 }
533
534 #[test]
535 fn test_aggressive_readahead() {
536 let config = MemoryOptimizationConfig {
537 readahead_strategy: ReadaheadStrategy::Aggressive,
538 ..Default::default()
539 };
540 let optimizer = MemoryOptimizer::new(config);
541
542 let recommendations = optimizer.get_prefetch_recommendations(10);
543 assert_eq!(recommendations.len(), 32);
544 }
545
546 #[test]
547 fn test_pattern_reset() {
548 let optimizer = MemoryOptimizer::default();
549
550 for page_id in 0..10 {
552 optimizer.record_access(page_id);
553 }
554
555 assert!(optimizer.is_sequential_access());
556
557 optimizer.reset_pattern_detection();
559
560 assert_eq!(optimizer.stats().sequential_percentage(), 0.0);
562 }
563
564 #[test]
565 fn test_recommended_readahead_distance() {
566 let optimizer = MemoryOptimizer::default();
567
568 for page_id in 0..20 {
570 optimizer.record_access(page_id);
571 }
572
573 let distance = optimizer.recommended_readahead();
574 assert!(distance >= 8); }
576
577 #[test]
578 fn test_memory_pool_capacity_limit() {
579 let config = MemoryOptimizationConfig {
580 enable_memory_pool: true,
581 pool_size: 2,
582 ..Default::default()
583 };
584 let optimizer = MemoryOptimizer::new(config);
585
586 let buf1 = optimizer.acquire_buffer();
588 let buf2 = optimizer.acquire_buffer();
589 let buf3 = optimizer.acquire_buffer();
590
591 optimizer.release_buffer(buf1);
593 optimizer.release_buffer(buf2);
594 optimizer.release_buffer(buf3);
595
596 let stats = optimizer.pool_stats().unwrap();
598 assert_eq!(stats.available, 2); }
600
601 #[test]
602 fn test_prefetch_statistics() {
603 let optimizer = MemoryOptimizer::default();
604
605 for i in 0..5 {
607 optimizer.get_prefetch_recommendations(i * 10);
608 }
609
610 let stats = optimizer.stats();
611 assert!(stats.prefetch_requests.load(Ordering::Relaxed) > 0);
612 }
613
614 #[test]
615 fn test_mixed_access_pattern() {
616 let optimizer = MemoryOptimizer::default();
617
618 for i in 0..5 {
620 optimizer.record_access(i);
621 }
622 optimizer.record_access(100);
623 optimizer.record_access(200);
624
625 let stats = optimizer.stats();
627 assert!(stats.sequential_accesses.load(Ordering::Relaxed) > 0);
628 assert!(stats.random_accesses.load(Ordering::Relaxed) > 0);
629 }
630}