diff options
| author | Michael Woerister <michaelwoerister@posteo> | 2019-09-27 14:03:09 +0200 |
|---|---|---|
| committer | Michael Woerister <michaelwoerister@posteo> | 2019-09-30 13:30:47 +0200 |
| commit | b0b073cdb07ccd392747fcaf1f1e949fe8921c1b (patch) | |
| tree | 6e7195ff54e975c8f0779da5d4d84fb5fab3e4df | |
| parent | bf8491e72ef0d5ea0ef683e9d149408a717545e4 (diff) | |
Self-Profiling: Refactor SelfProfiler API to be RAII based where possible.
| -rw-r--r-- | src/librustc/session/mod.rs | 24 | ||||
| -rw-r--r-- | src/librustc/ty/context.rs | 4 | ||||
| -rw-r--r-- | src/librustc/util/profiling.rs | 326 |
3 files changed, 244 insertions, 110 deletions
diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 8e9c2735c39..e52f2788e6b 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -32,7 +32,7 @@ use syntax::source_map; use syntax::parse::{self, ParseSess}; use syntax::symbol::Symbol; use syntax_pos::{MultiSpan, Span}; -use crate::util::profiling::SelfProfiler; +use crate::util::profiling::{SelfProfiler, SelfProfilerRef}; use rustc_target::spec::{PanicStrategy, RelroLevel, Target, TargetTriple}; use rustc_data_structures::flock; @@ -129,7 +129,7 @@ pub struct Session { pub profile_channel: Lock<Option<mpsc::Sender<ProfileQueriesMsg>>>, /// Used by `-Z self-profile`. - pub self_profiling: Option<Arc<SelfProfiler>>, + pub prof: SelfProfilerRef, /// Some measurements that are being gathered during compilation. pub perf_stats: PerfStats, @@ -835,24 +835,6 @@ impl Session { } } - #[inline(never)] - #[cold] - fn profiler_active<F: FnOnce(&SelfProfiler) -> ()>(&self, f: F) { - match &self.self_profiling { - None => bug!("profiler_active() called but there was no profiler active"), - Some(profiler) => { - f(&profiler); - } - } - } - - #[inline(always)] - pub fn profiler<F: FnOnce(&SelfProfiler) -> ()>(&self, f: F) { - if unlikely!(self.self_profiling.is_some()) { - self.profiler_active(f) - } - } - pub fn print_perf_stats(&self) { println!( "Total time spent computing symbol hashes: {}", @@ -1257,7 +1239,7 @@ fn build_session_( imported_macro_spans: OneThread::new(RefCell::new(FxHashMap::default())), incr_comp_session: OneThread::new(RefCell::new(IncrCompSession::NotInitialized)), cgu_reuse_tracker, - self_profiling: self_profiler, + prof: SelfProfilerRef::new(self_profiler), profile_channel: Lock::new(None), perf_stats: PerfStats { symbol_hash_time: Lock::new(Duration::from_secs(0)), diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 6c5d9a6dfdf..467f4c84ab3 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -45,6 +45,7 @@ use crate::ty::CanonicalPolyFnSig; use crate::util::common::ErrorReported; use crate::util::nodemap::{DefIdMap, DefIdSet, ItemLocalMap, ItemLocalSet}; use crate::util::nodemap::{FxHashMap, FxHashSet}; +use crate::util::profiling::SelfProfilerRef; use errors::DiagnosticBuilder; use arena::SyncDroplessArena; @@ -995,6 +996,8 @@ pub struct GlobalCtxt<'tcx> { pub dep_graph: DepGraph, + pub prof: SelfProfilerRef, + /// Common objects. pub common: Common<'tcx>, @@ -1225,6 +1228,7 @@ impl<'tcx> TyCtxt<'tcx> { arena: WorkerLocal::new(|_| Arena::default()), interners, dep_graph, + prof: s.prof.clone(), common, types: common_types, lifetimes: common_lifetimes, diff --git a/src/librustc/util/profiling.rs b/src/librustc/util/profiling.rs index 8624856a4f5..bd02e7f5a14 100644 --- a/src/librustc/util/profiling.rs +++ b/src/librustc/util/profiling.rs @@ -1,9 +1,9 @@ -use std::borrow::Cow; use std::error::Error; use std::fs; use std::mem::{self, Discriminant}; use std::path::Path; use std::process; +use std::sync::Arc; use std::thread::ThreadId; use std::u32; @@ -62,6 +62,206 @@ fn thread_id_to_u64(tid: ThreadId) -> u64 { unsafe { mem::transmute::<ThreadId, u64>(tid) } } + +/// A reference to the SelfProfiler. It can be cloned and sent across thread +/// boundaries at will. +#[derive(Clone)] +pub struct SelfProfilerRef { + // This field is `None` if self-profiling is disabled for the current + // compilation session. + profiler: Option<Arc<SelfProfiler>>, + + // We store the filter mask directly in the reference because that doesn't + // cost anything and allows for filtering with checking if the profiler is + // actually enabled. + event_filter_mask: EventFilter, +} + +impl SelfProfilerRef { + + pub fn new(profiler: Option<Arc<SelfProfiler>>) -> SelfProfilerRef { + // If there is no SelfProfiler then the filter mask is set to NONE, + // ensuring that nothing ever tries to actually access it. + let event_filter_mask = profiler + .as_ref() + .map(|p| p.event_filter_mask) + .unwrap_or(EventFilter::NONE); + + SelfProfilerRef { + profiler, + event_filter_mask, + } + } + + // This shim makes sure that calls only get executed if the filter mask + // lets them pass. It also contains some trickery to make sure that + // code is optimized for non-profiling compilation sessions, i.e. anything + // past the filter check is never inlined so it doesn't clutter the fast + // path. + #[inline(always)] + fn exec<F>(&self, event_filter: EventFilter, f: F) -> TimingGuard<'_> + where F: for<'a> FnOnce(&'a SelfProfiler) -> TimingGuard<'a> + { + #[inline(never)] + fn cold_call<F>(profiler_ref: &SelfProfilerRef, f: F) -> TimingGuard<'_> + where F: for<'a> FnOnce(&'a SelfProfiler) -> TimingGuard<'a> + { + let profiler = profiler_ref.profiler.as_ref().unwrap(); + f(&**profiler) + } + + if unlikely!(self.event_filter_mask.contains(event_filter)) { + cold_call(self, f) + } else { + TimingGuard::none() + } + } + + /// Start profiling a generic activity. Profiling continues until the + /// TimingGuard returned from this call is dropped. + #[inline(always)] + pub fn generic_activity(&self, event_id: &str) -> TimingGuard<'_> { + self.exec(EventFilter::GENERIC_ACTIVITIES, |profiler| { + let event_id = profiler.profiler.alloc_string(event_id); + TimingGuard::start( + profiler, + profiler.generic_activity_event_kind, + event_id + ) + }) + } + + /// Start profiling a generic activity. Profiling continues until + /// `generic_activity_end` is called. The RAII-based `generic_activity` + /// usually is the better alternative. + #[inline(always)] + pub fn generic_activity_start(&self, event_id: &str) { + self.non_guard_generic_event( + |profiler| profiler.generic_activity_event_kind, + |profiler| profiler.profiler.alloc_string(event_id), + EventFilter::GENERIC_ACTIVITIES, + TimestampKind::Start, + ); + } + + /// End profiling a generic activity that was started with + /// `generic_activity_start`. The RAII-based `generic_activity` usually is + /// the better alternative. + #[inline(always)] + pub fn generic_activity_end(&self, event_id: &str) { + self.non_guard_generic_event( + |profiler| profiler.generic_activity_event_kind, + |profiler| profiler.profiler.alloc_string(event_id), + EventFilter::GENERIC_ACTIVITIES, + TimestampKind::End, + ); + } + + /// Start profiling a query provider. Profiling continues until the + /// TimingGuard returned from this call is dropped. + #[inline(always)] + pub fn query_provider(&self, query_name: QueryName) -> TimingGuard<'_> { + self.exec(EventFilter::QUERY_PROVIDERS, |profiler| { + let event_id = SelfProfiler::get_query_name_string_id(query_name); + TimingGuard::start(profiler, profiler.query_event_kind, event_id) + }) + } + + /// Record a query in-memory cache hit. + #[inline(always)] + pub fn query_cache_hit(&self, query_name: QueryName) { + self.non_guard_query_event( + |profiler| profiler.query_cache_hit_event_kind, + query_name, + EventFilter::QUERY_CACHE_HITS, + TimestampKind::Instant, + ); + } + + /// Start profiling a query being blocked on a concurrent execution. + /// Profiling continues until `query_blocked_end` is called. + #[inline(always)] + pub fn query_blocked_start(&self, query_name: QueryName) { + self.non_guard_query_event( + |profiler| profiler.query_blocked_event_kind, + query_name, + EventFilter::QUERY_BLOCKED, + TimestampKind::Start, + ); + } + + /// End profiling a query being blocked on a concurrent execution. + #[inline(always)] + pub fn query_blocked_end(&self, query_name: QueryName) { + self.non_guard_query_event( + |profiler| profiler.query_blocked_event_kind, + query_name, + EventFilter::QUERY_BLOCKED, + TimestampKind::End, + ); + } + + /// Start profiling how long it takes to load a query result from the + /// incremental compilation on-disk cache. Profiling continues until the + /// TimingGuard returned from this call is dropped. + #[inline(always)] + pub fn incr_cache_loading(&self, query_name: QueryName) -> TimingGuard<'_> { + self.exec(EventFilter::INCR_CACHE_LOADS, |profiler| { + let event_id = SelfProfiler::get_query_name_string_id(query_name); + TimingGuard::start( + profiler, + profiler.incremental_load_result_event_kind, + event_id + ) + }) + } + + #[inline(always)] + fn non_guard_query_event( + &self, + event_kind: fn(&SelfProfiler) -> StringId, + query_name: QueryName, + event_filter: EventFilter, + timestamp_kind: TimestampKind + ) { + drop(self.exec(event_filter, |profiler| { + let event_id = SelfProfiler::get_query_name_string_id(query_name); + let thread_id = thread_id_to_u64(std::thread::current().id()); + + profiler.profiler.record_event( + event_kind(profiler), + event_id, + thread_id, + timestamp_kind, + ); + + TimingGuard::none() + })); + } + + #[inline(always)] + fn non_guard_generic_event<F: FnOnce(&SelfProfiler) -> StringId>( + &self, + event_kind: fn(&SelfProfiler) -> StringId, + event_id: F, + event_filter: EventFilter, + timestamp_kind: TimestampKind + ) { + drop(self.exec(event_filter, |profiler| { + let thread_id = thread_id_to_u64(std::thread::current().id()); + + profiler.profiler.record_event( + event_kind(profiler), + event_id(profiler), + thread_id, + timestamp_kind, + ); + + TimingGuard::none() + })); + } +} + pub struct SelfProfiler { profiler: Profiler, event_filter_mask: EventFilter, @@ -143,103 +343,51 @@ impl SelfProfiler { let id = SelfProfiler::get_query_name_string_id(query_name); self.profiler.alloc_string_with_reserved_id(id, query_name.as_str()); } +} - #[inline] - pub fn start_activity( - &self, - label: impl Into<Cow<'static, str>>, - ) { - if self.event_filter_mask.contains(EventFilter::GENERIC_ACTIVITIES) { - self.record(&label.into(), self.generic_activity_event_kind, TimestampKind::Start); - } - } - - #[inline] - pub fn end_activity( - &self, - label: impl Into<Cow<'static, str>>, - ) { - if self.event_filter_mask.contains(EventFilter::GENERIC_ACTIVITIES) { - self.record(&label.into(), self.generic_activity_event_kind, TimestampKind::End); - } - } - - #[inline] - pub fn record_query_hit(&self, query_name: QueryName) { - if self.event_filter_mask.contains(EventFilter::QUERY_CACHE_HITS) { - self.record_query(query_name, self.query_cache_hit_event_kind, TimestampKind::Instant); - } - } - - #[inline] - pub fn start_query(&self, query_name: QueryName) { - if self.event_filter_mask.contains(EventFilter::QUERY_PROVIDERS) { - self.record_query(query_name, self.query_event_kind, TimestampKind::Start); - } - } - - #[inline] - pub fn end_query(&self, query_name: QueryName) { - if self.event_filter_mask.contains(EventFilter::QUERY_PROVIDERS) { - self.record_query(query_name, self.query_event_kind, TimestampKind::End); - } - } - - #[inline] - pub fn incremental_load_result_start(&self, query_name: QueryName) { - if self.event_filter_mask.contains(EventFilter::INCR_CACHE_LOADS) { - self.record_query( - query_name, - self.incremental_load_result_event_kind, - TimestampKind::Start - ); - } - } - - #[inline] - pub fn incremental_load_result_end(&self, query_name: QueryName) { - if self.event_filter_mask.contains(EventFilter::INCR_CACHE_LOADS) { - self.record_query( - query_name, - self.incremental_load_result_event_kind, - TimestampKind::End - ); - } - } +#[must_use] +pub struct TimingGuard<'a>(Option<TimingGuardInternal<'a>>); - #[inline] - pub fn query_blocked_start(&self, query_name: QueryName) { - if self.event_filter_mask.contains(EventFilter::QUERY_BLOCKED) { - self.record_query(query_name, self.query_blocked_event_kind, TimestampKind::Start); - } - } +struct TimingGuardInternal<'a> { + raw_profiler: &'a Profiler, + event_id: StringId, + event_kind: StringId, + thread_id: u64, +} +impl<'a> TimingGuard<'a> { #[inline] - pub fn query_blocked_end(&self, query_name: QueryName) { - if self.event_filter_mask.contains(EventFilter::QUERY_BLOCKED) { - self.record_query(query_name, self.query_blocked_event_kind, TimestampKind::End); - } + pub fn start( + profiler: &'a SelfProfiler, + event_kind: StringId, + event_id: StringId, + ) -> TimingGuard<'a> { + let thread_id = thread_id_to_u64(std::thread::current().id()); + let raw_profiler = &profiler.profiler; + raw_profiler.record_event(event_kind, event_id, thread_id, TimestampKind::Start); + + TimingGuard(Some(TimingGuardInternal { + raw_profiler, + event_kind, + event_id, + thread_id, + })) } #[inline] - fn record(&self, event_id: &str, event_kind: StringId, timestamp_kind: TimestampKind) { - let thread_id = thread_id_to_u64(std::thread::current().id()); - - let event_id = self.profiler.alloc_string(event_id); - self.profiler.record_event(event_kind, event_id, thread_id, timestamp_kind); + pub fn none() -> TimingGuard<'a> { + TimingGuard(None) } +} +impl<'a> Drop for TimingGuardInternal<'a> { #[inline] - fn record_query( - &self, - query_name: QueryName, - event_kind: StringId, - timestamp_kind: TimestampKind, - ) { - let dep_node_name = SelfProfiler::get_query_name_string_id(query_name); - - let thread_id = thread_id_to_u64(std::thread::current().id()); - - self.profiler.record_event(event_kind, dep_node_name, thread_id, timestamp_kind); + fn drop(&mut self) { + self.raw_profiler.record_event( + self.event_kind, + self.event_id, + self.thread_id, + TimestampKind::End + ); } } |
