Skip to content

Commit 6416079

Browse files
authored
Rollup merge of rust-lang#65425 - nnethercote:optimize-BitIter, r=zackmdavis
Optimize `BitIter` A minor speed improvement.
2 parents 2dac8b9 + 60851b0 commit 6416079

File tree

1 file changed

+42
-21
lines changed

1 file changed

+42
-21
lines changed

src/librustc_index/bit_set.rs

+42-21
Original file line numberDiff line numberDiff line change
@@ -168,11 +168,7 @@ impl<T: Idx> BitSet<T> {
168168
/// Iterates over the indices of set bits in a sorted order.
169169
#[inline]
170170
pub fn iter(&self) -> BitIter<'_, T> {
171-
BitIter {
172-
cur: None,
173-
iter: self.words.iter().enumerate(),
174-
marker: PhantomData,
175-
}
171+
BitIter::new(&self.words)
176172
}
177173

178174
/// Duplicates the set as a hybrid set.
@@ -291,26 +287,55 @@ impl<T: Idx> ToString for BitSet<T> {
291287
}
292288

293289
pub struct BitIter<'a, T: Idx> {
294-
cur: Option<(Word, usize)>,
295-
iter: iter::Enumerate<slice::Iter<'a, Word>>,
290+
/// A copy of the current word, but with any already-visited bits cleared.
291+
/// (This lets us use `trailing_zeros()` to find the next set bit.) When it
292+
/// is reduced to 0, we move onto the next word.
293+
word: Word,
294+
295+
/// The offset (measured in bits) of the current word.
296+
offset: usize,
297+
298+
/// Underlying iterator over the words.
299+
iter: slice::Iter<'a, Word>,
300+
296301
marker: PhantomData<T>
297302
}
298303

304+
impl<'a, T: Idx> BitIter<'a, T> {
305+
#[inline]
306+
fn new(words: &'a [Word]) -> BitIter<'a, T> {
307+
// We initialize `word` and `offset` to degenerate values. On the first
308+
// call to `next()` we will fall through to getting the first word from
309+
// `iter`, which sets `word` to the first word (if there is one) and
310+
// `offset` to 0. Doing it this way saves us from having to maintain
311+
// additional state about whether we have started.
312+
BitIter {
313+
word: 0,
314+
offset: std::usize::MAX - (WORD_BITS - 1),
315+
iter: words.iter(),
316+
marker: PhantomData,
317+
}
318+
}
319+
}
320+
299321
impl<'a, T: Idx> Iterator for BitIter<'a, T> {
300322
type Item = T;
301323
fn next(&mut self) -> Option<T> {
302324
loop {
303-
if let Some((ref mut word, offset)) = self.cur {
304-
let bit_pos = word.trailing_zeros() as usize;
305-
if bit_pos != WORD_BITS {
306-
let bit = 1 << bit_pos;
307-
*word ^= bit;
308-
return Some(T::new(bit_pos + offset))
309-
}
325+
if self.word != 0 {
326+
// Get the position of the next set bit in the current word,
327+
// then clear the bit.
328+
let bit_pos = self.word.trailing_zeros() as usize;
329+
let bit = 1 << bit_pos;
330+
self.word ^= bit;
331+
return Some(T::new(bit_pos + self.offset))
310332
}
311333

312-
let (i, word) = self.iter.next()?;
313-
self.cur = Some((*word, WORD_BITS * i));
334+
// Move onto the next word. `wrapping_add()` is needed to handle
335+
// the degenerate initial value given to `offset` in `new()`.
336+
let word = self.iter.next()?;
337+
self.word = *word;
338+
self.offset = self.offset.wrapping_add(WORD_BITS);
314339
}
315340
}
316341
}
@@ -851,11 +876,7 @@ impl<R: Idx, C: Idx> BitMatrix<R, C> {
851876
pub fn iter(&self, row: R) -> BitIter<'_, C> {
852877
assert!(row.index() < self.num_rows);
853878
let (start, end) = self.range(row);
854-
BitIter {
855-
cur: None,
856-
iter: self.words[start..end].iter().enumerate(),
857-
marker: PhantomData,
858-
}
879+
BitIter::new(&self.words[start..end])
859880
}
860881

861882
/// Returns the number of elements in `row`.

0 commit comments

Comments
 (0)