mirror of
https://github.com/servo/servo.git
synced 2025-07-25 00:00:20 +01:00
Don't do recursive tail calls if there's work in the queue.
MozReview-Commit-ID: 8JEdjqAIYmQ
This commit is contained in:
parent
ce19c949db
commit
f04c25c33c
1 changed files with 24 additions and 29 deletions
|
@ -50,7 +50,7 @@ type NodeList<N> = SmallVec<[SendNode<N>; WORK_UNIT_MAX]>;
|
||||||
pub fn traverse_dom<E, D>(traversal: &D,
|
pub fn traverse_dom<E, D>(traversal: &D,
|
||||||
root: E,
|
root: E,
|
||||||
token: PreTraverseToken,
|
token: PreTraverseToken,
|
||||||
queue: &rayon::ThreadPool)
|
pool: &rayon::ThreadPool)
|
||||||
where E: TElement,
|
where E: TElement,
|
||||||
D: DomTraversal<E>,
|
D: DomTraversal<E>,
|
||||||
{
|
{
|
||||||
|
@ -82,16 +82,17 @@ pub fn traverse_dom<E, D>(traversal: &D,
|
||||||
let traversal_data = PerLevelTraversalData {
|
let traversal_data = PerLevelTraversalData {
|
||||||
current_dom_depth: depth,
|
current_dom_depth: depth,
|
||||||
};
|
};
|
||||||
let tls = ScopedTLS::<D::ThreadLocalContext>::new(queue);
|
let tls = ScopedTLS::<D::ThreadLocalContext>::new(pool);
|
||||||
let root = root.as_node().opaque();
|
let root = root.as_node().opaque();
|
||||||
|
|
||||||
queue.install(|| {
|
pool.install(|| {
|
||||||
rayon::scope(|scope| {
|
rayon::scope(|scope| {
|
||||||
traverse_nodes(nodes,
|
traverse_nodes(nodes,
|
||||||
DispatchMode::TailCall,
|
DispatchMode::TailCall,
|
||||||
root,
|
root,
|
||||||
traversal_data,
|
traversal_data,
|
||||||
scope,
|
scope,
|
||||||
|
pool,
|
||||||
traversal,
|
traversal,
|
||||||
&tls);
|
&tls);
|
||||||
});
|
});
|
||||||
|
@ -144,6 +145,7 @@ fn top_down_dom<'a, 'scope, E, D>(nodes: &'a [SendNode<E::ConcreteNode>],
|
||||||
root: OpaqueNode,
|
root: OpaqueNode,
|
||||||
mut traversal_data: PerLevelTraversalData,
|
mut traversal_data: PerLevelTraversalData,
|
||||||
scope: &'a rayon::Scope<'scope>,
|
scope: &'a rayon::Scope<'scope>,
|
||||||
|
pool: &'scope rayon::ThreadPool,
|
||||||
traversal: &'scope D,
|
traversal: &'scope D,
|
||||||
tls: &'scope ScopedTLS<'scope, D::ThreadLocalContext>)
|
tls: &'scope ScopedTLS<'scope, D::ThreadLocalContext>)
|
||||||
where E: TElement + 'scope,
|
where E: TElement + 'scope,
|
||||||
|
@ -179,6 +181,7 @@ fn top_down_dom<'a, 'scope, E, D>(nodes: &'a [SendNode<E::ConcreteNode>],
|
||||||
root,
|
root,
|
||||||
traversal_data_copy,
|
traversal_data_copy,
|
||||||
scope,
|
scope,
|
||||||
|
pool,
|
||||||
traversal,
|
traversal,
|
||||||
tls);
|
tls);
|
||||||
}
|
}
|
||||||
|
@ -208,6 +211,7 @@ fn top_down_dom<'a, 'scope, E, D>(nodes: &'a [SendNode<E::ConcreteNode>],
|
||||||
root,
|
root,
|
||||||
traversal_data,
|
traversal_data,
|
||||||
scope,
|
scope,
|
||||||
|
pool,
|
||||||
traversal,
|
traversal,
|
||||||
tls);
|
tls);
|
||||||
}
|
}
|
||||||
|
@ -231,6 +235,7 @@ fn traverse_nodes<'a, 'scope, E, D>(nodes: NodeList<E::ConcreteNode>,
|
||||||
root: OpaqueNode,
|
root: OpaqueNode,
|
||||||
traversal_data: PerLevelTraversalData,
|
traversal_data: PerLevelTraversalData,
|
||||||
scope: &'a rayon::Scope<'scope>,
|
scope: &'a rayon::Scope<'scope>,
|
||||||
|
pool: &'scope rayon::ThreadPool,
|
||||||
traversal: &'scope D,
|
traversal: &'scope D,
|
||||||
tls: &'scope ScopedTLS<'scope, D::ThreadLocalContext>)
|
tls: &'scope ScopedTLS<'scope, D::ThreadLocalContext>)
|
||||||
where E: TElement + 'scope,
|
where E: TElement + 'scope,
|
||||||
|
@ -238,42 +243,32 @@ fn traverse_nodes<'a, 'scope, E, D>(nodes: NodeList<E::ConcreteNode>,
|
||||||
{
|
{
|
||||||
debug_assert!(!nodes.is_empty());
|
debug_assert!(!nodes.is_empty());
|
||||||
|
|
||||||
|
// This is a tail call from the perspective of the caller. However, we only
|
||||||
|
// want to actually dispatch the job as a tail call if there's nothing left
|
||||||
|
// in our local queue. Otherwise we need to return to it to maintain proper
|
||||||
|
// breadth-first ordering.
|
||||||
|
let may_dispatch_tail = mode.is_tail_call() &&
|
||||||
|
!pool.current_thread_has_pending_tasks().unwrap();
|
||||||
|
|
||||||
// In the common case, our children fit within a single work unit, in which
|
// In the common case, our children fit within a single work unit, in which
|
||||||
// case we can pass the SmallVec directly and avoid extra allocation.
|
// case we can pass the SmallVec directly and avoid extra allocation.
|
||||||
if nodes.len() <= WORK_UNIT_MAX {
|
if nodes.len() <= WORK_UNIT_MAX {
|
||||||
if mode.is_tail_call() {
|
if may_dispatch_tail {
|
||||||
// If this is a tail call, bypass rayon and invoke top_down_dom directly.
|
top_down_dom(&nodes, root, traversal_data, scope, pool, traversal, tls);
|
||||||
top_down_dom(&nodes, root, traversal_data, scope, traversal, tls);
|
|
||||||
} else {
|
} else {
|
||||||
// The caller isn't done yet. Append to the queue and return synchronously.
|
|
||||||
scope.spawn(move |scope| {
|
scope.spawn(move |scope| {
|
||||||
let nodes = nodes;
|
let nodes = nodes;
|
||||||
top_down_dom(&nodes, root, traversal_data, scope, traversal, tls);
|
top_down_dom(&nodes, root, traversal_data, scope, pool, traversal, tls);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// FIXME(bholley): This should be an ArrayVec.
|
|
||||||
let mut first_chunk: Option<NodeList<E::ConcreteNode>> = None;
|
|
||||||
for chunk in nodes.chunks(WORK_UNIT_MAX) {
|
for chunk in nodes.chunks(WORK_UNIT_MAX) {
|
||||||
if mode.is_tail_call() && first_chunk.is_none() {
|
let boxed = chunk.iter().cloned().collect::<Vec<_>>().into_boxed_slice();
|
||||||
first_chunk = Some(chunk.iter().cloned().collect::<NodeList<E::ConcreteNode>>());
|
let traversal_data_copy = traversal_data.clone();
|
||||||
} else {
|
scope.spawn(move |scope| {
|
||||||
let boxed = chunk.iter().cloned().collect::<Vec<_>>().into_boxed_slice();
|
let b = boxed;
|
||||||
let traversal_data_copy = traversal_data.clone();
|
top_down_dom(&*b, root, traversal_data_copy, scope, pool, traversal, tls)
|
||||||
scope.spawn(move |scope| {
|
});
|
||||||
let b = boxed;
|
|
||||||
top_down_dom(&*b, root, traversal_data_copy, scope, traversal, tls)
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this is a tail call, bypass rayon for the first chunk and invoke top_down_dom
|
|
||||||
// directly.
|
|
||||||
debug_assert_eq!(first_chunk.is_some(), mode.is_tail_call());
|
|
||||||
if let Some(c) = first_chunk {
|
|
||||||
debug_assert_eq!(c.len(), WORK_UNIT_MAX);
|
|
||||||
top_down_dom(&*c, root, traversal_data, scope, traversal, tls);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue