-
Notifications
You must be signed in to change notification settings - Fork 12.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use sparse bitsets instead of dense ones for NLL results #48245
Conversation
d1d264a
to
de71da1
Compare
ee64b73
to
21aa4d8
Compare
Do we have data about how much this improves memory usage? And what its cost in execution time is, if any? |
@pnkfelix we should profile this 👍. I just started this as a wip thing and ended being a working solution quickly. I know the code is bad in some parts so I didn't bother until now to run benchs. I think I should do it now and then tidy up the code a bit 😃. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks nice!
@@ -260,7 +260,79 @@ impl BitMatrix { | |||
} | |||
} | |||
|
|||
#[derive(Clone)] | |||
#[derive(Clone, Debug)] | |||
pub struct SparseBitMatrix<I: Idx> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we are going to use a type I
, then I think this should be
SparseBitMatrix<R, C> where R: Idx, C: Idx {
vector: IndexVec<R, SparseBitSet<C>>
}
/// Create a new `rows x columns` matrix, initially empty. | ||
pub fn new(rows: usize, _columns: usize) -> SparseBitMatrix<I> { | ||
SparseBitMatrix { | ||
vector: vec![SparseBitSet::new(); rows], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and this would be IndexVec::from_elem_n(SparseBitSet::new(), rows)
/// `column` to the bitset for `row`. | ||
/// | ||
/// Returns true if this changed the matrix, and false otherwise. | ||
pub fn add(&mut self, row: usize, column: usize) -> bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and this would be row: R, column: C
/// variable. The columns consist of either universal regions or | ||
/// points in the CFG. | ||
#[derive(Clone)] | ||
pub(super) struct RegionValues { | ||
elements: Rc<RegionValueElements>, | ||
matrix: BitMatrix, | ||
matrix: SparseBitMatrix<usize>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
then this would be SparseBitMatrix<RegionVid, RegionElementIndex>
-- and some casting below would go away
21aa4d8
to
ffc357d
Compare
PR is now ready, I still need to run benchs, etc. Going to do that next. |
Using steps to reproduce here rust-lang/rust-memory-model#26 I see the following ...
As you can see we have a little gain here, unsure if it's what @nikomatsakis expected or if he expected more :/. I was algo trying to profile memory valgrind's massif, and I'm getting a segfault, unsure if you have made this run and how with rust ...
|
I did not expect a big impact here on this particular test case. But I think this will be more important on larger test cases. And the fact that we saw a small improvement means we're not paying a price for the improved memory usage. |
pub fn merge(&mut self, read: R, write: R) -> bool { | ||
let mut changed = false; | ||
|
||
let bit_set_read = self.vector[read].clone(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to avoid the clone, add a helper function like this one. Maybe we should add that somewhere else, if we don't have it already.
let bit_set_a = &self.vector[a]; | ||
let bit_set_b = &self.vector[b]; | ||
|
||
for a_val in bit_set_a.iter().filter(|x| bit_set_b.contains(*x)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I bet this method can just be removed. But if not, it could be made faster by adding a method intersection
on SparseBitset
. I guess it would work something like this:
let mut result = SparseBitset::new();
for (key, value) in self.map {
match other.map.get(key) {
None => { /* other has no values with these key */ }
Some(other_value) {
if value & other_value != 0 {
result.map.insert(key, value & other_value);
}
}
}
}
This of course generates a new sparse-bitset.
9305336
to
edc764e
Compare
impl<T> SliceExt<T> for [T] { | ||
type Item = T; | ||
|
||
fn pick2(&mut self, a: usize, b: usize) -> (&mut T, &mut T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add a comment here?
Something like:
Return mutable references to two distinct elements, a
and b
. Panics if a == b
.
Also, maybe we could call it get2_mut
? (similar to get_mut
?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At least I think we should add a _mut
suffix
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's not call it get2
since it doesn't return an Option
(as get
does).
pub trait SliceExt<T> { | ||
type Item; | ||
|
||
fn pick2(&mut self, a: usize, b: usize) -> (&mut T, &mut T); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Either:
- This should be
&mut Self::Item
and there is no need for theT
parameter
Or:
- Remove the
type Item
/// variable. The columns consist of either universal regions or | ||
/// points in the CFG. | ||
#[derive(Clone)] | ||
pub(super) struct RegionValues { | ||
elements: Rc<RegionValueElements>, | ||
matrix: BitMatrix, | ||
matrix: SparseBitMatrix<RegionVid, RegionElementIndex>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have to say, pushing up the "index types" is nice.
f3af3f7
to
d5811f7
Compare
@bors r+ |
📌 Commit d5811f7 has been approved by |
Could this use the chunked API from #47575 (comment)? |
if read != write { | ||
let (bit_set_read, bit_set_write) = self.vector.pick2_mut(read, write); | ||
|
||
for read_val in bit_set_read.iter() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this could be made faster with chunks, right? (this is going a bit at a time, right, not a word at a time?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I think @spastorino and I talked about that already at some point...)
// it does not always merge an entire row. That would | ||
// complicate causal tracking though. | ||
debug!( | ||
"add_universal_regions_outlived_by(from_region={:?}, to_region={:?})", | ||
from_region, | ||
to_region | ||
from_region, to_region | ||
); | ||
let mut changed = false; | ||
for elem in self.elements.all_universal_region_indices() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this could probably use chunks
@@ -238,7 +238,7 @@ impl RegionValues { | |||
where | |||
F: FnOnce(&CauseMap) -> Cause, | |||
{ | |||
if self.matrix.add(r.index(), i.index()) { | |||
if self.matrix.add(r, i) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we'd have to look at the callers of these add
methods...
(from IRC) |
@eddyb I'm inclined to land some variant of these changes, but otherwise let you revise and merge this with your own API when that is making progress. Seem reasonable? |
d5811f7
to
6a74615
Compare
@bors r+ |
📌 Commit 6a74615 has been approved by |
@nikomatsakis Yeah. Context is @spastorino explicitly asked for me to leave a comment on GitHub. |
As an aside, @spastorino if you format your pull request description with a line that says "Resolves #48170" instead of "this is for", GitHub will automagically link the PR and issue, and resolve the issue if/when the PR merges. Just a helpful usage hint. 😄 |
…tsakis Use sparse bitsets instead of dense ones for NLL results This is for rust-lang#48170. r? @nikomatsakis
This is for #48170.
r? @nikomatsakis