Skip to content

Commit 67cb41f

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. This commit reduces a `touch crates/editor/src/editor.rs` scenario in Zed for me from 14.5s to 11s.
1 parent 18d13b5 commit 67cb41f

13 files changed

Lines changed: 251 additions & 8 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4205,6 +4205,7 @@ dependencies = [
42054205
"rustc_expand",
42064206
"rustc_feature",
42074207
"rustc_fs_util",
4208+
"rustc_hashes",
42084209
"rustc_hir",
42094210
"rustc_hir_pretty",
42104211
"rustc_incremental",

compiler/rustc_codegen_ssa/src/back/symbol_export.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ use std::collections::hash_map::Entry::*;
22

33
use rustc_abi::{CanonAbi, X86Call};
44
use rustc_ast::expand::allocator::{AllocatorKind, NO_ALLOC_SHIM_IS_UNSTABLE, global_fn_name};
5+
use rustc_data_structures::fx::FxIndexMap;
56
use rustc_data_structures::unord::UnordMap;
7+
use rustc_hashes::Hash128;
68
use rustc_hir::def::DefKind;
79
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE, LocalDefId};
810
use rustc_middle::bug;
@@ -472,6 +474,30 @@ fn is_unreachable_local_definition_provider(tcx: TyCtxt<'_>, def_id: LocalDefId)
472474
!tcx.reachable_set(()).contains(&def_id)
473475
}
474476

477+
fn upstream_monomorphization_hashes_provider(
478+
tcx: TyCtxt<'_>,
479+
(): (),
480+
) -> FxIndexMap<Hash128, smallvec::SmallVec<[CrateNum; 1]>> {
481+
let mut map: FxIndexMap<Hash128, smallvec::SmallVec<[CrateNum; 1]>> = Default::default();
482+
for &cnum in tcx.crates(()) {
483+
for &hash in tcx.exported_generic_symbol_hashes(cnum) {
484+
map.entry(hash).or_default().push(cnum);
485+
}
486+
}
487+
// Sort each candidate list by StableCrateId for determinism.
488+
for candidates in map.values_mut() {
489+
candidates.sort_by_key(|&cnum| tcx.stable_crate_id(cnum));
490+
}
491+
map
492+
}
493+
494+
fn upstream_monomorphization_for_hash_provider(
495+
tcx: TyCtxt<'_>,
496+
hash: Hash128,
497+
) -> Option<&smallvec::SmallVec<[CrateNum; 1]>> {
498+
tcx.upstream_monomorphization_hashes(()).get(&hash)
499+
}
500+
475501
pub(crate) fn provide(providers: &mut Providers) {
476502
providers.queries.reachable_non_generics = reachable_non_generics_provider;
477503
providers.queries.is_reachable_non_generic = is_reachable_non_generic_provider_local;
@@ -481,6 +507,9 @@ pub(crate) fn provide(providers: &mut Providers) {
481507
providers.queries.is_unreachable_local_definition = is_unreachable_local_definition_provider;
482508
providers.queries.upstream_drop_glue_for = upstream_drop_glue_for_provider;
483509
providers.queries.upstream_async_drop_glue_for = upstream_async_drop_glue_for_provider;
510+
providers.queries.upstream_monomorphization_hashes = upstream_monomorphization_hashes_provider;
511+
providers.queries.upstream_monomorphization_for_hash =
512+
upstream_monomorphization_for_hash_provider;
484513
providers.queries.wasm_import_module_map = wasm_import_module_map;
485514
providers.extern_queries.is_reachable_non_generic = is_reachable_non_generic_provider_extern;
486515
providers.extern_queries.upstream_monomorphizations_for =

compiler/rustc_metadata/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ rustc_errors = { path = "../rustc_errors" }
1616
rustc_expand = { path = "../rustc_expand" }
1717
rustc_feature = { path = "../rustc_feature" }
1818
rustc_fs_util = { path = "../rustc_fs_util" }
19+
rustc_hashes = { path = "../rustc_hashes" }
1920
rustc_hir = { path = "../rustc_hir" }
2021
rustc_hir_pretty = { path = "../rustc_hir_pretty" }
2122
rustc_incremental = { path = "../rustc_incremental" }

compiler/rustc_metadata/src/rmeta/decoder.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1521,6 +1521,15 @@ impl<'a> CrateMetadataRef<'a> {
15211521
tcx.arena.alloc_from_iter(self.root.exported_generic_symbols.decode((self, tcx)))
15221522
}
15231523

1524+
fn exported_generic_symbol_hashes<'tcx>(
1525+
self,
1526+
tcx: TyCtxt<'tcx>,
1527+
) -> &'tcx [rustc_hashes::Hash128] {
1528+
let table: exported_symbol_hash_map::ExportedSymbolHashTableRef =
1529+
self.root.exported_generic_symbol_hashes.decode(&self.cdata.blob);
1530+
tcx.arena.alloc_from_iter(table.keys())
1531+
}
1532+
15241533
fn get_macro(self, tcx: TyCtxt<'_>, id: DefIndex) -> ast::MacroDef {
15251534
match self.def_kind(tcx, id) {
15261535
DefKind::Macro(_) => {

compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,7 @@ provide! { tcx, def_id, other, cdata,
407407
stable_order_of_exportable_impls => { tcx.arena.alloc(cdata.get_stable_order_of_exportable_impls(tcx).collect()) }
408408
exported_non_generic_symbols => { cdata.exported_non_generic_symbols(tcx) }
409409
exported_generic_symbols => { cdata.exported_generic_symbols(tcx) }
410+
exported_generic_symbol_hashes => { cdata.exported_generic_symbol_hashes(tcx) }
410411

411412
crate_extern_paths => { cdata.source().paths().cloned().collect() }
412413
expn_that_defined => { cdata.get_expn_that_defined(tcx, def_id.index) }

compiler/rustc_metadata/src/rmeta/encoder.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
701701
)
702702
});
703703

704+
let exported_generic_symbol_hashes = stat!("exported-symbol-hashes", || {
705+
self.encode_exported_generic_symbol_hashes(tcx.exported_generic_symbols(LOCAL_CRATE))
706+
});
707+
704708
// Encode the hygiene data.
705709
// IMPORTANT: this *must* be the last thing that we encode (other than `SourceMap`). The
706710
// process of encoding other items (e.g. `optimized_mir`) may cause us to load data from
@@ -768,6 +772,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
768772
stable_order_of_exportable_impls,
769773
exported_non_generic_symbols,
770774
exported_generic_symbols,
775+
exported_generic_symbol_hashes,
771776
interpret_alloc_index,
772777
tables,
773778
syntax_contexts,
@@ -2258,6 +2263,30 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
22582263
self.lazy_array(exported_symbols.iter().cloned())
22592264
}
22602265

2266+
fn encode_exported_generic_symbol_hashes(
2267+
&mut self,
2268+
exported_symbols: &[(ExportedSymbol<'tcx>, SymbolExportInfo)],
2269+
) -> LazyValue<exported_symbol_hash_map::ExportedSymbolHashTableRef> {
2270+
use exported_symbol_hash_map::{ExportedSymbolHashTable, ExportedSymbolHashTableRef};
2271+
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
2272+
2273+
let tcx = self.tcx;
2274+
let mut table = ExportedSymbolHashTable::with_capacity(exported_symbols.len(), 87);
2275+
2276+
for (sym, _) in exported_symbols {
2277+
if let Some(instance) = sym.to_instance(tcx) {
2278+
let hash = tcx.with_stable_hashing_context(|mut hcx| {
2279+
let mut hasher = StableHasher::new();
2280+
instance.hash_stable(&mut hcx, &mut hasher);
2281+
hasher.finish()
2282+
});
2283+
table.insert(&hash, &());
2284+
}
2285+
}
2286+
2287+
self.lazy(ExportedSymbolHashTableRef::Owned(table))
2288+
}
2289+
22612290
fn encode_dylib_dependency_formats(&mut self) -> LazyArray<Option<LinkagePreference>> {
22622291
empty_proc_macro!(self);
22632292
let formats = self.tcx.dependency_formats(());
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
use rustc_data_structures::owned_slice::OwnedSlice;
2+
use rustc_hashes::Hash128;
3+
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
4+
5+
use crate::rmeta::EncodeContext;
6+
use crate::rmeta::decoder::BlobDecodeContext;
7+
8+
#[derive(Clone, Default)]
9+
pub(crate) struct ExportedSymbolHashConfig;
10+
11+
impl odht::Config for ExportedSymbolHashConfig {
12+
type Key = Hash128;
13+
type Value = ();
14+
15+
type EncodedKey = [u8; 16];
16+
type EncodedValue = [u8; 0];
17+
18+
type H = odht::UnHashFn;
19+
20+
#[inline]
21+
fn encode_key(k: &Hash128) -> [u8; 16] {
22+
k.as_u128().to_le_bytes()
23+
}
24+
25+
#[inline]
26+
fn encode_value(_v: &()) -> [u8; 0] {
27+
[]
28+
}
29+
30+
#[inline]
31+
fn decode_key(k: &[u8; 16]) -> Hash128 {
32+
Hash128::new(u128::from_le_bytes(*k))
33+
}
34+
35+
#[inline]
36+
fn decode_value(_v: &[u8; 0]) {}
37+
}
38+
39+
pub(crate) type ExportedSymbolHashTable = odht::HashTableOwned<ExportedSymbolHashConfig>;
40+
41+
pub(crate) enum ExportedSymbolHashTableRef {
42+
/// Zero-copy view into metadata bytes.
43+
OwnedFromMetadata(odht::HashTable<ExportedSymbolHashConfig, OwnedSlice>),
44+
/// Locally built table, used during encoding.
45+
Owned(ExportedSymbolHashTable),
46+
}
47+
48+
impl ExportedSymbolHashTableRef {
49+
pub(crate) fn keys(&self) -> Vec<Hash128> {
50+
match self {
51+
Self::OwnedFromMetadata(table) => table.iter().map(|(k, ())| k).collect(),
52+
Self::Owned(table) => table.iter().map(|(k, ())| k).collect(),
53+
}
54+
}
55+
}
56+
57+
impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for ExportedSymbolHashTableRef {
58+
fn encode(&self, e: &mut EncodeContext<'a, 'tcx>) {
59+
let raw_bytes = match self {
60+
ExportedSymbolHashTableRef::Owned(table) => table.raw_bytes(),
61+
ExportedSymbolHashTableRef::OwnedFromMetadata(_) => {
62+
panic!(
63+
"ExportedSymbolHashTableRef::OwnedFromMetadata variant \
64+
only exists for deserialization"
65+
)
66+
}
67+
};
68+
e.emit_usize(raw_bytes.len());
69+
e.emit_raw_bytes(raw_bytes);
70+
}
71+
}
72+
73+
impl<'a> Decodable<BlobDecodeContext<'a>> for ExportedSymbolHashTableRef {
74+
fn decode(d: &mut BlobDecodeContext<'a>) -> ExportedSymbolHashTableRef {
75+
let len = d.read_usize();
76+
let pos = d.position();
77+
let o = d.blob().bytes().clone().slice(|blob| &blob[pos..pos + len]);
78+
79+
// Advance the decoder's position past the raw bytes we just sliced.
80+
let _ = d.read_raw_bytes(len);
81+
82+
let inner = odht::HashTable::from_raw_bytes(o).unwrap_or_else(|e| {
83+
panic!("decode error for ExportedSymbolHashTable: {e}");
84+
});
85+
ExportedSymbolHashTableRef::OwnedFromMetadata(inner)
86+
}
87+
}

compiler/rustc_metadata/src/rmeta/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ pub(crate) use decoder::{CrateMetadata, CrateNumMap, MetadataBlob, TargetModifie
66
use def_path_hash_map::DefPathHashMapRef;
77
use encoder::EncodeContext;
88
pub use encoder::{EncodedMetadata, encode_metadata, rendered_const};
9+
use exported_symbol_hash_map::ExportedSymbolHashTableRef;
910
pub(crate) use parameterized::ParameterizedOverTcx;
1011
use rustc_abi::{FieldIdx, ReprOptions, VariantIdx};
1112
use rustc_data_structures::fx::FxHashMap;
@@ -49,6 +50,7 @@ use crate::eii::EiiMapEncodedKeyValue;
4950
mod decoder;
5051
mod def_path_hash_map;
5152
mod encoder;
53+
mod exported_symbol_hash_map;
5254
mod parameterized;
5355
mod table;
5456

@@ -276,6 +278,7 @@ pub(crate) struct CrateRoot {
276278
stable_order_of_exportable_impls: LazyArray<(DefIndex, usize)>,
277279
exported_non_generic_symbols: LazyArray<(ExportedSymbol<'static>, SymbolExportInfo)>,
278280
exported_generic_symbols: LazyArray<(ExportedSymbol<'static>, SymbolExportInfo)>,
281+
exported_generic_symbol_hashes: LazyValue<ExportedSymbolHashTableRef>,
279282

280283
syntax_contexts: SyntaxContextTable,
281284
expn_data: ExpnDataTable,

compiler/rustc_metadata/src/rmeta/parameterized.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ trivially_parameterized_over_tcx! {
7373
crate::rmeta::RawDefId,
7474
crate::rmeta::TraitImpls,
7575
crate::rmeta::VariantData,
76+
crate::rmeta::exported_symbol_hash_map::ExportedSymbolHashTableRef,
7677
rustc_abi::ReprOptions,
7778
rustc_ast::DelimArgs,
7879
rustc_hir::Attribute,

compiler/rustc_middle/src/middle/exported_symbols.rs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
22
use rustc_macros::{Decodable, Encodable, HashStable, TyDecodable, TyEncodable};
33

4-
use crate::ty::{self, GenericArgsRef, Ty, TyCtxt};
4+
use crate::ty::{self, GenericArgsRef, Instance, InstanceKind, Ty, TyCtxt};
55

66
/// The SymbolExportLevel of a symbols specifies from which kinds of crates
77
/// the symbol will be exported. `C` symbols will be exported from any
@@ -57,6 +57,39 @@ pub enum ExportedSymbol<'tcx> {
5757
}
5858

5959
impl<'tcx> ExportedSymbol<'tcx> {
60+
/// Convert to the corresponding [`Instance`], if this is a generic symbol.
61+
///
62+
/// Returns `None` for `NonGeneric`, `ThreadLocalShim`, `NoDefId`, and when
63+
/// required lang items are unavailable.
64+
pub fn to_instance(&self, tcx: TyCtxt<'tcx>) -> Option<Instance<'tcx>> {
65+
match *self {
66+
ExportedSymbol::Generic(def_id, args) => {
67+
Some(Instance { def: InstanceKind::Item(def_id), args })
68+
}
69+
ExportedSymbol::DropGlue(ty) => {
70+
let def_id = tcx.lang_items().drop_in_place_fn()?;
71+
Some(Instance {
72+
def: InstanceKind::DropGlue(def_id, Some(ty)),
73+
args: tcx.mk_args(&[ty.into()]),
74+
})
75+
}
76+
ExportedSymbol::AsyncDropGlueCtorShim(ty) => {
77+
let def_id = tcx.lang_items().async_drop_in_place_fn()?;
78+
Some(Instance {
79+
def: InstanceKind::AsyncDropGlueCtorShim(def_id, ty),
80+
args: tcx.mk_args(&[ty.into()]),
81+
})
82+
}
83+
ExportedSymbol::AsyncDropGlue(def_id, ty) => Some(Instance {
84+
def: InstanceKind::AsyncDropGlue(def_id, ty),
85+
args: tcx.mk_args(&[ty.into()]),
86+
}),
87+
ExportedSymbol::NonGeneric(..)
88+
| ExportedSymbol::ThreadLocalShim(..)
89+
| ExportedSymbol::NoDefId(..) => None,
90+
}
91+
}
92+
6093
/// This is the symbol name of an instance if it is instantiated in the
6194
/// local crate.
6295
pub fn symbol_name_for_local_instance(&self, tcx: TyCtxt<'tcx>) -> ty::SymbolName<'tcx> {

0 commit comments

Comments
 (0)