Skip to content

Commit 098d4fd

Browse files
committed
Auto merge of #119977 - Mark-Simulacrum:defid-cache, r=cjgillot
Cache local DefId-keyed queries without hashing This caches local DefId-keyed queries using just an IndexVec. This costs ~5% extra max-rss at most but brings significant runtime improvement, up to 13% cycle counts (mean: 4%) on primary benchmarks. It's possible that further tweaks could reduce the memory overhead further but this win seems worth landing despite the increased memory, particularly with regards to eliminating the present set in non-incr or storing it inline (skip list?) with the main data. We tried applying this scheme to all keys in the [first perf run] but found that it carried a significant memory hit (50%). instructions/cycle counts were also much more mixed, though that may have been due to the lack of the present set optimization (needed for fast iter() calls in incremental scenarios). Closes #45275 [first perf run]: https://perf.rust-lang.org/compare.html?start=30dfb9e046aeb878db04332c74de76e52fb7db10&end=6235575300d8e6e2cc6f449cb9048722ef43f9c7&stat=instructions:u
2 parents 92f2e0a + 3784964 commit 098d4fd

File tree

3 files changed

+82
-3
lines changed

3 files changed

+82
-3
lines changed

compiler/rustc_middle/src/query/keys.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::ty::{self, Ty, TyCtxt};
99
use crate::ty::{GenericArg, GenericArgsRef};
1010
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LocalModDefId, ModDefId, LOCAL_CRATE};
1111
use rustc_hir::hir_id::{HirId, OwnerId};
12+
use rustc_query_system::query::DefIdCacheSelector;
1213
use rustc_query_system::query::{DefaultCacheSelector, SingleCacheSelector, VecCacheSelector};
1314
use rustc_span::symbol::{Ident, Symbol};
1415
use rustc_span::{Span, DUMMY_SP};
@@ -152,7 +153,7 @@ impl Key for LocalDefId {
152153
}
153154

154155
impl Key for DefId {
155-
type CacheSelector = DefaultCacheSelector<Self>;
156+
type CacheSelector = DefIdCacheSelector;
156157

157158
fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
158159
tcx.def_span(*self)

compiler/rustc_query_system/src/query/caches.rs

+78-1
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ use crate::dep_graph::DepNodeIndex;
22

33
use rustc_data_structures::fx::FxHashMap;
44
use rustc_data_structures::sharded::{self, Sharded};
5-
use rustc_data_structures::sync::OnceLock;
5+
use rustc_data_structures::sync::{Lock, OnceLock};
6+
use rustc_hir::def_id::LOCAL_CRATE;
67
use rustc_index::{Idx, IndexVec};
8+
use rustc_span::def_id::DefId;
9+
use rustc_span::def_id::DefIndex;
710
use std::fmt::Debug;
811
use std::hash::Hash;
912
use std::marker::PhantomData;
@@ -148,6 +151,8 @@ where
148151

149152
#[inline(always)]
150153
fn lookup(&self, key: &K) -> Option<(V, DepNodeIndex)> {
154+
// FIXME: lock_shard_by_hash will use high bits which are usually zero in the index() passed
155+
// here. This makes sharding essentially useless, always selecting the zero'th shard.
151156
let lock = self.cache.lock_shard_by_hash(key.index() as u64);
152157
if let Some(Some(value)) = lock.get(*key) { Some(*value) } else { None }
153158
}
@@ -168,3 +173,75 @@ where
168173
}
169174
}
170175
}
176+
177+
pub struct DefIdCacheSelector;
178+
179+
impl<'tcx, V: 'tcx> CacheSelector<'tcx, V> for DefIdCacheSelector {
180+
type Cache = DefIdCache<V>
181+
where
182+
V: Copy;
183+
}
184+
185+
pub struct DefIdCache<V> {
186+
/// Stores the local DefIds in a dense map. Local queries are much more often dense, so this is
187+
/// a win over hashing query keys at marginal memory cost (~5% at most) compared to FxHashMap.
188+
///
189+
/// The second element of the tuple is the set of keys actually present in the IndexVec, used
190+
/// for faster iteration in `iter()`.
191+
// FIXME: This may want to be sharded, like VecCache. However *how* to shard an IndexVec isn't
192+
// super clear; VecCache is effectively not sharded today (see FIXME there). For now just omit
193+
// that complexity here.
194+
local: Lock<(IndexVec<DefIndex, Option<(V, DepNodeIndex)>>, Vec<DefIndex>)>,
195+
foreign: DefaultCache<DefId, V>,
196+
}
197+
198+
impl<V> Default for DefIdCache<V> {
199+
fn default() -> Self {
200+
DefIdCache { local: Default::default(), foreign: Default::default() }
201+
}
202+
}
203+
204+
impl<V> QueryCache for DefIdCache<V>
205+
where
206+
V: Copy,
207+
{
208+
type Key = DefId;
209+
type Value = V;
210+
211+
#[inline(always)]
212+
fn lookup(&self, key: &DefId) -> Option<(V, DepNodeIndex)> {
213+
if key.krate == LOCAL_CRATE {
214+
let cache = self.local.lock();
215+
cache.0.get(key.index).and_then(|v| *v)
216+
} else {
217+
self.foreign.lookup(key)
218+
}
219+
}
220+
221+
#[inline]
222+
fn complete(&self, key: DefId, value: V, index: DepNodeIndex) {
223+
if key.krate == LOCAL_CRATE {
224+
let mut cache = self.local.lock();
225+
let (cache, present) = &mut *cache;
226+
let slot = cache.ensure_contains_elem(key.index, Default::default);
227+
if slot.is_none() {
228+
// FIXME: Only store the present set when running in incremental mode. `iter` is not
229+
// used outside of saving caches to disk and self-profile.
230+
present.push(key.index);
231+
}
232+
*slot = Some((value, index));
233+
} else {
234+
self.foreign.complete(key, value, index)
235+
}
236+
}
237+
238+
fn iter(&self, f: &mut dyn FnMut(&Self::Key, &Self::Value, DepNodeIndex)) {
239+
let guard = self.local.lock();
240+
let (cache, present) = &*guard;
241+
for &idx in present.iter() {
242+
let value = cache[idx].unwrap();
243+
f(&DefId { krate: LOCAL_CRATE, index: idx }, &value.0, value.1);
244+
}
245+
self.foreign.iter(f);
246+
}
247+
}

compiler/rustc_query_system/src/query/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ pub use self::job::{
1010

1111
mod caches;
1212
pub use self::caches::{
13-
CacheSelector, DefaultCacheSelector, QueryCache, SingleCacheSelector, VecCacheSelector,
13+
CacheSelector, DefIdCacheSelector, DefaultCacheSelector, QueryCache, SingleCacheSelector,
14+
VecCacheSelector,
1415
};
1516

1617
mod config;

0 commit comments

Comments
 (0)