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