feat!: add lazy BTreeMap iterator for improved performance (breaking API change)#375
feat!: add lazy BTreeMap iterator for improved performance (breaking API change)#375
Conversation
|
|
|
|
|
|
…res into maksym/entry-iter
There was a problem hiding this comment.
Pull Request Overview
This PR introduces lazy deserialization for BTreeMap iteration by wrapping entries in a LazyEntry that defers key/value cloning until accessed, at the cost of a breaking change to the iterator’s Item type.
- Introduce
LazyEntryand switchIter::Itemfrom(K, V)toLazyEntry<'_, K, V, M> - Replace
Box<Node<K>>withRc<Node<K>>to share node ownership between iterators and entries - Update all iterator consumers (tests, benchmarks, and internal APIs) to call
.into_pair(),.key(), or.value()as needed
Reviewed Changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tests/api_conformance.rs | Updated stable.iter() and stable.range() calls to use into_pair() |
| src/btreeset.rs | Adapted next() to call entry.key().clone() on LazyEntry |
| src/btreemap/proptests.rs | Updated property tests to map LazyEntry into pairs with into_pair() |
| src/btreemap/iter.rs | Swapped Box<Node<K>> for Rc<Node<K>>, added LazyEntry and updated iterator logic |
| src/btreemap.rs | Revised docs and public API (added collect_e helper, updated examples) |
| benchmarks/nns/src/nns_vote_cascading.rs | Changed .map() closures to use entry.key() / entry.value() |
| benchmarks/nns/canbench_results.yml | Updated benchmark instruction counts |
| benchmarks/btreeset/canbench_results.yml | Updated benchmark instruction counts |
| benchmarks/btreemap/src/main.rs | Updated benchmark closures to use entry.key() / entry.value() |
| benchmarks/btreemap/canbench_results.yml | Updated benchmark instruction counts |
Comments suppressed due to low confidence (3)
src/btreemap/iter.rs:450
- [nitpick]
LazyEntryis a public type but lacks documentation comments explaining its purpose and usage. Please add doc comments to describe the struct and its methods (key,value,into_pair).
pub struct LazyEntry<'a, K, V, M>
src/btreemap.rs:1164
- [nitpick] Consider re-exporting
LazyEntryin thebtreemapmodule (e.g.pub use iter::LazyEntry;) so consumers ofmap.iter()can easily import and discover the entry type without reaching into internal modules.
pub fn iter(&self) -> Iter<'_, K, V, M> {
src/btreemap.rs:1374
- [nitpick] The helper function name
collect_eis not very descriptive—consider renaming it to something likecollect_pairsorcollect_entriesto clarify that it transformsLazyEntryinto(K, V)pairs.
fn collect_e<'a, K, V, M>(it: impl Iterator<Item = LazyEntry<'a, K, V, M>>) -> Vec<(K, V)>
…res into maksym/entry-iter
berestovskyy
left a comment
There was a problem hiding this comment.
Looks good, thanks!
This PR changes the
BTreeMapiterator'sItemtype from(K, V)toLazyEntryto enable lazy evaluation of keys and values.This is a BREAKING API CHANGE, as it deviates from the canonical
(K, V)pattern ofstd::collections::BTreeMap.The key motivation is performance: previously, each
next()call eagerly deserialized and cloned key and value, even if the consumer discarded them. WithLazyEntry, key and value access is deferred until explicitly requested, significantly reducing unnecessary allocations and cloning in cases where only part of the entry is needed or iteration is short-circuited.Code change summary:
LazyEntrywith on-demand access to key/value.Box<Node<K>>withRc<Node<K>>to allow shared ownership between iterator and entries..into_pair()where eager materialization is needed.Performance change summary:
BTreeSet: 68 improvements, no regressions. Median instruction count dropped by -6.61%, best case -17.24%BTreeMap: 1 small regression (+2.09%), 10 improvements, with up to -98.33% fewer instructions in range-heavy cases