From 3bf270fae12512f94b7039a8a693ce4f6cf9b1dc Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Fri, 28 Mar 2025 13:26:55 +0100 Subject: [PATCH 01/12] improve contains_key --- src/btreemap.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/btreemap.rs b/src/btreemap.rs index b06a7f65..07b2b198 100644 --- a/src/btreemap.rs +++ b/src/btreemap.rs @@ -538,7 +538,7 @@ where /// Returns `true` if the key exists in the map, `false` otherwise. pub fn contains_key(&self, key: &K) -> bool { - self.get(key).is_some() + self.root_addr != NULL && self.get_helper(self.root_addr, key).is_some() } /// Returns `true` if the map contains no elements. From 6f4b2a9dec59452a208be8d7dbf63bc8151d59e4 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Fri, 28 Mar 2025 13:31:27 +0100 Subject: [PATCH 02/12] add contains_key_helper --- src/btreemap.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/btreemap.rs b/src/btreemap.rs index 07b2b198..0b233469 100644 --- a/src/btreemap.rs +++ b/src/btreemap.rs @@ -538,7 +538,18 @@ where /// Returns `true` if the key exists in the map, `false` otherwise. pub fn contains_key(&self, key: &K) -> bool { - self.root_addr != NULL && self.get_helper(self.root_addr, key).is_some() + self.root_addr != NULL && self.contains_key_helper(self.root_addr, key) + } + + fn contains_key_helper(&self, node_addr: Address, key: &K) -> bool { + let node = self.load_node(node_addr); + match node.search(key) { + Ok(_) => true, + Err(idx) => match node.node_type() { + NodeType::Leaf => false, + NodeType::Internal => self.contains_key_helper(node.child(idx), key), + }, + } } /// Returns `true` if the map contains no elements. From 2e632c364c1f96e9e0b679ba31def895cfc51d23 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Fri, 28 Mar 2025 13:40:13 +0100 Subject: [PATCH 03/12] . --- src/btreemap.rs | 44 +++++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/src/btreemap.rs b/src/btreemap.rs index 0b233469..eb285861 100644 --- a/src/btreemap.rs +++ b/src/btreemap.rs @@ -514,40 +514,34 @@ where if self.root_addr == NULL { return None; } - - self.get_helper(self.root_addr, key) - .map(Cow::Owned) - .map(V::from_bytes) - } - - fn get_helper(&self, node_addr: Address, key: &K) -> Option> { - let node = self.load_node(node_addr); - match node.search(key) { - Ok(idx) => Some(node.into_entry(idx, self.memory()).1), - Err(idx) => { - match node.node_type() { - NodeType::Leaf => None, // Key not found. - NodeType::Internal => { - // The key isn't in the node. Look for the key in the child. - self.get_helper(node.child(idx), key) - } - } - } - } + self.traverse(self.root_addr, key, |node, idx| { + node.into_entry(idx, self.memory()).1 + }) + .map(Cow::Owned) + .map(V::from_bytes) } /// Returns `true` if the key exists in the map, `false` otherwise. pub fn contains_key(&self, key: &K) -> bool { - self.root_addr != NULL && self.contains_key_helper(self.root_addr, key) + if self.root_addr == NULL { + return false; + } + self.traverse(self.root_addr, key, |_, _| ()).is_some() } - fn contains_key_helper(&self, node_addr: Address, key: &K) -> bool { + /// Generic recursive traversal helper. + /// + /// Recursively traverses from `node_addr`, invoking `f` when a matching key is found, or stops at a leaf. + fn traverse(&self, node_addr: Address, key: &K, f: F) -> Option + where + F: Fn(Node, usize) -> R + Clone, + { let node = self.load_node(node_addr); match node.search(key) { - Ok(_) => true, + Ok(idx) => Some(f(node, idx)), Err(idx) => match node.node_type() { - NodeType::Leaf => false, - NodeType::Internal => self.contains_key_helper(node.child(idx), key), + NodeType::Leaf => None, // Key not found. + NodeType::Internal => self.traverse(node.child(idx), key, f), }, } } From b899e0cb311edbe7cc633391cca95ae89c6f7829 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Mon, 7 Apr 2025 20:25:01 +0200 Subject: [PATCH 04/12] cleanup --- src/btreemap.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/btreemap.rs b/src/btreemap.rs index eb285861..db622409 100644 --- a/src/btreemap.rs +++ b/src/btreemap.rs @@ -515,6 +515,7 @@ where return None; } self.traverse(self.root_addr, key, |node, idx| { + // The key exists in the node. Get the value. node.into_entry(idx, self.memory()).1 }) .map(Cow::Owned) @@ -529,9 +530,7 @@ where self.traverse(self.root_addr, key, |_, _| ()).is_some() } - /// Generic recursive traversal helper. - /// - /// Recursively traverses from `node_addr`, invoking `f` when a matching key is found, or stops at a leaf. + /// Recursively traverses from `node_addr`, calling `f` on a match; stops at a leaf. fn traverse(&self, node_addr: Address, key: &K, f: F) -> Option where F: Fn(Node, usize) -> R + Clone, From 5baa9c1bad838667127f6a312046eb4c8b58fb64 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Mon, 7 Apr 2025 20:29:20 +0200 Subject: [PATCH 05/12] . --- src/btreemap.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/btreemap.rs b/src/btreemap.rs index db622409..43c76e0a 100644 --- a/src/btreemap.rs +++ b/src/btreemap.rs @@ -515,7 +515,7 @@ where return None; } self.traverse(self.root_addr, key, |node, idx| { - // The key exists in the node. Get the value. + // The key exists in the node, get the value. node.into_entry(idx, self.memory()).1 }) .map(Cow::Owned) @@ -527,7 +527,10 @@ where if self.root_addr == NULL { return false; } - self.traverse(self.root_addr, key, |_, _| ()).is_some() + self.traverse(self.root_addr, key, |_, _| { + // The key exists in the node, do nothing. + }) + .is_some() } /// Recursively traverses from `node_addr`, calling `f` on a match; stops at a leaf. From 912e2f697cfeba65ab751e8d153be4f25023aa99 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Mon, 7 Apr 2025 20:37:07 +0200 Subject: [PATCH 06/12] . --- src/btreemap.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/btreemap.rs b/src/btreemap.rs index 43c76e0a..f5b6e35d 100644 --- a/src/btreemap.rs +++ b/src/btreemap.rs @@ -509,28 +509,22 @@ where node.save(self.allocator_mut()); } - /// Returns the value associated with the given key if it exists. + /// Returns the value for the given key, if it exists. pub fn get(&self, key: &K) -> Option { if self.root_addr == NULL { return None; } self.traverse(self.root_addr, key, |node, idx| { - // The key exists in the node, get the value. - node.into_entry(idx, self.memory()).1 + node.into_entry(idx, self.memory()).1 // Extract value. }) .map(Cow::Owned) .map(V::from_bytes) } - /// Returns `true` if the key exists in the map, `false` otherwise. + /// Returns true if the key exists. pub fn contains_key(&self, key: &K) -> bool { - if self.root_addr == NULL { - return false; - } - self.traverse(self.root_addr, key, |_, _| { - // The key exists in the node, do nothing. - }) - .is_some() + // An empty closure returns Some(()) if the key is found. + self.root_addr != NULL && self.traverse(self.root_addr, key, |_, _| ()).is_some() } /// Recursively traverses from `node_addr`, calling `f` on a match; stops at a leaf. From 87394e2de19e0d21c728e6e45645b80e5ae0cd20 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Mon, 7 Apr 2025 20:43:02 +0200 Subject: [PATCH 07/12] add contains_key benchmark --- benchmarks/src/btreemap.rs | 48 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/benchmarks/src/btreemap.rs b/benchmarks/src/btreemap.rs index 47d959a9..1c8e3397 100644 --- a/benchmarks/src/btreemap.rs +++ b/benchmarks/src/btreemap.rs @@ -707,6 +707,54 @@ fn get_helper( }) } +/// Benchmarks contains_key of a BTreeMap. +#[bench(raw)] +pub fn btreemap_contains_key_blob_4_1024() -> BenchResult { + contains_key_blob_helper::<4, 1024>() +} + +#[bench(raw)] +pub fn btreemap_contains_key_blob_4_1024_v2() -> BenchResult { + contains_key_blob_helper_v2::<4, 1024>() +} + +// Profiles contains_key on a large number of random blobs from a btreemap. +fn contains_key_blob_helper() -> BenchResult { + let btree = BTreeMap::new_v1(DefaultMemoryImpl::default()); + contains_key_helper::, Blob>(btree) +} + +fn contains_key_blob_helper_v2() -> BenchResult { + let btree = BTreeMap::new(DefaultMemoryImpl::default()); + contains_key_helper::, Blob>(btree) +} + +fn contains_key_helper( + mut btree: BTreeMap, +) -> BenchResult { + let num_keys = 10_000; + let mut rng = Rng::from_seed(0); + let mut random_keys = Vec::with_capacity(num_keys); + let mut random_values = Vec::with_capacity(num_keys); + + for _ in 0..num_keys { + random_keys.push(K::random(&mut rng)); + random_values.push(V::random(&mut rng)); + } + + // Insert the keys into the btree. + for (k, v) in random_keys.iter().zip(random_values.into_iter()) { + btree.insert(k.clone(), v); + } + + // Checks if the keys are in the map. + bench_fn(|| { + for k in random_keys.into_iter() { + btree.contains_key(&k); + } + }) +} + // Inserts a large number of random blobs into a btreemap, then profiles removing them. fn remove_blob_helper() -> BenchResult { let btree = BTreeMap::new_v1(DefaultMemoryImpl::default()); From a80026980dabd4c0100076d2eea9e1da9dbf4e9d Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Mon, 7 Apr 2025 20:44:06 +0200 Subject: [PATCH 08/12] update canbench results --- canbench_results.yml | 62 ++++++++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/canbench_results.yml b/canbench_results.yml index 23a5f07b..e68ce309 100644 --- a/canbench_results.yml +++ b/canbench_results.yml @@ -1,145 +1,157 @@ benches: + btreemap_contains_key_blob_4_1024: + total: + instructions: 166001914 + heap_increase: 0 + stable_memory_increase: 0 + scopes: {} + btreemap_contains_key_blob_4_1024_v2: + total: + instructions: 246601950 + heap_increase: 0 + stable_memory_increase: 0 + scopes: {} btreemap_get_blob_128_1024: total: - instructions: 871309202 + instructions: 871377876 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_get_blob_128_1024_v2: total: - instructions: 952323558 + instructions: 952392232 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_get_blob_16_1024: total: - instructions: 246794283 + instructions: 246862999 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_get_blob_16_1024_v2: total: - instructions: 322817530 + instructions: 322886246 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_get_blob_256_1024: total: - instructions: 1438305373 + instructions: 1438374032 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_get_blob_256_1024_v2: total: - instructions: 1522441414 + instructions: 1522510073 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_get_blob_32_1024: total: - instructions: 280038069 + instructions: 280106753 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_get_blob_32_1024_v2: total: - instructions: 357990649 + instructions: 358059333 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_get_blob_4_1024: total: - instructions: 183602777 + instructions: 183878301 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_get_blob_4_1024_v2: total: - instructions: 268129985 + instructions: 268405509 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_get_blob_512_1024: total: - instructions: 2573953601 + instructions: 2574022258 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_get_blob_512_1024_v2: total: - instructions: 2652827669 + instructions: 2652896326 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_get_blob_512_1024_v2_mem_manager: total: - instructions: 2758867134 + instructions: 2758935791 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_get_blob_64_1024: total: - instructions: 522384319 + instructions: 522452995 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_get_blob_64_1024_v2: total: - instructions: 602402025 + instructions: 602470701 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_get_blob_8_1024: total: - instructions: 217332289 + instructions: 217400958 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_get_blob_8_1024_v2: total: - instructions: 299640561 + instructions: 299709230 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_get_blob_8_u64: total: - instructions: 201109665 + instructions: 201178258 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_get_blob_8_u64_v2: total: - instructions: 297999015 + instructions: 298067608 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_get_u64_blob_8: total: - instructions: 175387736 + instructions: 175651041 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_get_u64_blob_8_v2: total: - instructions: 249704264 + instructions: 249967569 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_get_u64_u64: total: - instructions: 176095721 + instructions: 176359016 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_get_u64_u64_v2: total: - instructions: 256392975 + instructions: 256656270 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_get_u64_u64_v2_mem_manager: total: - instructions: 335916653 + instructions: 336179948 heap_increase: 0 stable_memory_increase: 0 scopes: {} @@ -731,4 +743,4 @@ benches: heap_increase: 0 stable_memory_increase: 1 scopes: {} -version: 0.1.9 +version: 0.1.11 From 10b6c782ca004d9bc0cbe7c22a4fc17bb3e33dc9 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Tue, 8 Apr 2025 10:33:37 +0200 Subject: [PATCH 09/12] improve readability --- benchmarks/src/btreemap.rs | 4 ++-- src/btreemap.rs | 10 ++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/benchmarks/src/btreemap.rs b/benchmarks/src/btreemap.rs index 1c8e3397..ca478de6 100644 --- a/benchmarks/src/btreemap.rs +++ b/benchmarks/src/btreemap.rs @@ -707,7 +707,7 @@ fn get_helper( }) } -/// Benchmarks contains_key of a BTreeMap. +/// Benchmarks `contains_key` of a BTreeMap. #[bench(raw)] pub fn btreemap_contains_key_blob_4_1024() -> BenchResult { contains_key_blob_helper::<4, 1024>() @@ -718,7 +718,7 @@ pub fn btreemap_contains_key_blob_4_1024_v2() -> BenchResult { contains_key_blob_helper_v2::<4, 1024>() } -// Profiles contains_key on a large number of random blobs from a btreemap. +// Profiles `contains_key` on a large number of random blobs from a btreemap. fn contains_key_blob_helper() -> BenchResult { let btree = BTreeMap::new_v1(DefaultMemoryImpl::default()); contains_key_helper::, Blob>(btree) diff --git a/src/btreemap.rs b/src/btreemap.rs index f5b6e35d..c203c3b7 100644 --- a/src/btreemap.rs +++ b/src/btreemap.rs @@ -527,18 +527,16 @@ where self.root_addr != NULL && self.traverse(self.root_addr, key, |_, _| ()).is_some() } - /// Recursively traverses from `node_addr`, calling `f` on a match; stops at a leaf. + /// Recursively traverses from `node_addr`, calling `f` when `key` is found; stops at a leaf if not. fn traverse(&self, node_addr: Address, key: &K, f: F) -> Option where F: Fn(Node, usize) -> R + Clone, { let node = self.load_node(node_addr); match node.search(key) { - Ok(idx) => Some(f(node, idx)), - Err(idx) => match node.node_type() { - NodeType::Leaf => None, // Key not found. - NodeType::Internal => self.traverse(node.child(idx), key, f), - }, + Ok(idx) => Some(f(node, idx)), // Key found. + Err(_idx) if node.node_type() == NodeType::Leaf => None, // At leaf: key missing. + Err(idx) => self.traverse(node.child(idx), key, f), // Search in child. } } From ea65755c11cc7e3252b7b2f5b14bd009c81d2fc9 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Tue, 8 Apr 2025 10:37:39 +0200 Subject: [PATCH 10/12] . --- src/btreemap.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/btreemap.rs b/src/btreemap.rs index c203c3b7..af182b5f 100644 --- a/src/btreemap.rs +++ b/src/btreemap.rs @@ -534,9 +534,11 @@ where { let node = self.load_node(node_addr); match node.search(key) { - Ok(idx) => Some(f(node, idx)), // Key found. - Err(_idx) if node.node_type() == NodeType::Leaf => None, // At leaf: key missing. - Err(idx) => self.traverse(node.child(idx), key, f), // Search in child. + Ok(idx) => Some(f(node, idx)), // Key found. + Err(idx) => match node.node_type() { + NodeType::Leaf => None, // At leaf: key missing. + NodeType::Internal => self.traverse(node.child(idx), key, f), // Search in child. + }, } } From f8dd9d436ad2e0b1dcf8f1c20c575c9d72d77257 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Tue, 8 Apr 2025 10:40:21 +0200 Subject: [PATCH 11/12] . --- src/btreemap.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/btreemap.rs b/src/btreemap.rs index af182b5f..1a9c1ef8 100644 --- a/src/btreemap.rs +++ b/src/btreemap.rs @@ -527,17 +527,18 @@ where self.root_addr != NULL && self.traverse(self.root_addr, key, |_, _| ()).is_some() } - /// Recursively traverses from `node_addr`, calling `f` when `key` is found; stops at a leaf if not. + /// Recursively traverses from `node_addr`, invoking `f` if `key` is found. Stops at a leaf if not. fn traverse(&self, node_addr: Address, key: &K, f: F) -> Option where F: Fn(Node, usize) -> R + Clone, { let node = self.load_node(node_addr); + // Look for the key in the current node. match node.search(key) { - Ok(idx) => Some(f(node, idx)), // Key found. + Ok(idx) => Some(f(node, idx)), // Key found: apply f. Err(idx) => match node.node_type() { - NodeType::Leaf => None, // At leaf: key missing. - NodeType::Internal => self.traverse(node.child(idx), key, f), // Search in child. + NodeType::Leaf => None, // At a leaf: key not present. + NodeType::Internal => self.traverse(node.child(idx), key, f), // Continue search in child. }, } } From 945c3d5b5423f1174ec8ea95328a2ce195fb84a7 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Tue, 8 Apr 2025 10:41:04 +0200 Subject: [PATCH 12/12] . --- src/btreemap.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/btreemap.rs b/src/btreemap.rs index 1a9c1ef8..1ccb2700 100644 --- a/src/btreemap.rs +++ b/src/btreemap.rs @@ -535,7 +535,7 @@ where let node = self.load_node(node_addr); // Look for the key in the current node. match node.search(key) { - Ok(idx) => Some(f(node, idx)), // Key found: apply f. + Ok(idx) => Some(f(node, idx)), // Key found: apply `f`. Err(idx) => match node.node_type() { NodeType::Leaf => None, // At a leaf: key not present. NodeType::Internal => self.traverse(node.child(idx), key, f), // Continue search in child.