Eliminate the sequential/traversal parallel distinction in favor of a unified adaptive driver.

MozReview-Commit-ID: ADVTNJntzmp
This commit is contained in:
Bobby Holley 2017-08-24 11:48:24 -07:00
parent f7c6b2f04e
commit 707ab455bb
13 changed files with 164 additions and 208 deletions

View file

@ -23,15 +23,13 @@
#![deny(missing_docs)]
use arrayvec::ArrayVec;
use context::{StyleContext, ThreadLocalStyleContext, TraversalStatistics};
use dom::{OpaqueNode, SendNode, TElement, TNode};
use context::{StyleContext, ThreadLocalStyleContext};
use dom::{OpaqueNode, SendNode, TElement};
use itertools::Itertools;
use rayon;
use scoped_tls::ScopedTLS;
use smallvec::SmallVec;
use std::borrow::Borrow;
use time;
use traversal::{DomTraversal, PerLevelTraversalData, PreTraverseToken};
use traversal::{DomTraversal, PerLevelTraversalData};
/// The minimum stack size for a thread in the styling pool, in kilobytes.
pub const STYLE_THREAD_STACK_SIZE_KB: usize = 128;
@ -54,59 +52,6 @@ pub const WORK_UNIT_MAX: usize = 16;
/// threads, so we keep it compact.
type WorkUnit<N> = ArrayVec<[SendNode<N>; WORK_UNIT_MAX]>;
/// Entry point for the parallel traversal.
#[allow(unsafe_code)]
pub fn traverse_dom<E, D>(traversal: &D,
root: E,
token: PreTraverseToken,
pool: &rayon::ThreadPool)
where E: TElement,
D: DomTraversal<E>,
{
debug_assert!(traversal.is_parallel());
debug_assert!(token.should_traverse());
let dump_stats = traversal.shared_context().options.dump_style_statistics;
let start_time = if dump_stats { Some(time::precise_time_s()) } else { None };
let traversal_data = PerLevelTraversalData {
current_dom_depth: root.depth(),
};
let tls = ScopedTLS::<ThreadLocalStyleContext<E>>::new(pool);
let send_root = unsafe { SendNode::new(root.as_node()) };
pool.install(|| {
rayon::scope(|scope| {
let root = send_root;
let root_opaque = root.opaque();
traverse_nodes(Some(root).into_iter(),
DispatchMode::TailCall,
true,
root_opaque,
traversal_data,
scope,
pool,
traversal,
&tls);
});
});
// Dump statistics to stdout if requested.
if dump_stats {
let slots = unsafe { tls.unsafe_get() };
let mut aggregate = slots.iter().fold(TraversalStatistics::default(), |acc, t| {
match *t.borrow() {
None => acc,
Some(ref cx) => &cx.borrow().statistics + &acc,
}
});
aggregate.finish(traversal, start_time.unwrap());
if aggregate.is_large_traversal() {
println!("{}", aggregate);
}
}
}
/// A callback to create our thread local context. This needs to be
/// out of line so we don't allocate stack space for the entire struct
/// in the caller.
@ -255,8 +200,10 @@ fn top_down_dom<'a, 'scope, E, D>(nodes: &'a [SendNode<E::ConcreteNode>],
/// Controls whether traverse_nodes may make a recursive call to continue
/// doing work, or whether it should always dispatch work asynchronously.
#[derive(Clone, Copy, PartialEq)]
enum DispatchMode {
pub enum DispatchMode {
/// This is the last operation by the caller.
TailCall,
/// This is not the last operation by the caller.
NotTailCall,
}
@ -267,7 +214,7 @@ impl DispatchMode {
/// Enqueues |nodes| for processing, possibly on this thread if the tail call
/// conditions are met.
#[inline]
fn traverse_nodes<'a, 'scope, E, D, I>(
pub fn traverse_nodes<'a, 'scope, E, D, I>(
nodes: I,
mode: DispatchMode,
recursion_ok: bool,