-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
html5ever in the rustc-perf repository is memory-intensive #52190
Conversation
This doesn't currently pass tests - the error is related to the SCC stuff and I've not had a great deal of time to look into it. |
The job Click to expand the log.
I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact |
The job Click to expand the log.
I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact |
The job Click to expand the log.
I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact |
☔ The latest upstream changes (presumably #51987) made this pull request unmergeable. Please resolve the merge conflicts. |
The job Click to expand the log.
I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact |
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.
First round of review, on the changes to Sparse
etc
@@ -293,15 +293,23 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> { | |||
/// | |||
/// Returns true if this changed the matrix, and false otherwise. | |||
pub fn add(&mut self, row: R, column: C) -> bool { | |||
self.vector[row].insert(column) | |||
if let None = self.vector.get(row) { | |||
self.vector.push(SparseBitSet::new()); |
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 doesn't seem right -- imagine that the vector starts out as empty, but we invoke add(2, 0)
-- then we have to push two things. I think the method you want is either resize
or -- perhaps mildly more efficient -- resize_with
. If index
where a plain vector, you would do something like this:
self.vector.resize_with(row + 1, || SparseBitSet::new())
However, that won't work because vector
is an IndexVec
and it doesn't offer resize_with
. You could use self.vector.raw.resize_with
, but better would be to add a method to IndexVec
:
impl<I, T> IndexVec<I, T> {
fn resize_to_elem(&mut self, elem: I, fill_value: impl FnMut() -> T) {
let min_new_len = elem.as_usize() + 1;
self.raw.resize_with(min_new_len, fill_value);
}
}
and then invoke self.vector.resize_to_elem(row, || SparseBitSet::new());
.
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.
Fixed this.
self.vector.push(SparseBitSet::new()); | ||
} | ||
|
||
if let Some(row) = self.vector.get_mut(row) { |
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.
Now this can be self.vector[row].insert(column)
as before
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.
Fixed this.
@@ -325,6 +333,14 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> { | |||
changed | |||
} | |||
|
|||
/// Merge a row, `from`, into the `into` row. | |||
pub fn merge_into(&mut self, into: R, from: &SparseBitSet<C>) -> bool { | |||
match self.vector.get_mut(into) { |
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 should again use the resize_to_elem
helper;
self.vector.resize_to_elem(into, || SparseBitSet::new());
self.vector[row].merge_into(from);
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.
Fixed this.
@@ -445,6 +471,15 @@ impl<I: Idx> SparseBitSet<I> { | |||
} | |||
} | |||
|
|||
/// Merge two sparse bit sets. | |||
pub fn merge_into(&mut self, from: &SparseBitSet<I>) -> 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.
I think should be called insert_from
.
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.
Fixed this.
let p2 = location_table.mid_index(*location); | ||
iter::once((r, p1)).chain(iter::once((r, p2))) | ||
})); | ||
for (r, _) in regioncx.liveness_constraints() { |
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 could also generate these during typeck -- we generate other facts then iirc
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.
Fixed this.
@@ -94,20 +94,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { | |||
} | |||
return; | |||
} | |||
|
|||
for constraint in self.constraint_graph.outgoing_edges(current_region) { | |||
assert_eq!(self.constraints[constraint].sup, current_region); |
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.
wait — why this diff?
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 accidental
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.
Fixed this.
@@ -38,7 +38,7 @@ mod dump_mir; | |||
mod error_reporting; | |||
mod graphviz; | |||
mod values; | |||
use self::values::{RegionValueElements, RegionValues}; | |||
crate use self::values::{RegionElement, RegionElementIndex, RegionValueElements, RegionValues}; |
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.
In this code, I've been trying to stick to a style that avoids crate use
and I'd prefer to keep doing so. In particular, I want to have things have just one home and that is where you import them from (unlike the rest of the compiler where I cannot tell, based on the filename where I find some code, what path I am supposed to use).
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.
Fixed this.
|
||
for (region, location_set) in liveness_constraints.iter_enumerated() { | ||
let scc = constraint_sccs.scc(region); | ||
scc_values.merge_into(scc, location_set); |
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.
👍
self.liveness_constraints.iter_enumerated() | ||
} | ||
|
||
/// Number of liveness constaints in region inference context. |
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.
Typo: s/containts/constraints
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 would usually call this num_liveness_constraints
, but then again I've been trying to use fewer and fewer contractions...
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.
Fixed this.
} | ||
|
||
/// Returns all the elements contained in a given region's value. | ||
crate fn elements_contained_in<'a>( |
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 should be called something like region_live_at
or region_liveness_elements
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.
Fixed this.
cx.constraints.liveness_set.push((live_region, location)); | ||
if let Some(borrowck_context) = cx.borrowck_context { | ||
let region_vid = borrowck_context.universal_regions.to_region_vid(live_region); | ||
borrowck_context.constraints.liveness_constraints.add_element(region_vid, location); |
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.
Here we can also do
if let Some(all_facts) = &mut borrowck_context.all_facts {
all_facts.region_live_at.push(...);
}
and then -- I suspect -- we can remove that big awkward loop above.
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.
Fixed this.
@@ -603,20 +617,19 @@ struct TypeChecker<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { | |||
region_bound_pairs: &'a [(ty::Region<'tcx>, GenericKind<'tcx>)], | |||
implicit_region_bound: Option<ty::Region<'tcx>>, | |||
reported_errors: FxHashSet<(Ty<'tcx>, Span)>, | |||
constraints: MirTypeckRegionConstraints<'tcx>, | |||
borrowck_context: Option<BorrowCheckContext<'a, 'tcx>>, | |||
borrowck_context: &'a mut Option<BorrowCheckContext<'a, 'tcx>>, |
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.
Seems odd for this to be &mut Option<>
and not Option<&mut>
, but it doesn't really matter. If you did the latter, you would have to do:
if let Some(borrowck_context) = &mut self.borrowck_context
instead of this
if let Some(borrowck_context) = self.borrowck_context
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.
Fixed this.
late_bound_region); | ||
borrowck_context.constraints | ||
.liveness_constraints | ||
.add_element(region_vid, term_location); |
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 wouldn't need to modify all-facts here necessarily -- iirc, polonius only cares about liveness in variables -- i.e., regions that exist across many points in the CFG.
Pushed a commit that addresses the comments above. This still ICEs in the SCC code and I've yet to find time to dig into why that is. |
The job Click to expand the log.
I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact |
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.
Seems good to me -- let me check the travis errors..
@@ -293,6 +293,11 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> { | |||
/// | |||
/// Returns true if this changed the matrix, and false otherwise. | |||
pub fn add(&mut self, row: R, column: C) -> bool { | |||
self.vector.resize_to_elem(row, || SparseBitSet::new()); | |||
if let None = self.vector.get(row) { |
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 this if let
is dead-code now, right?
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.
Yes, must have forgot to clean that up. I'll remove this in my next commit.
@@ -767,7 +781,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { | |||
locations, data | |||
); | |||
|
|||
if let Some(borrowck_context) = &mut self.borrowck_context { | |||
if let Some(ref mut borrowck_context) = self.borrowck_context { |
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.
These two are equivalent, no? I moderately prefer Some(foo) = &mut self.borrowck_context
but it doesn't matter much.
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.
Ah, I misread your previous comment about Option<&mut T>
rather than &mut Option<T>
and made this change due to that. I can change it back if you'd prefer.
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'd prefer to change it back, yes.
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.
ref mut is dead to me now :)
.. | ||
} = match &mut self.borrowck_context { | ||
Some(borrowck_context) => borrowck_context, | ||
} = match self.borrowck_context { |
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.
likely, here I personally prefer the more modern style
OK, I fixed the problems I see locally. I'm going to try for a try run so we can measure the impact. I'm quite curious. |
@bors try |
WIP: html5ever in the rustc-perf repository is memory-intensive Part of #52028. Rebased atop of #51987. r? @nikomatsakis
@rust-timer build c1ea2d1 |
Success: Queued c1ea2d1 with parent 3d5753f, comparison URL. |
11% on instructions and 83.5% max-rss for |
Also modify `SparseBitMatrix` so that it does not require knowing the dimensions in advance, but instead grows on demand.
cf3f9b9
to
8b94d16
Compare
@davidtwco I took the liberty of squashing the commits |
@bors r+ p=1 Giving high priority because this high memory usage is a major NLL problem |
📋 Looks like this PR is still in progress, ignoring approval. Hint: Remove WIP from this PR's title when it is ready for review. |
@bors r+ p=1 Giving high priority because this high memory usage is a major NLL problem |
📌 Commit 8b94d16 has been approved by |
⌛ Testing commit 8b94d16 with merge 3395384319ccc52765a13627ad41f72b40ae650b... |
💔 Test failed - status-travis |
The job Click to expand the log.
I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact |
1 similar comment
The job Click to expand the log.
I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact |
@bors retry |
@bors p=15 |
html5ever in the rustc-perf repository is memory-intensive Part of #52028. Rebased atop of #51987. r? @nikomatsakis
☀️ Test successful - status-appveyor, status-travis |
Using a `BTreeMap` to represent rows in the bit matrix is really slow. This patch changes things so that each row is represented by a `BitVector`. This is a less sparse representation, but a much faster one. As a result, `SparseBitSet` and `SparseChunk` can be removed. Other minor changes in this patch. - It renames `BitVector::insert()` as `merge()`, which matches the terminology in the other classes in bitvec.rs. - It removes `SparseBitMatrix::is_subset()`, which is unused. - It reinstates `RegionValueElements::num_elements()`, which rust-lang#52190 had removed. - It removes a low-value `debug!` call in `SparseBitMatrix::add()`.
Part of #52028. Rebased atop of #51987.
r? @nikomatsakis