1use super::{
2 adaptive::AdaptiveState,
3 MemoryConfiguration, MemoryPoolOptions, MemoryUsage, PoolType,
4 memory_pool::{ExclusiveMemoryPool, MemoryPool, PersistentPool, SlicedPool},
5};
6use crate::runtime::{
7 config::{
8 RudaRuntimeConfig, RuntimeConfig,
9 memory::{MemoryLogLevel, PersistentMemory},
10 },
11 logging::ServerLogger,
12 memory_management::{BytesFormat, memory_pool::Slice},
13 server::IoError,
14 storage::{ComputeStorage, StorageHandle},
15};
16
17use alloc::format;
18use alloc::string::{String, ToString};
19#[cfg(not(exclusive_memory_only))]
20use alloc::vec;
21use alloc::vec::Vec;
22use ruda_core::{backtrace::BackTrace, stub::Arc};
23use ruda_core::ir::MemoryDeviceProperties;
24
25pub use super::memory_pool::{ManagedMemoryBinding, handle::*};
26
27#[allow(clippy::large_enum_variant)]
30pub(super) enum DynamicPool {
31 Sliced(SlicedPool),
32 Exclusive(ExclusiveMemoryPool),
33}
34
35impl DynamicPool {
36 pub(super) fn find_at(&self, location: MemoryLocation) -> Result<&Slice, IoError> {
37 match self {
38 DynamicPool::Sliced(pool) => pool.find_at(location),
39 DynamicPool::Exclusive(pool) => pool.find_at(location),
40 }
41 }
42}
43
44impl MemoryPool for DynamicPool {
45 fn accept(&self, size: u64) -> bool {
46 match self {
47 DynamicPool::Sliced(pool) => pool.accept(size),
48 DynamicPool::Exclusive(pool) => pool.accept(size),
49 }
50 }
51
52 fn find(&self, binding: &ManagedMemoryBinding) -> Result<&Slice, IoError> {
53 self.find_at(binding.descriptor().location())
54 }
55
56 #[cfg_attr(feature = "runtime-tracing", tracing::instrument(level = "trace", skip(self)))]
57 fn try_reserve(&mut self, size: u64) -> Option<ManagedMemoryHandle> {
58 match self {
59 DynamicPool::Sliced(m) => m.try_reserve(size),
60 DynamicPool::Exclusive(m) => m.try_reserve(size),
61 }
62 }
63
64 #[cfg_attr(
65 feature = "runtime-tracing",
66 tracing::instrument(level = "trace", skip(self, storage))
67 )]
68 fn alloc<Storage: ComputeStorage>(
69 &mut self,
70 storage: &mut Storage,
71 size: u64,
72 ) -> Result<ManagedMemoryHandle, IoError> {
73 match self {
74 DynamicPool::Sliced(m) => m.alloc(storage, size),
75 DynamicPool::Exclusive(m) => m.alloc(storage, size),
76 }
77 }
78
79 fn get_memory_usage(&self) -> MemoryUsage {
80 match self {
81 DynamicPool::Sliced(m) => m.get_memory_usage(),
82 DynamicPool::Exclusive(m) => m.get_memory_usage(),
83 }
84 }
85
86 fn cleanup<Storage: ComputeStorage>(
87 &mut self,
88 storage: &mut Storage,
89 alloc_nr: u64,
90 explicit: bool,
91 ) {
92 match self {
93 DynamicPool::Sliced(m) => m.cleanup(storage, alloc_nr, explicit),
94 DynamicPool::Exclusive(m) => m.cleanup(storage, alloc_nr, explicit),
95 };
96 storage.flush();
97 }
98
99 fn bind(
100 &mut self,
101 reserved: ManagedMemoryHandle,
102 assigned: ManagedMemoryHandle,
103 cursor: u64,
104 ) -> Result<(), IoError> {
105 match self {
106 DynamicPool::Sliced(m) => m.bind(reserved, assigned, cursor),
107 DynamicPool::Exclusive(m) => m.bind(reserved, assigned, cursor),
108 }
109 }
110}
111
112#[derive(Default, Clone, Copy, Debug)]
113pub enum MemoryAllocationMode {
115 #[default]
117 Auto,
118 Persistent,
121}
122
123pub struct MemoryManagement<Storage> {
125 name: String,
126 persistent: PersistentPool,
127 pools: Vec<DynamicPool>,
128 adaptive: Option<AdaptiveState>,
129 storage: Storage,
130 alloc_reserve_count: u64,
131 mode: MemoryAllocationMode,
132 config: PersistentMemory,
133 logger: Arc<ServerLogger>,
134}
135
136fn generate_bucket_sizes(
137 start_size: u64,
138 end_size: u64,
139 max_buckets: usize,
140 alignment: u64,
141) -> Vec<u64> {
142 let mut buckets = Vec::with_capacity(max_buckets);
143 let log_min = (start_size as f64).ln();
144 let log_max = (end_size as f64).ln();
145 let log_range = log_max - log_min;
146
147 for i in 0..max_buckets {
149 let p = i as f64 / (max_buckets - 1) as f64;
150 let log_size = log_min + log_range * p;
152 let size = log_size.exp() as u64;
153 let aligned_size = size.next_multiple_of(alignment);
154 buckets.push(aligned_size);
155 }
156
157 buckets.dedup();
158 buckets
159}
160
161const DEALLOC_SCALE_MB: u64 = 1024 * 1024 * 1024;
162const BASE_DEALLOC_PERIOD: u64 = 5000;
163
164#[derive(Debug)]
166pub struct MemoryManagementOptions {
167 name: String,
169 memory: MemoryAllocationOption,
171}
172
173impl MemoryManagementOptions {
174 pub fn new<S: Into<String>>(name: S) -> Self {
176 Self {
177 name: name.into(),
178 memory: MemoryAllocationOption::FromConfig,
179 }
180 }
181
182 pub fn mode(mut self, mode: MemoryAllocationMode) -> Self {
184 self.memory = MemoryAllocationOption::Provided(mode);
185 self
186 }
187}
188
189#[derive(Default, Debug)]
190enum MemoryAllocationOption {
192 #[default]
193 FromConfig,
195 Provided(MemoryAllocationMode),
197}
198
199impl<Storage: ComputeStorage> MemoryManagement<Storage> {
200 pub fn from_configuration(
202 storage: Storage,
203 properties: &MemoryDeviceProperties,
204 config: MemoryConfiguration,
205 logger: Arc<ServerLogger>,
206 options: MemoryManagementOptions,
207 ) -> Self {
208 #[cfg(not(exclusive_memory_only))]
209 let adaptive = matches!(&config, MemoryConfiguration::Adaptive)
210 .then(|| AdaptiveState::new(properties));
211 #[cfg(exclusive_memory_only)]
212 let adaptive: Option<AdaptiveState> = None;
213 let pool_options = match config {
214 #[cfg(not(exclusive_memory_only))]
215 MemoryConfiguration::Adaptive => adaptive.as_ref().expect("adaptive layout").pool_options(),
216 #[cfg(not(exclusive_memory_only))]
217 MemoryConfiguration::SubSlices => {
218 let memory_alignment = properties.alignment;
220 let max_page = properties.max_page_size;
221 let mut pools = Vec::new();
222
223 const MB: u64 = 1024 * 1024;
224
225 pools.push(MemoryPoolOptions {
228 pool_type: PoolType::ExclusivePages { max_alloc_size: 0 },
229 dealloc_period: None,
230 });
231
232 let mut current = max_page;
233 let mut max_sizes = vec![];
234 let mut page_sizes = vec![];
235 let mut base = pools.len() as u32;
236
237 while current >= 32 * MB {
238 current /= 4;
239
240 current = current.next_multiple_of(memory_alignment);
242
243 max_sizes.push(current / 2u64.pow(base));
244 page_sizes.push(current);
245 base += 1;
246 }
247
248 max_sizes.reverse();
249 page_sizes.reverse();
250
251 for i in 0..max_sizes.len() {
252 let max = max_sizes[i];
253 let page_size = page_sizes[i];
254
255 pools.push(MemoryPoolOptions {
256 pool_type: PoolType::SlicedPages {
258 page_size,
259 max_slice_size: max,
260 },
261 dealloc_period: None,
262 });
263 }
264
265 pools.push(MemoryPoolOptions {
267 pool_type: PoolType::SlicedPages {
268 page_size: max_page / memory_alignment * memory_alignment,
269 max_slice_size: max_page / memory_alignment * memory_alignment,
270 },
271 dealloc_period: None,
272 });
273 pools
274 }
275 MemoryConfiguration::ExclusivePages => {
276 const MIN_BUCKET_SIZE: u64 = 1024 * 32;
280 const NUM_POOLS: usize = 24;
281
282 let sizes = generate_bucket_sizes(
283 MIN_BUCKET_SIZE,
284 properties.max_page_size,
285 NUM_POOLS,
286 properties.alignment,
287 );
288
289 sizes
290 .iter()
291 .map(|&size| {
292 let dealloc_period = (BASE_DEALLOC_PERIOD as f64
293 * (1.0 + size as f64 / (DEALLOC_SCALE_MB as f64)).round())
294 as u64;
295
296 MemoryPoolOptions {
297 pool_type: PoolType::ExclusivePages {
298 max_alloc_size: size,
299 },
300 dealloc_period: Some(dealloc_period),
301 }
302 })
303 .collect()
304 }
305 MemoryConfiguration::Custom { pool_options } => pool_options,
306 };
307
308 logger.log_memory(
309 |level| !matches!(level, MemoryLogLevel::Disabled),
310 || {
311 let mut msg = String::new();
312 for pool in pool_options.iter() {
313 msg += &format!("[{}] Using memory pool: \n {pool:?}\n", options.name);
314 }
315 msg
316 },
317 );
318
319 let pools: Vec<_> = pool_options
320 .iter()
321 .enumerate()
322 .map(|(pool_pos, options)| {
323 let pool_pos = pool_pos as u8;
324
325 match options.pool_type {
326 PoolType::SlicedPages {
327 page_size,
328 max_slice_size,
329 } => DynamicPool::Sliced(SlicedPool::new(
330 page_size,
331 max_slice_size,
332 properties.alignment,
333 pool_pos,
334 )),
335 PoolType::ExclusivePages { max_alloc_size } => {
336 DynamicPool::Exclusive(ExclusiveMemoryPool::new(
337 max_alloc_size,
338 properties.alignment,
339 options.dealloc_period.unwrap_or(u64::MAX),
340 pool_pos,
341 ))
342 }
343 }
344 })
345 .collect();
346
347 let config = RudaRuntimeConfig::get().memory.persistent_memory.clone();
348
349 let mode = match options.memory {
350 MemoryAllocationOption::Provided(mode) => mode,
351 MemoryAllocationOption::FromConfig => match config {
352 PersistentMemory::Enabled => MemoryAllocationMode::Auto,
353 PersistentMemory::Disabled => MemoryAllocationMode::Auto,
354 PersistentMemory::Enforced => MemoryAllocationMode::Persistent,
355 },
356 };
357
358 Self {
359 name: options.name,
360 persistent: PersistentPool::new(
361 properties.max_page_size,
362 properties.alignment,
363 if adaptive.is_some() { u8::MAX } else { pools.len() as u8 },
364 ),
365 pools,
366 adaptive,
367 storage,
368 alloc_reserve_count: 0,
369 mode,
370 config,
371 logger,
372 }
373 }
374
375 pub fn mode(&mut self, mode: MemoryAllocationMode) {
377 let mode = match self.config {
379 PersistentMemory::Enabled => mode,
380 PersistentMemory::Disabled | PersistentMemory::Enforced => return,
381 };
382
383 self.logger.log_memory(
384 |level| !matches!(level, MemoryLogLevel::Disabled),
385 || {
386 format!(
387 "[{}] Setting memory allocation mode: from {:?} => {mode:?}",
388 self.name, self.mode
389 )
390 },
391 );
392 self.mode = mode;
393 }
394
395 pub fn cleanup(&mut self, explicit: bool) {
397 self.logger.log_memory(
398 |level| !matches!(level, MemoryLogLevel::Disabled) && explicit,
399 || "Manual memory cleanup ...".to_string(),
400 );
401
402 self.persistent
403 .cleanup(&mut self.storage, self.alloc_reserve_count, explicit);
404
405 for pool in self.pools.iter_mut() {
406 pool.cleanup(&mut self.storage, self.alloc_reserve_count, explicit);
407 }
408 }
409
410 pub fn get_cursor(&self, binding: ManagedMemoryBinding) -> Result<u64, IoError> {
412 let slice = self.find(binding)?;
413 Ok(slice.cursor)
414 }
415
416 fn find(&self, binding: ManagedMemoryBinding) -> Result<&Slice, IoError> {
418 let location = binding.descriptor().location();
419
420 if location.pool >= self.pools.len() as u8 {
421 return self.persistent.find_at(location);
422 }
423
424 let pool =
425 self.pools
426 .get(location.pool as usize)
427 .ok_or_else(|| IoError::NotFound {
428 backtrace: BackTrace::capture(),
429 reason: format!("Pool {} doesn't exist", location.pool).into(),
430 })?;
431
432 let slice = pool.find_at(location)?;
433
434 assert_eq!(slice.handle.descriptor(), binding.descriptor());
435
436 Ok(slice)
437 }
438
439 pub fn get_storage(&mut self, binding: ManagedMemoryBinding) -> Result<StorageHandle, IoError> {
441 let slice = self.find(binding)?;
442 Ok(slice.storage.clone())
443 }
444
445 pub fn get_resource(
447 &mut self,
448 binding: ManagedMemoryBinding,
449 offset_start: Option<u64>,
450 offset_end: Option<u64>,
451 ) -> Result<Storage::Resource, IoError> {
452 let pin_binding = self.adaptive.as_ref().map(|_| binding.clone());
453 let handle = self.get_storage(binding)?;
454
455 let handle = match offset_start {
456 Some(offset) => handle.offset_start(offset),
457 None => handle,
458 };
459 let handle = match offset_end {
460 Some(offset) => handle.offset_end(offset),
461 None => handle,
462 };
463 if let Some(binding) = pin_binding {
464 Ok(self.storage().get_pinned(&handle, binding))
465 } else {
466 Ok(self.storage().get(&handle))
467 }
468 }
469
470 pub fn relocation_pending(&self, size: u64) -> bool {
473 self.adaptive.as_ref().is_some_and(|state| state.pending(&self.pools, size))
474 }
475
476 #[cfg_attr(feature = "runtime-tracing", tracing::instrument(level = "trace", skip(self)))]
478 pub fn reserve(&mut self, size: u64) -> Result<ManagedMemoryHandle, IoError> {
479 self.alloc_reserve_count += 1;
482
483 if let Some(val) = self.persistent.try_reserve(size) {
484 self.logger.log_memory(
485 |level| matches!(level, MemoryLogLevel::Full),
486 || {
487 format!(
488 "[{}] Reserved memory {size} using persistent memory",
489 self.name
490 )
491 },
492 );
493 return Ok(val);
494 }
495
496 if matches!(self.mode, MemoryAllocationMode::Persistent) || self.persistent.has_size(size) {
497 let allocated = self.persistent.alloc(&mut self.storage, size);
498
499 self.logger.log_memory(
500 |level| !matches!(level, MemoryLogLevel::Disabled),
501 || {
502 format!(
503 "[{}] Allocated a new memory page using persistent memory, \n{}",
504 self.name, self,
505 )
506 },
507 );
508 return allocated;
509 }
510
511 self.logger.log_memory(
512 |level| matches!(level, MemoryLogLevel::Full),
513 || {
514 format!(
515 "[{}] Reserved memory {} using dynamic pool",
516 self.name,
517 BytesFormat::new(size)
518 )
519 },
520 );
521
522 if let Some(adaptive) = &mut self.adaptive {
523 return adaptive.reserve(&mut self.pools, &mut self.storage, size);
524 }
525
526 let pool = self
528 .pools
529 .iter_mut()
530 .find(|p| p.accept(size))
531 .ok_or(IoError::BufferTooBig {
532 size,
533 backtrace: BackTrace::capture(),
534 })?;
535
536 if let Some(slice) = pool.try_reserve(size) {
537 return Ok(slice);
538 }
539
540 let allocated = pool.alloc(&mut self.storage, size);
541
542 self.logger.log_memory(
543 |level| matches!(level, MemoryLogLevel::Full),
544 || {
545 format!(
546 "[{}], Allocated a new memory page, current usage: \n{}",
547 self.name, self
548 )
549 },
550 );
551
552 allocated
553 }
554
555 pub fn storage(&mut self) -> &mut Storage {
566 &mut self.storage
567 }
568
569 pub fn memory_usage(&self) -> MemoryUsage {
571 let memory_usage = self.pools.iter().map(|x| x.get_memory_usage()).fold(
572 MemoryUsage {
573 number_allocs: 0,
574 bytes_in_use: 0,
575 bytes_padding: 0,
576 bytes_reserved: 0,
577 },
578 |m1, m2| m1.combine(m2),
579 );
580 memory_usage.combine(self.persistent.get_memory_usage())
581 }
582
583 pub fn print_memory_usage(&self) {
585 #[cfg(feature = "runtime-std")]
586 log::info!("{}", self.memory_usage());
587 }
588
589 pub fn bind(
591 &mut self,
592 reserved: ManagedMemoryHandle,
593 assigned: ManagedMemoryHandle,
594 cursor: u64,
595 ) -> Result<(), IoError> {
596 let descriptor = reserved.descriptor();
597
598 if descriptor.location().init == 0 {
599 return Err(IoError::NotFound {
600 backtrace: BackTrace::capture(),
601 reason: "Reserved memory isn't initialized".into(),
602 });
603 }
604
605 let pool_index = descriptor.location().pool as usize;
606 if pool_index >= self.pools.len() {
607 return self.persistent.bind(reserved, assigned, cursor);
608 }
609
610 self.pools
611 .get_mut(pool_index)
612 .map(|p| p.bind(reserved, assigned, cursor))
613 .ok_or_else(|| IoError::NotFound {
614 backtrace: BackTrace::capture(),
615 reason: format!("Memory pool {} doesn't exist", pool_index).into(),
616 })?
617 }
618}
619
620impl<Storage: ComputeStorage> core::fmt::Display for MemoryManagement<Storage> {
621 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
622 f.write_str("\n# MemoryManagement\n\n")?;
623 f.write_fmt(format_args!(" - name: {:?}\n", self.name))?;
624 f.write_fmt(format_args!("\n## Persistent\n\n{}", self.persistent))?;
625 f.write_str("\n## Dynamic\n\n")?;
626
627 for pool in self.pools.iter() {
628 match pool {
629 DynamicPool::Sliced(pool) => f.write_fmt(format_args!("{pool}\n"))?,
630 DynamicPool::Exclusive(pool) => f.write_fmt(format_args!("{pool}\n"))?,
631 }
632 }
633 let memory_usage = self.memory_usage();
634 f.write_fmt(format_args!("\n## Summary\n\n{memory_usage}"))?;
635
636 Ok(())
637 }
638}
639
640impl<Storage> core::fmt::Debug for MemoryManagement<Storage> {
641 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
642 f.write_str(
643 alloc::format!(
644 "DynamicMemoryManagement {:?}",
645 core::any::type_name::<Storage>(),
646 )
647 .as_str(),
648 )
649 }
650}
651
652#[cfg(test)]
653mod tests {
654 use super::*;
655 use crate::runtime::{memory_management::MemoryManagement, storage::BytesStorage};
656 use alloc::vec;
657
658 const DUMMY_MEM_PROPS: MemoryDeviceProperties = MemoryDeviceProperties {
659 max_page_size: 128 * 1024 * 1024,
660 alignment: 32,
661 };
662
663 fn options() -> MemoryManagementOptions {
664 MemoryManagementOptions {
665 name: "test".into(),
666 memory: MemoryAllocationOption::FromConfig,
667 }
668 }
669
670 #[test_log::test]
672 #[cfg(not(exclusive_memory_only))]
673 fn test_handle_mutability() {
674 let mut memory_management = MemoryManagement::from_configuration(
675 BytesStorage::default(),
676 &DUMMY_MEM_PROPS,
677 MemoryConfiguration::SubSlices,
678 Arc::new(ServerLogger::default()),
679 options(),
680 );
681 let handle = memory_management.reserve(10).unwrap();
682 let other_ref = handle.clone();
683 assert!(!handle.can_mut(), "Handle can't be mut when multiple ref.");
684 drop(other_ref);
685 assert!(handle.can_mut(), "Handle should be mut when only one ref.");
686 }
687
688 #[test_log::test]
690 #[cfg(not(exclusive_memory_only))]
691 fn test_memory_usage() {
692 let max_page_size = 512;
693
694 let mut memory_management = MemoryManagement::from_configuration(
695 BytesStorage::default(),
696 &DUMMY_MEM_PROPS,
697 MemoryConfiguration::Custom {
698 pool_options: vec![MemoryPoolOptions {
699 pool_type: PoolType::ExclusivePages {
700 max_alloc_size: max_page_size,
701 },
702 dealloc_period: None,
703 }],
704 },
705 Arc::new(ServerLogger::default()),
706 options(),
707 );
708 let handle = memory_management.reserve(100);
709 let usage = memory_management.memory_usage();
710
711 assert_eq!(usage.bytes_in_use, 100);
712 assert!(usage.bytes_reserved >= 100 && usage.bytes_reserved <= max_page_size);
713
714 drop(handle);
716 let _handle = memory_management.reserve(100);
717 let usage_new = memory_management.memory_usage();
718 assert_eq!(usage, usage_new);
719 }
720
721 #[test_log::test]
722 fn alloc_two_chunks_on_one_page() {
723 let page_size = 2048;
724
725 let mut memory_management = MemoryManagement::from_configuration(
726 BytesStorage::default(),
727 &DUMMY_MEM_PROPS,
728 MemoryConfiguration::Custom {
729 pool_options: vec![MemoryPoolOptions {
730 pool_type: PoolType::SlicedPages {
731 page_size,
732 max_slice_size: page_size,
733 },
734 dealloc_period: None,
735 }],
736 },
737 Arc::new(ServerLogger::default()),
738 options(),
739 );
740
741 let alloc_size = 512;
742 let _handle = memory_management.reserve(alloc_size);
743 let _new_handle = memory_management.reserve(alloc_size);
744
745 let usage = memory_management.memory_usage();
746 assert_eq!(usage.number_allocs, 2);
747 assert_eq!(usage.bytes_in_use, alloc_size * 2);
748 assert_eq!(usage.bytes_reserved, page_size);
749 }
750
751 #[test_log::test]
752 fn alloc_reuses_storage() {
753 let page_size = 512;
755
756 let mut memory_management = MemoryManagement::from_configuration(
757 BytesStorage::default(),
758 &DUMMY_MEM_PROPS,
759 MemoryConfiguration::Custom {
760 pool_options: vec![MemoryPoolOptions {
761 pool_type: PoolType::SlicedPages {
762 page_size,
763 max_slice_size: page_size,
764 },
765 dealloc_period: None,
766 }],
767 },
768 Arc::new(ServerLogger::default()),
769 options(),
770 );
771
772 let alloc_size = 512;
773 let _handle = memory_management.reserve(alloc_size);
774 drop(_handle);
775 let _new_handle = memory_management.reserve(alloc_size);
776
777 let usage = memory_management.memory_usage();
778 assert_eq!(usage.number_allocs, 1);
779 assert_eq!(usage.bytes_in_use, alloc_size);
780 assert_eq!(usage.bytes_reserved, page_size);
781 }
782
783 #[test_log::test]
784 fn alloc_allocs_new_storage() {
785 let page_size = 1024;
786
787 let mut memory_management = MemoryManagement::from_configuration(
788 BytesStorage::default(),
789 &DUMMY_MEM_PROPS,
790 MemoryConfiguration::Custom {
791 pool_options: vec![MemoryPoolOptions {
792 pool_type: PoolType::SlicedPages {
793 page_size,
794 max_slice_size: page_size,
795 },
796 dealloc_period: None,
797 }],
798 },
799 Arc::new(ServerLogger::default()),
800 options(),
801 );
802
803 let alloc_size = 768;
804 let _handle = memory_management.reserve(alloc_size);
805 let _new_handle = memory_management.reserve(alloc_size);
806
807 let usage = memory_management.memory_usage();
808 assert_eq!(usage.number_allocs, 2);
809 assert_eq!(usage.bytes_in_use, alloc_size * 2);
810 assert_eq!(usage.bytes_reserved, page_size * 2);
811 }
812
813 #[test_log::test]
814 fn alloc_respects_alignment_size() {
815 let page_size = 500;
816 let mut memory_management = MemoryManagement::from_configuration(
817 BytesStorage::default(),
818 &MemoryDeviceProperties {
819 max_page_size: page_size,
820 alignment: 50,
821 },
822 MemoryConfiguration::Custom {
823 pool_options: vec![MemoryPoolOptions {
824 pool_type: PoolType::SlicedPages {
825 page_size,
826 max_slice_size: page_size,
827 },
828 dealloc_period: None,
829 }],
830 },
831 Arc::new(ServerLogger::default()),
832 options(),
833 );
834 let alloc_size = 40;
835 let _handle = memory_management.reserve(alloc_size);
836 let _new_handle = memory_management.reserve(alloc_size);
837 let usage = memory_management.memory_usage();
838 assert_eq!(usage.bytes_padding, 10 * 2);
840 }
841
842 #[test_log::test]
843 fn allocs_on_correct_page() {
844 let sizes = [100, 200, 300, 400];
845
846 let pools = sizes
847 .iter()
848 .map(|size| MemoryPoolOptions {
849 pool_type: PoolType::SlicedPages {
850 page_size: *size,
851 max_slice_size: *size,
852 },
853 dealloc_period: None,
854 })
855 .collect();
856 let mut memory_management = MemoryManagement::from_configuration(
857 BytesStorage::default(),
858 &MemoryDeviceProperties {
859 max_page_size: 128 * 1024 * 1024,
860 alignment: 10,
861 },
862 MemoryConfiguration::Custom {
863 pool_options: pools,
864 },
865 Arc::new(ServerLogger::default()),
866 options(),
867 );
868 let alloc_sizes = [50, 150, 250, 350];
870 let _handles = alloc_sizes.map(|s| memory_management.reserve(s));
871
872 let usage = memory_management.memory_usage();
873
874 assert_eq!(usage.bytes_in_use, alloc_sizes.iter().sum::<u64>());
876 assert!(usage.bytes_reserved >= sizes.iter().sum::<u64>());
877 }
878
879 #[test_log::test]
880 #[cfg(not(exclusive_memory_only))]
881 fn allocate_deallocate_reallocate() {
882 let mut memory_management = MemoryManagement::from_configuration(
883 BytesStorage::default(),
884 &MemoryDeviceProperties {
885 max_page_size: 128 * 1024 * 1024,
886 alignment: 32,
887 },
888 MemoryConfiguration::SubSlices,
889 Arc::new(ServerLogger::default()),
890 options(),
891 );
892 let handles: Vec<_> = (0..5)
894 .map(|i| memory_management.reserve(1000 * (i + 1)))
895 .collect();
896 let usage_before = memory_management.memory_usage();
897 drop(handles);
899 let _new_handles: Vec<_> = (0..5)
901 .map(|i| memory_management.reserve(1000 * (i + 1)))
902 .collect();
903 let usage_after = memory_management.memory_usage();
904 assert_eq!(usage_before.number_allocs, usage_after.number_allocs);
905 assert_eq!(usage_before.bytes_in_use, usage_after.bytes_in_use);
906 assert!(usage_before.bytes_reserved >= usage_after.bytes_reserved);
908 }
909
910 #[test_log::test]
911 #[cfg(not(exclusive_memory_only))]
912 fn test_fragmentation_resistance() {
913 let mut memory_management = MemoryManagement::from_configuration(
914 BytesStorage::default(),
915 &MemoryDeviceProperties {
916 max_page_size: 128 * 1024 * 1024,
917 alignment: 32,
918 },
919 MemoryConfiguration::SubSlices,
920 Arc::new(ServerLogger::default()),
921 options(),
922 );
923 let sizes = [50, 1000, 100, 5000, 200, 10000, 300];
925 let handles: Vec<_> = sizes
926 .iter()
927 .map(|&size| memory_management.reserve(size).unwrap())
928 .collect();
929 let usage_before = memory_management.memory_usage();
930 for i in (0..handles.len()).step_by(2) {
932 drop(handles[i].clone());
933 }
934 for &size in &sizes[0..sizes.len() / 2] {
936 memory_management.reserve(size).unwrap();
937 }
938 let usage_after = memory_management.memory_usage();
939 assert!(usage_after.bytes_reserved <= (usage_before.bytes_reserved as f64 * 1.1) as u64);
941 }
942
943 #[test_log::test]
945 fn noslice_test_handle_mutability() {
946 let mut memory_management = MemoryManagement::from_configuration(
947 BytesStorage::default(),
948 &(MemoryDeviceProperties {
949 max_page_size: 128 * 1024 * 1024,
950 alignment: 32,
951 }),
952 MemoryConfiguration::ExclusivePages,
953 Arc::new(ServerLogger::default()),
954 options(),
955 );
956 let handle = memory_management.reserve(10).unwrap();
957 let other_ref = handle.clone();
958 assert!(!handle.can_mut(), "Handle can't be mut when multiple ref.");
959 drop(other_ref);
960 assert!(handle.can_mut(), "Handle should be mut when only one ref.");
961 }
962
963 #[test_log::test]
964 fn noslice_alloc_two_chunk() {
965 let mut memory_management = MemoryManagement::from_configuration(
966 BytesStorage::default(),
967 &DUMMY_MEM_PROPS,
968 MemoryConfiguration::Custom {
969 pool_options: vec![MemoryPoolOptions {
970 pool_type: PoolType::ExclusivePages {
971 max_alloc_size: 1024,
972 },
973 dealloc_period: None,
974 }],
975 },
976 Arc::new(ServerLogger::default()),
977 options(),
978 );
979
980 let alloc_size = 512;
981 let _handle = memory_management.reserve(alloc_size);
982 let _new_handle = memory_management.reserve(alloc_size);
983
984 let usage = memory_management.memory_usage();
985 assert_eq!(usage.number_allocs, 2);
986 assert_eq!(usage.bytes_in_use, alloc_size * 2);
987 assert!(usage.bytes_reserved >= alloc_size * 2);
988 }
989
990 #[test_log::test]
991 fn noslice_alloc_reuses_storage() {
992 let mut memory_management = MemoryManagement::from_configuration(
994 BytesStorage::default(),
995 &DUMMY_MEM_PROPS,
996 MemoryConfiguration::Custom {
997 pool_options: vec![MemoryPoolOptions {
998 pool_type: PoolType::ExclusivePages {
999 max_alloc_size: 1024,
1000 },
1001 dealloc_period: None,
1002 }],
1003 },
1004 Arc::new(ServerLogger::default()),
1005 options(),
1006 );
1007
1008 let alloc_size = 512;
1009 let _handle = memory_management.reserve(alloc_size);
1010 drop(_handle);
1011 let _new_handle = memory_management.reserve(alloc_size);
1012
1013 let usage = memory_management.memory_usage();
1014 assert_eq!(usage.number_allocs, 1);
1015 assert_eq!(usage.bytes_in_use, alloc_size);
1016 assert!(usage.bytes_reserved >= alloc_size);
1017 }
1018
1019 #[test_log::test]
1020 fn noslice_alloc_allocs_new_storage() {
1021 let mut memory_management = MemoryManagement::from_configuration(
1022 BytesStorage::default(),
1023 &DUMMY_MEM_PROPS,
1024 MemoryConfiguration::Custom {
1025 pool_options: vec![MemoryPoolOptions {
1026 pool_type: PoolType::ExclusivePages {
1027 max_alloc_size: 1024,
1028 },
1029 dealloc_period: None,
1030 }],
1031 },
1032 Arc::new(ServerLogger::default()),
1033 options(),
1034 );
1035
1036 let alloc_size = 768;
1037 let _handle = memory_management.reserve(alloc_size);
1038 let _new_handle = memory_management.reserve(alloc_size);
1039 let usage = memory_management.memory_usage();
1040 assert_eq!(usage.number_allocs, 2);
1041 assert_eq!(usage.bytes_in_use, alloc_size * 2);
1042 assert!(usage.bytes_reserved >= alloc_size * 2);
1043 }
1044
1045 #[test_log::test]
1046 fn noslice_alloc_respects_alignment_size() {
1047 let mut memory_management = MemoryManagement::from_configuration(
1048 BytesStorage::default(),
1049 &MemoryDeviceProperties {
1050 max_page_size: DUMMY_MEM_PROPS.max_page_size,
1051 alignment: 50,
1052 },
1053 MemoryConfiguration::Custom {
1054 pool_options: vec![MemoryPoolOptions {
1055 pool_type: PoolType::ExclusivePages {
1056 max_alloc_size: 50 * 20,
1057 },
1058 dealloc_period: None,
1059 }],
1060 },
1061 Arc::new(ServerLogger::default()),
1062 options(),
1063 );
1064 let alloc_size = 40;
1065 let _handle = memory_management.reserve(alloc_size);
1066 let _new_handle = memory_management.reserve(alloc_size);
1067 let usage = memory_management.memory_usage();
1068 assert_eq!(usage.bytes_padding, 10 * 2);
1070 }
1071
1072 #[test_log::test]
1073 fn noslice_allocs_on_correct_page() {
1074 let pools = [100, 200, 300, 400]
1075 .iter()
1076 .map(|&size| MemoryPoolOptions {
1077 pool_type: PoolType::SlicedPages {
1078 page_size: size,
1079 max_slice_size: size,
1080 },
1081 dealloc_period: None,
1082 })
1083 .collect();
1084 let mut memory_management = MemoryManagement::from_configuration(
1085 BytesStorage::default(),
1086 &MemoryDeviceProperties {
1087 max_page_size: DUMMY_MEM_PROPS.max_page_size,
1088 alignment: 10,
1089 },
1090 MemoryConfiguration::Custom {
1091 pool_options: pools,
1092 },
1093 Arc::new(ServerLogger::default()),
1094 options(),
1095 );
1096 let alloc_sizes = [50, 150, 250, 350];
1098 let _handles = alloc_sizes.map(|s| memory_management.reserve(s));
1099 let usage = memory_management.memory_usage();
1100 assert_eq!(usage.bytes_in_use, alloc_sizes.iter().sum::<u64>());
1102 }
1103
1104 #[test_log::test]
1105 fn noslice_allocate_deallocate_reallocate() {
1106 let mut memory_management = MemoryManagement::from_configuration(
1107 BytesStorage::default(),
1108 &MemoryDeviceProperties {
1109 max_page_size: 128 * 1024 * 1024,
1110 alignment: 32,
1111 },
1112 MemoryConfiguration::ExclusivePages,
1113 Arc::new(ServerLogger::default()),
1114 options(),
1115 );
1116 let handles: Vec<_> = (0..5)
1118 .map(|i| memory_management.reserve(1000 * (i + 1)))
1119 .collect();
1120 let usage_before = memory_management.memory_usage();
1121 drop(handles);
1123 let _new_handles: Vec<_> = (0..5)
1125 .map(|i| memory_management.reserve(1000 * (i + 1)))
1126 .collect();
1127 let usage_after = memory_management.memory_usage();
1128 assert_eq!(usage_before.number_allocs, usage_after.number_allocs);
1129 assert_eq!(usage_before.bytes_in_use, usage_after.bytes_in_use);
1130 assert_eq!(usage_before.bytes_reserved, usage_after.bytes_reserved);
1131 }
1132}