Improve how QueryCaches and QueryStates are stored#152835
Improve how QueryCaches and QueryStates are stored#152835nnethercote wants to merge 4 commits intorust-lang:mainfrom
QueryCaches and QueryStates are stored#152835Conversation
The tuple used by `mk_query_stack_frame_extra` currently uses `QueryVTable`. The next commit will make `QueryVTable` no longer impl `DynSync`, which will prevent it from being used in the tuple. So this commit changes the tuple to use just the three necessary fields from `QueryVTable` instead of the whole thing.
`QuerySystem` has these fields: ``` pub states: QueryStates<'tcx>, pub caches: QueryCaches<'tcx>, pub query_vtables: PerQueryVTables<'tcx>, ``` Each one has an entry for each query. Some methods that take a query-specific `QueryVTable` (via a `SemiDynamicQueryDispatcher` wrapper) need to access the corresponding query-specific states and/or caches. So `QueryVTable` stores the *byte offset* to the relevant fields within `states` and `caches`, and uses that to (with `unsafe`) access the fields. This is bizarre, and I hope it made sense in the past when the code was structured differently. This commit moves `QueryState` and `QueryCache` inside `QueryVTable`. As a result, the query-specific states and/or caches are directly accessible, and no unsafe offset computations are required. Much simpler and more normal. Also, `QueryCaches` and `QueryStates` are no longer needed, which makes `define_callbacks!` a bit simpler. `QueryVTable` no longer impls `DynSync`, which required the change in the preceding commit.
Currently type variables that impl `QueryCache` are called either `C` or `Cache`. I find the former clearer because single char type variables names are very common and longer type variable names are easy to mistake for type or trait names -- e.g. I find the trait bound `C: QueryCache` much easier and faster to read than `Cache: QueryCache`.
|
|
|
@bors try @rust-timer queue |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Improve how `QueryCache`s and `QueryState`s are stored
|
Putting the state and cache directly in the vtable is not a possibility I had considered. It stretches the concept of “vtable” a bit, and it gives up on being able to treat the vtable as mere metadata, but it does have advantages for actually looking up the state/cache given a vtable reference. Genuinely unsure how to feel about this overall. It doesn’t jump out to me as something obviously-good or obviously-bad; the tradeoffs are more subtle. |
|
I agree about stretching the meaning of "vtable". Beyond that I think it's a clear win. The offset code is ridiculous, it screams "something went wrong here". More generally, it makes more sense to have a struct containing |
|
This PR also touches on some areas that I haven’t been able to work on because they’re still waiting for #152747 to be merged, so having this PR also interfere with that is starting to feel overwhelming. |
This comment has been minimized.
This comment has been minimized.
`QueryEngine` is a struct with one function pointer per query. This commit removes it by moving each function pointer into the relevant query's vtable, which is already a collection of function pointers for that query. This makes things simpler.
|
Finished benchmarking commit (6efacd9): comparison URL. Overall result: ✅ improvements - no action neededBenchmarking this pull request means it may be perf-sensitive – we'll automatically label it not fit for rolling up. You can override this, but we strongly advise not to, due to possible changes in compiler perf. @bors rollup=never Instruction countOur most reliable metric. Used to determine the overall result above. However, even this metric can be noisy.
Max RSS (memory usage)Results (secondary -3.2%)A less reliable metric. May be of interest, but not used to determine the overall result above.
CyclesResults (primary 2.1%)A less reliable metric. May be of interest, but not used to determine the overall result above.
Binary sizeThis benchmark run did not return any relevant results for this metric. Bootstrap: 487.064s -> 482.434s (-0.95%) |
|
I've been asking you for a lot of reviews; I'm happy to ask a different reviewer if you want a break. |
|
☔ The latest upstream changes (presumably #152747) made this pull request unmergeable. Please resolve the merge conflicts. |
|
I was skeptical of this PR at first, because it was unclear to me whether moving state/cache into the vtable was a good idea overall, and I wasn't convinced by the motivation of wanting to get rid of the unsafe offset fields (even though I don't like the offset fields). However, after some more consideration, I'm coming around to the idea that putting per-query state/caches in the vtable is a fine idea in its own right. So now I'm pretty happy with the direction of this PR. I have some remarks to write up, and I'll want to do another pass, but overall this PR looks good to me. |
| fn mk_query_stack_frame_extra<'tcx, K>( | ||
| (tcx, name, dep_kind, description_fn, key): ( | ||
| TyCtxt<'tcx>, | ||
| &'static str, | ||
| DepKind, | ||
| fn(TyCtxt<'tcx>, K) -> String, | ||
| K, | ||
| ), |
There was a problem hiding this comment.
Suggestion: Based on my local testing, I think this entire commit can be avoided by just adding a QueryVTable<'tcx, C>: DynSync bound to create_deferred_query_stack_frame.
(This makes sense because the state and cache should be DynSync, and the caller knows that this happens to be true of every concrete state and cache.)
There was a problem hiding this comment.
If you apply this suggestion, the commit description of the next commit will need some adjustment.
| query_state: Default::default(), | ||
| query_cache: Default::default(), |
There was a problem hiding this comment.
Question/suggestion: Should we get rid of the redundant query_ and rename these to state and cache?
| pub(crate) fn query_get_at<'tcx, C>( | ||
| tcx: TyCtxt<'tcx>, | ||
| execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option<Cache::Value>, | ||
| query_cache: &Cache, | ||
| execute_query: fn(TyCtxt<'tcx>, Span, C::Key, QueryMode) -> Option<C::Value>, | ||
| query_cache: &C, | ||
| span: Span, | ||
| key: Cache::Key, | ||
| ) -> Cache::Value | ||
| key: C::Key, | ||
| ) -> C::Value | ||
| where | ||
| Cache: QueryCache, | ||
| C: QueryCache, |
There was a problem hiding this comment.
Remark: I weakly prefer Cache over C, but if you feel strongly about it then I don't mind compromising on C. The single-letter convention does have the advantage of clearly indicating a type variable.
| } | ||
|
|
||
| pub fn make_query_vtables<'tcx>() -> queries::PerQueryVTables<'tcx> { | ||
| pub fn make_query_vtables<'tcx>(incremental: bool) -> queries::PerQueryVTables<'tcx> { |
There was a problem hiding this comment.
If you want to take this opportunity to also rename PerQueryVTables back to QueryVTables, feel free. The more verbose name did its job at a more confusing time, but the simpler QueryVTables should be fine now.
QueryVTablehas two fieldsquery_stateandquery_cachethat are byte offsets of fields in other structs. They are used inunsafecombination withbyte_addandcastto access said fields. I don't like this at all and this PR replaces them with something sensible.r? @Zalathar