Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use rayon to drive parallel layout and styling. #13641

Merged
merged 2 commits into from Nov 14, 2016
Merged
Changes from 1 commit
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

Next

style: Use rayon instead of our custom work queue.

  • Loading branch information
emilio committed Nov 14, 2016
commit 73917cce83d2225b51b29c374d861d71ec69435f
@@ -31,6 +31,7 @@ parking_lot = "0.3.3"
plugins = {path = "../plugins"}
profile_traits = {path = "../profile_traits"}
range = {path = "../range"}
rayon = "0.5"
script_layout_interface = {path = "../script_layout_interface"}
script_traits = {path = "../script_traits"}
selectors = "0.14"
@@ -44,6 +44,7 @@ extern crate plugins as servo_plugins;
extern crate profile_traits;
#[macro_use]
extern crate range;
extern crate rayon;
extern crate script_layout_interface;
extern crate script_traits;
extern crate serde;
@@ -12,12 +12,11 @@ use context::{LayoutContext, SharedLayoutContext};
use flow::{self, Flow, MutableFlowUtils, PostorderFlowTraversal, PreorderFlowTraversal};
use flow_ref::FlowRef;
use profile_traits::time::{self, TimerMetadata, profile};
use rayon;
use std::mem;
use std::sync::atomic::{AtomicIsize, Ordering};
use style::dom::UnsafeNode;
use style::parallel::{CHUNK_SIZE, WorkQueueData};
use style::parallel::run_queue_with_custom_work_data_type;
use style::workqueue::{WorkQueue, WorkUnit, WorkerProxy};
use style::parallel::CHUNK_SIZE;
use traversal::{AssignISizes, BubbleISizes};
use traversal::AssignBSizes;
use util::opts;
@@ -50,10 +49,8 @@ pub fn borrowed_flow_to_unsafe_flow(flow: &Flow) -> UnsafeFlow {
}
}

pub type UnsafeFlowList = (Box<Vec<UnsafeNode>>, usize);

pub type ChunkedFlowTraversalFunction =
extern "Rust" fn(UnsafeFlowList, &mut WorkerProxy<SharedLayoutContext, UnsafeFlowList>);
pub type ChunkedFlowTraversalFunction<'scope> =
extern "Rust" fn(Box<[UnsafeFlow]>, &'scope SharedLayoutContext, &rayon::Scope<'scope>);

pub type FlowTraversalFunction = extern "Rust" fn(UnsafeFlow, &SharedLayoutContext);

@@ -133,27 +130,35 @@ trait ParallelPostorderFlowTraversal : PostorderFlowTraversal {

/// A parallel top-down flow traversal.
trait ParallelPreorderFlowTraversal : PreorderFlowTraversal {
fn run_parallel(&self,
unsafe_flows: UnsafeFlowList,
proxy: &mut WorkerProxy<SharedLayoutContext, UnsafeFlowList>);
fn run_parallel<'scope>(&self,
unsafe_flows: &[UnsafeFlow],
layout_context: &'scope SharedLayoutContext,
scope: &rayon::Scope<'scope>);

fn should_record_thread_ids(&self) -> bool;

#[inline(always)]
fn run_parallel_helper(&self,
unsafe_flows: UnsafeFlowList,
proxy: &mut WorkerProxy<SharedLayoutContext, UnsafeFlowList>,
top_down_func: ChunkedFlowTraversalFunction,
bottom_up_func: FlowTraversalFunction) {
let mut discovered_child_flows = Vec::new();
for unsafe_flow in *unsafe_flows.0 {
fn run_parallel_helper<'scope>(&self,
unsafe_flows: &[UnsafeFlow],
layout_context: &'scope SharedLayoutContext,
scope: &rayon::Scope<'scope>,
top_down_func: ChunkedFlowTraversalFunction<'scope>,
bottom_up_func: FlowTraversalFunction)
{
let mut discovered_child_flows = vec![];
for unsafe_flow in unsafe_flows {
let mut had_children = false;
unsafe {
// Get a real flow.
let flow: &mut Flow = mem::transmute(unsafe_flow);
let flow: &mut Flow = mem::transmute(*unsafe_flow);

if self.should_record_thread_ids() {
flow::mut_base(flow).thread_id = proxy.worker_index();
// FIXME(emilio): With the switch to rayon we can no longer
// access a thread id from here easily. Either instrument
// rayon (the unstable feature) to get a worker thread
// identifier, or remove all the layout tinting mode.
//
// flow::mut_base(flow).thread_id = proxy.worker_index();

This comment has been minimized.

Copy link
@larsbergstrom

larsbergstrom Oct 7, 2016

Contributor

Do we no longer use this?

This comment has been minimized.

Copy link
@emilio

emilio Oct 7, 2016

Author Member

That was for the layout tinting debug mode, I'm not sure it still works now.

}

if self.should_process(flow) {
@@ -170,25 +175,29 @@ trait ParallelPreorderFlowTraversal : PreorderFlowTraversal {

// If there were no more children, start assigning block-sizes.
if !had_children {
bottom_up_func(unsafe_flow, proxy.user_data())
bottom_up_func(*unsafe_flow, layout_context)
}
}

for chunk in discovered_child_flows.chunks(CHUNK_SIZE) {
proxy.push(WorkUnit {
fun: top_down_func,
data: (box chunk.iter().cloned().collect(), 0),
let nodes = chunk.iter().cloned().collect::<Vec<_>>().into_boxed_slice();

scope.spawn(move |scope| {
top_down_func(nodes, layout_context, scope);
});
}
}
}

impl<'a> ParallelPreorderFlowTraversal for AssignISizes<'a> {
fn run_parallel(&self,
unsafe_flows: UnsafeFlowList,
proxy: &mut WorkerProxy<SharedLayoutContext, UnsafeFlowList>) {
fn run_parallel<'scope>(&self,
unsafe_flows: &[UnsafeFlow],
layout_context: &'scope SharedLayoutContext,
scope: &rayon::Scope<'scope>)
{
self.run_parallel_helper(unsafe_flows,
proxy,
layout_context,
scope,
assign_inline_sizes,
assign_block_sizes_and_store_overflow)
}
@@ -200,13 +209,13 @@ impl<'a> ParallelPreorderFlowTraversal for AssignISizes<'a> {

impl<'a> ParallelPostorderFlowTraversal for AssignBSizes<'a> {}

fn assign_inline_sizes(unsafe_flows: UnsafeFlowList,
proxy: &mut WorkerProxy<SharedLayoutContext, UnsafeFlowList>) {
let shared_layout_context = proxy.user_data();
fn assign_inline_sizes<'scope>(unsafe_flows: Box<[UnsafeFlow]>,
shared_layout_context: &'scope SharedLayoutContext,
scope: &rayon::Scope<'scope>) {
let assign_inline_sizes_traversal = AssignISizes {
shared_context: &shared_layout_context.style_context,
};
assign_inline_sizes_traversal.run_parallel(unsafe_flows, proxy)
assign_inline_sizes_traversal.run_parallel(&unsafe_flows, shared_layout_context, scope)
}

fn assign_block_sizes_and_store_overflow(
@@ -224,20 +233,21 @@ pub fn traverse_flow_tree_preorder(
profiler_metadata: Option<TimerMetadata>,
time_profiler_chan: time::ProfilerChan,
shared_layout_context: &SharedLayoutContext,
queue: &mut WorkQueue<SharedLayoutContext, WorkQueueData>) {
queue: &rayon::ThreadPool) {
if opts::get().bubble_inline_sizes_separately {
let layout_context = LayoutContext::new(shared_layout_context);
let bubble_inline_sizes = BubbleISizes { layout_context: &layout_context };
root.traverse_postorder(&bubble_inline_sizes);
}

run_queue_with_custom_work_data_type(queue, |queue| {
profile(time::ProfilerCategory::LayoutParallelWarmup, profiler_metadata,
time_profiler_chan, || {
queue.push(WorkUnit {
fun: assign_inline_sizes,
data: (box vec![borrowed_flow_to_unsafe_flow(root)], 0),
})
let nodes = vec![borrowed_flow_to_unsafe_flow(root)].into_boxed_slice();

queue.install(move || {
rayon::scope(move |scope| {
profile(time::ProfilerCategory::LayoutParallelWarmup,
profiler_metadata, time_profiler_chan, move || {
assign_inline_sizes(nodes, &shared_layout_context, scope);
});
});
}, shared_layout_context);
});
}
@@ -27,6 +27,7 @@ net_traits = {path = "../net_traits"}
parking_lot = {version = "0.3.3", features = ["nightly"]}
plugins = {path = "../plugins"}
profile_traits = {path = "../profile_traits"}
rayon = "0.5"
script = {path = "../script"}
script_layout_interface = {path = "../script_layout_interface"}
script_traits = {path = "../script_traits"}
@@ -34,6 +34,7 @@ extern crate net_traits;
extern crate parking_lot;
#[macro_use]
extern crate profile_traits;
extern crate rayon;
extern crate script;
extern crate script_layout_interface;
extern crate script_traits;
@@ -107,14 +108,12 @@ use style::dom::{TDocument, TElement, TNode};
use style::error_reporting::{ParseErrorReporter, StdoutErrorReporter};
use style::logical_geometry::LogicalPoint;
use style::media_queries::{Device, MediaType};
use style::parallel::WorkQueueData;
use style::parser::ParserContextExtraData;
use style::selector_matching::Stylist;
use style::servo::restyle_damage::{REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, REPOSITION, STORE_OVERFLOW};
use style::stylesheets::{Origin, Stylesheet, UserAgentStylesheets};
use style::thread_state;
use style::timer::Timer;
use style::workqueue::WorkQueue;
use url::Url;
use util::geometry::max_rect;
use util::opts;
@@ -173,7 +172,7 @@ pub struct LayoutThread {
first_reflow: bool,

/// The workers that we use for parallel operation.
parallel_traversal: Option<WorkQueue<SharedLayoutContext, WorkQueueData>>,
parallel_traversal: Option<rayon::ThreadPool>,

/// Starts at zero, and increased by one every time a layout completes.
/// This can be used to easily check for invalid stale data.
@@ -383,7 +382,9 @@ impl LayoutThread {
MediaType::Screen,
opts::get().initial_window_size.to_f32() * ScaleFactor::new(1.0));
let parallel_traversal = if layout_threads != 1 {
WorkQueue::new("LayoutWorker", thread_state::LAYOUT, layout_threads).ok()
let configuration =
rayon::Configuration::new().set_num_threads(layout_threads);
rayon::ThreadPool::new(configuration).ok()
} else {
None
};
@@ -711,19 +712,6 @@ impl LayoutThread {
size: heap_size_of_local_context(),
});

// ... as do each of the LayoutWorkers, if present.
if let Some(ref traversal) = self.parallel_traversal {
let sizes = traversal.heap_size_of_tls(heap_size_of_local_context);
for (i, size) in sizes.iter().enumerate() {
reports.push(Report {
path: path![formatted_url,
format!("layout-worker-{}-local-context", i)],
kind: ReportKind::ExplicitJemallocHeapSize,
size: *size,
});
}
}

reports_chan.send(reports);
}

@@ -773,9 +761,8 @@ impl LayoutThread {
/// Shuts down the layout thread now. If there are any DOM nodes left, layout will now (safely)
/// crash.
fn exit_now(&mut self) {
if let Some(ref mut traversal) = self.parallel_traversal {
traversal.shutdown()
}
// Drop the rayon threadpool if present.
let _ = self.parallel_traversal.take();
}

fn handle_add_stylesheet<'a, 'b>(&self,
@@ -855,7 +842,7 @@ impl LayoutThread {
/// This corresponds to `Reflow()` in Gecko and `layout()` in WebKit/Blink and should be
/// benchmarked against those two. It is marked `#[inline(never)]` to aid profiling.
#[inline(never)]
fn solve_constraints_parallel(traversal: &mut WorkQueue<SharedLayoutContext, WorkQueueData>,
fn solve_constraints_parallel(traversal: &rayon::ThreadPool,
layout_root: &mut Flow,
profiler_metadata: Option<TimerMetadata>,
time_profiler_chan: time::ProfilerChan,

Some generated files are not rendered by default. Learn more.

@@ -13,19 +13,18 @@ path = "lib.rs"
doctest = false

[features]
gecko = ["nsstring_vendor"]
gecko = ["nsstring_vendor", "num_cpus", "rayon/unstable"]
servo = ["serde/unstable", "serde", "serde_derive", "heapsize_derive",
"style_traits/servo", "app_units/plugins", "servo_atoms", "html5ever-atoms",
"cssparser/heap_size", "cssparser/serde-serialization",
"url/heap_size", "plugins"]
"url/heap_size", "plugins", "rayon/unstable"]
testing = []

[dependencies]
app_units = "0.3"
bitflags = "0.7"
cfg-if = "0.1.0"
cssparser = "0.7"
deque = "0.3.1"
encoding = "0.2"
euclid = "0.10.1"
fnv = "1.0"
@@ -34,17 +33,16 @@ heapsize_derive = {version = "0.1", optional = true}
html5ever-atoms = {version = "0.1", optional = true}
lazy_static = "0.2"
log = "0.3.5"
libc = "0.2"
matches = "0.1"
nsstring_vendor = {path = "gecko_bindings/nsstring_vendor", optional = true}
num-integer = "0.1.32"
num-traits = "0.1.32"
num_cpus = "1.1.0"
ordered-float = "0.2.2"
owning_ref = "0.2.2"
parking_lot = "0.3.3"
quickersort = "2.0.0"
rand = "0.3"
rayon = "0.5"
rustc-serialize = "0.3"
selectors = "0.14"
serde = {version = "0.8", optional = true}
@@ -58,11 +56,12 @@ url = "1.2"
util = {path = "../util"}
plugins = {path = "../plugins", optional = true}

[dependencies.num_cpus]
optional = true
version = "1.0"

[target.'cfg(windows)'.dependencies]
kernel32-sys = "0.2"

[target.'cfg(not(windows))'.dependencies]
libc = "0.2"

[build-dependencies]
walkdir = "0.1"
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.