Move Sharded maps into each QueryCache impl

This commit is contained in:
Mark Rousskov 2022-02-19 22:44:19 -05:00
parent 3b348d932a
commit 9deed6f74e
8 changed files with 53 additions and 122 deletions

View File

@ -129,7 +129,7 @@ impl<K: Eq + Hash + Copy + IntoPointer> ShardedHashMap<K, ()> {
} }
#[inline] #[inline]
fn make_hash<K: Hash + ?Sized>(val: &K) -> u64 { pub fn make_hash<K: Hash + ?Sized>(val: &K) -> u64 {
let mut state = FxHasher::default(); let mut state = FxHasher::default();
val.hash(&mut state); val.hash(&mut state);
state.finish() state.finish()

View File

@ -210,7 +210,7 @@ macro_rules! define_callbacks {
#[derive(Default)] #[derive(Default)]
pub struct QueryCaches<$tcx> { pub struct QueryCaches<$tcx> {
$($(#[$attr])* pub $name: QueryCacheStore<query_storage::$name<$tcx>>,)* $($(#[$attr])* pub $name: query_storage::$name<$tcx>,)*
} }
impl<$tcx> TyCtxtEnsure<$tcx> { impl<$tcx> TyCtxtEnsure<$tcx> {

View File

@ -13,7 +13,7 @@ use rustc_middle::thir;
use rustc_middle::ty::codec::{RefDecodable, TyDecoder, TyEncoder}; use rustc_middle::ty::codec::{RefDecodable, TyDecoder, TyEncoder};
use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_query_system::dep_graph::DepContext; use rustc_query_system::dep_graph::DepContext;
use rustc_query_system::query::{QueryContext, QuerySideEffects}; use rustc_query_system::query::{QueryCache, QueryContext, QuerySideEffects};
use rustc_serialize::{ use rustc_serialize::{
opaque::{self, FileEncodeResult, FileEncoder, IntEncodedWithFixedSize}, opaque::{self, FileEncodeResult, FileEncoder, IntEncodedWithFixedSize},
Decodable, Decoder, Encodable, Encoder, Decodable, Decoder, Encodable, Encoder,
@ -1034,7 +1034,7 @@ where
assert!(Q::query_state(tcx).all_inactive()); assert!(Q::query_state(tcx).all_inactive());
let cache = Q::query_cache(tcx); let cache = Q::query_cache(tcx);
let mut res = Ok(()); let mut res = Ok(());
cache.iter_results(&mut |key, value, dep_node| { cache.iter(&mut |key, value, dep_node| {
if res.is_err() { if res.is_err() {
return; return;
} }

View File

@ -337,7 +337,7 @@ macro_rules! define_queries {
} }
#[inline(always)] #[inline(always)]
fn query_cache<'a>(tcx: QueryCtxt<$tcx>) -> &'a QueryCacheStore<Self::Cache> fn query_cache<'a>(tcx: QueryCtxt<$tcx>) -> &'a Self::Cache
where 'tcx:'a where 'tcx:'a
{ {
&tcx.query_caches.$name &tcx.query_caches.$name

View File

@ -4,7 +4,7 @@ use rustc_data_structures::profiling::SelfProfiler;
use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE};
use rustc_hir::definitions::DefPathData; use rustc_hir::definitions::DefPathData;
use rustc_middle::ty::{TyCtxt, WithOptConstParam}; use rustc_middle::ty::{TyCtxt, WithOptConstParam};
use rustc_query_system::query::{QueryCache, QueryCacheStore}; use rustc_query_system::query::QueryCache;
use std::fmt::Debug; use std::fmt::Debug;
use std::io::Write; use std::io::Write;
@ -229,7 +229,7 @@ where
fn alloc_self_profile_query_strings_for_query_cache<'tcx, C>( fn alloc_self_profile_query_strings_for_query_cache<'tcx, C>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
query_name: &'static str, query_name: &'static str,
query_cache: &QueryCacheStore<C>, query_cache: &C,
string_cache: &mut QueryKeyStringCache, string_cache: &mut QueryKeyStringCache,
) where ) where
C: QueryCache, C: QueryCache,
@ -251,7 +251,7 @@ fn alloc_self_profile_query_strings_for_query_cache<'tcx, C>(
// locked while doing so. Instead we copy out the // locked while doing so. Instead we copy out the
// `(query_key, dep_node_index)` pairs and release the lock again. // `(query_key, dep_node_index)` pairs and release the lock again.
let mut query_keys_and_indices = Vec::new(); let mut query_keys_and_indices = Vec::new();
query_cache.iter_results(&mut |k, _, i| query_keys_and_indices.push((k.clone(), i))); query_cache.iter(&mut |k, _, i| query_keys_and_indices.push((k.clone(), i)));
// Now actually allocate the strings. If allocating the strings // Now actually allocate the strings. If allocating the strings
// generates new entries in the query cache, we'll miss them but // generates new entries in the query cache, we'll miss them but
@ -276,7 +276,7 @@ fn alloc_self_profile_query_strings_for_query_cache<'tcx, C>(
let event_id = event_id_builder.from_label(query_name).to_string_id(); let event_id = event_id_builder.from_label(query_name).to_string_id();
let mut query_invocation_ids = Vec::new(); let mut query_invocation_ids = Vec::new();
query_cache.iter_results(&mut |_, _, i| { query_cache.iter(&mut |_, _, i| {
query_invocation_ids.push(i.into()); query_invocation_ids.push(i.into());
}); });

View File

@ -1,9 +1,9 @@
use crate::dep_graph::DepNodeIndex; use crate::dep_graph::DepNodeIndex;
use crate::query::plumbing::{QueryCacheStore, QueryLookup}; use crate::query::plumbing::QueryLookup;
use rustc_arena::TypedArena; use rustc_arena::TypedArena;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sharded::Sharded; use rustc_data_structures::sharded::{self, Sharded};
use rustc_data_structures::sync::WorkerLocal; use rustc_data_structures::sync::WorkerLocal;
use std::default::Default; use std::default::Default;
use std::fmt::Debug; use std::fmt::Debug;
@ -25,15 +25,13 @@ pub trait QueryStorage {
pub trait QueryCache: QueryStorage + Sized { pub trait QueryCache: QueryStorage + Sized {
type Key: Hash + Eq + Clone + Debug; type Key: Hash + Eq + Clone + Debug;
type Sharded: Default;
/// Checks if the query is already computed and in the cache. /// Checks if the query is already computed and in the cache.
/// It returns the shard index and a lock guard to the shard, /// It returns the shard index and a lock guard to the shard,
/// which will be used if the query is not in the cache and we need /// which will be used if the query is not in the cache and we need
/// to compute it. /// to compute it.
fn lookup<'s, R, OnHit>( fn lookup<R, OnHit>(
&self, &self,
state: &'s QueryCacheStore<Self>,
key: &Self::Key, key: &Self::Key,
// `on_hit` can be called while holding a lock to the query state shard. // `on_hit` can be called while holding a lock to the query state shard.
on_hit: OnHit, on_hit: OnHit,
@ -41,19 +39,9 @@ pub trait QueryCache: QueryStorage + Sized {
where where
OnHit: FnOnce(&Self::Stored, DepNodeIndex) -> R; OnHit: FnOnce(&Self::Stored, DepNodeIndex) -> R;
fn complete( fn complete(&self, key: Self::Key, value: Self::Value, index: DepNodeIndex) -> Self::Stored;
&self,
lock_sharded_storage: &mut Self::Sharded,
key: Self::Key,
value: Self::Value,
index: DepNodeIndex,
) -> Self::Stored;
fn iter( fn iter(&self, f: &mut dyn FnMut(&Self::Key, &Self::Value, DepNodeIndex));
&self,
shards: &Sharded<Self::Sharded>,
f: &mut dyn FnMut(&Self::Key, &Self::Value, DepNodeIndex),
);
} }
pub struct DefaultCacheSelector; pub struct DefaultCacheSelector;
@ -62,11 +50,13 @@ impl<K: Eq + Hash, V: Clone> CacheSelector<K, V> for DefaultCacheSelector {
type Cache = DefaultCache<K, V>; type Cache = DefaultCache<K, V>;
} }
pub struct DefaultCache<K, V>(PhantomData<(K, V)>); pub struct DefaultCache<K, V> {
shards: Sharded<FxHashMap<K, (V, DepNodeIndex)>>,
}
impl<K, V> Default for DefaultCache<K, V> { impl<K, V> Default for DefaultCache<K, V> {
fn default() -> Self { fn default() -> Self {
DefaultCache(PhantomData) DefaultCache { shards: Default::default() }
} }
} }
@ -87,19 +77,16 @@ where
V: Clone + Debug, V: Clone + Debug,
{ {
type Key = K; type Key = K;
type Sharded = FxHashMap<K, (V, DepNodeIndex)>;
#[inline(always)] #[inline(always)]
fn lookup<'s, R, OnHit>( fn lookup<R, OnHit>(&self, key: &K, on_hit: OnHit) -> Result<R, QueryLookup>
&self,
state: &'s QueryCacheStore<Self>,
key: &K,
on_hit: OnHit,
) -> Result<R, QueryLookup>
where where
OnHit: FnOnce(&V, DepNodeIndex) -> R, OnHit: FnOnce(&V, DepNodeIndex) -> R,
{ {
let (lookup, lock) = state.get_lookup(key); let key_hash = sharded::make_hash(key);
let shard = sharded::get_shard_index_by_hash(key_hash);
let lock = self.shards.get_shard_by_index(shard).lock();
let lookup = QueryLookup { key_hash, shard };
let result = lock.raw_entry().from_key_hashed_nocheck(lookup.key_hash, key); let result = lock.raw_entry().from_key_hashed_nocheck(lookup.key_hash, key);
if let Some((_, value)) = result { if let Some((_, value)) = result {
@ -111,23 +98,13 @@ where
} }
#[inline] #[inline]
fn complete( fn complete(&self, key: K, value: V, index: DepNodeIndex) -> Self::Stored {
&self, self.shards.get_shard_by_value(&key).lock().insert(key, (value.clone(), index));
lock_sharded_storage: &mut Self::Sharded,
key: K,
value: V,
index: DepNodeIndex,
) -> Self::Stored {
lock_sharded_storage.insert(key, (value.clone(), index));
value value
} }
fn iter( fn iter(&self, f: &mut dyn FnMut(&Self::Key, &Self::Value, DepNodeIndex)) {
&self, let shards = self.shards.lock_shards();
shards: &Sharded<Self::Sharded>,
f: &mut dyn FnMut(&Self::Key, &Self::Value, DepNodeIndex),
) {
let shards = shards.lock_shards();
for shard in shards.iter() { for shard in shards.iter() {
for (k, v) in shard.iter() { for (k, v) in shard.iter() {
f(k, &v.0, v.1); f(k, &v.0, v.1);
@ -144,12 +121,15 @@ impl<'tcx, K: Eq + Hash, V: 'tcx> CacheSelector<K, V> for ArenaCacheSelector<'tc
pub struct ArenaCache<'tcx, K, V> { pub struct ArenaCache<'tcx, K, V> {
arena: WorkerLocal<TypedArena<(V, DepNodeIndex)>>, arena: WorkerLocal<TypedArena<(V, DepNodeIndex)>>,
phantom: PhantomData<(K, &'tcx V)>, shards: Sharded<FxHashMap<K, &'tcx (V, DepNodeIndex)>>,
} }
impl<'tcx, K, V> Default for ArenaCache<'tcx, K, V> { impl<'tcx, K, V> Default for ArenaCache<'tcx, K, V> {
fn default() -> Self { fn default() -> Self {
ArenaCache { arena: WorkerLocal::new(|_| TypedArena::default()), phantom: PhantomData } ArenaCache {
arena: WorkerLocal::new(|_| TypedArena::default()),
shards: Default::default(),
}
} }
} }
@ -171,19 +151,16 @@ where
V: Debug, V: Debug,
{ {
type Key = K; type Key = K;
type Sharded = FxHashMap<K, &'tcx (V, DepNodeIndex)>;
#[inline(always)] #[inline(always)]
fn lookup<'s, R, OnHit>( fn lookup<R, OnHit>(&self, key: &K, on_hit: OnHit) -> Result<R, QueryLookup>
&self,
state: &'s QueryCacheStore<Self>,
key: &K,
on_hit: OnHit,
) -> Result<R, QueryLookup>
where where
OnHit: FnOnce(&&'tcx V, DepNodeIndex) -> R, OnHit: FnOnce(&&'tcx V, DepNodeIndex) -> R,
{ {
let (lookup, lock) = state.get_lookup(key); let key_hash = sharded::make_hash(key);
let shard = sharded::get_shard_index_by_hash(key_hash);
let lock = self.shards.get_shard_by_index(shard).lock();
let lookup = QueryLookup { key_hash, shard };
let result = lock.raw_entry().from_key_hashed_nocheck(lookup.key_hash, key); let result = lock.raw_entry().from_key_hashed_nocheck(lookup.key_hash, key);
if let Some((_, value)) = result { if let Some((_, value)) = result {
@ -195,25 +172,15 @@ where
} }
#[inline] #[inline]
fn complete( fn complete(&self, key: K, value: V, index: DepNodeIndex) -> Self::Stored {
&self,
lock_sharded_storage: &mut Self::Sharded,
key: K,
value: V,
index: DepNodeIndex,
) -> Self::Stored {
let value = self.arena.alloc((value, index)); let value = self.arena.alloc((value, index));
let value = unsafe { &*(value as *const _) }; let value = unsafe { &*(value as *const _) };
lock_sharded_storage.insert(key, value); self.shards.get_shard_by_value(&key).lock().insert(key, value);
&value.0 &value.0
} }
fn iter( fn iter(&self, f: &mut dyn FnMut(&Self::Key, &Self::Value, DepNodeIndex)) {
&self, let shards = self.shards.lock_shards();
shards: &Sharded<Self::Sharded>,
f: &mut dyn FnMut(&Self::Key, &Self::Value, DepNodeIndex),
) {
let shards = shards.lock_shards();
for shard in shards.iter() { for shard in shards.iter() {
for (k, v) in shard.iter() { for (k, v) in shard.iter() {
f(k, &v.0, v.1); f(k, &v.0, v.1);

View File

@ -4,7 +4,7 @@ use crate::dep_graph::DepNode;
use crate::dep_graph::SerializedDepNodeIndex; use crate::dep_graph::SerializedDepNodeIndex;
use crate::ich::StableHashingContext; use crate::ich::StableHashingContext;
use crate::query::caches::QueryCache; use crate::query::caches::QueryCache;
use crate::query::{QueryCacheStore, QueryContext, QueryState}; use crate::query::{QueryContext, QueryState};
use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fingerprint::Fingerprint;
use rustc_errors::DiagnosticBuilder; use rustc_errors::DiagnosticBuilder;
@ -64,7 +64,7 @@ pub trait QueryDescription<CTX: QueryContext>: QueryConfig {
CTX: 'a; CTX: 'a;
// Don't use this method to access query results, instead use the methods on TyCtxt // Don't use this method to access query results, instead use the methods on TyCtxt
fn query_cache<'a>(tcx: CTX) -> &'a QueryCacheStore<Self::Cache> fn query_cache<'a>(tcx: CTX) -> &'a Self::Cache
where where
CTX: 'a; CTX: 'a;

View File

@ -12,7 +12,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHasher};
#[cfg(parallel_compiler)] #[cfg(parallel_compiler)]
use rustc_data_structures::profiling::TimingGuard; use rustc_data_structures::profiling::TimingGuard;
use rustc_data_structures::sharded::{get_shard_index_by_hash, Sharded}; use rustc_data_structures::sharded::{get_shard_index_by_hash, Sharded};
use rustc_data_structures::sync::{Lock, LockGuard}; use rustc_data_structures::sync::Lock;
use rustc_data_structures::thin_vec::ThinVec; use rustc_data_structures::thin_vec::ThinVec;
use rustc_errors::{DiagnosticBuilder, FatalError}; use rustc_errors::{DiagnosticBuilder, FatalError};
use rustc_session::Session; use rustc_session::Session;
@ -24,21 +24,10 @@ use std::hash::{Hash, Hasher};
use std::mem; use std::mem;
use std::ptr; use std::ptr;
pub struct QueryCacheStore<C: QueryCache> {
cache: C,
shards: Sharded<C::Sharded>,
}
impl<C: QueryCache + Default> Default for QueryCacheStore<C> {
fn default() -> Self {
Self { cache: C::default(), shards: Default::default() }
}
}
/// Values used when checking a query cache which can be reused on a cache-miss to execute the query. /// Values used when checking a query cache which can be reused on a cache-miss to execute the query.
pub struct QueryLookup { pub struct QueryLookup {
pub(super) key_hash: u64, pub(super) key_hash: u64,
shard: usize, pub(super) shard: usize,
} }
// We compute the key's hash once and then use it for both the // We compute the key's hash once and then use it for both the
@ -50,22 +39,6 @@ fn hash_for_shard<K: Hash>(key: &K) -> u64 {
hasher.finish() hasher.finish()
} }
impl<C: QueryCache> QueryCacheStore<C> {
pub(super) fn get_lookup<'tcx>(
&'tcx self,
key: &C::Key,
) -> (QueryLookup, LockGuard<'tcx, C::Sharded>) {
let key_hash = hash_for_shard(key);
let shard = get_shard_index_by_hash(key_hash);
let lock = self.shards.get_shard_by_index(shard).lock();
(QueryLookup { key_hash, shard }, lock)
}
pub fn iter_results(&self, f: &mut dyn FnMut(&C::Key, &C::Value, DepNodeIndex)) {
self.cache.iter(&self.shards, f)
}
}
struct QueryStateShard<K> { struct QueryStateShard<K> {
active: FxHashMap<K, QueryResult>, active: FxHashMap<K, QueryResult>,
} }
@ -239,12 +212,7 @@ where
/// Completes the query by updating the query cache with the `result`, /// Completes the query by updating the query cache with the `result`,
/// signals the waiter and forgets the JobOwner, so it won't poison the query /// signals the waiter and forgets the JobOwner, so it won't poison the query
fn complete<C>( fn complete<C>(self, cache: &C, result: C::Value, dep_node_index: DepNodeIndex) -> C::Stored
self,
cache: &QueryCacheStore<C>,
result: C::Value,
dep_node_index: DepNodeIndex,
) -> C::Stored
where where
C: QueryCache<Key = K>, C: QueryCache<Key = K>,
{ {
@ -265,10 +233,7 @@ where
QueryResult::Poisoned => panic!(), QueryResult::Poisoned => panic!(),
} }
}; };
let result = { let result = cache.complete(key, result, dep_node_index);
let mut lock = cache.shards.get_shard_by_index(shard).lock();
cache.cache.complete(&mut lock, key, result, dep_node_index)
};
(job, result) (job, result)
}; };
@ -334,7 +299,7 @@ where
#[inline] #[inline]
pub fn try_get_cached<'a, CTX, C, R, OnHit>( pub fn try_get_cached<'a, CTX, C, R, OnHit>(
tcx: CTX, tcx: CTX,
cache: &'a QueryCacheStore<C>, cache: &'a C,
key: &C::Key, key: &C::Key,
// `on_hit` can be called while holding a lock to the query cache // `on_hit` can be called while holding a lock to the query cache
on_hit: OnHit, on_hit: OnHit,
@ -344,7 +309,7 @@ where
CTX: DepContext, CTX: DepContext,
OnHit: FnOnce(&C::Stored) -> R, OnHit: FnOnce(&C::Stored) -> R,
{ {
cache.cache.lookup(cache, &key, |value, index| { cache.lookup(&key, |value, index| {
if unlikely!(tcx.profiler().enabled()) { if unlikely!(tcx.profiler().enabled()) {
tcx.profiler().query_cache_hit(index.into()); tcx.profiler().query_cache_hit(index.into());
} }
@ -356,7 +321,7 @@ where
fn try_execute_query<CTX, C>( fn try_execute_query<CTX, C>(
tcx: CTX, tcx: CTX,
state: &QueryState<C::Key>, state: &QueryState<C::Key>,
cache: &QueryCacheStore<C>, cache: &C,
span: Span, span: Span,
key: C::Key, key: C::Key,
lookup: QueryLookup, lookup: QueryLookup,
@ -375,14 +340,13 @@ where
(result, Some(dep_node_index)) (result, Some(dep_node_index))
} }
TryGetJob::Cycle(error) => { TryGetJob::Cycle(error) => {
let result = mk_cycle(tcx, error, query.handle_cycle_error, &cache.cache); let result = mk_cycle(tcx, error, query.handle_cycle_error, cache);
(result, None) (result, None)
} }
#[cfg(parallel_compiler)] #[cfg(parallel_compiler)]
TryGetJob::JobCompleted(query_blocked_prof_timer) => { TryGetJob::JobCompleted(query_blocked_prof_timer) => {
let (v, index) = cache let (v, index) = cache
.cache .lookup(&key, |value, index| (value.clone(), index))
.lookup(cache, &key, |value, index| (value.clone(), index))
.unwrap_or_else(|_| panic!("value must be in cache after waiting")); .unwrap_or_else(|_| panic!("value must be in cache after waiting"));
if unlikely!(tcx.dep_context().profiler().enabled()) { if unlikely!(tcx.dep_context().profiler().enabled()) {
@ -760,7 +724,7 @@ where
// We may be concurrently trying both execute and force a query. // We may be concurrently trying both execute and force a query.
// Ensure that only one of them runs the query. // Ensure that only one of them runs the query.
let cache = Q::query_cache(tcx); let cache = Q::query_cache(tcx);
let cached = cache.cache.lookup(cache, &key, |_, index| { let cached = cache.lookup(&key, |_, index| {
if unlikely!(tcx.dep_context().profiler().enabled()) { if unlikely!(tcx.dep_context().profiler().enabled()) {
tcx.dep_context().profiler().query_cache_hit(index.into()); tcx.dep_context().profiler().query_cache_hit(index.into());
} }