Auto merge of #108638 - Zoxc:erase-query-values-map, r=cjgillot
Use dynamic dispatch for queries This replaces most concrete query values `V` with `MaybeUninit<[u8; { size_of::<V>() }]>` reducing the code instantiated by queries. The compile time of `rustc_query_impl` is reduced by 27%. It is an alternative to https://github.com/rust-lang/rust/pull/107937 which uses unstable const generics while this uses a `EraseType` trait which maps query values to their erased variant. This is achieved by introducing an `Erased` type which does sanity check with `cfg(debug_assertions)`. The query caches gets instantiated with these erased types leaving the code in `rustc_query_system` unaware of them. `rustc_query_system` is changed to use instances of `QueryConfig` so that `rustc_query_impl` can pass in `DynamicConfig` which holds a pointer to a virtual table. <table><tr><td rowspan="2">Benchmark</td><td colspan="1"><b>Before</b></th><td colspan="2"><b>After</b></th></tr><tr><td align="right">Time</td><td align="right">Time</td><td align="right">%</th></tr><tr><td>🟣 <b>clap</b>:check</td><td align="right">1.7055s</td><td align="right">1.6949s</td><td align="right"> -0.62%</td></tr><tr><td>🟣 <b>hyper</b>:check</td><td align="right">0.2547s</td><td align="right">0.2528s</td><td align="right"> -0.73%</td></tr><tr><td>🟣 <b>regex</b>:check</td><td align="right">0.9590s</td><td align="right">0.9553s</td><td align="right"> -0.39%</td></tr><tr><td>🟣 <b>syn</b>:check</td><td align="right">1.5457s</td><td align="right">1.5440s</td><td align="right"> -0.11%</td></tr><tr><td>🟣 <b>syntex_syntax</b>:check</td><td align="right">5.9092s</td><td align="right">5.9009s</td><td align="right"> -0.14%</td></tr><tr><td>Total</td><td align="right">10.3741s</td><td align="right">10.3479s</td><td align="right"> -0.25%</td></tr><tr><td>Summary</td><td align="right">1.0000s</td><td align="right">0.9960s</td><td align="right"> -0.40%</td></tr></table> <table><tr><td rowspan="2">Benchmark</td><td colspan="1"><b>Before</b></th><td colspan="2"><b>After</b></th></tr><tr><td align="right">Time</td><td align="right">Time</td><td align="right">%</th></tr><tr><td>🟣 <b>clap</b>:check:initial</td><td align="right">2.0605s</td><td align="right">2.0575s</td><td align="right"> -0.15%</td></tr><tr><td>🟣 <b>hyper</b>:check:initial</td><td align="right">0.3218s</td><td align="right">0.3216s</td><td align="right"> -0.07%</td></tr><tr><td>🟣 <b>regex</b>:check:initial</td><td align="right">1.1848s</td><td align="right">1.1839s</td><td align="right"> -0.07%</td></tr><tr><td>🟣 <b>syn</b>:check:initial</td><td align="right">1.9409s</td><td align="right">1.9376s</td><td align="right"> -0.17%</td></tr><tr><td>🟣 <b>syntex_syntax</b>:check:initial</td><td align="right">7.3105s</td><td align="right">7.2928s</td><td align="right"> -0.24%</td></tr><tr><td>Total</td><td align="right">12.8185s</td><td align="right">12.7935s</td><td align="right"> -0.20%</td></tr><tr><td>Summary</td><td align="right">1.0000s</td><td align="right">0.9986s</td><td align="right"> -0.14%</td></tr></table> <table><tr><td rowspan="2">Benchmark</td><td colspan="1"><b>Before</b></th><td colspan="2"><b>After</b></th></tr><tr><td align="right">Time</td><td align="right">Time</td><td align="right">%</th></tr><tr><td>🟣 <b>clap</b>:check:unchanged</td><td align="right">0.4606s</td><td align="right">0.4617s</td><td align="right"> 0.24%</td></tr><tr><td>🟣 <b>hyper</b>:check:unchanged</td><td align="right">0.1335s</td><td align="right">0.1336s</td><td align="right"> 0.08%</td></tr><tr><td>🟣 <b>regex</b>:check:unchanged</td><td align="right">0.3324s</td><td align="right">0.3346s</td><td align="right"> 0.65%</td></tr><tr><td>🟣 <b>syn</b>:check:unchanged</td><td align="right">0.6268s</td><td align="right">0.6307s</td><td align="right"> 0.64%</td></tr><tr><td>🟣 <b>syntex_syntax</b>:check:unchanged</td><td align="right">1.8248s</td><td align="right">1.8508s</td><td align="right">💔 1.43%</td></tr><tr><td>Total</td><td align="right">3.3779s</td><td align="right">3.4113s</td><td align="right"> 0.99%</td></tr><tr><td>Summary</td><td align="right">1.0000s</td><td align="right">1.0061s</td><td align="right"> 0.61%</td></tr></table> It's based on https://github.com/rust-lang/rust/pull/108167. r? `@cjgillot`
This commit is contained in:
commit
8e8116cfe5
13 changed files with 416 additions and 254 deletions
|
@ -4,6 +4,7 @@ use crate::dep_graph::{DepNode, DepNodeParams, SerializedDepNodeIndex};
|
|||
use crate::error::HandleCycleError;
|
||||
use crate::ich::StableHashingContext;
|
||||
use crate::query::caches::QueryCache;
|
||||
use crate::query::DepNodeIndex;
|
||||
use crate::query::{QueryContext, QueryInfo, QueryState};
|
||||
|
||||
use rustc_data_structures::fingerprint::Fingerprint;
|
||||
|
@ -12,8 +13,6 @@ use std::hash::Hash;
|
|||
|
||||
pub type HashResult<V> = Option<fn(&mut StableHashingContext<'_>, &V) -> Fingerprint>;
|
||||
|
||||
pub type TryLoadFromDisk<Qcx, V> = Option<fn(Qcx, SerializedDepNodeIndex) -> Option<V>>;
|
||||
|
||||
pub trait QueryConfig<Qcx: QueryContext>: Copy {
|
||||
fn name(self) -> &'static str;
|
||||
|
||||
|
@ -43,7 +42,13 @@ pub trait QueryConfig<Qcx: QueryContext>: Copy {
|
|||
|
||||
fn compute(self, tcx: Qcx, key: Self::Key) -> Self::Value;
|
||||
|
||||
fn try_load_from_disk(self, qcx: Qcx, idx: &Self::Key) -> TryLoadFromDisk<Qcx, Self::Value>;
|
||||
fn try_load_from_disk(
|
||||
self,
|
||||
tcx: Qcx,
|
||||
key: &Self::Key,
|
||||
prev_index: SerializedDepNodeIndex,
|
||||
index: DepNodeIndex,
|
||||
) -> Option<Self::Value>;
|
||||
|
||||
fn loadable_from_disk(self, qcx: Qcx, key: &Self::Key, idx: SerializedDepNodeIndex) -> bool;
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ pub use self::caches::{
|
|||
};
|
||||
|
||||
mod config;
|
||||
pub use self::config::{HashResult, QueryConfig, TryLoadFromDisk};
|
||||
pub use self::config::{HashResult, QueryConfig};
|
||||
|
||||
use crate::dep_graph::DepKind;
|
||||
use crate::dep_graph::{DepNodeIndex, HasDepContext, SerializedDepNodeIndex};
|
||||
|
|
|
@ -564,59 +564,44 @@ where
|
|||
|
||||
// First we try to load the result from the on-disk cache.
|
||||
// Some things are never cached on disk.
|
||||
if let Some(try_load_from_disk) = query.try_load_from_disk(qcx, &key) {
|
||||
let prof_timer = qcx.dep_context().profiler().incr_cache_loading();
|
||||
|
||||
// The call to `with_query_deserialization` enforces that no new `DepNodes`
|
||||
// are created during deserialization. See the docs of that method for more
|
||||
// details.
|
||||
let result = qcx
|
||||
.dep_context()
|
||||
.dep_graph()
|
||||
.with_query_deserialization(|| try_load_from_disk(qcx, prev_dep_node_index));
|
||||
|
||||
prof_timer.finish_with_query_invocation_id(dep_node_index.into());
|
||||
|
||||
if let Some(result) = result {
|
||||
if std::intrinsics::unlikely(
|
||||
qcx.dep_context().sess().opts.unstable_opts.query_dep_graph,
|
||||
) {
|
||||
dep_graph_data.mark_debug_loaded_from_disk(*dep_node)
|
||||
}
|
||||
|
||||
let prev_fingerprint = dep_graph_data.prev_fingerprint_of(prev_dep_node_index);
|
||||
// If `-Zincremental-verify-ich` is specified, re-hash results from
|
||||
// the cache and make sure that they have the expected fingerprint.
|
||||
//
|
||||
// If not, we still seek to verify a subset of fingerprints loaded
|
||||
// from disk. Re-hashing results is fairly expensive, so we can't
|
||||
// currently afford to verify every hash. This subset should still
|
||||
// give us some coverage of potential bugs though.
|
||||
let try_verify = prev_fingerprint.split().1.as_u64() % 32 == 0;
|
||||
if std::intrinsics::unlikely(
|
||||
try_verify || qcx.dep_context().sess().opts.unstable_opts.incremental_verify_ich,
|
||||
) {
|
||||
incremental_verify_ich(
|
||||
*qcx.dep_context(),
|
||||
dep_graph_data,
|
||||
&result,
|
||||
prev_dep_node_index,
|
||||
query.hash_result(),
|
||||
query.format_value(),
|
||||
);
|
||||
}
|
||||
|
||||
return Some((result, dep_node_index));
|
||||
if let Some(result) = query.try_load_from_disk(qcx, key, prev_dep_node_index, dep_node_index) {
|
||||
if std::intrinsics::unlikely(qcx.dep_context().sess().opts.unstable_opts.query_dep_graph) {
|
||||
dep_graph_data.mark_debug_loaded_from_disk(*dep_node)
|
||||
}
|
||||
|
||||
// We always expect to find a cached result for things that
|
||||
// can be forced from `DepNode`.
|
||||
debug_assert!(
|
||||
!qcx.dep_context().fingerprint_style(dep_node.kind).reconstructible(),
|
||||
"missing on-disk cache entry for reconstructible {dep_node:?}"
|
||||
);
|
||||
let prev_fingerprint = dep_graph_data.prev_fingerprint_of(prev_dep_node_index);
|
||||
// If `-Zincremental-verify-ich` is specified, re-hash results from
|
||||
// the cache and make sure that they have the expected fingerprint.
|
||||
//
|
||||
// If not, we still seek to verify a subset of fingerprints loaded
|
||||
// from disk. Re-hashing results is fairly expensive, so we can't
|
||||
// currently afford to verify every hash. This subset should still
|
||||
// give us some coverage of potential bugs though.
|
||||
let try_verify = prev_fingerprint.split().1.as_u64() % 32 == 0;
|
||||
if std::intrinsics::unlikely(
|
||||
try_verify || qcx.dep_context().sess().opts.unstable_opts.incremental_verify_ich,
|
||||
) {
|
||||
incremental_verify_ich(
|
||||
*qcx.dep_context(),
|
||||
dep_graph_data,
|
||||
&result,
|
||||
prev_dep_node_index,
|
||||
query.hash_result(),
|
||||
query.format_value(),
|
||||
);
|
||||
}
|
||||
|
||||
return Some((result, dep_node_index));
|
||||
}
|
||||
|
||||
// We always expect to find a cached result for things that
|
||||
// can be forced from `DepNode`.
|
||||
debug_assert!(
|
||||
!query.cache_on_disk(*qcx.dep_context(), key)
|
||||
|| !qcx.dep_context().fingerprint_style(dep_node.kind).reconstructible(),
|
||||
"missing on-disk cache entry for {dep_node:?}"
|
||||
);
|
||||
|
||||
// Sanity check for the logic in `ensure`: if the node is green and the result loadable,
|
||||
// we should actually be able to load it.
|
||||
debug_assert!(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue