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 compact(&mut self) -> Result<(), IoError> {
414 if let Some(adaptive) = self.adaptive.as_mut() {
415 adaptive.compact(&mut self.pools, &mut self.storage)?;
416 }
417 self.cleanup(true);
418 Ok(())
419 }
420
421 pub fn get_cursor(&self, binding: ManagedMemoryBinding) -> Result<u64, IoError> {
423 let slice = self.find(binding)?;
424 Ok(slice.cursor)
425 }
426
427 fn find(&self, binding: ManagedMemoryBinding) -> Result<&Slice, IoError> {
429 let location = binding.descriptor().location();
430
431 if location.pool >= self.pools.len() as u8 {
432 return self.persistent.find_at(location);
433 }
434
435 let pool =
436 self.pools
437 .get(location.pool as usize)
438 .ok_or_else(|| IoError::NotFound {
439 backtrace: BackTrace::capture(),
440 reason: format!("Pool {} doesn't exist", location.pool).into(),
441 })?;
442
443 let slice = pool.find_at(location)?;
444
445 assert_eq!(slice.handle.descriptor(), binding.descriptor());
446
447 Ok(slice)
448 }
449
450 pub fn get_storage(&mut self, binding: ManagedMemoryBinding) -> Result<StorageHandle, IoError> {
452 let slice = self.find(binding)?;
453 Ok(slice.storage.clone())
454 }
455
456 pub fn get_resource(
458 &mut self,
459 binding: ManagedMemoryBinding,
460 offset_start: Option<u64>,
461 offset_end: Option<u64>,
462 ) -> Result<Storage::Resource, IoError> {
463 let pin_binding = self.adaptive.as_ref().map(|_| binding.clone());
464 let handle = self.get_storage(binding)?;
465
466 let handle = match offset_start {
467 Some(offset) => handle.offset_start(offset),
468 None => handle,
469 };
470 let handle = match offset_end {
471 Some(offset) => handle.offset_end(offset),
472 None => handle,
473 };
474 if let Some(binding) = pin_binding {
475 Ok(self.storage().get_pinned(&handle, binding))
476 } else {
477 Ok(self.storage().get(&handle))
478 }
479 }
480
481 pub fn relocation_pending(&self, size: u64) -> bool {
484 self.adaptive.as_ref().is_some_and(|state| state.pending(&self.pools, size))
485 }
486
487 #[cfg_attr(feature = "runtime-tracing", tracing::instrument(level = "trace", skip(self)))]
489 pub fn reserve(&mut self, size: u64) -> Result<ManagedMemoryHandle, IoError> {
490 self.alloc_reserve_count += 1;
493
494 if let Some(val) = self.persistent.try_reserve(size) {
495 self.logger.log_memory(
496 |level| matches!(level, MemoryLogLevel::Full),
497 || {
498 format!(
499 "[{}] Reserved memory {size} using persistent memory",
500 self.name
501 )
502 },
503 );
504 return Ok(val);
505 }
506
507 if matches!(self.mode, MemoryAllocationMode::Persistent) || self.persistent.has_size(size) {
508 let allocated = self.persistent.alloc(&mut self.storage, size);
509
510 self.logger.log_memory(
511 |level| !matches!(level, MemoryLogLevel::Disabled),
512 || {
513 format!(
514 "[{}] Allocated a new memory page using persistent memory, \n{}",
515 self.name, self,
516 )
517 },
518 );
519 return allocated;
520 }
521
522 self.logger.log_memory(
523 |level| matches!(level, MemoryLogLevel::Full),
524 || {
525 format!(
526 "[{}] Reserved memory {} using dynamic pool",
527 self.name,
528 BytesFormat::new(size)
529 )
530 },
531 );
532
533 if let Some(adaptive) = &mut self.adaptive {
534 return adaptive.reserve(&mut self.pools, &mut self.storage, size);
535 }
536
537 let pool = self
539 .pools
540 .iter_mut()
541 .find(|p| p.accept(size))
542 .ok_or(IoError::BufferTooBig {
543 size,
544 backtrace: BackTrace::capture(),
545 })?;
546
547 if let Some(slice) = pool.try_reserve(size) {
548 return Ok(slice);
549 }
550
551 let allocated = pool.alloc(&mut self.storage, size);
552
553 self.logger.log_memory(
554 |level| matches!(level, MemoryLogLevel::Full),
555 || {
556 format!(
557 "[{}], Allocated a new memory page, current usage: \n{}",
558 self.name, self
559 )
560 },
561 );
562
563 allocated
564 }
565
566 pub fn storage(&mut self) -> &mut Storage {
577 &mut self.storage
578 }
579
580 pub fn memory_usage(&self) -> MemoryUsage {
582 let memory_usage = self.pools.iter().map(|x| x.get_memory_usage()).fold(
583 MemoryUsage {
584 number_allocs: 0,
585 bytes_in_use: 0,
586 bytes_padding: 0,
587 bytes_reserved: 0,
588 },
589 |m1, m2| m1.combine(m2),
590 );
591 memory_usage.combine(self.persistent.get_memory_usage())
592 }
593
594 pub fn print_memory_usage(&self) {
596 #[cfg(feature = "runtime-std")]
597 log::info!("{}", self.memory_usage());
598 }
599
600 pub fn bind(
602 &mut self,
603 reserved: ManagedMemoryHandle,
604 assigned: ManagedMemoryHandle,
605 cursor: u64,
606 ) -> Result<(), IoError> {
607 let descriptor = reserved.descriptor();
608
609 if descriptor.location().init == 0 {
610 return Err(IoError::NotFound {
611 backtrace: BackTrace::capture(),
612 reason: "Reserved memory isn't initialized".into(),
613 });
614 }
615
616 let pool_index = descriptor.location().pool as usize;
617 if pool_index >= self.pools.len() {
618 return self.persistent.bind(reserved, assigned, cursor);
619 }
620
621 self.pools
622 .get_mut(pool_index)
623 .map(|p| p.bind(reserved, assigned, cursor))
624 .ok_or_else(|| IoError::NotFound {
625 backtrace: BackTrace::capture(),
626 reason: format!("Memory pool {} doesn't exist", pool_index).into(),
627 })?
628 }
629}
630
631impl<Storage: ComputeStorage> core::fmt::Display for MemoryManagement<Storage> {
632 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
633 f.write_str("\n# MemoryManagement\n\n")?;
634 f.write_fmt(format_args!(" - name: {:?}\n", self.name))?;
635 f.write_fmt(format_args!("\n## Persistent\n\n{}", self.persistent))?;
636 f.write_str("\n## Dynamic\n\n")?;
637
638 for pool in self.pools.iter() {
639 match pool {
640 DynamicPool::Sliced(pool) => f.write_fmt(format_args!("{pool}\n"))?,
641 DynamicPool::Exclusive(pool) => f.write_fmt(format_args!("{pool}\n"))?,
642 }
643 }
644 let memory_usage = self.memory_usage();
645 f.write_fmt(format_args!("\n## Summary\n\n{memory_usage}"))?;
646
647 Ok(())
648 }
649}
650
651impl<Storage> core::fmt::Debug for MemoryManagement<Storage> {
652 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
653 f.write_str(
654 alloc::format!(
655 "DynamicMemoryManagement {:?}",
656 core::any::type_name::<Storage>(),
657 )
658 .as_str(),
659 )
660 }
661}
662
663#[cfg(test)]
664mod tests {
665 use super::*;
666 use crate::runtime::{memory_management::MemoryManagement, storage::BytesStorage};
667 use alloc::vec;
668
669 const DUMMY_MEM_PROPS: MemoryDeviceProperties = MemoryDeviceProperties {
670 max_page_size: 128 * 1024 * 1024,
671 alignment: 32,
672 };
673
674 fn options() -> MemoryManagementOptions {
675 MemoryManagementOptions {
676 name: "test".into(),
677 memory: MemoryAllocationOption::FromConfig,
678 }
679 }
680
681 #[test_log::test]
683 #[cfg(not(exclusive_memory_only))]
684 fn test_handle_mutability() {
685 let mut memory_management = MemoryManagement::from_configuration(
686 BytesStorage::default(),
687 &DUMMY_MEM_PROPS,
688 MemoryConfiguration::SubSlices,
689 Arc::new(ServerLogger::default()),
690 options(),
691 );
692 let handle = memory_management.reserve(10).unwrap();
693 let other_ref = handle.clone();
694 assert!(!handle.can_mut(), "Handle can't be mut when multiple ref.");
695 drop(other_ref);
696 assert!(handle.can_mut(), "Handle should be mut when only one ref.");
697 }
698
699 #[test_log::test]
701 #[cfg(not(exclusive_memory_only))]
702 fn test_memory_usage() {
703 let max_page_size = 512;
704
705 let mut memory_management = MemoryManagement::from_configuration(
706 BytesStorage::default(),
707 &DUMMY_MEM_PROPS,
708 MemoryConfiguration::Custom {
709 pool_options: vec![MemoryPoolOptions {
710 pool_type: PoolType::ExclusivePages {
711 max_alloc_size: max_page_size,
712 },
713 dealloc_period: None,
714 }],
715 },
716 Arc::new(ServerLogger::default()),
717 options(),
718 );
719 let handle = memory_management.reserve(100);
720 let usage = memory_management.memory_usage();
721
722 assert_eq!(usage.bytes_in_use, 100);
723 assert!(usage.bytes_reserved >= 100 && usage.bytes_reserved <= max_page_size);
724
725 drop(handle);
727 let _handle = memory_management.reserve(100);
728 let usage_new = memory_management.memory_usage();
729 assert_eq!(usage, usage_new);
730 }
731
732 #[test_log::test]
733 fn alloc_two_chunks_on_one_page() {
734 let page_size = 2048;
735
736 let mut memory_management = MemoryManagement::from_configuration(
737 BytesStorage::default(),
738 &DUMMY_MEM_PROPS,
739 MemoryConfiguration::Custom {
740 pool_options: vec![MemoryPoolOptions {
741 pool_type: PoolType::SlicedPages {
742 page_size,
743 max_slice_size: page_size,
744 },
745 dealloc_period: None,
746 }],
747 },
748 Arc::new(ServerLogger::default()),
749 options(),
750 );
751
752 let alloc_size = 512;
753 let _handle = memory_management.reserve(alloc_size);
754 let _new_handle = memory_management.reserve(alloc_size);
755
756 let usage = memory_management.memory_usage();
757 assert_eq!(usage.number_allocs, 2);
758 assert_eq!(usage.bytes_in_use, alloc_size * 2);
759 assert_eq!(usage.bytes_reserved, page_size);
760 }
761
762 #[test_log::test]
763 fn alloc_reuses_storage() {
764 let page_size = 512;
766
767 let mut memory_management = MemoryManagement::from_configuration(
768 BytesStorage::default(),
769 &DUMMY_MEM_PROPS,
770 MemoryConfiguration::Custom {
771 pool_options: vec![MemoryPoolOptions {
772 pool_type: PoolType::SlicedPages {
773 page_size,
774 max_slice_size: page_size,
775 },
776 dealloc_period: None,
777 }],
778 },
779 Arc::new(ServerLogger::default()),
780 options(),
781 );
782
783 let alloc_size = 512;
784 let _handle = memory_management.reserve(alloc_size);
785 drop(_handle);
786 let _new_handle = memory_management.reserve(alloc_size);
787
788 let usage = memory_management.memory_usage();
789 assert_eq!(usage.number_allocs, 1);
790 assert_eq!(usage.bytes_in_use, alloc_size);
791 assert_eq!(usage.bytes_reserved, page_size);
792 }
793
794 #[test_log::test]
795 fn alloc_allocs_new_storage() {
796 let page_size = 1024;
797
798 let mut memory_management = MemoryManagement::from_configuration(
799 BytesStorage::default(),
800 &DUMMY_MEM_PROPS,
801 MemoryConfiguration::Custom {
802 pool_options: vec![MemoryPoolOptions {
803 pool_type: PoolType::SlicedPages {
804 page_size,
805 max_slice_size: page_size,
806 },
807 dealloc_period: None,
808 }],
809 },
810 Arc::new(ServerLogger::default()),
811 options(),
812 );
813
814 let alloc_size = 768;
815 let _handle = memory_management.reserve(alloc_size);
816 let _new_handle = memory_management.reserve(alloc_size);
817
818 let usage = memory_management.memory_usage();
819 assert_eq!(usage.number_allocs, 2);
820 assert_eq!(usage.bytes_in_use, alloc_size * 2);
821 assert_eq!(usage.bytes_reserved, page_size * 2);
822 }
823
824 #[test_log::test]
825 fn alloc_respects_alignment_size() {
826 let page_size = 500;
827 let mut memory_management = MemoryManagement::from_configuration(
828 BytesStorage::default(),
829 &MemoryDeviceProperties {
830 max_page_size: page_size,
831 alignment: 50,
832 },
833 MemoryConfiguration::Custom {
834 pool_options: vec![MemoryPoolOptions {
835 pool_type: PoolType::SlicedPages {
836 page_size,
837 max_slice_size: page_size,
838 },
839 dealloc_period: None,
840 }],
841 },
842 Arc::new(ServerLogger::default()),
843 options(),
844 );
845 let alloc_size = 40;
846 let _handle = memory_management.reserve(alloc_size);
847 let _new_handle = memory_management.reserve(alloc_size);
848 let usage = memory_management.memory_usage();
849 assert_eq!(usage.bytes_padding, 10 * 2);
851 }
852
853 #[test_log::test]
854 fn allocs_on_correct_page() {
855 let sizes = [100, 200, 300, 400];
856
857 let pools = sizes
858 .iter()
859 .map(|size| MemoryPoolOptions {
860 pool_type: PoolType::SlicedPages {
861 page_size: *size,
862 max_slice_size: *size,
863 },
864 dealloc_period: None,
865 })
866 .collect();
867 let mut memory_management = MemoryManagement::from_configuration(
868 BytesStorage::default(),
869 &MemoryDeviceProperties {
870 max_page_size: 128 * 1024 * 1024,
871 alignment: 10,
872 },
873 MemoryConfiguration::Custom {
874 pool_options: pools,
875 },
876 Arc::new(ServerLogger::default()),
877 options(),
878 );
879 let alloc_sizes = [50, 150, 250, 350];
881 let _handles = alloc_sizes.map(|s| memory_management.reserve(s));
882
883 let usage = memory_management.memory_usage();
884
885 assert_eq!(usage.bytes_in_use, alloc_sizes.iter().sum::<u64>());
887 assert!(usage.bytes_reserved >= sizes.iter().sum::<u64>());
888 }
889
890 #[test_log::test]
891 #[cfg(not(exclusive_memory_only))]
892 fn allocate_deallocate_reallocate() {
893 let mut memory_management = MemoryManagement::from_configuration(
894 BytesStorage::default(),
895 &MemoryDeviceProperties {
896 max_page_size: 128 * 1024 * 1024,
897 alignment: 32,
898 },
899 MemoryConfiguration::SubSlices,
900 Arc::new(ServerLogger::default()),
901 options(),
902 );
903 let handles: Vec<_> = (0..5)
905 .map(|i| memory_management.reserve(1000 * (i + 1)))
906 .collect();
907 let usage_before = memory_management.memory_usage();
908 drop(handles);
910 let _new_handles: Vec<_> = (0..5)
912 .map(|i| memory_management.reserve(1000 * (i + 1)))
913 .collect();
914 let usage_after = memory_management.memory_usage();
915 assert_eq!(usage_before.number_allocs, usage_after.number_allocs);
916 assert_eq!(usage_before.bytes_in_use, usage_after.bytes_in_use);
917 assert!(usage_before.bytes_reserved >= usage_after.bytes_reserved);
919 }
920
921 #[test_log::test]
922 #[cfg(not(exclusive_memory_only))]
923 fn test_fragmentation_resistance() {
924 let mut memory_management = MemoryManagement::from_configuration(
925 BytesStorage::default(),
926 &MemoryDeviceProperties {
927 max_page_size: 128 * 1024 * 1024,
928 alignment: 32,
929 },
930 MemoryConfiguration::SubSlices,
931 Arc::new(ServerLogger::default()),
932 options(),
933 );
934 let sizes = [50, 1000, 100, 5000, 200, 10000, 300];
936 let handles: Vec<_> = sizes
937 .iter()
938 .map(|&size| memory_management.reserve(size).unwrap())
939 .collect();
940 let usage_before = memory_management.memory_usage();
941 for i in (0..handles.len()).step_by(2) {
943 drop(handles[i].clone());
944 }
945 for &size in &sizes[0..sizes.len() / 2] {
947 memory_management.reserve(size).unwrap();
948 }
949 let usage_after = memory_management.memory_usage();
950 assert!(usage_after.bytes_reserved <= (usage_before.bytes_reserved as f64 * 1.1) as u64);
952 }
953
954 #[test_log::test]
956 fn noslice_test_handle_mutability() {
957 let mut memory_management = MemoryManagement::from_configuration(
958 BytesStorage::default(),
959 &(MemoryDeviceProperties {
960 max_page_size: 128 * 1024 * 1024,
961 alignment: 32,
962 }),
963 MemoryConfiguration::ExclusivePages,
964 Arc::new(ServerLogger::default()),
965 options(),
966 );
967 let handle = memory_management.reserve(10).unwrap();
968 let other_ref = handle.clone();
969 assert!(!handle.can_mut(), "Handle can't be mut when multiple ref.");
970 drop(other_ref);
971 assert!(handle.can_mut(), "Handle should be mut when only one ref.");
972 }
973
974 #[test_log::test]
975 fn noslice_alloc_two_chunk() {
976 let mut memory_management = MemoryManagement::from_configuration(
977 BytesStorage::default(),
978 &DUMMY_MEM_PROPS,
979 MemoryConfiguration::Custom {
980 pool_options: vec![MemoryPoolOptions {
981 pool_type: PoolType::ExclusivePages {
982 max_alloc_size: 1024,
983 },
984 dealloc_period: None,
985 }],
986 },
987 Arc::new(ServerLogger::default()),
988 options(),
989 );
990
991 let alloc_size = 512;
992 let _handle = memory_management.reserve(alloc_size);
993 let _new_handle = memory_management.reserve(alloc_size);
994
995 let usage = memory_management.memory_usage();
996 assert_eq!(usage.number_allocs, 2);
997 assert_eq!(usage.bytes_in_use, alloc_size * 2);
998 assert!(usage.bytes_reserved >= alloc_size * 2);
999 }
1000
1001 #[test_log::test]
1002 fn noslice_alloc_reuses_storage() {
1003 let mut memory_management = MemoryManagement::from_configuration(
1005 BytesStorage::default(),
1006 &DUMMY_MEM_PROPS,
1007 MemoryConfiguration::Custom {
1008 pool_options: vec![MemoryPoolOptions {
1009 pool_type: PoolType::ExclusivePages {
1010 max_alloc_size: 1024,
1011 },
1012 dealloc_period: None,
1013 }],
1014 },
1015 Arc::new(ServerLogger::default()),
1016 options(),
1017 );
1018
1019 let alloc_size = 512;
1020 let _handle = memory_management.reserve(alloc_size);
1021 drop(_handle);
1022 let _new_handle = memory_management.reserve(alloc_size);
1023
1024 let usage = memory_management.memory_usage();
1025 assert_eq!(usage.number_allocs, 1);
1026 assert_eq!(usage.bytes_in_use, alloc_size);
1027 assert!(usage.bytes_reserved >= alloc_size);
1028 }
1029
1030 #[test_log::test]
1031 fn noslice_alloc_allocs_new_storage() {
1032 let mut memory_management = MemoryManagement::from_configuration(
1033 BytesStorage::default(),
1034 &DUMMY_MEM_PROPS,
1035 MemoryConfiguration::Custom {
1036 pool_options: vec![MemoryPoolOptions {
1037 pool_type: PoolType::ExclusivePages {
1038 max_alloc_size: 1024,
1039 },
1040 dealloc_period: None,
1041 }],
1042 },
1043 Arc::new(ServerLogger::default()),
1044 options(),
1045 );
1046
1047 let alloc_size = 768;
1048 let _handle = memory_management.reserve(alloc_size);
1049 let _new_handle = memory_management.reserve(alloc_size);
1050 let usage = memory_management.memory_usage();
1051 assert_eq!(usage.number_allocs, 2);
1052 assert_eq!(usage.bytes_in_use, alloc_size * 2);
1053 assert!(usage.bytes_reserved >= alloc_size * 2);
1054 }
1055
1056 #[test_log::test]
1057 fn noslice_alloc_respects_alignment_size() {
1058 let mut memory_management = MemoryManagement::from_configuration(
1059 BytesStorage::default(),
1060 &MemoryDeviceProperties {
1061 max_page_size: DUMMY_MEM_PROPS.max_page_size,
1062 alignment: 50,
1063 },
1064 MemoryConfiguration::Custom {
1065 pool_options: vec![MemoryPoolOptions {
1066 pool_type: PoolType::ExclusivePages {
1067 max_alloc_size: 50 * 20,
1068 },
1069 dealloc_period: None,
1070 }],
1071 },
1072 Arc::new(ServerLogger::default()),
1073 options(),
1074 );
1075 let alloc_size = 40;
1076 let _handle = memory_management.reserve(alloc_size);
1077 let _new_handle = memory_management.reserve(alloc_size);
1078 let usage = memory_management.memory_usage();
1079 assert_eq!(usage.bytes_padding, 10 * 2);
1081 }
1082
1083 #[test_log::test]
1084 fn noslice_allocs_on_correct_page() {
1085 let pools = [100, 200, 300, 400]
1086 .iter()
1087 .map(|&size| MemoryPoolOptions {
1088 pool_type: PoolType::SlicedPages {
1089 page_size: size,
1090 max_slice_size: size,
1091 },
1092 dealloc_period: None,
1093 })
1094 .collect();
1095 let mut memory_management = MemoryManagement::from_configuration(
1096 BytesStorage::default(),
1097 &MemoryDeviceProperties {
1098 max_page_size: DUMMY_MEM_PROPS.max_page_size,
1099 alignment: 10,
1100 },
1101 MemoryConfiguration::Custom {
1102 pool_options: pools,
1103 },
1104 Arc::new(ServerLogger::default()),
1105 options(),
1106 );
1107 let alloc_sizes = [50, 150, 250, 350];
1109 let _handles = alloc_sizes.map(|s| memory_management.reserve(s));
1110 let usage = memory_management.memory_usage();
1111 assert_eq!(usage.bytes_in_use, alloc_sizes.iter().sum::<u64>());
1113 }
1114
1115 #[test_log::test]
1116 fn noslice_allocate_deallocate_reallocate() {
1117 let mut memory_management = MemoryManagement::from_configuration(
1118 BytesStorage::default(),
1119 &MemoryDeviceProperties {
1120 max_page_size: 128 * 1024 * 1024,
1121 alignment: 32,
1122 },
1123 MemoryConfiguration::ExclusivePages,
1124 Arc::new(ServerLogger::default()),
1125 options(),
1126 );
1127 let handles: Vec<_> = (0..5)
1129 .map(|i| memory_management.reserve(1000 * (i + 1)))
1130 .collect();
1131 let usage_before = memory_management.memory_usage();
1132 drop(handles);
1134 let _new_handles: Vec<_> = (0..5)
1136 .map(|i| memory_management.reserve(1000 * (i + 1)))
1137 .collect();
1138 let usage_after = memory_management.memory_usage();
1139 assert_eq!(usage_before.number_allocs, usage_after.number_allocs);
1140 assert_eq!(usage_before.bytes_in_use, usage_after.bytes_in_use);
1141 assert_eq!(usage_before.bytes_reserved, usage_after.bytes_reserved);
1142 }
1143}