Skip to content

Commit c6d54c7

Browse files
committed
rustc_codegen_ssa: Make upstream monomorphizations representation sparse
Upstream monomorphisations are a blessing and a curse of Zed's build performance. On one hand, we do benefit from it, as builds with share-generics disabled are slower for us (plus we don't really want to use nightly anyways). On the other, deserializing query results takes *a lot* of time. For some crates close to the end of our compilation pipeline, it's over 400ms per crate. To make matters worse, I've measured a hit ratio of upstream generics. A sample of such measurement goes as follows: ``` upstream_monomorphization returned None for 28501 distinct monomorphizations. upstream_monomorphization returned Some 2518 times. Results came from 163 distinct CrateNums. In total, there are 619731 instantiations ``` This is horrid for us, as we're using a very small percentage of the map that we spend so much time deserializing from. This commit tries to (rather clumsily) move us towards a sparse representation of upstream_monomorphizations. Instead of storing <DefId, (GenericArgsRef<'_>)> which is rather heavy to deserialize, we'll resort to storing Hashes of Instances. I plan to make this more foolproof, hence this commit is marked as WIP. For one, we should probably keep the projection queries. Also, it might be worthwhile to store index pointing at entry within exported_generics of target crate in order to remedy a potential for collisions. This commit reduces a `touch crates/editor/src/editor.rs` scenario in Zed for me from 14.5s to 11s.
1 parent cdb4236 commit c6d54c7

File tree

3 files changed

+84
-139
lines changed

3 files changed

+84
-139
lines changed

compiler/rustc_codegen_ssa/src/back/symbol_export.rs

Lines changed: 60 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
use std::collections::hash_map::Entry::*;
2-
31
use rustc_abi::{CanonAbi, X86Call};
42
use rustc_ast::expand::allocator::{
53
ALLOC_ERROR_HANDLER, ALLOCATOR_METHODS, NO_ALLOC_SHIM_IS_UNSTABLE, global_fn_name,
64
};
7-
use rustc_data_structures::unord::UnordMap;
5+
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
6+
use rustc_hashes::Hash128;
87
use rustc_hir::def::DefKind;
98
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE, LocalDefId};
109
use rustc_middle::bug;
@@ -384,86 +383,75 @@ fn exported_generic_symbols_provider_local<'tcx>(
384383
tcx.arena.alloc_from_iter(symbols)
385384
}
386385

387-
fn upstream_monomorphizations_provider(
386+
fn upstream_monomorphizations_of_crate_provider(
388387
tcx: TyCtxt<'_>,
389-
(): (),
390-
) -> DefIdMap<UnordMap<GenericArgsRef<'_>, CrateNum>> {
391-
let cnums = tcx.crates(());
392-
393-
let mut instances: DefIdMap<UnordMap<_, _>> = Default::default();
394-
388+
cnum: CrateNum,
389+
) -> FxIndexSet<Hash128> {
390+
use rustc_data_structures::stable_hasher::HashStable;
395391
let drop_in_place_fn_def_id = tcx.lang_items().drop_in_place_fn();
396392
let async_drop_in_place_fn_def_id = tcx.lang_items().async_drop_in_place_fn();
397-
398-
for &cnum in cnums.iter() {
399-
for (exported_symbol, _) in tcx.exported_generic_symbols(cnum).iter() {
400-
let (def_id, args) = match *exported_symbol {
401-
ExportedSymbol::Generic(def_id, args) => (def_id, args),
402-
ExportedSymbol::DropGlue(ty) => {
403-
if let Some(drop_in_place_fn_def_id) = drop_in_place_fn_def_id {
404-
(drop_in_place_fn_def_id, tcx.mk_args(&[ty.into()]))
405-
} else {
406-
// `drop_in_place` in place does not exist, don't try
407-
// to use it.
408-
continue;
409-
}
410-
}
411-
ExportedSymbol::AsyncDropGlueCtorShim(ty) => {
412-
if let Some(async_drop_in_place_fn_def_id) = async_drop_in_place_fn_def_id {
413-
(async_drop_in_place_fn_def_id, tcx.mk_args(&[ty.into()]))
414-
} else {
415-
continue;
416-
}
417-
}
418-
ExportedSymbol::AsyncDropGlue(def_id, ty) => (def_id, tcx.mk_args(&[ty.into()])),
419-
ExportedSymbol::NonGeneric(..)
420-
| ExportedSymbol::ThreadLocalShim(..)
421-
| ExportedSymbol::NoDefId(..) => unreachable!("{exported_symbol:?}"),
422-
};
423-
424-
let args_map = instances.entry(def_id).or_default();
425-
426-
match args_map.entry(args) {
427-
Occupied(mut e) => {
428-
// If there are multiple monomorphizations available,
429-
// we select one deterministically.
430-
let other_cnum = *e.get();
431-
if tcx.stable_crate_id(other_cnum) > tcx.stable_crate_id(cnum) {
432-
e.insert(cnum);
433-
}
393+
let mut instances = FxIndexSet::default();
394+
for (exported_symbol, _) in tcx.exported_generic_symbols(cnum).iter() {
395+
let instance = match *exported_symbol {
396+
ExportedSymbol::Generic(def_id, args) => rustc_middle::ty::Instance {
397+
args,
398+
def: rustc_middle::ty::InstanceKind::Item(def_id),
399+
},
400+
ExportedSymbol::DropGlue(ty) => {
401+
let Some(drop_in_place_fn_def_id) = drop_in_place_fn_def_id else {
402+
// `drop_in_place` in place does not exist, don't try
403+
// to use it.
404+
continue;
405+
};
406+
let args = tcx.mk_args(&[ty.clone().into()]);
407+
rustc_middle::ty::Instance {
408+
args,
409+
def: rustc_middle::ty::InstanceKind::DropGlue(
410+
drop_in_place_fn_def_id,
411+
Some(ty),
412+
),
434413
}
435-
Vacant(e) => {
436-
e.insert(cnum);
414+
}
415+
ExportedSymbol::AsyncDropGlueCtorShim(ty) => {
416+
let Some(drop_in_place) = async_drop_in_place_fn_def_id else {
417+
continue;
418+
};
419+
let args = tcx.mk_args(&[ty.clone().into()]);
420+
rustc_middle::ty::Instance {
421+
args,
422+
def: rustc_middle::ty::InstanceKind::AsyncDropGlueCtorShim(drop_in_place, ty),
437423
}
438424
}
439-
}
440-
}
425+
ExportedSymbol::AsyncDropGlue(def_id, ty) => rustc_middle::ty::Instance {
426+
args: tcx.mk_args(&[ty.clone().into()]),
427+
def: rustc_middle::ty::InstanceKind::AsyncDropGlue(def_id, ty),
428+
},
429+
ExportedSymbol::NonGeneric(..)
430+
| ExportedSymbol::ThreadLocalShim(..)
431+
| ExportedSymbol::NoDefId(..) => unreachable!("{exported_symbol:?}"),
432+
};
441433

434+
tcx.with_stable_hashing_context(|mut tcx| {
435+
let mut hasher = rustc_data_structures::stable_hasher::StableHasher::new();
436+
instance.hash_stable(&mut tcx, &mut hasher);
437+
instances.insert(hasher.finish());
438+
});
439+
}
442440
instances
443441
}
444442

445-
fn upstream_monomorphizations_for_provider(
446-
tcx: TyCtxt<'_>,
447-
def_id: DefId,
448-
) -> Option<&UnordMap<GenericArgsRef<'_>, CrateNum>> {
449-
assert!(!def_id.is_local());
450-
tcx.upstream_monomorphizations(()).get(&def_id)
451-
}
443+
fn upstream_monomorphizations_provider(tcx: TyCtxt<'_>, _: ()) -> FxIndexMap<Hash128, CrateNum> {
444+
let cnums = tcx.crates(());
445+
let mut instances: FxIndexMap<Hash128, CrateNum> = Default::default();
452446

453-
fn upstream_drop_glue_for_provider<'tcx>(
454-
tcx: TyCtxt<'tcx>,
455-
args: GenericArgsRef<'tcx>,
456-
) -> Option<CrateNum> {
457-
let def_id = tcx.lang_items().drop_in_place_fn()?;
458-
tcx.upstream_monomorphizations_for(def_id)?.get(&args).cloned()
459-
}
447+
for cnum in cnums.iter() {
448+
let hashes = tcx.upstream_monomorphizations_of_crate(*cnum);
449+
for hash in hashes {
450+
instances.entry(*hash).or_insert(*cnum);
451+
}
452+
}
460453

461-
fn upstream_async_drop_glue_for_provider<'tcx>(
462-
tcx: TyCtxt<'tcx>,
463-
args: GenericArgsRef<'tcx>,
464-
) -> Option<CrateNum> {
465-
let def_id = tcx.lang_items().async_drop_in_place_fn()?;
466-
tcx.upstream_monomorphizations_for(def_id)?.get(&args).cloned()
454+
instances
467455
}
468456

469457
fn is_unreachable_local_definition_provider(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
@@ -477,12 +465,9 @@ pub(crate) fn provide(providers: &mut Providers) {
477465
providers.exported_generic_symbols = exported_generic_symbols_provider_local;
478466
providers.upstream_monomorphizations = upstream_monomorphizations_provider;
479467
providers.is_unreachable_local_definition = is_unreachable_local_definition_provider;
480-
providers.upstream_drop_glue_for = upstream_drop_glue_for_provider;
481-
providers.upstream_async_drop_glue_for = upstream_async_drop_glue_for_provider;
482468
providers.wasm_import_module_map = wasm_import_module_map;
483469
providers.extern_queries.is_reachable_non_generic = is_reachable_non_generic_provider_extern;
484-
providers.extern_queries.upstream_monomorphizations_for =
485-
upstream_monomorphizations_for_provider;
470+
providers.upstream_monomorphizations_of_crate = upstream_monomorphizations_of_crate_provider;
486471
}
487472

488473
pub(crate) fn allocator_shim_symbols(

compiler/rustc_middle/src/query/mod.rs

Lines changed: 6 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ use rustc_data_structures::steal::Steal;
7676
use rustc_data_structures::svh::Svh;
7777
use rustc_data_structures::unord::{UnordMap, UnordSet};
7878
use rustc_errors::ErrorGuaranteed;
79+
use rustc_hashes::Hash128;
7980
use rustc_hir::attrs::StrippedCfgItem;
8081
use rustc_hir::def::{DefKind, DocLinkResMap};
8182
use rustc_hir::def_id::{
@@ -1960,65 +1961,15 @@ rustc_queries! {
19601961
/// crate. Instead use the narrower `upstream_monomorphizations_for`,
19611962
/// `upstream_drop_glue_for`, `upstream_async_drop_glue_for`, or,
19621963
/// even better, `Instance::upstream_monomorphization()`.
1963-
query upstream_monomorphizations(_: ()) -> &'tcx DefIdMap<UnordMap<GenericArgsRef<'tcx>, CrateNum>> {
1964+
query upstream_monomorphizations(_: ()) -> &'tcx FxIndexMap<Hash128, CrateNum> {
19641965
arena_cache
19651966
desc { "collecting available upstream monomorphizations" }
19661967
}
19671968

1968-
/// Returns the set of upstream monomorphizations available for the
1969-
/// generic function identified by the given `def_id`. The query makes
1970-
/// sure to make a stable selection if the same monomorphization is
1971-
/// available in multiple upstream crates.
1972-
///
1973-
/// You likely want to call `Instance::upstream_monomorphization()`
1974-
/// instead of invoking this query directly.
1975-
query upstream_monomorphizations_for(def_id: DefId)
1976-
-> Option<&'tcx UnordMap<GenericArgsRef<'tcx>, CrateNum>>
1977-
{
1978-
desc { |tcx|
1979-
"collecting available upstream monomorphizations for `{}`",
1980-
tcx.def_path_str(def_id),
1981-
}
1982-
separate_provide_extern
1983-
}
1984-
1985-
/// Returns the upstream crate that exports drop-glue for the given
1986-
/// type (`args` is expected to be a single-item list containing the
1987-
/// type one wants drop-glue for).
1988-
///
1989-
/// This is a subset of `upstream_monomorphizations_for` in order to
1990-
/// increase dep-tracking granularity. Otherwise adding or removing any
1991-
/// type with drop-glue in any upstream crate would invalidate all
1992-
/// functions calling drop-glue of an upstream type.
1993-
///
1994-
/// You likely want to call `Instance::upstream_monomorphization()`
1995-
/// instead of invoking this query directly.
1996-
///
1997-
/// NOTE: This query could easily be extended to also support other
1998-
/// common functions that have are large set of monomorphizations
1999-
/// (like `Clone::clone` for example).
2000-
query upstream_drop_glue_for(args: GenericArgsRef<'tcx>) -> Option<CrateNum> {
2001-
desc { "available upstream drop-glue for `{:?}`", args }
2002-
}
2003-
2004-
/// Returns the upstream crate that exports async-drop-glue for
2005-
/// the given type (`args` is expected to be a single-item list
2006-
/// containing the type one wants async-drop-glue for).
2007-
///
2008-
/// This is a subset of `upstream_monomorphizations_for` in order
2009-
/// to increase dep-tracking granularity. Otherwise adding or
2010-
/// removing any type with async-drop-glue in any upstream crate
2011-
/// would invalidate all functions calling async-drop-glue of an
2012-
/// upstream type.
2013-
///
2014-
/// You likely want to call `Instance::upstream_monomorphization()`
2015-
/// instead of invoking this query directly.
2016-
///
2017-
/// NOTE: This query could easily be extended to also support other
2018-
/// common functions that have are large set of monomorphizations
2019-
/// (like `Clone::clone` for example).
2020-
query upstream_async_drop_glue_for(args: GenericArgsRef<'tcx>) -> Option<CrateNum> {
2021-
desc { "available upstream async-drop-glue for `{:?}`", args }
1969+
query upstream_monomorphizations_of_crate(_: CrateNum) -> &'tcx FxIndexSet<Hash128> {
1970+
arena_cache
1971+
cache_on_disk_if { true }
1972+
desc { "collecting available upstream monomorphizations for crate" }
20221973
}
20231974

20241975
/// Returns a list of all `extern` blocks of a crate.

compiler/rustc_middle/src/ty/instance.rs

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ use std::assert_matches::assert_matches;
22
use std::fmt;
33

44
use rustc_data_structures::fx::FxHashMap;
5+
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
56
use rustc_errors::ErrorGuaranteed;
7+
use rustc_hashes::Hash128;
68
use rustc_hir as hir;
79
use rustc_hir::def::{CtorKind, DefKind, Namespace};
810
use rustc_hir::def_id::{CrateNum, DefId};
@@ -224,18 +226,25 @@ impl<'tcx> Instance<'tcx> {
224226
return None;
225227
}
226228

227-
match self.def {
228-
InstanceKind::Item(def) => tcx
229-
.upstream_monomorphizations_for(def)
230-
.and_then(|monos| monos.get(&self.args).cloned()),
231-
InstanceKind::DropGlue(_, Some(_)) => tcx.upstream_drop_glue_for(self.args),
229+
let defining_crates = match self.def {
230+
InstanceKind::Item(_)
231+
| InstanceKind::DropGlue(_, Some(_))
232+
| InstanceKind::AsyncDropGlueCtorShim(_, _) => {
233+
let hash: Hash128 = tcx.with_stable_hashing_context(|mut hcx| {
234+
let mut hasher = StableHasher::new();
235+
self.hash_stable(&mut hcx, &mut hasher);
236+
hasher.finish()
237+
});
238+
tcx.upstream_monomorphizations(()).get(&hash)
239+
}
240+
232241
InstanceKind::AsyncDropGlue(_, _) => None,
233242
InstanceKind::FutureDropPollShim(_, _, _) => None,
234-
InstanceKind::AsyncDropGlueCtorShim(_, _) => {
235-
tcx.upstream_async_drop_glue_for(self.args)
236-
}
243+
237244
_ => None,
238-
}
245+
}?;
246+
247+
Some(*defining_crates)
239248
}
240249
}
241250

0 commit comments

Comments
 (0)