diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 8dcf140cd2ad6..e58c9d75a9db2 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -1279,6 +1279,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0"), perf_stats: bool = (false, parse_bool, [UNTRACKED], "print some performance-related statistics"), + query_stats: bool = (false, parse_bool, [UNTRACKED], + "print some statistics about the query system"), hir_stats: bool = (false, parse_bool, [UNTRACKED], "print some statistics about AST and HIR"), always_encode_mir: bool = (false, parse_bool, [TRACKED], diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 289ef4da99227..ea69d466ba6c5 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -903,7 +903,7 @@ pub struct GlobalCtxt<'tcx> { /// as well as all upstream crates. Only populated in incremental mode. pub def_path_hash_to_def_id: Option>, - pub(crate) queries: query::Queries<'tcx>, + pub queries: query::Queries<'tcx>, // Records the free variables referenced by every closure // expression. Do not track deps for this, just recompute it from diff --git a/src/librustc/ty/query/mod.rs b/src/librustc/ty/query/mod.rs index cbdec2ef2ba81..22bd1cd90a754 100644 --- a/src/librustc/ty/query/mod.rs +++ b/src/librustc/ty/query/mod.rs @@ -53,6 +53,7 @@ use rustc_target::spec::PanicStrategy; use std::borrow::Cow; use std::ops::Deref; use std::sync::Arc; +use std::intrinsics::type_name; use syntax_pos::{Span, DUMMY_SP}; use syntax_pos::symbol::InternedString; use syntax::attr; diff --git a/src/librustc/ty/query/plumbing.rs b/src/librustc/ty/query/plumbing.rs index 99da77491ca54..5d23ee0994a06 100644 --- a/src/librustc/ty/query/plumbing.rs +++ b/src/librustc/ty/query/plumbing.rs @@ -27,6 +27,8 @@ use syntax::source_map::DUMMY_SP; pub struct QueryCache<'tcx, D: QueryConfig<'tcx> + ?Sized> { pub(super) results: FxHashMap>, pub(super) active: FxHashMap>, + #[cfg(debug_assertions)] + pub(super) cache_hits: usize, } pub(super) struct QueryValue { @@ -50,6 +52,8 @@ impl<'tcx, M: QueryConfig<'tcx>> Default for QueryCache<'tcx, M> { QueryCache { results: FxHashMap::default(), active: FxHashMap::default(), + #[cfg(debug_assertions)] + cache_hits: 0, } } } @@ -114,6 +118,10 @@ impl<'a, 'tcx, Q: QueryDescription<'tcx>> JobOwner<'a, 'tcx, Q> { }); let result = Ok((value.value.clone(), value.index)); + #[cfg(debug_assertions)] + { + lock.cache_hits += 1; + } return TryGetJob::JobCompleted(result); } let job = match lock.active.entry((*key).clone()) { @@ -752,6 +760,101 @@ macro_rules! define_queries_inner { jobs } + + pub fn print_stats(&self) { + let mut queries = Vec::new(); + + #[derive(Clone)] + struct QueryStats { + name: &'static str, + cache_hits: usize, + key_size: usize, + key_type: &'static str, + value_size: usize, + value_type: &'static str, + entry_count: usize, + } + + fn stats<'tcx, Q: QueryConfig<'tcx>>( + name: &'static str, + map: &QueryCache<'tcx, Q> + ) -> QueryStats { + QueryStats { + name, + #[cfg(debug_assertions)] + cache_hits: map.cache_hits, + #[cfg(not(debug_assertions))] + cache_hits: 0, + key_size: mem::size_of::(), + key_type: unsafe { type_name::() }, + value_size: mem::size_of::(), + value_type: unsafe { type_name::() }, + entry_count: map.results.len(), + } + } + + $( + queries.push(stats::>( + stringify!($name), + &*self.$name.lock() + )); + )* + + if cfg!(debug_assertions) { + let hits: usize = queries.iter().map(|s| s.cache_hits).sum(); + let results: usize = queries.iter().map(|s| s.entry_count).sum(); + println!("\nQuery cache hit rate: {}", hits as f64 / (hits + results) as f64); + } + + let mut query_key_sizes = queries.clone(); + query_key_sizes.sort_by_key(|q| q.key_size); + println!("\nLarge query keys:"); + for q in query_key_sizes.iter().rev() + .filter(|q| q.key_size > 8) { + println!( + " {} - {} x {} - {}", + q.name, + q.key_size, + q.entry_count, + q.key_type + ); + } + + let mut query_value_sizes = queries.clone(); + query_value_sizes.sort_by_key(|q| q.value_size); + println!("\nLarge query values:"); + for q in query_value_sizes.iter().rev() + .filter(|q| q.value_size > 8) { + println!( + " {} - {} x {} - {}", + q.name, + q.value_size, + q.entry_count, + q.value_type + ); + } + + if cfg!(debug_assertions) { + let mut query_cache_hits = queries.clone(); + query_cache_hits.sort_by_key(|q| q.cache_hits); + println!("\nQuery cache hits:"); + for q in query_cache_hits.iter().rev() { + println!( + " {} - {} ({}%)", + q.name, + q.cache_hits, + q.cache_hits as f64 / (q.cache_hits + q.entry_count) as f64 + ); + } + } + + let mut query_value_count = queries.clone(); + query_value_count.sort_by_key(|q| q.entry_count); + println!("\nQuery value count:"); + for q in query_value_count.iter().rev() { + println!(" {} - {}", q.name, q.entry_count); + } + } } #[allow(nonstandard_style)] @@ -940,7 +1043,7 @@ macro_rules! define_queries_inner { macro_rules! define_queries_struct { (tcx: $tcx:tt, input: ($(([$($modifiers:tt)*] [$($attr:tt)*] [$name:ident]))*)) => { - pub(crate) struct Queries<$tcx> { + pub struct Queries<$tcx> { /// This provides access to the incr. comp. on-disk cache for query results. /// Do not access this directly. It is only meant to be used by /// `DepGraph::try_mark_green()` and the query infrastructure. diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 62dc8691e65df..9b232edc11d4a 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -328,6 +328,10 @@ pub fn compile_input( } } + if tcx.sess.opts.debugging_opts.query_stats { + tcx.queries.print_stats(); + } + Ok((outputs.clone(), ongoing_codegen, tcx.dep_graph.clone())) }, )??