Skip to content

Commit 8877f4c

Browse files
committed
Improve union of sparse and dense hybrid set
This optimization speeds up the union of a hybrid bitset when that switches it from a sparse representation to a dense bitset. It now clones the dense bitset and integrate only the spare elements instead of densifying the sparse bitset, initializing all elements, and then a union on two dense bitset, touching all words a second time.
1 parent 7fa6e87 commit 8877f4c

File tree

1 file changed

+45
-4
lines changed

1 file changed

+45
-4
lines changed

Diff for: src/librustc_data_structures/bit_set.rs

+45-4
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,45 @@ impl<T: Idx> BitSet<T> {
181181
// Note: we currently don't bother trying to make a Sparse set.
182182
HybridBitSet::Dense(self.to_owned())
183183
}
184+
185+
/// Set `self = self | other`. In contrast to `union` returns `true` if the set contains at
186+
/// least one bit that is not in `other` (i.e. `other` is not a superset of `self`).
187+
///
188+
/// This is an optimization for union of a hybrid bitset.
189+
fn reverse_union_sparse(&mut self, sparse: &SparseBitSet<T>) -> bool {
190+
assert!(sparse.domain_size == self.domain_size);
191+
self.clear_excess_bits();
192+
193+
let mut not_already = false;
194+
// Index of the current word not yet merged.
195+
let mut current_index = 0;
196+
// Mask of bits that came from the sparse set in the current word.
197+
let mut new_bit_mask = 0;
198+
for (word_index, mask) in sparse.iter().map(|x| word_index_and_mask(*x)) {
199+
// Next bit is in a word not inspected yet.
200+
if word_index > current_index {
201+
self.words[current_index] |= new_bit_mask;
202+
// Were there any bits in the old word that did not occur in the sparse set?
203+
not_already |= (self.words[current_index] ^ new_bit_mask) != 0;
204+
// Check all words we skipped for any set bit.
205+
not_already |= self.words[current_index+1..word_index].iter().any(|&x| x != 0);
206+
// Update next word.
207+
current_index = word_index;
208+
// Reset bit mask, no bits have been merged yet.
209+
new_bit_mask = 0;
210+
}
211+
// Add bit and mark it as coming from the sparse set.
212+
// self.words[word_index] |= mask;
213+
new_bit_mask |= mask;
214+
}
215+
self.words[current_index] |= new_bit_mask;
216+
// Any bits in the last inspected word that were not in the sparse set?
217+
not_already |= (self.words[current_index] ^ new_bit_mask) != 0;
218+
// Any bits in the tail? Note `clear_excess_bits` before.
219+
not_already |= self.words[current_index+1..].iter().any(|&x| x != 0);
220+
221+
not_already
222+
}
184223
}
185224

186225
/// This is implemented by all the bitsets so that BitSet::union() can be
@@ -518,10 +557,12 @@ impl<T: Idx> HybridBitSet<T> {
518557
changed
519558
}
520559
HybridBitSet::Dense(other_dense) => {
521-
// `self` is sparse and `other` is dense. Densify
522-
// `self` and then do the bitwise union.
523-
let mut new_dense = self_sparse.to_dense();
524-
let changed = new_dense.union(other_dense);
560+
// `self` is sparse and `other` is dense. Clone the
561+
// other set and do the bitwise union with sparse
562+
// `self`. This avoids traversing the dense
563+
// representation twice.
564+
let mut new_dense = other_dense.clone();
565+
let changed = new_dense.reverse_union_sparse(self_sparse);
525566
*self = HybridBitSet::Dense(new_dense);
526567
changed
527568
}

0 commit comments

Comments
 (0)