Skip to content

Commit 4731326

Browse files
committed
Auto merge of #157024 - Jamesbarford:experiment/bitset-perf, r=<try>
Experiment with lazy allocation for empty DenseBitSet storage
2 parents ddc1a64 + ac283ed commit 4731326

2 files changed

Lines changed: 194 additions & 44 deletions

File tree

compiler/rustc_index/src/bit_set.rs

Lines changed: 139 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::hash::{Hash, Hasher};
12
use std::marker::PhantomData;
23
use std::ops::{Bound, Range, RangeBounds};
34
use std::rc::Rc;
@@ -112,10 +113,10 @@ macro_rules! bit_relations_inherent_impls {
112113
/// will panic if the bitsets have differing domain sizes.
113114
///
114115
#[cfg_attr(feature = "nightly", derive(Decodable_NoContext, Encodable_NoContext))]
115-
#[derive(Eq, PartialEq, Hash)]
116116
pub struct DenseBitSet<T> {
117117
domain_size: usize,
118-
words: Vec<Word>,
118+
// Lazily allocated. `None` represents a full set of zero words.
119+
words: Option<Vec<Word>>,
119120
marker: PhantomData<T>,
120121
}
121122

@@ -124,61 +125,90 @@ impl<T> DenseBitSet<T> {
124125
pub fn domain_size(&self) -> usize {
125126
self.domain_size
126127
}
128+
129+
#[inline]
130+
fn num_words(&self) -> usize {
131+
num_words(self.domain_size)
132+
}
133+
134+
#[inline]
135+
fn words(&self) -> &[Word] {
136+
self.words.as_deref().unwrap_or(&[])
137+
}
138+
139+
#[inline]
140+
fn ensure_words(&mut self) -> &mut Vec<Word> {
141+
let num_words = self.num_words();
142+
self.words.get_or_insert_with(|| vec![0; num_words])
143+
}
144+
145+
#[inline]
146+
fn word(&self, word_index: usize) -> Word {
147+
self.words().get(word_index).copied().unwrap_or(0)
148+
}
127149
}
128150

129151
impl<T: Idx> DenseBitSet<T> {
130152
/// Creates a new, empty bitset with a given `domain_size`.
131153
#[inline]
132154
pub fn new_empty(domain_size: usize) -> DenseBitSet<T> {
133-
let num_words = num_words(domain_size);
134-
DenseBitSet { domain_size, words: vec![0; num_words], marker: PhantomData }
155+
DenseBitSet { domain_size, words: None, marker: PhantomData }
135156
}
136157

137158
/// Creates a new, filled bitset with a given `domain_size`.
138159
#[inline]
139160
pub fn new_filled(domain_size: usize) -> DenseBitSet<T> {
140161
let num_words = num_words(domain_size);
141-
let mut result =
142-
DenseBitSet { domain_size, words: vec![!0; num_words], marker: PhantomData };
162+
let words = if num_words == 0 { None } else { Some(vec![!0; num_words]) };
163+
let mut result = DenseBitSet { domain_size, words, marker: PhantomData };
143164
result.clear_excess_bits();
144165
result
145166
}
146167

147168
/// Clear all elements.
148169
#[inline]
149170
pub fn clear(&mut self) {
150-
self.words.fill(0);
171+
if let Some(words) = &mut self.words {
172+
words.fill(0);
173+
}
151174
}
152175

153176
/// Clear excess bits in the final word.
154177
fn clear_excess_bits(&mut self) {
155-
clear_excess_bits_in_final_word(self.domain_size, &mut self.words);
178+
if let Some(words) = &mut self.words {
179+
clear_excess_bits_in_final_word(self.domain_size, words);
180+
}
156181
}
157182

158183
/// Count the number of set bits in the set.
159184
pub fn count(&self) -> usize {
160-
count_ones(&self.words)
185+
count_ones(self.words())
161186
}
162187

163188
/// Returns `true` if `self` contains `elem`.
164189
#[inline]
165190
pub fn contains(&self, elem: T) -> bool {
166191
assert!(elem.index() < self.domain_size);
167192
let (word_index, mask) = word_index_and_mask(elem);
168-
(self.words[word_index] & mask) != 0
193+
(self.word(word_index) & mask) != 0
169194
}
170195

171196
/// Is `self` is a (non-strict) superset of `other`?
172197
#[inline]
173198
pub fn superset(&self, other: &DenseBitSet<T>) -> bool {
174199
assert_eq!(self.domain_size, other.domain_size);
175-
self.words.iter().zip(&other.words).all(|(a, b)| (a & b) == *b)
200+
match (&self.words, &other.words) {
201+
(_, None) => true,
202+
(None, Some(other_words)) => other_words.iter().all(|&word| word == 0),
203+
(Some(_), Some(_)) => (0..self.num_words())
204+
.all(|index| (self.word(index) & other.word(index)) == other.word(index)),
205+
}
176206
}
177207

178208
/// Is the set empty?
179209
#[inline]
180210
pub fn is_empty(&self) -> bool {
181-
self.words.iter().all(|a| *a == 0)
211+
self.words().iter().all(|a| *a == 0)
182212
}
183213

184214
/// Insert `elem`. Returns whether the set has changed.
@@ -191,7 +221,7 @@ impl<T: Idx> DenseBitSet<T> {
191221
self.domain_size,
192222
);
193223
let (word_index, mask) = word_index_and_mask(elem);
194-
let word_ref = &mut self.words[word_index];
224+
let word_ref = &mut self.ensure_words()[word_index];
195225
let word = *word_ref;
196226
let new_word = word | mask;
197227
*word_ref = new_word;
@@ -206,28 +236,32 @@ impl<T: Idx> DenseBitSet<T> {
206236

207237
let (start_word_index, start_mask) = word_index_and_mask(start);
208238
let (end_word_index, end_mask) = word_index_and_mask(end);
239+
let words = self.ensure_words();
209240

210241
// Set all words in between start and end (exclusively of both).
211242
for word_index in (start_word_index + 1)..end_word_index {
212-
self.words[word_index] = !0;
243+
words[word_index] = !0;
213244
}
214245

215246
if start_word_index != end_word_index {
216247
// Start and end are in different words, so we handle each in turn.
217248
//
218249
// We set all leading bits. This includes the start_mask bit.
219-
self.words[start_word_index] |= !(start_mask - 1);
250+
words[start_word_index] |= !(start_mask - 1);
220251
// And all trailing bits (i.e. from 0..=end) in the end word,
221252
// including the end.
222-
self.words[end_word_index] |= end_mask | (end_mask - 1);
253+
words[end_word_index] |= end_mask | (end_mask - 1);
223254
} else {
224-
self.words[start_word_index] |= end_mask | (end_mask - start_mask);
255+
words[start_word_index] |= end_mask | (end_mask - start_mask);
225256
}
226257
}
227258

228259
/// Sets all bits to true.
229260
pub fn insert_all(&mut self) {
230-
self.words.fill(!0);
261+
if self.domain_size == 0 {
262+
return;
263+
}
264+
self.ensure_words().fill(!0);
231265
self.clear_excess_bits();
232266
}
233267

@@ -241,16 +275,16 @@ impl<T: Idx> DenseBitSet<T> {
241275
let (end_word_index, end_mask) = word_index_and_mask(end);
242276

243277
if start_word_index == end_word_index {
244-
self.words[start_word_index] & (end_mask | (end_mask - start_mask)) != 0
278+
self.word(start_word_index) & (end_mask | (end_mask - start_mask)) != 0
245279
} else {
246-
if self.words[start_word_index] & !(start_mask - 1) != 0 {
280+
if self.word(start_word_index) & !(start_mask - 1) != 0 {
247281
return true;
248282
}
249283

250284
let remaining = start_word_index + 1..end_word_index;
251285
if remaining.start <= remaining.end {
252-
self.words[remaining].iter().any(|&w| w != 0)
253-
|| self.words[end_word_index] & (end_mask | (end_mask - 1)) != 0
286+
self.words().get(remaining).is_some_and(|words| words.iter().any(|&w| w != 0))
287+
|| self.word(end_word_index) & (end_mask | (end_mask - 1)) != 0
254288
} else {
255289
false
256290
}
@@ -261,8 +295,11 @@ impl<T: Idx> DenseBitSet<T> {
261295
#[inline]
262296
pub fn remove(&mut self, elem: T) -> bool {
263297
assert!(elem.index() < self.domain_size);
298+
let Some(words) = &mut self.words else {
299+
return false;
300+
};
264301
let (word_index, mask) = word_index_and_mask(elem);
265-
let word_ref = &mut self.words[word_index];
302+
let word_ref = &mut words[word_index];
266303
let word = *word_ref;
267304
let new_word = word & !mask;
268305
*word_ref = new_word;
@@ -272,15 +309,18 @@ impl<T: Idx> DenseBitSet<T> {
272309
/// Iterates over the indices of set bits in a sorted order.
273310
#[inline]
274311
pub fn iter(&self) -> BitIter<'_, T> {
275-
BitIter::new(&self.words)
312+
BitIter::new(self.words())
276313
}
277314

278315
pub fn last_set_in(&self, range: impl RangeBounds<T>) -> Option<T> {
279316
let (start, end) = inclusive_start_end(range, self.domain_size)?;
317+
let Some(words) = &self.words else {
318+
return None;
319+
};
280320
let (start_word_index, _) = word_index_and_mask(start);
281321
let (end_word_index, end_mask) = word_index_and_mask(end);
282322

283-
let end_word = self.words[end_word_index] & (end_mask | (end_mask - 1));
323+
let end_word = words[end_word_index] & (end_mask | (end_mask - 1));
284324
if end_word != 0 {
285325
let pos = max_bit(end_word) + WORD_BITS * end_word_index;
286326
if start <= pos {
@@ -291,11 +331,10 @@ impl<T: Idx> DenseBitSet<T> {
291331
// We exclude end_word_index from the range here, because we don't want
292332
// to limit ourselves to *just* the last word: the bits set it in may be
293333
// after `end`, so it may not work out.
294-
if let Some(offset) =
295-
self.words[start_word_index..end_word_index].iter().rposition(|&w| w != 0)
334+
if let Some(offset) = words[start_word_index..end_word_index].iter().rposition(|&w| w != 0)
296335
{
297336
let word_idx = start_word_index + offset;
298-
let start_word = self.words[word_idx];
337+
let start_word = words[word_idx];
299338
let pos = max_bit(start_word) + WORD_BITS * word_idx;
300339
if start <= pos {
301340
return Some(T::new(pos));
@@ -319,7 +358,13 @@ impl<T: Idx> DenseBitSet<T> {
319358
// quickly and accurately detect whether the update changed anything.
320359
// But that's only worth doing if there's an actual use-case.
321360

322-
update_words(&mut self.words, &other.words, |a, b| a | !b);
361+
let other_words = other.words();
362+
let words = self.ensure_words();
363+
if other_words.is_empty() {
364+
words.fill(!0);
365+
} else {
366+
update_words(words, other_words, |a, b| a | !b);
367+
}
323368
// The bitwise update `a | !b` can result in the last word containing
324369
// out-of-domain bits, so we need to clear them.
325370
self.clear_excess_bits();
@@ -330,17 +375,38 @@ impl<T: Idx> DenseBitSet<T> {
330375
impl<T: Idx> BitRelations<DenseBitSet<T>> for DenseBitSet<T> {
331376
fn union(&mut self, other: &DenseBitSet<T>) -> bool {
332377
assert_eq!(self.domain_size, other.domain_size);
333-
update_words(&mut self.words, &other.words, |a, b| a | b)
378+
let Some(other_words) = &other.words else {
379+
return false;
380+
};
381+
let Some(words) = &mut self.words else {
382+
if other_words.iter().all(|&word| word == 0) {
383+
return false;
384+
}
385+
self.words = Some(other_words.clone());
386+
return true;
387+
};
388+
update_words(words, other_words, |a, b| a | b)
334389
}
335390

336391
fn subtract(&mut self, other: &DenseBitSet<T>) -> bool {
337392
assert_eq!(self.domain_size, other.domain_size);
338-
update_words(&mut self.words, &other.words, |a, b| a & !b)
393+
let (Some(words), Some(other_words)) = (&mut self.words, &other.words) else {
394+
return false;
395+
};
396+
update_words(words, other_words, |a, b| a & !b)
339397
}
340398

341399
fn intersect(&mut self, other: &DenseBitSet<T>) -> bool {
342400
assert_eq!(self.domain_size, other.domain_size);
343-
update_words(&mut self.words, &other.words, |a, b| a & b)
401+
let Some(words) = &mut self.words else {
402+
return false;
403+
};
404+
let Some(other_words) = &other.words else {
405+
let changed = words.iter().any(|&word| word != 0);
406+
words.fill(0);
407+
return changed;
408+
};
409+
update_words(words, other_words, |a, b| a & b)
344410
}
345411
}
346412

@@ -365,6 +431,27 @@ impl<T> Clone for DenseBitSet<T> {
365431
}
366432
}
367433

434+
impl<T> PartialEq for DenseBitSet<T> {
435+
fn eq(&self, other: &Self) -> bool {
436+
self.domain_size == other.domain_size
437+
&& (0..self.num_words()).all(|index| self.word(index) == other.word(index))
438+
}
439+
}
440+
441+
impl<T> Eq for DenseBitSet<T> {}
442+
443+
impl<T> Hash for DenseBitSet<T> {
444+
fn hash<H: Hasher>(&self, state: &mut H) {
445+
self.domain_size.hash(state);
446+
447+
let num_words = self.num_words();
448+
num_words.hash(state);
449+
for index in 0..num_words {
450+
self.word(index).hash(state);
451+
}
452+
}
453+
}
454+
368455
impl<T: Idx> fmt::Debug for DenseBitSet<T> {
369456
fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
370457
w.debug_list().entries(self.iter()).finish()
@@ -380,8 +467,8 @@ impl<T: Idx> ToString for DenseBitSet<T> {
380467

381468
// i tracks how many bits we have printed so far.
382469
let mut i = 0;
383-
for word in &self.words {
384-
let mut word = *word;
470+
for word_index in 0..self.num_words() {
471+
let mut word = self.word(word_index);
385472
for _ in 0..WORD_BYTES {
386473
// for each byte in `word`:
387474
let remain = self.domain_size - i;
@@ -1306,8 +1393,10 @@ impl<T: Idx> GrowableBitSet<T> {
13061393
}
13071394

13081395
let min_num_words = num_words(min_domain_size);
1309-
if self.bit_set.words.len() < min_num_words {
1310-
self.bit_set.words.resize(min_num_words, 0)
1396+
if let Some(words) = &mut self.bit_set.words {
1397+
if words.len() < min_num_words {
1398+
words.resize(min_num_words, 0)
1399+
}
13111400
}
13121401
}
13131402

@@ -1357,7 +1446,7 @@ impl<T: Idx> GrowableBitSet<T> {
13571446
#[inline]
13581447
pub fn contains(&self, elem: T) -> bool {
13591448
let (word_index, mask) = word_index_and_mask(elem);
1360-
self.bit_set.words.get(word_index).is_some_and(|word| (word & mask) != 0)
1449+
self.bit_set.words().get(word_index).is_some_and(|word| (word & mask) != 0)
13611450
}
13621451

13631452
#[inline]
@@ -1419,13 +1508,16 @@ impl<R: Idx, C: Idx> BitMatrix<R, C> {
14191508
pub fn from_row_n(row: &DenseBitSet<C>, num_rows: usize) -> BitMatrix<R, C> {
14201509
let num_columns = row.domain_size();
14211510
let words_per_row = num_words(num_columns);
1422-
assert_eq!(words_per_row, row.words.len());
1423-
BitMatrix {
1424-
num_rows,
1425-
num_columns,
1426-
words: iter::repeat_n(&row.words, num_rows).flatten().cloned().collect(),
1427-
marker: PhantomData,
1511+
let mut words = vec![0; num_rows * words_per_row];
1512+
if let Some(row_words) = &row.words {
1513+
assert_eq!(words_per_row, row_words.len());
1514+
if words_per_row > 0 {
1515+
for matrix_row in words.chunks_exact_mut(words_per_row) {
1516+
matrix_row.copy_from_slice(row_words);
1517+
}
1518+
}
14281519
}
1520+
BitMatrix { num_rows, num_columns, words, marker: PhantomData }
14291521
}
14301522

14311523
pub fn rows(&self) -> impl Iterator<Item = R> {
@@ -1517,8 +1609,11 @@ impl<R: Idx, C: Idx> BitMatrix<R, C> {
15171609
pub fn union_row_with(&mut self, with: &DenseBitSet<C>, write: R) -> bool {
15181610
assert!(write.index() < self.num_rows);
15191611
assert_eq!(with.domain_size(), self.num_columns);
1612+
let Some(with_words) = &with.words else {
1613+
return false;
1614+
};
15201615
let (write_start, write_end) = self.range(write);
1521-
update_words(&mut self.words[write_start..write_end], &with.words, |a, b| a | b)
1616+
update_words(&mut self.words[write_start..write_end], with_words, |a, b| a | b)
15221617
}
15231618

15241619
/// Sets every cell in `row` to true.

0 commit comments

Comments
 (0)