Skip to content

Commit d11a701

Browse files
committed
Auto merge of #338 - AngelicosPhosphoros:master, r=Amanieu
Add shortcircuit in iteration if we yielded all elements Current implementation works little slower than `set.iter().take(set.len())`. See my comment [here](rust-lang/rust#97215 (comment)). So why not avoid extra integer which added by `Iterator::take` if we can add limiting logic into our iterator itself? I don't really know how this change affects [reflect_toogle_full](https://github.com/rust-lang/hashbrown/blob/f3a9f211d06f78c5beb81ac22ea08fdc269e068f/src/raw/mod.rs#L2019) and implementation of [FusedIterator](https://github.com/rust-lang/hashbrown/blob/f3a9f211d06f78c5beb81ac22ea08fdc269e068f/src/raw/mod.rs#L2150). Maybe I should make inner iterator "jump" to the end of its memory block?
2 parents 3dbcdcc + ea120c7 commit d11a701

File tree

1 file changed

+42
-27
lines changed

1 file changed

+42
-27
lines changed

src/raw/mod.rs

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1907,6 +1907,32 @@ impl<T> RawIterRange<T> {
19071907
}
19081908
}
19091909
}
1910+
1911+
/// # Safety
1912+
/// If DO_CHECK_PTR_RANGE is false, caller must ensure that we never try to iterate
1913+
/// after yielding all elements.
1914+
#[cfg_attr(feature = "inline-more", inline)]
1915+
unsafe fn next_impl<const DO_CHECK_PTR_RANGE: bool>(&mut self) -> Option<Bucket<T>> {
1916+
loop {
1917+
if let Some(index) = self.current_group.lowest_set_bit() {
1918+
self.current_group = self.current_group.remove_lowest_bit();
1919+
return Some(self.data.next_n(index));
1920+
}
1921+
1922+
if DO_CHECK_PTR_RANGE && self.next_ctrl >= self.end {
1923+
return None;
1924+
}
1925+
1926+
// We might read past self.end up to the next group boundary,
1927+
// but this is fine because it only occurs on tables smaller
1928+
// than the group size where the trailing control bytes are all
1929+
// EMPTY. On larger tables self.end is guaranteed to be aligned
1930+
// to the group size (since tables are power-of-two sized).
1931+
self.current_group = Group::load_aligned(self.next_ctrl).match_full();
1932+
self.data = self.data.next_n(Group::WIDTH);
1933+
self.next_ctrl = self.next_ctrl.add(Group::WIDTH);
1934+
}
1935+
}
19101936
}
19111937

19121938
// We make raw iterators unconditionally Send and Sync, and let the PhantomData
@@ -1932,25 +1958,8 @@ impl<T> Iterator for RawIterRange<T> {
19321958
#[cfg_attr(feature = "inline-more", inline)]
19331959
fn next(&mut self) -> Option<Bucket<T>> {
19341960
unsafe {
1935-
loop {
1936-
if let Some(index) = self.current_group.lowest_set_bit() {
1937-
self.current_group = self.current_group.remove_lowest_bit();
1938-
return Some(self.data.next_n(index));
1939-
}
1940-
1941-
if self.next_ctrl >= self.end {
1942-
return None;
1943-
}
1944-
1945-
// We might read past self.end up to the next group boundary,
1946-
// but this is fine because it only occurs on tables smaller
1947-
// than the group size where the trailing control bytes are all
1948-
// EMPTY. On larger tables self.end is guaranteed to be aligned
1949-
// to the group size (since tables are power-of-two sized).
1950-
self.current_group = Group::load_aligned(self.next_ctrl).match_full();
1951-
self.data = self.data.next_n(Group::WIDTH);
1952-
self.next_ctrl = self.next_ctrl.add(Group::WIDTH);
1953-
}
1961+
// SAFETY: We set checker flag to true.
1962+
self.next_impl::<true>()
19541963
}
19551964
}
19561965

@@ -2128,16 +2137,22 @@ impl<T> Iterator for RawIter<T> {
21282137

21292138
#[cfg_attr(feature = "inline-more", inline)]
21302139
fn next(&mut self) -> Option<Bucket<T>> {
2131-
if let Some(b) = self.iter.next() {
2140+
// Inner iterator iterates over buckets
2141+
// so it can do unnecessary work if we already yielded all items.
2142+
if self.items == 0 {
2143+
return None;
2144+
}
2145+
2146+
let nxt = unsafe {
2147+
// SAFETY: We check number of items to yield using `items` field.
2148+
self.iter.next_impl::<false>()
2149+
};
2150+
2151+
if nxt.is_some() {
21322152
self.items -= 1;
2133-
Some(b)
2134-
} else {
2135-
// We don't check against items == 0 here to allow the
2136-
// compiler to optimize away the item count entirely if the
2137-
// iterator length is never queried.
2138-
debug_assert_eq!(self.items, 0);
2139-
None
21402153
}
2154+
2155+
nxt
21412156
}
21422157

21432158
#[inline]

0 commit comments

Comments
 (0)