Skip to content

Commit a57d5d7

Browse files
committed
Auto merge of #52250 - nnethercote:no-SparseBitMatrix, r=nikomatsakis
Speed up `SparseBitMatrix` use in `RegionValues`. In practice, these matrices range from 10% to 90%+ full once they are filled in, so the dense representation is better. This reduces the runtime of Check Nll builds of `inflate` by 32%, and several other benchmarks by 1--5%. It also increases max-rss of `clap-rs` by 30% and a couple of others by up to 5%, while decreasing max-rss of `coercions` by 14%. I think the speed-ups justify the max-rss increases. r? @nikomatsakis
2 parents 0ad6179 + 798209e commit a57d5d7

File tree

2 files changed

+37
-212
lines changed

2 files changed

+37
-212
lines changed

src/librustc_data_structures/bitvec.rs

+28-208
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
// except according to those terms.
1010

1111
use indexed_vec::{Idx, IndexVec};
12-
use std::collections::btree_map::Entry;
13-
use std::collections::BTreeMap;
1412
use std::iter::FromIterator;
1513
use std::marker::PhantomData;
1614

@@ -72,7 +70,7 @@ impl BitVector {
7270
}
7371

7472
#[inline]
75-
pub fn insert_all(&mut self, all: &BitVector) -> bool {
73+
pub fn merge(&mut self, all: &BitVector) -> bool {
7674
assert!(self.data.len() == all.data.len());
7775
let mut changed = false;
7876
for (i, j) in self.data.iter_mut().zip(&all.data) {
@@ -271,20 +269,26 @@ impl BitMatrix {
271269
}
272270
}
273271

272+
/// A moderately sparse bit matrix: rows are appended lazily, but columns
273+
/// within appended rows are instantiated fully upon creation.
274274
#[derive(Clone, Debug)]
275275
pub struct SparseBitMatrix<R, C>
276276
where
277277
R: Idx,
278278
C: Idx,
279279
{
280-
vector: IndexVec<R, SparseBitSet<C>>,
280+
columns: usize,
281+
vector: IndexVec<R, BitVector>,
282+
marker: PhantomData<C>,
281283
}
282284

283285
impl<R: Idx, C: Idx> SparseBitMatrix<R, C> {
284286
/// Create a new empty sparse bit matrix with no rows or columns.
285-
pub fn new() -> Self {
287+
pub fn new(columns: usize) -> Self {
286288
Self {
289+
columns,
287290
vector: IndexVec::new(),
291+
marker: PhantomData,
288292
}
289293
}
290294

@@ -293,23 +297,18 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> {
293297
///
294298
/// Returns true if this changed the matrix, and false otherwise.
295299
pub fn add(&mut self, row: R, column: C) -> bool {
296-
debug!(
297-
"add(row={:?}, column={:?}, current_len={})",
298-
row,
299-
column,
300-
self.vector.len()
301-
);
300+
let columns = self.columns;
302301
self.vector
303-
.ensure_contains_elem(row, || SparseBitSet::new());
304-
self.vector[row].insert(column)
302+
.ensure_contains_elem(row, || BitVector::new(columns));
303+
self.vector[row].insert(column.index())
305304
}
306305

307306
/// Do the bits from `row` contain `column`? Put another way, is
308307
/// the matrix cell at `(row, column)` true? Put yet another way,
309308
/// if the matrix represents (transitive) reachability, can
310309
/// `row` reach `column`?
311310
pub fn contains(&self, row: R, column: C) -> bool {
312-
self.vector.get(row).map_or(false, |r| r.contains(column))
311+
self.vector.get(row).map_or(false, |r| r.contains(column.index()))
313312
}
314313

315314
/// Add the bits from row `read` to the bits from row `write`,
@@ -320,39 +319,23 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> {
320319
/// `write` can reach everything that `read` can (and
321320
/// potentially more).
322321
pub fn merge(&mut self, read: R, write: R) -> bool {
323-
let mut changed = false;
324-
325-
if read != write {
326-
if self.vector.get(read).is_some() {
327-
self.vector
328-
.ensure_contains_elem(write, || SparseBitSet::new());
329-
let (bit_set_read, bit_set_write) = self.vector.pick2_mut(read, write);
330-
331-
for read_chunk in bit_set_read.chunks() {
332-
changed = changed | bit_set_write.insert_chunk(read_chunk).any();
333-
}
334-
}
322+
if read == write || self.vector.get(read).is_none() {
323+
return false;
335324
}
336325

337-
changed
326+
let columns = self.columns;
327+
self.vector
328+
.ensure_contains_elem(write, || BitVector::new(columns));
329+
let (bitvec_read, bitvec_write) = self.vector.pick2_mut(read, write);
330+
bitvec_write.merge(bitvec_read)
338331
}
339332

340333
/// Merge a row, `from`, into the `into` row.
341-
pub fn merge_into(&mut self, into: R, from: &SparseBitSet<C>) -> bool {
334+
pub fn merge_into(&mut self, into: R, from: &BitVector) -> bool {
335+
let columns = self.columns;
342336
self.vector
343-
.ensure_contains_elem(into, || SparseBitSet::new());
344-
self.vector[into].insert_from(from)
345-
}
346-
347-
/// True if `sub` is a subset of `sup`
348-
pub fn is_subset(&self, sub: R, sup: R) -> bool {
349-
sub == sup || {
350-
let bit_set_sub = &self.vector[sub];
351-
let bit_set_sup = &self.vector[sup];
352-
bit_set_sub
353-
.chunks()
354-
.all(|read_chunk| read_chunk.bits_eq(bit_set_sup.contains_chunk(read_chunk)))
355-
}
337+
.ensure_contains_elem(into, || BitVector::new(columns));
338+
self.vector[into].merge(from)
356339
}
357340

358341
/// Number of elements in the matrix.
@@ -363,178 +346,15 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> {
363346
/// Iterates through all the columns set to true in a given row of
364347
/// the matrix.
365348
pub fn iter<'a>(&'a self, row: R) -> impl Iterator<Item = C> + 'a {
366-
self.vector.get(row).into_iter().flat_map(|r| r.iter())
349+
self.vector.get(row).into_iter().flat_map(|r| r.iter().map(|n| C::new(n)))
367350
}
368351

369352
/// Iterates through each row and the accompanying bit set.
370-
pub fn iter_enumerated<'a>(&'a self) -> impl Iterator<Item = (R, &'a SparseBitSet<C>)> + 'a {
353+
pub fn iter_enumerated<'a>(&'a self) -> impl Iterator<Item = (R, &'a BitVector)> + 'a {
371354
self.vector.iter_enumerated()
372355
}
373356
}
374357

375-
#[derive(Clone, Debug)]
376-
pub struct SparseBitSet<I: Idx> {
377-
chunk_bits: BTreeMap<u32, Word>,
378-
_marker: PhantomData<I>,
379-
}
380-
381-
#[derive(Copy, Clone)]
382-
pub struct SparseChunk<I> {
383-
key: u32,
384-
bits: Word,
385-
_marker: PhantomData<I>,
386-
}
387-
388-
impl<I: Idx> SparseChunk<I> {
389-
#[inline]
390-
pub fn one(index: I) -> Self {
391-
let index = index.index();
392-
let key_usize = index / 128;
393-
let key = key_usize as u32;
394-
assert_eq!(key as usize, key_usize);
395-
SparseChunk {
396-
key,
397-
bits: 1 << (index % 128),
398-
_marker: PhantomData,
399-
}
400-
}
401-
402-
#[inline]
403-
pub fn any(&self) -> bool {
404-
self.bits != 0
405-
}
406-
407-
#[inline]
408-
pub fn bits_eq(&self, other: SparseChunk<I>) -> bool {
409-
self.bits == other.bits
410-
}
411-
412-
pub fn iter(&self) -> impl Iterator<Item = I> {
413-
let base = self.key as usize * 128;
414-
let mut bits = self.bits;
415-
(0..128)
416-
.map(move |i| {
417-
let current_bits = bits;
418-
bits >>= 1;
419-
(i, current_bits)
420-
})
421-
.take_while(|&(_, bits)| bits != 0)
422-
.filter_map(move |(i, bits)| {
423-
if (bits & 1) != 0 {
424-
Some(I::new(base + i))
425-
} else {
426-
None
427-
}
428-
})
429-
}
430-
}
431-
432-
impl<I: Idx> SparseBitSet<I> {
433-
pub fn new() -> Self {
434-
SparseBitSet {
435-
chunk_bits: BTreeMap::new(),
436-
_marker: PhantomData,
437-
}
438-
}
439-
440-
pub fn capacity(&self) -> usize {
441-
self.chunk_bits.len() * 128
442-
}
443-
444-
/// Returns a chunk containing only those bits that are already
445-
/// present. You can test therefore if `self` contains all the
446-
/// bits in chunk already by doing `chunk ==
447-
/// self.contains_chunk(chunk)`.
448-
pub fn contains_chunk(&self, chunk: SparseChunk<I>) -> SparseChunk<I> {
449-
SparseChunk {
450-
bits: self.chunk_bits
451-
.get(&chunk.key)
452-
.map_or(0, |bits| bits & chunk.bits),
453-
..chunk
454-
}
455-
}
456-
457-
/// Modifies `self` to contain all the bits from `chunk` (in
458-
/// addition to any pre-existing bits); returns a new chunk that
459-
/// contains only those bits that were newly added. You can test
460-
/// if anything was inserted by invoking `any()` on the returned
461-
/// value.
462-
pub fn insert_chunk(&mut self, chunk: SparseChunk<I>) -> SparseChunk<I> {
463-
if chunk.bits == 0 {
464-
return chunk;
465-
}
466-
let bits = self.chunk_bits.entry(chunk.key).or_insert(0);
467-
let old_bits = *bits;
468-
let new_bits = old_bits | chunk.bits;
469-
*bits = new_bits;
470-
let changed = new_bits ^ old_bits;
471-
SparseChunk {
472-
bits: changed,
473-
..chunk
474-
}
475-
}
476-
477-
/// Insert into bit set from another bit set.
478-
pub fn insert_from(&mut self, from: &SparseBitSet<I>) -> bool {
479-
let mut changed = false;
480-
for read_chunk in from.chunks() {
481-
changed = changed | self.insert_chunk(read_chunk).any();
482-
}
483-
changed
484-
}
485-
486-
pub fn remove_chunk(&mut self, chunk: SparseChunk<I>) -> SparseChunk<I> {
487-
if chunk.bits == 0 {
488-
return chunk;
489-
}
490-
let changed = match self.chunk_bits.entry(chunk.key) {
491-
Entry::Occupied(mut bits) => {
492-
let old_bits = *bits.get();
493-
let new_bits = old_bits & !chunk.bits;
494-
if new_bits == 0 {
495-
bits.remove();
496-
} else {
497-
bits.insert(new_bits);
498-
}
499-
new_bits ^ old_bits
500-
}
501-
Entry::Vacant(_) => 0,
502-
};
503-
SparseChunk {
504-
bits: changed,
505-
..chunk
506-
}
507-
}
508-
509-
pub fn clear(&mut self) {
510-
self.chunk_bits.clear();
511-
}
512-
513-
pub fn chunks<'a>(&'a self) -> impl Iterator<Item = SparseChunk<I>> + 'a {
514-
self.chunk_bits.iter().map(|(&key, &bits)| SparseChunk {
515-
key,
516-
bits,
517-
_marker: PhantomData,
518-
})
519-
}
520-
521-
pub fn contains(&self, index: I) -> bool {
522-
self.contains_chunk(SparseChunk::one(index)).any()
523-
}
524-
525-
pub fn insert(&mut self, index: I) -> bool {
526-
self.insert_chunk(SparseChunk::one(index)).any()
527-
}
528-
529-
pub fn remove(&mut self, index: I) -> bool {
530-
self.remove_chunk(SparseChunk::one(index)).any()
531-
}
532-
533-
pub fn iter<'a>(&'a self) -> impl Iterator<Item = I> + 'a {
534-
self.chunks().flat_map(|chunk| chunk.iter())
535-
}
536-
}
537-
538358
#[inline]
539359
fn words(elements: usize) -> usize {
540360
(elements + WORD_BITS - 1) / WORD_BITS
@@ -584,8 +404,8 @@ fn union_two_vecs() {
584404
assert!(!vec1.insert(3));
585405
assert!(vec2.insert(5));
586406
assert!(vec2.insert(64));
587-
assert!(vec1.insert_all(&vec2));
588-
assert!(!vec1.insert_all(&vec2));
407+
assert!(vec1.merge(&vec2));
408+
assert!(!vec1.merge(&vec2));
589409
assert!(vec1.contains(3));
590410
assert!(!vec1.contains(4));
591411
assert!(vec1.contains(5));

src/librustc_mir/borrow_check/nll/region_infer/values.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
use rustc::mir::{BasicBlock, Location, Mir};
1212
use rustc::ty::RegionVid;
13-
use rustc_data_structures::bitvec::{SparseBitMatrix, SparseBitSet};
13+
use rustc_data_structures::bitvec::{BitVector, SparseBitMatrix};
1414
use rustc_data_structures::indexed_vec::Idx;
1515
use rustc_data_structures::indexed_vec::IndexVec;
1616
use std::fmt::Debug;
@@ -55,6 +55,11 @@ impl RegionValueElements {
5555
}
5656
}
5757

58+
/// Total number of element indices that exist.
59+
crate fn num_elements(&self) -> usize {
60+
self.num_points + self.num_universal_regions
61+
}
62+
5863
/// Converts an element of a region value into a `RegionElementIndex`.
5964
crate fn index<T: ToElementIndex>(&self, elem: T) -> RegionElementIndex {
6065
elem.to_element_index(self)
@@ -186,7 +191,7 @@ impl<N: Idx> RegionValues<N> {
186191
crate fn new(elements: &Rc<RegionValueElements>) -> Self {
187192
Self {
188193
elements: elements.clone(),
189-
matrix: SparseBitMatrix::new(),
194+
matrix: SparseBitMatrix::new(elements.num_elements()),
190195
}
191196
}
192197

@@ -217,12 +222,12 @@ impl<N: Idx> RegionValues<N> {
217222
/// Iterates through each row and the accompanying bit set.
218223
pub fn iter_enumerated<'a>(
219224
&'a self
220-
) -> impl Iterator<Item = (N, &'a SparseBitSet<RegionElementIndex>)> + 'a {
225+
) -> impl Iterator<Item = (N, &'a BitVector)> + 'a {
221226
self.matrix.iter_enumerated()
222227
}
223228

224229
/// Merge a row, `from`, originating in another `RegionValues` into the `into` row.
225-
pub fn merge_into(&mut self, into: N, from: &SparseBitSet<RegionElementIndex>) -> bool {
230+
pub fn merge_into(&mut self, into: N, from: &BitVector) -> bool {
226231
self.matrix.merge_into(into, from)
227232
}
228233

0 commit comments

Comments
 (0)