Skip to content

Commit

Permalink
Auto merge of servo#20302 - upsuper:traversal-stats, r=bholley
Browse files Browse the repository at this point in the history
Include some traversal statistics in style tracing marker

This is the Servo side changes of [bug 1444296](https://bugzilla.mozilla.org/show_bug.cgi?id=1444296).

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/20302)
<!-- Reviewable:end -->
  • Loading branch information
bors-servo authored Mar 15, 2018
2 parents 863f847 + 368fb57 commit 8e6cfbc
Show file tree
Hide file tree
Showing 5 changed files with 4,865 additions and 2,798 deletions.
113 changes: 55 additions & 58 deletions components/style/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ impl ElementCascadeInputs {
/// thread and then combine them after the threads join via the Add
/// implementation below.
#[derive(Default)]
pub struct TraversalStatistics {
pub struct PerThreadTraversalStatistics {
/// The total number of elements traversed.
pub elements_traversed: u32,
/// The number of elements where has_styles() went from false to true.
Expand All @@ -308,6 +308,28 @@ pub struct TraversalStatistics {
/// The number of styles reused via rule node comparison from the
/// StyleSharingCache.
pub styles_reused: u32,
}

/// Implementation of Add to aggregate statistics across different threads.
impl<'a> ops::Add for &'a PerThreadTraversalStatistics {
type Output = PerThreadTraversalStatistics;
fn add(self, other: Self) -> PerThreadTraversalStatistics {
PerThreadTraversalStatistics {
elements_traversed: self.elements_traversed + other.elements_traversed,
elements_styled: self.elements_styled + other.elements_styled,
elements_matched: self.elements_matched + other.elements_matched,
styles_shared: self.styles_shared + other.styles_shared,
styles_reused: self.styles_reused + other.styles_reused,
}
}
}

/// Statistics gathered during the traversal plus some information from
/// other sources including stylist.
#[derive(Default)]
pub struct TraversalStatistics {
/// Aggregated statistics gathered during the traversal.
pub aggregated: PerThreadTraversalStatistics,
/// The number of selectors in the stylist.
pub selectors: u32,
/// The number of revalidation selectors.
Expand All @@ -321,38 +343,9 @@ pub struct TraversalStatistics {
/// Time spent in the traversal, in milliseconds.
pub traversal_time_ms: f64,
/// Whether this was a parallel traversal.
pub is_parallel: Option<bool>,
pub is_parallel: bool,
/// Whether this is a "large" traversal.
pub is_large: Option<bool>,
}

/// Implementation of Add to aggregate statistics across different threads.
impl<'a> ops::Add for &'a TraversalStatistics {
type Output = TraversalStatistics;
fn add(self, other: Self) -> TraversalStatistics {
debug_assert!(self.traversal_time_ms == 0.0 && other.traversal_time_ms == 0.0,
"traversal_time_ms should be set at the end by the caller");
debug_assert!(self.selectors == 0, "set at the end");
debug_assert!(self.revalidation_selectors == 0, "set at the end");
debug_assert!(self.dependency_selectors == 0, "set at the end");
debug_assert!(self.declarations == 0, "set at the end");
debug_assert!(self.stylist_rebuilds == 0, "set at the end");
TraversalStatistics {
elements_traversed: self.elements_traversed + other.elements_traversed,
elements_styled: self.elements_styled + other.elements_styled,
elements_matched: self.elements_matched + other.elements_matched,
styles_shared: self.styles_shared + other.styles_shared,
styles_reused: self.styles_reused + other.styles_reused,
selectors: 0,
revalidation_selectors: 0,
dependency_selectors: 0,
declarations: 0,
stylist_rebuilds: 0,
traversal_time_ms: 0.0,
is_parallel: None,
is_large: None,
}
}
pub is_large: bool,
}

/// Format the statistics in a way that the performance test harness understands.
Expand All @@ -361,16 +354,16 @@ impl fmt::Display for TraversalStatistics {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
debug_assert!(self.traversal_time_ms != 0.0, "should have set traversal time");
writeln!(f, "[PERF] perf block start")?;
writeln!(f, "[PERF],traversal,{}", if self.is_parallel.unwrap() {
writeln!(f, "[PERF],traversal,{}", if self.is_parallel {
"parallel"
} else {
"sequential"
})?;
writeln!(f, "[PERF],elements_traversed,{}", self.elements_traversed)?;
writeln!(f, "[PERF],elements_styled,{}", self.elements_styled)?;
writeln!(f, "[PERF],elements_matched,{}", self.elements_matched)?;
writeln!(f, "[PERF],styles_shared,{}", self.styles_shared)?;
writeln!(f, "[PERF],styles_reused,{}", self.styles_reused)?;
writeln!(f, "[PERF],elements_traversed,{}", self.aggregated.elements_traversed)?;
writeln!(f, "[PERF],elements_styled,{}", self.aggregated.elements_styled)?;
writeln!(f, "[PERF],elements_matched,{}", self.aggregated.elements_matched)?;
writeln!(f, "[PERF],styles_shared,{}", self.aggregated.styles_shared)?;
writeln!(f, "[PERF],styles_reused,{}", self.aggregated.styles_reused)?;
writeln!(f, "[PERF],selectors,{}", self.selectors)?;
writeln!(f, "[PERF],revalidation_selectors,{}", self.revalidation_selectors)?;
writeln!(f, "[PERF],dependency_selectors,{}", self.dependency_selectors)?;
Expand All @@ -382,29 +375,33 @@ impl fmt::Display for TraversalStatistics {
}

impl TraversalStatistics {
/// Computes the traversal time given the start time in seconds.
pub fn finish<E, D>(&mut self, traversal: &D, parallel: bool, start: f64)
/// Generate complete traversal statistics.
///
/// The traversal time is computed given the start time in seconds.
pub fn new<E, D>(
aggregated: PerThreadTraversalStatistics,
traversal: &D,
parallel: bool,
start: f64
) -> TraversalStatistics
where
E: TElement,
D: DomTraversal<E>,
{
let threshold = traversal.shared_context().options.style_statistics_threshold;
let stylist = traversal.shared_context().stylist;

self.is_parallel = Some(parallel);
self.is_large = Some(self.elements_traversed as usize >= threshold);
self.traversal_time_ms = (time::precise_time_s() - start) * 1000.0;
self.selectors = stylist.num_selectors() as u32;
self.revalidation_selectors = stylist.num_revalidation_selectors() as u32;
self.dependency_selectors = stylist.num_invalidations() as u32;
self.declarations = stylist.num_declarations() as u32;
self.stylist_rebuilds = stylist.num_rebuilds() as u32;
}

/// Returns whether this traversal is 'large' in order to avoid console spam
/// from lots of tiny traversals.
pub fn is_large_traversal(&self) -> bool {
self.is_large.unwrap()
let is_large = aggregated.elements_traversed as usize >= threshold;
TraversalStatistics {
aggregated,
selectors: stylist.num_selectors() as u32,
revalidation_selectors: stylist.num_revalidation_selectors() as u32,
dependency_selectors: stylist.num_invalidations() as u32,
declarations: stylist.num_declarations() as u32,
stylist_rebuilds: stylist.num_rebuilds() as u32,
traversal_time_ms: (time::precise_time_s() - start) * 1000.0,
is_parallel: parallel,
is_large
}
}
}

Expand Down Expand Up @@ -714,7 +711,7 @@ pub struct ThreadLocalStyleContext<E: TElement> {
/// than the current element).
pub selector_flags: SelectorFlagsMap<E>,
/// Statistics about the traversal.
pub statistics: TraversalStatistics,
pub statistics: PerThreadTraversalStatistics,
/// The struct used to compute and cache font metrics from style
/// for evaluation of the font-relative em/ch units and font-size
pub font_metrics_provider: E::FontMetricsProvider,
Expand All @@ -736,7 +733,7 @@ impl<E: TElement> ThreadLocalStyleContext<E> {
new_animations_sender: shared.local_context_creation_data.lock().unwrap().new_animations_sender.clone(),
tasks: SequentialTaskList(Vec::new()),
selector_flags: SelectorFlagsMap::new(),
statistics: TraversalStatistics::default(),
statistics: PerThreadTraversalStatistics::default(),
font_metrics_provider: E::FontMetricsProvider::create_from(shared),
stack_limit_checker: StackLimitChecker::new(
(STYLE_THREAD_STACK_SIZE_KB - STACK_SAFETY_MARGIN_KB) * 1024),
Expand All @@ -753,7 +750,7 @@ impl<E: TElement> ThreadLocalStyleContext<E> {
bloom_filter: StyleBloom::new(),
tasks: SequentialTaskList(Vec::new()),
selector_flags: SelectorFlagsMap::new(),
statistics: TraversalStatistics::default(),
statistics: PerThreadTraversalStatistics::default(),
font_metrics_provider: E::FontMetricsProvider::create_from(shared),
stack_limit_checker: StackLimitChecker::new(
(STYLE_THREAD_STACK_SIZE_KB - STACK_SAFETY_MARGIN_KB) * 1024),
Expand Down
55 changes: 49 additions & 6 deletions components/style/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
#![deny(missing_docs)]

use context::{StyleContext, ThreadLocalStyleContext};
use context::{StyleContext, PerThreadTraversalStatistics};
use context::{ThreadLocalStyleContext, TraversalStatistics};
use dom::{SendNode, TElement, TNode};
use parallel;
use parallel::{DispatchMode, WORK_UNIT_MAX};
Expand All @@ -18,6 +19,36 @@ use std::mem;
use time;
use traversal::{DomTraversal, PerLevelTraversalData, PreTraverseToken};

#[cfg(feature = "servo")]
fn should_report_statistics() -> bool {
false
}

#[cfg(feature = "gecko")]
fn should_report_statistics() -> bool {
unsafe { ::gecko_bindings::structs::ServoTraversalStatistics_sActive }
}

#[cfg(feature = "servo")]
fn report_statistics(_stats: &PerThreadTraversalStatistics) {
unreachable!("Servo never report stats");
}

#[cfg(feature = "gecko")]
fn report_statistics(stats: &PerThreadTraversalStatistics) {
// This should only be called in the main thread, or it may be racy
// to update the statistics in a global variable.
debug_assert!(unsafe { ::gecko_bindings::bindings::Gecko_IsMainThread() });
let gecko_stats = unsafe {
&mut ::gecko_bindings::structs::ServoTraversalStatistics_sSingleton
};
gecko_stats.mElementsTraversed += stats.elements_traversed;
gecko_stats.mElementsStyled += stats.elements_styled;
gecko_stats.mElementsMatched += stats.elements_matched;
gecko_stats.mStylesShared += stats.styles_shared;
gecko_stats.mStylesReused += stats.styles_reused;
}

/// Do a DOM traversal for top-down and (optionally) bottom-up processing,
/// generic over `D`.
///
Expand All @@ -41,6 +72,7 @@ where
let root =
token.traversal_root().expect("Should've ensured we needed to traverse");

let report_stats = should_report_statistics();
let dump_stats = traversal.shared_context().options.dump_style_statistics;
let start_time = if dump_stats { Some(time::precise_time_s()) } else { None };

Expand Down Expand Up @@ -113,8 +145,8 @@ where
}
}

// dump statistics to stdout if requested
if dump_stats {
// Collect statistics from thread-locals if requested.
if dump_stats || report_stats {
let mut aggregate =
mem::replace(&mut context.thread_local.statistics, Default::default());
let parallel = maybe_tls.is_some();
Expand All @@ -128,9 +160,20 @@ where
});
}

aggregate.finish(traversal, parallel, start_time.unwrap());
if aggregate.is_large_traversal() {
println!("{}", aggregate);
if report_stats {
report_statistics(&aggregate);
}
// dump statistics to stdout if requested
if dump_stats {
let stats = TraversalStatistics::new(
aggregate,
traversal,
parallel,
start_time.unwrap()
);
if stats.is_large {
println!("{}", stats);
}
}
}
}
Loading

0 comments on commit 8e6cfbc

Please sign in to comment.