diff --git a/README.md b/README.md index b5af4899..c84f28fd 100644 --- a/README.md +++ b/README.md @@ -108,10 +108,11 @@ Consider this migration scenario: ```rust use ic_stable_structures::{ memory_manager::{MemoryId, MemoryManager}, - BTreeMap, DefaultMemoryImpl, + BTreeMap, DefaultMemoryImpl, Memory, }; -let mem_mgr = MemoryManager::init(DefaultMemoryImpl::default()); +let mem = DefaultMemoryImpl::default(); +let mem_mgr = MemoryManager::init(mem.clone()); let (mem_id_a, mem_id_b) = (MemoryId::new(0), MemoryId::new(1)); // ======================================== @@ -122,10 +123,14 @@ map_a.insert(1, b'A'); // Populate map A with data let data = map_a.get(&1); // Extract data for migration map_a.clear_new(); // A is now empty drop(map_a); // Memory stays allocated to mem_id_a +let actual_size_before_migration = mem.size(); let mut map_b: BTreeMap = BTreeMap::init(mem_mgr.get(mem_id_b)); map_b.insert(1, data.unwrap()); // B allocates NEW memory - // Result: 2x memory usage +let actual_size_after_migration = mem.size(); + // Result: ~2x memory usage + // Memory allocation grew (waste) +assert!(actual_size_before_migration < actual_size_after_migration); // ======================================== // Scenario 2: WITH reclamation @@ -133,12 +138,17 @@ map_b.insert(1, data.unwrap()); // B allocates NEW memory let mut map_a: BTreeMap = BTreeMap::init(mem_mgr.get(mem_id_a)); map_a.insert(1, b'A'); // Populate map A with data let data = map_a.get(&1); // Extract data for migration +map_a.clear_new(); // A is now empty drop(map_a); // Drop A completely +let actual_size_before_migration = mem.size(); mem_mgr.reclaim_memory(mem_id_a); // Free A's memory buckets for reuse let mut map_b: BTreeMap = BTreeMap::init(mem_mgr.get(mem_id_b)); map_b.insert(1, data.unwrap()); // B reuses A's reclaimed memory buckets +let actual_size_after_migration = mem.size(); // Result: 1x memory usage + // Memory allocation stayed the same (no waste) +assert!(actual_size_before_migration == actual_size_after_migration); ``` **Important**: Always drop the original structure before calling `reclaim_memory`. diff --git a/docs/src/concepts/memory-manager.md b/docs/src/concepts/memory-manager.md index 04c18c9f..bf4452b9 100644 --- a/docs/src/concepts/memory-manager.md +++ b/docs/src/concepts/memory-manager.md @@ -47,10 +47,11 @@ The `MemoryManager` provides a `reclaim_memory` method to efficiently handle the ```rust use ic_stable_structures::{ memory_manager::{MemoryId, MemoryManager}, - BTreeMap, DefaultMemoryImpl, + BTreeMap, DefaultMemoryImpl, Memory, }; -let mem_mgr = MemoryManager::init(DefaultMemoryImpl::default()); +let mem = DefaultMemoryImpl::default(); +let mem_mgr = MemoryManager::init(mem.clone()); let (mem_id_a, mem_id_b) = (MemoryId::new(0), MemoryId::new(1)); // ======================================== @@ -61,10 +62,14 @@ map_a.insert(1, b'A'); // Populate map A with data let data = map_a.get(&1); // Extract data for migration map_a.clear_new(); // A is now empty drop(map_a); // Memory stays allocated to mem_id_a +let actual_size_before_migration = mem.size(); let mut map_b: BTreeMap = BTreeMap::init(mem_mgr.get(mem_id_b)); map_b.insert(1, data.unwrap()); // B allocates NEW memory - // Result: 2x memory usage +let actual_size_after_migration = mem.size(); + // Result: ~2x memory usage + // Memory allocation grew (waste) +assert!(actual_size_before_migration < actual_size_after_migration); // ======================================== // Scenario 2: WITH reclamation @@ -72,12 +77,17 @@ map_b.insert(1, data.unwrap()); // B allocates NEW memory let mut map_a: BTreeMap = BTreeMap::init(mem_mgr.get(mem_id_a)); map_a.insert(1, b'A'); // Populate map A with data let data = map_a.get(&1); // Extract data for migration +map_a.clear_new(); // A is now empty drop(map_a); // Drop A completely +let actual_size_before_migration = mem.size(); mem_mgr.reclaim_memory(mem_id_a); // Free A's memory buckets for reuse let mut map_b: BTreeMap = BTreeMap::init(mem_mgr.get(mem_id_b)); map_b.insert(1, data.unwrap()); // B reuses A's reclaimed memory buckets +let actual_size_after_migration = mem.size(); // Result: 1x memory usage + // Memory allocation stayed the same (no waste) +assert!(actual_size_before_migration == actual_size_after_migration); ``` ```admonish info ""