-
Notifications
You must be signed in to change notification settings - Fork 13.3k
fix non_blanket_impls iteration order #89016
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
Conversation
@bors try @rust-timer queue |
Awaiting bors try build completion. @rustbot label: +S-waiting-on-perf |
⌛ Trying commit 92bbb2f7811fe8c9b6148b38597960a601f768ee with merge 91bb5f03171b819eba4a4463ce46e49de79a7e3b... |
☀️ Try build successful - checks-actions |
1 similar comment
☀️ Try build successful - checks-actions |
Queued 91bb5f03171b819eba4a4463ce46e49de79a7e3b with parent 2b5ddf3, future comparison URL. |
Finished benchmarking commit (91bb5f03171b819eba4a4463ce46e49de79a7e3b): comparison url. Summary: This change led to moderate relevant mixed results 🤷 in compiler performance.
If you disagree with this performance assessment, please file an issue in rust-lang/rustc-perf. Benchmarking this pull request likely means that it is perf-sensitive, so we're automatically marking it as not fit for rolling up. While you can manually mark this PR as fit for rollup, we strongly recommend not doing so since this PR led to changes in compiler perf. Next Steps: If you can justify the regressions found in this try perf run, please indicate this with @bors rollup=never |
pub struct TraitImpls { | ||
blanket_impls: Vec<DefId>, | ||
/// Impls indexed by their simplified self type, for fast lookup. | ||
non_blanket_impls: FxHashMap<fast_reject::SimplifiedType, Vec<DefId>>, | ||
non_blanket_impls: FxIndexMap<fast_reject::SimplifiedType, Vec<DefId>>, |
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.
Hm, is this enough?
It looks like insertion into non_blanket_impls happens in a loop which is a result of tcx.crates(())
which isn't stable across compilation sessions I'd expect (though I could be wrong).
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.
The way crates(())
is computed, it looks like it is always (1..=num_crates).map(CrateNum)
(0 is reserved for the current crate and does not have any data) and it I would assume the crates get added to the CStore
in a deterministic order.
We do have stable_crate_id
which orders crates independently of their cnum
. I could be that CrateNum
s are not deterministically assigned to their actual crate data, though it seems quite buggy to me if this doesn't invalidate query caches in that case. e.g. trait_impls_of_provider
just uses the order of crates(())
which influences the way winnowing works for example.
A bit out of my depth here, maybe @rust-lang/wg-incr-comp can help and clarify here 😅 ❤️
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.
Yeah, we probably do end up with an ordering that is mostly stable from run to run (presuming no shifts in code or other sources), and I think incremental will track the query fine. My guess is that we could avoid marking the trait query as red if the crate load order changes (e.g., because the first code using that crate is moved?) if this used a stable crate ID, but it may not be worth it. Ultimately this PR does seem like it makes things strictly better here :)
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 that crate ordering is indeed deterministic, since it isn't something that we discover dynamically? But indeed I bet @michaelwoerister or @petrochenkov would be certain.
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.
Crate are assigned their CrateNum in the order in which they are required by the resolver. As the resolver is not incremental, this order is deterministic but not stable. On the other hand, it is not obvious at all.
Incremental computes a hash for crates(())
based on their order in the vector and their stable_crate_id
. We could make it independent on the order of crates by having the crates
query sort its result by increasing stable_crate_id
.
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 that parts of the code might rely on tcx.crates(())
being sorted by CrateNum
, for better or worse. I would not recommend trying to change that as part of this PR.
crates.sort(); | ||
|
||
for &cnum in crates.iter() { | ||
for &cnum in tcx.crates(()) { |
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.
are these changes just "cleanup" or is there some substantive change in the order in which things are done here?
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.
just cleanup, the only change of behavior is the second commit here.
crates(())
already returns the cnums
in order, so the code i deleted in the other commits should just be noops
r=me, but I'm going to make this since the primary question seems to have to do with incremental. |
.map(|cnum| { | ||
if tcx.dep_kind(CrateNum::new(cnum)) == CrateDepKind::Explicit { | ||
let mut ret = tcx.crates(()).iter() | ||
.map(|&cnum| { |
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.
Do we need to skip the leading CrateNum(0)
?
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.
🤷 CrateNum(0)
is the local crate so I don't think we need to do any linking for that.
I think that explicitly dealing with the local crate and only iterating over all external ones is probably better than specialcasing the first item of the crates
iter. But that's a fairly uninformed opinion
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 don't understand how that code works in the first place? last_crate
is tcx.crates(()).len()
and then it creates a CrateNum(last_crate)
? That should not be a valid CrateNum
, 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.
Oh, tcx.crates(())
only returns upstream CrateNum
s. That is far from obvious. I think the query should be renamed to upstream_crate_nums
or something. The same goes for CStore::crates_untracked
. Would you be up for doing that, @lcnr? (but maybe in a separate PR)
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.
extern_crates
maybe? 🤔 though i prefer doing that in a separate PR, want to merge this as it is blocking some further work
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've personally always used the term "upstream" (e.g. see
rust/compiler/rustc_middle/src/query/mod.rs
Line 1264 in 15d9ba0
query upstream_monomorphizations(_: ()) -> DefIdMap<FxHashMap<SubstsRef<'tcx>, CrateNum>> { |
extern crate foo
suggests otherwise :)
.
Anyway, I'm fine with doing this in another PR. You can r=me on this one.
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.
upstream
is also fine 😆 was thinking of extern crate something
^^
2fe7145
to
7c076fe
Compare
This comment has been minimized.
This comment has been minimized.
7c076fe
to
a8b6a91
Compare
a8b6a91
to
01bcddb
Compare
I'll take a look tomorrow. |
} | ||
|
||
blanket_impls.hash_stable(hcx, hasher); | ||
} |
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 actually looks like it is unsound, right @Aaron1011 & @cjgillot? If the order is observable, we must not "stabilize" it via sorting.
This looks good to me. If anything it makes the code more correct (see #89016 (comment)). I would also recommend renaming the |
@bors r=nikomatsakis,michaelwoerister rollup=never |
📌 Commit 01bcddb has been approved by |
☀️ Test successful - checks-actions |
Tested on commit rust-lang/rust@bf64232. Direct link to PR: <rust-lang/rust#89016> 🎉 rls on linux: test-fail → test-pass (cc @Xanewok).
Finished benchmarking commit (bf64232): comparison url. Summary: This change led to large relevant mixed results 🤷 in compiler performance.
If you disagree with this performance assessment, please file an issue in rust-lang/rustc-perf. Next Steps: If you can justify the regressions found in this perf run, please indicate this with @rustbot label: +perf-regression |
The improvements outweigh the regressions with the only regressions being in historically noisy benchmarks. As such I'm going to mark this as triaged. @rustbot label +perf-regression-triaged |
We sometimes iterate over all
non_blanket_impls
, not sure if this is observable outsideof error messages (i.e. as incremental bugs). This should fix the underlying issue of #86986.
second attempt of #88718
r? @nikomatsakis