mirror of
https://github.com/servo/servo.git
synced 2025-06-22 08:08:59 +01:00
layout: Switch parallel selector matching over to using work stealing.
10% speedup over what we had before.
This commit is contained in:
parent
d944567b9a
commit
cb2e225b14
4 changed files with 81 additions and 60 deletions
|
@ -8,19 +8,14 @@ use css::node_style::StyledNode;
|
||||||
use layout::incremental;
|
use layout::incremental;
|
||||||
use layout::util::LayoutDataAccess;
|
use layout::util::LayoutDataAccess;
|
||||||
use layout::wrapper::LayoutNode;
|
use layout::wrapper::LayoutNode;
|
||||||
use servo_util::task::spawn_named;
|
|
||||||
|
|
||||||
use extra::arc::{Arc, RWArc};
|
use extra::arc::Arc;
|
||||||
use std::cast;
|
|
||||||
use std::libc::uintptr_t;
|
|
||||||
use std::rt;
|
|
||||||
use std::vec;
|
|
||||||
use style::{TNode, Stylist, cascade};
|
use style::{TNode, Stylist, cascade};
|
||||||
use style::{Before, After};
|
use style::{Before, After};
|
||||||
|
|
||||||
pub trait MatchMethods {
|
pub trait MatchMethods {
|
||||||
fn match_node(&self, stylist: &Stylist);
|
fn match_node(&self, stylist: &Stylist);
|
||||||
fn match_subtree(&self, stylist: RWArc<Stylist>);
|
fn match_subtree(&self, stylist: &Stylist);
|
||||||
|
|
||||||
fn cascade_subtree(&self, parent: Option<LayoutNode>);
|
fn cascade_subtree(&self, parent: Option<LayoutNode>);
|
||||||
}
|
}
|
||||||
|
@ -47,53 +42,12 @@ impl<'ln> MatchMethods for LayoutNode<'ln> {
|
||||||
None => fail!("no layout data")
|
None => fail!("no layout data")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn match_subtree(&self, stylist: RWArc<Stylist>) {
|
|
||||||
let num_tasks = rt::default_sched_threads() * 2;
|
|
||||||
let mut node_count = 0;
|
|
||||||
let mut nodes_per_task = vec::from_elem(num_tasks, ~[]);
|
|
||||||
|
|
||||||
|
fn match_subtree(&self, stylist: &Stylist) {
|
||||||
for node in self.traverse_preorder() {
|
for node in self.traverse_preorder() {
|
||||||
if node.is_element() {
|
if node.is_element() {
|
||||||
nodes_per_task[node_count % num_tasks].push(node);
|
|
||||||
node_count += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let (port, chan) = SharedChan::new();
|
|
||||||
let mut num_spawned = 0;
|
|
||||||
|
|
||||||
for nodes in nodes_per_task.move_iter() {
|
|
||||||
if nodes.len() > 0 {
|
|
||||||
let chan = chan.clone();
|
|
||||||
let stylist = stylist.clone();
|
|
||||||
|
|
||||||
// FIXME(pcwalton): This transmute is to work around the fact that we have no
|
|
||||||
// mechanism for safe fork/join parallelism. If we had such a thing, then we could
|
|
||||||
// close over the lifetime-bounded `LayoutNode`. But we can't, so we force it with
|
|
||||||
// a transmute.
|
|
||||||
let evil: uintptr_t = unsafe {
|
|
||||||
cast::transmute(nodes)
|
|
||||||
};
|
|
||||||
|
|
||||||
let evil = Some(evil);
|
|
||||||
spawn_named("MatchMethods for LayoutNode", proc() {
|
|
||||||
let mut evil = evil;
|
|
||||||
let nodes: ~[LayoutNode] = unsafe {
|
|
||||||
cast::transmute(evil.take_unwrap())
|
|
||||||
};
|
|
||||||
|
|
||||||
stylist.read(|stylist| {
|
|
||||||
for node in nodes.iter() {
|
|
||||||
node.match_node(stylist);
|
node.match_node(stylist);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
chan.send(());
|
|
||||||
});
|
|
||||||
num_spawned += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _ in range(0, num_spawned) {
|
|
||||||
port.recv();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ use gfx::font_context::{FontContext, FontContextInfo};
|
||||||
use servo_msg::constellation_msg::ConstellationChan;
|
use servo_msg::constellation_msg::ConstellationChan;
|
||||||
use servo_net::local_image_cache::LocalImageCache;
|
use servo_net::local_image_cache::LocalImageCache;
|
||||||
use servo_util::geometry::Au;
|
use servo_util::geometry::Au;
|
||||||
|
use style::Stylist;
|
||||||
|
|
||||||
#[thread_local]
|
#[thread_local]
|
||||||
static mut FONT_CONTEXT: *mut FontContext = 0 as *mut FontContext;
|
static mut FONT_CONTEXT: *mut FontContext = 0 as *mut FontContext;
|
||||||
|
@ -39,6 +40,11 @@ pub struct LayoutContext {
|
||||||
|
|
||||||
/// Information needed to construct a font context.
|
/// Information needed to construct a font context.
|
||||||
font_context_info: FontContextInfo,
|
font_context_info: FontContextInfo,
|
||||||
|
|
||||||
|
/// The CSS selector stylist.
|
||||||
|
///
|
||||||
|
/// FIXME(pcwalton): Make this no longer an unsafe pointer once we have fast `RWArc`s.
|
||||||
|
stylist: *Stylist,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LayoutContext {
|
impl LayoutContext {
|
||||||
|
|
|
@ -22,7 +22,7 @@ use layout::parallel;
|
||||||
use layout::util::{LayoutDataAccess, OpaqueNode, LayoutDataWrapper};
|
use layout::util::{LayoutDataAccess, OpaqueNode, LayoutDataWrapper};
|
||||||
use layout::wrapper::LayoutNode;
|
use layout::wrapper::LayoutNode;
|
||||||
|
|
||||||
use extra::arc::{Arc, MutexArc, RWArc};
|
use extra::arc::{Arc, MutexArc};
|
||||||
use geom::rect::Rect;
|
use geom::rect::Rect;
|
||||||
use geom::size::Size2D;
|
use geom::size::Size2D;
|
||||||
use gfx::display_list::{ClipDisplayItemClass, DisplayItem, DisplayItemIterator, DisplayList};
|
use gfx::display_list::{ClipDisplayItemClass, DisplayItem, DisplayItemIterator, DisplayList};
|
||||||
|
@ -91,7 +91,7 @@ pub struct LayoutTask {
|
||||||
/// A cached display list.
|
/// A cached display list.
|
||||||
display_list: Option<Arc<DisplayList<OpaqueNode>>>,
|
display_list: Option<Arc<DisplayList<OpaqueNode>>>,
|
||||||
|
|
||||||
stylist: RWArc<Stylist>,
|
stylist: ~Stylist,
|
||||||
|
|
||||||
/// The workers that we use for parallel operation.
|
/// The workers that we use for parallel operation.
|
||||||
parallel_traversal: Option<WorkQueue<*mut LayoutContext,UnsafeFlow>>,
|
parallel_traversal: Option<WorkQueue<*mut LayoutContext,UnsafeFlow>>,
|
||||||
|
@ -274,7 +274,7 @@ impl LayoutTask {
|
||||||
leaf_set: MutexArc::new(LeafSet::new()),
|
leaf_set: MutexArc::new(LeafSet::new()),
|
||||||
|
|
||||||
display_list: None,
|
display_list: None,
|
||||||
stylist: RWArc::new(new_stylist()),
|
stylist: ~new_stylist(),
|
||||||
parallel_traversal: parallel_traversal,
|
parallel_traversal: parallel_traversal,
|
||||||
profiler_chan: profiler_chan,
|
profiler_chan: profiler_chan,
|
||||||
opts: opts.clone()
|
opts: opts.clone()
|
||||||
|
@ -302,6 +302,7 @@ impl LayoutTask {
|
||||||
constellation_chan: self.constellation_chan.clone(),
|
constellation_chan: self.constellation_chan.clone(),
|
||||||
leaf_set: self.leaf_set.clone(),
|
leaf_set: self.leaf_set.clone(),
|
||||||
font_context_info: font_context_info,
|
font_context_info: font_context_info,
|
||||||
|
stylist: &*self.stylist,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -379,10 +380,7 @@ impl LayoutTask {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_add_stylesheet(&mut self, sheet: Stylesheet) {
|
fn handle_add_stylesheet(&mut self, sheet: Stylesheet) {
|
||||||
let mut sheet = Some(sheet);
|
self.stylist.add_stylesheet(sheet, AuthorOrigin)
|
||||||
self.stylist.write(|stylist| {
|
|
||||||
stylist.add_stylesheet(sheet.take_unwrap(), AuthorOrigin);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds the flow tree.
|
/// Builds the flow tree.
|
||||||
|
@ -521,7 +519,12 @@ impl LayoutTask {
|
||||||
ReflowDocumentDamage => {}
|
ReflowDocumentDamage => {}
|
||||||
_ => {
|
_ => {
|
||||||
profile(time::LayoutSelectorMatchCategory, self.profiler_chan.clone(), || {
|
profile(time::LayoutSelectorMatchCategory, self.profiler_chan.clone(), || {
|
||||||
node.match_subtree(self.stylist.clone());
|
match self.parallel_traversal {
|
||||||
|
None => node.match_subtree(self.stylist),
|
||||||
|
Some(ref mut traversal) => {
|
||||||
|
parallel::match_subtree(node, &mut layout_ctx, traversal)
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
profile(time::LayoutSelectorCascadeCategory, self.profiler_chan.clone(), || {
|
profile(time::LayoutSelectorCascadeCategory, self.profiler_chan.clone(), || {
|
||||||
node.cascade_subtree(None);
|
node.cascade_subtree(None);
|
||||||
|
|
|
@ -2,19 +2,25 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
//! Implements parallel traversals over the flow tree.
|
//! Implements parallel traversals over the DOM and flow trees.
|
||||||
|
//!
|
||||||
|
//! This code is highly unsafe. Keep this file small and easy to audit.
|
||||||
|
|
||||||
|
use css::matching::MatchMethods;
|
||||||
use layout::context::LayoutContext;
|
use layout::context::LayoutContext;
|
||||||
use layout::flow::{Flow, LeafSet, PostorderFlowTraversal};
|
use layout::flow::{Flow, LeafSet, PostorderFlowTraversal};
|
||||||
use layout::flow;
|
use layout::flow;
|
||||||
use layout::layout_task::{AssignHeightsAndStoreOverflowTraversal, BubbleWidthsTraversal};
|
use layout::layout_task::{AssignHeightsAndStoreOverflowTraversal, BubbleWidthsTraversal};
|
||||||
|
use layout::wrapper::LayoutNode;
|
||||||
|
|
||||||
use extra::arc::MutexArc;
|
use extra::arc::MutexArc;
|
||||||
use servo_util::time::{ProfilerChan, profile};
|
use servo_util::time::{ProfilerChan, profile};
|
||||||
use servo_util::time;
|
use servo_util::time;
|
||||||
use servo_util::workqueue::{WorkQueue, WorkUnit, WorkerProxy};
|
use servo_util::workqueue::{WorkQueue, WorkUnit, WorkerProxy};
|
||||||
use std::cast;
|
use std::cast;
|
||||||
|
use std::ptr;
|
||||||
use std::sync::atomics::{AtomicInt, Relaxed, SeqCst};
|
use std::sync::atomics::{AtomicInt, Relaxed, SeqCst};
|
||||||
|
use style::{Stylist, TNode};
|
||||||
|
|
||||||
pub enum TraversalKind {
|
pub enum TraversalKind {
|
||||||
BubbleWidthsTraversalKind,
|
BubbleWidthsTraversalKind,
|
||||||
|
@ -39,6 +45,14 @@ pub fn mut_owned_flow_to_unsafe_flow(flow: *mut ~Flow) -> UnsafeFlow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type UnsafeLayoutNode = (uint, uint);
|
||||||
|
|
||||||
|
fn layout_node_to_unsafe_layout_node(node: &LayoutNode) -> UnsafeLayoutNode {
|
||||||
|
unsafe {
|
||||||
|
cast::transmute_copy(node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Information that we need stored in each flow.
|
/// Information that we need stored in each flow.
|
||||||
pub struct FlowParallelInfo {
|
pub struct FlowParallelInfo {
|
||||||
/// The number of children that still need work done.
|
/// The number of children that still need work done.
|
||||||
|
@ -101,6 +115,30 @@ impl<'a> ParallelPostorderFlowTraversal for BubbleWidthsTraversal<'a> {}
|
||||||
|
|
||||||
impl<'a> ParallelPostorderFlowTraversal for AssignHeightsAndStoreOverflowTraversal<'a> {}
|
impl<'a> ParallelPostorderFlowTraversal for AssignHeightsAndStoreOverflowTraversal<'a> {}
|
||||||
|
|
||||||
|
fn match_node(unsafe_layout_node: UnsafeLayoutNode,
|
||||||
|
proxy: &mut WorkerProxy<*mut LayoutContext,UnsafeLayoutNode>) {
|
||||||
|
unsafe {
|
||||||
|
let layout_context: &mut LayoutContext = cast::transmute(*proxy.user_data());
|
||||||
|
|
||||||
|
// Get a real layout node.
|
||||||
|
let node: LayoutNode = cast::transmute(unsafe_layout_node);
|
||||||
|
|
||||||
|
// Enqueue kids.
|
||||||
|
for kid in node.children() {
|
||||||
|
if kid.is_element() {
|
||||||
|
proxy.push(WorkUnit {
|
||||||
|
fun: match_node,
|
||||||
|
data: layout_node_to_unsafe_layout_node(&kid),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform the CSS selector matching.
|
||||||
|
let stylist: &Stylist = cast::transmute(layout_context.stylist);
|
||||||
|
node.match_node(stylist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn bubble_widths(unsafe_flow: UnsafeFlow, proxy: &mut WorkerProxy<*mut LayoutContext,UnsafeFlow>) {
|
fn bubble_widths(unsafe_flow: UnsafeFlow, proxy: &mut WorkerProxy<*mut LayoutContext,UnsafeFlow>) {
|
||||||
let layout_context: &mut LayoutContext = unsafe {
|
let layout_context: &mut LayoutContext = unsafe {
|
||||||
cast::transmute(*proxy.user_data())
|
cast::transmute(*proxy.user_data())
|
||||||
|
@ -122,6 +160,24 @@ fn assign_heights_and_store_overflow(unsafe_flow: UnsafeFlow,
|
||||||
assign_heights_traversal.run_parallel(unsafe_flow)
|
assign_heights_traversal.run_parallel(unsafe_flow)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn match_subtree(root_node: &LayoutNode,
|
||||||
|
layout_context: &mut LayoutContext,
|
||||||
|
queue: &mut WorkQueue<*mut LayoutContext,UnsafeLayoutNode>) {
|
||||||
|
unsafe {
|
||||||
|
queue.data = cast::transmute(layout_context)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enqueue the root node.
|
||||||
|
queue.push(WorkUnit {
|
||||||
|
fun: match_node,
|
||||||
|
data: layout_node_to_unsafe_layout_node(root_node),
|
||||||
|
});
|
||||||
|
|
||||||
|
queue.run();
|
||||||
|
|
||||||
|
queue.data = ptr::mut_null()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn traverse_flow_tree(kind: TraversalKind,
|
pub fn traverse_flow_tree(kind: TraversalKind,
|
||||||
leaf_set: &MutexArc<LeafSet>,
|
leaf_set: &MutexArc<LeafSet>,
|
||||||
profiler_chan: ProfilerChan,
|
profiler_chan: ProfilerChan,
|
||||||
|
@ -147,6 +203,8 @@ pub fn traverse_flow_tree(kind: TraversalKind,
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
queue.run()
|
queue.run();
|
||||||
|
|
||||||
|
queue.data = ptr::mut_null()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue