Skip to content

Commit

Permalink
perf: Optimize BTree search using binary search (#767)
Browse files Browse the repository at this point in the history
* perf: Optimize BTree search using binary search

This commit optimizes the search function in the BTree implementation
by replacing the linear search with a binary search algorithm. This
change significantly improves the search performance, especially for
large trees.

Implementation details:
- Modified the `search` method in the `BTree` struct
- Replaced the while loop with `binary_search` method on the `keys` vector

Complexity analysis:
- Previous implementation: O(n) per node, where n is the number of keys
- New implementation: O(log n) per node

Benchmark results:
- Environment: MacOS 14.5, Apple M1 Pro, 32 GB RAM
- Dataset: 1,000,000 random integers for insertion
- Search: 1,000,000 searches for the key 500,000
- Before:
  - Insertion: 3.002587333s
  - Search: 2.334683584s
- After:
  - Insertion: 2.998482583s
  - Search: 288.659458ms

Note: Insertion time remains largely unchanged, as expected. All
existing tests pass with the new implementation.

Benchmark code is not included in this commit.

* tests: expand existing `test_search`

---------

Co-authored-by: Piotr Idzik <[email protected]>
  • Loading branch information
ie-Yoshisaur and vil02 committed Aug 8, 2024
1 parent 65ca19d commit f08e936
Showing 1 changed file with 50 additions and 27 deletions.
77 changes: 50 additions & 27 deletions src/data_structures/b_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,20 +146,16 @@ where

pub fn search(&self, key: T) -> bool {
let mut current_node = &self.root;
let mut index: isize;
loop {
index = isize::try_from(current_node.keys.len()).ok().unwrap() - 1;
while index >= 0 && current_node.keys[index as usize] > key {
index -= 1;
}

let u_index: usize = usize::try_from(index + 1).ok().unwrap();
if index >= 0 && current_node.keys[u_index - 1] == key {
break true;
} else if current_node.is_leaf() {
break false;
} else {
current_node = &current_node.children[u_index];
match current_node.keys.binary_search(&key) {
Ok(_) => return true,
Err(index) => {
if current_node.is_leaf() {
return false;
} else {
current_node = &current_node.children[index];
}
}
}
}
}
Expand All @@ -169,19 +165,46 @@ where
mod test {
use super::BTree;

#[test]
fn test_search() {
let mut tree = BTree::new(2);
tree.insert(10);
tree.insert(20);
tree.insert(30);
tree.insert(5);
tree.insert(6);
tree.insert(7);
tree.insert(11);
tree.insert(12);
tree.insert(15);
assert!(tree.search(15));
assert!(!tree.search(16));
macro_rules! test_search {
($($name:ident: $number_of_children:expr,)*) => {
$(
#[test]
fn $name() {
let mut tree = BTree::new($number_of_children);
tree.insert(10);
tree.insert(20);
tree.insert(30);
tree.insert(5);
tree.insert(6);
tree.insert(7);
tree.insert(11);
tree.insert(12);
tree.insert(15);
assert!(!tree.search(4));
assert!(tree.search(5));
assert!(tree.search(6));
assert!(tree.search(7));
assert!(!tree.search(8));
assert!(!tree.search(9));
assert!(tree.search(10));
assert!(tree.search(11));
assert!(tree.search(12));
assert!(!tree.search(13));
assert!(!tree.search(14));
assert!(tree.search(15));
assert!(!tree.search(16));
}
)*
}
}

test_search! {
children_2: 2,
children_3: 3,
children_4: 4,
children_5: 5,
children_10: 10,
children_60: 60,
children_101: 101,
}
}

0 comments on commit f08e936

Please sign in to comment.