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