Skip to content
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

Make region inference use a dirty list #47766

Merged
merged 4 commits into from
Jan 29, 2018

Conversation

spastorino
Copy link
Member

Copy link
Contributor

@nikomatsakis nikomatsakis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great. Left a small suggestion.

},
);
let dependency_map = self.build_dependency_map();
let mut dirty_list = self.constraints.clone();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I think the dirty list ought to be either refs to constraints or indices of constraints. Since you used indices for the dependency-map, let's go with those. (It'll also avoid any borrowing issues.)

So something like:

let mut dirty_list: Vec<_> = (0..self.containts.len()).collect();

let mut dirty_list = self.constraints.clone();

debug!("propagate_constraints: --------------------");
while let Some(constraint) = dirty_list.pop() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

while let Some(constraint_index) = dirty_list.pop() {
    let constraint = &self.constraints[constraint_index];
    ...
}

debug!("propagate_constraints: sup={:?}", constraint.sup);

if let Some(constraint_deps) = dependency_map.get(&constraint.sup) {
for dep_idx in constraint_deps {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now this can be

if let Some(constraint_deps) = dependency_map.get(&constraint.sup) {
    dirty_list.extend(constraint_deps);
}

Not that it matters much, but generally invoking extend is better than for loop that calls push. This is because sometimes the iterator knows how long it will be, which lets dirty_list reallocate its backing array more efficiently.

Finally, you could simplify this further, like so:

dirty_list.extend(
    dependency_map.get(&constraint.sup).unwrap_or(&vec![])
);

but I'm not sure that's really clearer (arguably less so). The key here is that creating an empty vector (i.e., vec![]) is basically free -- it's just some null values, no allocation or anything. In the case where I have a "map to vec", I often do this style, under the idea that a missing entry is equivalent to an empty vector. But either way really.

@spastorino
Copy link
Member Author

@nikomatsakis applied all the requested changes

@kennytm kennytm added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jan 26, 2018
@leonardo-m
Copy link

Have you run Clippy on your changes?

@nikomatsakis
Copy link
Contributor

@leonardo-m why would we do that? is there some specific lint you are imagining?

fwiw, we don't usually run clippy on the compiler.

Copy link
Contributor

@nikomatsakis nikomatsakis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looking great! left 2 comments -- one is a nit, but also it's probably worth adding a bit vec to avoid duplicates as we discussed.

}
debug!("propagate_constraints: --------------------");
while let Some(constraint_idx) = dirty_list.pop() {
let constraint = self.constraints[constraint_idx];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could be &self.constraints[constraint_idx] -- no reason to copy the value out

debug!("propagate_constraints: sup={:?}", constraint.sup);

dirty_list.extend(
dependency_map.get(&constraint.sup).unwrap_or(&vec![])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as you said, it might be nice to have a bitvec storing what is already present in the dirty_list, which you would then have to clear each time we pop something out of the dirty list.

@spastorino
Copy link
Member Author

@nikomatsakis ready!

@leonardo-m
Copy link

we don't usually run clippy on the compiler.

I didn't know that Clippy is not used on the compiler. Clippy gives lot of false positives, but some advice is good, so perhaps you should use Clippy...

is there some specific lint you are imagining?

I think Clippy often suggests to replace or_insert with or_insert_with

@nikomatsakis
Copy link
Contributor

I think Clippy often suggests to replace or_insert with or_insert_with

Ah. This case is better left as or_insert; or at least no worse, since creating an empty vector is just some constant bits.

debug!("propagate_constraints: sup={:?}", constraint.sup);
changed = true;
for dep_idx in dependency_map.get(&constraint.sup).unwrap_or(&vec![]) {
if !dirty_bit_vec.contains(*dep_idx) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can change this to:

if dirty_bit_vec.insert(*dep_idx) {
    dirty_list.push(*dep_idx);
}

and save a bit of execution time.

debug!("propagate_constraints: sub={:?}", constraint.sub);
debug!("propagate_constraints: sup={:?}", constraint.sup);
changed = true;
for dep_idx in dependency_map.get(&constraint.sup).unwrap_or(&vec![]) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I'd usually write for &dep_idx in ... just so you don't need the *dep_idx below

debug!("\n");
}

self.inferred_values = Some(inferred_values);
}

fn build_dependency_map(&self) -> HashMap<RegionVid, Vec<usize>> {
Copy link
Contributor

@nikomatsakis nikomatsakis Jan 27, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a comment here would be nice, actually. You could steal the stuff I wrote in the issue description (i.e., the example). But something like this would suffice:


Builds up a map from each region variable X to a vector with the indices of constraints that need to be re-evaluated when X changes. These are constraints like Y: X @ P -- so if X changed, we may need to grow Y.

I suspect the lines would be long for tidy.
Copy link
Contributor

@nikomatsakis nikomatsakis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since I know you just stepped out, I might make these changes :)

);
let dependency_map = self.build_dependency_map();
let mut dirty_list: Vec<_> = (0..self.constraints.len()).collect();
let mut dirty_bit_vec = BitVector::new(dirty_list.len());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this starts as all zeroes, but we really want it to start as all ones I suppose. But it won't be wrong this way, just mildly less efficient. Actually, we could "invert the sense" of the vector, and have it store a 1 if the thing is not present....call it clean_bit_vec...


debug!("propagate_constraints: --------------------");
while let Some(constraint_idx) = dirty_list.pop() {
dirty_bit_vec.remove(constraint_idx);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...in that case, this would be clean_bit_vec.insert(constraint_idx);...

debug!("propagate_constraints: sup={:?}", constraint.sup);
changed = true;
for &dep_idx in dependency_map.get(&constraint.sup).unwrap_or(&vec![]) {
if dirty_bit_vec.insert(dep_idx) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...and this would be if clean_bit_vec.remove(dep_idx) {

@nikomatsakis
Copy link
Contributor

My measurements here locally using the testcase from #47267:

  • No changes, no NLL: ~405 ms
  • No changes, with NLL: ~608 ms, so NLL adds around 203ms
  • This PR, with NLL: ~518ms, so NLL adds around 113ms

So this looks like a solid win for NLL to me! :)

These are averages of 6 runs. The results were fairly stable but jumped around some. The test case is kind of small. So take them with a grain of salt. Here is a gist with my full measurements in case you are curious, each one is labeled with the SHA1 commit hash and whether it had NLL enabled or disabled.

@nikomatsakis
Copy link
Contributor

@bors r+

@bors
Copy link
Contributor

bors commented Jan 27, 2018

📌 Commit 205eba8 has been approved by nikomatsakis

@leonardo-m
Copy link

These are averages of 6 runs. The results were fairly stable but jumped around some. The test case is kind of small.

The next Nightly I'll test this with a much larger amount of code.

@nikomatsakis
Copy link
Contributor

@leonardo-m if you have a test, I can run it now.

@bors
Copy link
Contributor

bors commented Jan 27, 2018

⌛ Testing commit 205eba8 with merge 4349bc51453d0c74495589c8036622a9b1a05726...

@bors
Copy link
Contributor

bors commented Jan 27, 2018

💔 Test failed - status-appveyor

@kennytm
Copy link
Member

kennytm commented Jan 28, 2018

@bors retry #46903

@bors
Copy link
Contributor

bors commented Jan 29, 2018

⌛ Testing commit 205eba8 with merge b7d4de9edf202b3a9cd889bf038ee663aecaf573...

@bors
Copy link
Contributor

bors commented Jan 29, 2018

💔 Test failed - status-travis

@kennytm
Copy link
Member

kennytm commented Jan 29, 2018

@bors retry #43283

@kennytm kennytm added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jan 29, 2018
@bors
Copy link
Contributor

bors commented Jan 29, 2018

⌛ Testing commit 205eba8 with merge 70f7d58...

bors added a commit that referenced this pull request Jan 29, 2018
@bors
Copy link
Contributor

bors commented Jan 29, 2018

☀️ Test successful - status-appveyor, status-travis
Approved by: nikomatsakis
Pushing 70f7d58 to master...

@bors bors merged commit 205eba8 into rust-lang:master Jan 29, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants