Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: BTreeMap: make range().count() more efficient #190

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
2c8e0aa
.
dragoljub-duric Feb 7, 2024
d205b6e
fix clippy
dragoljub-duric Feb 7, 2024
38dc7b5
Add benchmark
dragoljub-duric Feb 7, 2024
1858ceb
Fix next_without_loading_value().
dragoljub-duric Feb 8, 2024
edce423
Make iter_count_test a proptest.
dragoljub-duric Feb 8, 2024
c1c0a56
.
dragoljub-duric Feb 8, 2024
e549d7f
.
dragoljub-duric Feb 8, 2024
e58b408
.
dragoljub-duric Feb 8, 2024
9fad566
fix proptest
dragoljub-duric Feb 8, 2024
1e17542
Recatro iter without code duplication.
dragoljub-duric Feb 8, 2024
aa45bf3
.
dragoljub-duric Feb 9, 2024
b328ff6
.
dragoljub-duric Feb 9, 2024
404a481
Update benchmarks/src/btreemap.rs
dragoljub-duric Feb 9, 2024
86c99bc
Apply comments
dragoljub-duric Feb 9, 2024
409270d
Merge branch 'Add_iter_count_benches' into EXC-1548-b-tree-map-make-r…
dragoljub-duric Feb 9, 2024
6c9acf2
Rerun benches
dragoljub-duric Feb 9, 2024
2fd5bc9
Merge branch 'EXC-1553-upgrade-rust-version-in-stable-structures-ci' …
dragoljub-duric Feb 9, 2024
e90542f
Merge branch 'Add_iter_count_benches' into EXC-1548-b-tree-map-make-r…
dragoljub-duric Feb 9, 2024
c0de7f2
.
dragoljub-duric Feb 12, 2024
84c70c4
refactor next_internal to avoid cloning
dragoljub-duric Feb 12, 2024
f56cfc8
remove Clone
dragoljub-duric Feb 12, 2024
ea9dc95
Merge branch 'main' into EXC-1548-b-tree-map-make-range-count-more-ef…
dragoljub-duric Feb 12, 2024
b1640ff
cnabench_results from main
dragoljub-duric Feb 13, 2024
358f4ab
.
dragoljub-duric Feb 13, 2024
9ca6ba6
move test to btreemap/proptests.rs
dragoljub-duric Feb 13, 2024
d032bb5
rerun canbench
dragoljub-duric Feb 13, 2024
83a6418
clippy
dragoljub-duric Feb 13, 2024
46d9893
Adress comment
dragoljub-duric Feb 13, 2024
55d9b22
Update src/btreemap/iter.rs
dragoljub-duric Feb 14, 2024
b658417
Apply suggestion from comments.
dragoljub-duric Feb 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions canbench_results.yml
Original file line number Diff line number Diff line change
Expand Up @@ -367,13 +367,13 @@ benches:
scopes: {}
btreemap_iter_count_10mib_values:
total:
instructions: 23609229
instructions: 497381
heap_increase: 0
stable_memory_increase: 0
scopes: {}
btreemap_iter_count_small_values:
total:
instructions: 25579067
instructions: 10116842
heap_increase: 0
stable_memory_increase: 0
scopes: {}
Expand Down
81 changes: 49 additions & 32 deletions src/btreemap/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,19 +71,13 @@ where
range,
}
}
}

impl<K, V, M> Iterator for Iter<'_, K, V, M>
where
K: Storable + Ord + Clone,
V: Storable,
M: Memory,
{
type Item = (K, V);

fn next(&mut self) -> Option<Self::Item> {
match self.cursors.pop() {
Some(Cursor::Address(address)) => {
// Iterates to find the next element in the requested range.
// If it exists, `map` is applied to that element and the result is returned.
fn next_map<T, F: Fn(&Node<K>, usize) -> T>(&mut self, map: F) -> Option<T> {
dragoljub-duric marked this conversation as resolved.
Show resolved Hide resolved
// If the cursors are empty. Iteration is complete.
match self.cursors.pop()? {
Cursor::Address(address) => {
if address != NULL {
// Load the node at the given address, and add it to the cursors.
let node = self.map.load_node(address);
Expand All @@ -97,13 +91,13 @@ where
node,
});
}
self.next()
self.next_map(map)
}

Some(Cursor::Node {
Cursor::Node {
node,
next: Index::Child(child_idx),
}) => {
} => {
let child_address = node.child(child_idx);

// After iterating on the child, iterate on the next _entry_ in this node.
Expand All @@ -116,19 +110,26 @@ where
// Add the child to the top of the cursors to be iterated on first.
self.cursors.push(Cursor::Address(child_address));

self.next()
self.next_map(map)
}

Some(Cursor::Node {
Cursor::Node {
node,
next: Index::Entry(entry_idx),
}) => {
} => {
if entry_idx >= node.entries_len() {
// No more entries to iterate on in this node.
return self.next();
return self.next_map(map);
}

let (key, encoded_value) = node.entry(entry_idx, self.map.memory());
// If the key does not belong to the range, iteration stops.
if !self.range.contains(node.key(entry_idx)) {
// Clear all cursors to avoid needless work in subsequent calls.
self.cursors = vec![];
return None;
}

let res = map(&node, entry_idx);

// Add to the cursors the next element to be traversed.
self.cursors.push(Cursor::Node {
Expand All @@ -141,23 +142,39 @@ where
node,
});

// If the key does not belong to the range, iteration stops.
if !self.range.contains(&key) {
// Clear all cursors to avoid needless work in subsequent calls.
self.cursors = vec![];
return None;
}

Some((key, V::from_bytes(Cow::Owned(encoded_value))))
}
None => {
// The cursors are empty. Iteration is complete.
None
Some(res)
}
}
}
}

impl<K, V, M> Iterator for Iter<'_, K, V, M>
where
K: Storable + Ord + Clone,
V: Storable,
M: Memory,
{
type Item = (K, V);

fn next(&mut self) -> Option<Self::Item> {
dragoljub-duric marked this conversation as resolved.
Show resolved Hide resolved
self.next_map(|node, entry_idx| {
let (key, encoded_value) = node.entry(entry_idx, self.map.memory());
(key, V::from_bytes(Cow::Owned(encoded_value)))
})
}

fn count(mut self) -> usize
where
Self: Sized,
{
let mut cnt = 0;
while self.next_map(|_, _| ()).is_some() {
cnt += 1;
}
cnt
}
}

#[cfg(test)]
mod test {
use super::*;
Expand Down
15 changes: 15 additions & 0 deletions src/btreemap/proptests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,21 @@ fn map_upper_bound_iter(#[strategy(pvec(0u64..u64::MAX -1 , 10..100))] keys: Vec
});
}

#[proptest(cases = 10)]
fn iter_count_test(#[strategy(0..250u8)] start: u8, #[strategy(#start..255u8)] end: u8) {
btree_test(|mut btree| {
for i in start..end {
assert_eq!(btree.insert(b(&[i]), b(&[])), None);
}

for i in start..end {
for j in i..end {
assert_eq!(btree.range(b(&[i])..b(&[j])).count(), (j - i) as usize);
}
}
});
}

// Given an operation, executes it on the given stable btreemap and standard btreemap, verifying
// that the result of the operation is equal in both btrees.
fn execute_operation<M: Memory>(
Expand Down
Loading