style: Make Servo use a single thread-pool for layout-related tasks per-process.

Instead of per-document. This also allows to reuse this thread-pool if needed
for other stuff, like parallel CSS parsing (#22478), and to share more code with
Gecko, which is always nice.
This commit is contained in:
Emilio Cobos Álvarez 2018-12-17 23:46:42 +01:00
parent 27bb33cb9e
commit 006e71c7de
12 changed files with 56 additions and 77 deletions

View file

@ -219,7 +219,6 @@ impl Pipeline {
window_size: window_size, window_size: window_size,
pipeline_port: pipeline_port, pipeline_port: pipeline_port,
content_process_shutdown_chan: Some(layout_content_process_shutdown_chan), content_process_shutdown_chan: Some(layout_content_process_shutdown_chan),
layout_threads: PREFS.get("layout.threads").as_u64().expect("count") as usize,
}; };
if let Err(e) = if let Err(e) =
@ -556,12 +555,6 @@ impl UnprivilegedPipelineContent {
Some(self.layout_content_process_shutdown_chan), Some(self.layout_content_process_shutdown_chan),
self.webrender_api_sender, self.webrender_api_sender,
self.webrender_document, self.webrender_document,
self.prefs
.get("layout.threads")
.expect("exists")
.value()
.as_u64()
.expect("count") as usize,
paint_time_metrics, paint_time_metrics,
); );

View file

@ -102,10 +102,11 @@ use std::thread;
use std::time::Duration; use std::time::Duration;
use style::animation::Animation; use style::animation::Animation;
use style::context::{QuirksMode, RegisteredSpeculativePainter, RegisteredSpeculativePainters}; use style::context::{QuirksMode, RegisteredSpeculativePainter, RegisteredSpeculativePainters};
use style::context::{SharedStyleContext, StyleSystemOptions, ThreadLocalStyleContextCreationInfo}; use style::context::{SharedStyleContext, ThreadLocalStyleContextCreationInfo};
use style::dom::{ShowSubtree, ShowSubtreeDataAndPrimaryValues, TElement, TNode}; use style::dom::{ShowSubtree, ShowSubtreeDataAndPrimaryValues, TElement, TNode};
use style::driver; use style::driver;
use style::error_reporting::RustLogReporter; use style::error_reporting::RustLogReporter;
use style::global_style_data::{GLOBAL_STYLE_DATA, STYLE_THREAD_POOL};
use style::invalidation::element::restyle_hints::RestyleHint; use style::invalidation::element::restyle_hints::RestyleHint;
use style::logical_geometry::LogicalPoint; use style::logical_geometry::LogicalPoint;
use style::media_queries::{Device, MediaList, MediaType}; use style::media_queries::{Device, MediaList, MediaType};
@ -178,9 +179,6 @@ pub struct LayoutThread {
/// Is this the first reflow in this LayoutThread? /// Is this the first reflow in this LayoutThread?
first_reflow: Cell<bool>, first_reflow: Cell<bool>,
/// The workers that we use for parallel operation.
parallel_traversal: Option<rayon::ThreadPool>,
/// Flag to indicate whether to use parallel operations /// Flag to indicate whether to use parallel operations
parallel_flag: bool, parallel_flag: bool,
@ -238,10 +236,6 @@ pub struct LayoutThread {
/// only be a test-mode timer during testing for animations. /// only be a test-mode timer during testing for animations.
timer: Timer, timer: Timer,
// Number of layout threads. This is copied from `servo_config::opts`, but we'd
// rather limit the dependency on that module here.
layout_threads: usize,
/// Paint time metrics. /// Paint time metrics.
paint_time_metrics: PaintTimeMetrics, paint_time_metrics: PaintTimeMetrics,
@ -270,7 +264,6 @@ impl LayoutThreadFactory for LayoutThread {
content_process_shutdown_chan: Option<IpcSender<()>>, content_process_shutdown_chan: Option<IpcSender<()>>,
webrender_api_sender: webrender_api::RenderApiSender, webrender_api_sender: webrender_api::RenderApiSender,
webrender_document: webrender_api::DocumentId, webrender_document: webrender_api::DocumentId,
layout_threads: usize,
paint_time_metrics: PaintTimeMetrics, paint_time_metrics: PaintTimeMetrics,
) { ) {
thread::Builder::new() thread::Builder::new()
@ -308,7 +301,6 @@ impl LayoutThreadFactory for LayoutThread {
mem_profiler_chan.clone(), mem_profiler_chan.clone(),
webrender_api_sender, webrender_api_sender,
webrender_document, webrender_document,
layout_threads,
paint_time_metrics, paint_time_metrics,
); );
@ -470,7 +462,6 @@ impl LayoutThread {
mem_profiler_chan: profile_mem::ProfilerChan, mem_profiler_chan: profile_mem::ProfilerChan,
webrender_api_sender: webrender_api::RenderApiSender, webrender_api_sender: webrender_api::RenderApiSender,
webrender_document: webrender_api::DocumentId, webrender_document: webrender_api::DocumentId,
layout_threads: usize,
paint_time_metrics: PaintTimeMetrics, paint_time_metrics: PaintTimeMetrics,
) -> LayoutThread { ) -> LayoutThread {
// The device pixel ratio is incorrect (it does not have the hidpi value), // The device pixel ratio is incorrect (it does not have the hidpi value),
@ -481,17 +472,6 @@ impl LayoutThread {
TypedScale::new(opts::get().device_pixels_per_px.unwrap_or(1.0)), TypedScale::new(opts::get().device_pixels_per_px.unwrap_or(1.0)),
); );
let workers = rayon::ThreadPoolBuilder::new()
.num_threads(layout_threads)
.start_handler(|_| thread_state::initialize_layout_worker_thread())
.build();
let parallel_traversal = if layout_threads > 1 {
Some(workers.expect("ThreadPool creation failed"))
} else {
None
};
debug!("Possible layout Threads: {}", layout_threads);
// Create the channel on which new animations can be sent. // Create the channel on which new animations can be sent.
let (new_animations_sender, new_animations_receiver) = unbounded(); let (new_animations_sender, new_animations_receiver) = unbounded();
@ -521,7 +501,6 @@ impl LayoutThread {
first_reflow: Cell::new(true), first_reflow: Cell::new(true),
font_cache_receiver: font_cache_receiver, font_cache_receiver: font_cache_receiver,
font_cache_sender: ipc_font_cache_sender, font_cache_sender: ipc_font_cache_sender,
parallel_traversal: parallel_traversal,
parallel_flag: true, parallel_flag: true,
generation: Cell::new(0), generation: Cell::new(0),
new_animations_sender: new_animations_sender, new_animations_sender: new_animations_sender,
@ -563,7 +542,6 @@ impl LayoutThread {
} else { } else {
Timer::new() Timer::new()
}, },
layout_threads: layout_threads,
paint_time_metrics: paint_time_metrics, paint_time_metrics: paint_time_metrics,
layout_query_waiting_time: Histogram::new(), layout_query_waiting_time: Histogram::new(),
} }
@ -596,8 +574,8 @@ impl LayoutThread {
id: self.id, id: self.id,
style_context: SharedStyleContext { style_context: SharedStyleContext {
stylist: &self.stylist, stylist: &self.stylist,
options: StyleSystemOptions::default(), options: GLOBAL_STYLE_DATA.options.clone(),
guards: guards, guards,
visited_styles_enabled: false, visited_styles_enabled: false,
running_animations: self.running_animations.clone(), running_animations: self.running_animations.clone(),
expired_animations: self.expired_animations.clone(), expired_animations: self.expired_animations.clone(),
@ -881,7 +859,6 @@ impl LayoutThread {
info.content_process_shutdown_chan, info.content_process_shutdown_chan,
self.webrender_api.clone_sender(), self.webrender_api.clone_sender(),
self.webrender_document, self.webrender_document,
info.layout_threads,
info.paint_time_metrics, info.paint_time_metrics,
); );
} }
@ -924,8 +901,6 @@ impl LayoutThread {
); );
self.root_flow.borrow_mut().take(); self.root_flow.borrow_mut().take();
// Drop the rayon threadpool if present.
let _ = self.parallel_traversal.take();
self.background_hang_monitor.unregister(); self.background_hang_monitor.unregister();
} }
@ -1166,7 +1141,7 @@ impl LayoutThread {
// Parallelize if there's more than 750 objects based on rzambre's suggestion // Parallelize if there's more than 750 objects based on rzambre's suggestion
// https://github.com/servo/servo/issues/10110 // https://github.com/servo/servo/issues/10110
self.parallel_flag = self.layout_threads > 1 && data.dom_count > 750; self.parallel_flag = data.dom_count > 750;
debug!("layout: received layout request for: {}", self.url); debug!("layout: received layout request for: {}", self.url);
debug!("Number of objects in DOM: {}", data.dom_count); debug!("Number of objects in DOM: {}", data.dom_count);
debug!("layout: parallel? {}", self.parallel_flag); debug!("layout: parallel? {}", self.parallel_flag);
@ -1373,10 +1348,13 @@ impl LayoutThread {
// Create a layout context for use throughout the following passes. // Create a layout context for use throughout the following passes.
let mut layout_context = self.build_layout_context(guards.clone(), true, &map); let mut layout_context = self.build_layout_context(guards.clone(), true, &map);
let thread_pool = if self.parallel_flag { let (thread_pool, num_threads) = if self.parallel_flag {
self.parallel_traversal.as_ref() (
STYLE_THREAD_POOL.style_thread_pool.as_ref(),
STYLE_THREAD_POOL.num_threads,
)
} else { } else {
None (None, 1)
}; };
let traversal = RecalcStyleAndConstructFlows::new(layout_context); let traversal = RecalcStyleAndConstructFlows::new(layout_context);
@ -1404,14 +1382,14 @@ impl LayoutThread {
}, },
); );
// TODO(pcwalton): Measure energy usage of text shaping, perhaps? // TODO(pcwalton): Measure energy usage of text shaping, perhaps?
let text_shaping_time = (font::get_and_reset_text_shaping_performance_counter() as u64) / let text_shaping_time =
(self.layout_threads as u64); font::get_and_reset_text_shaping_performance_counter() / num_threads;
profile_time::send_profile_data( profile_time::send_profile_data(
profile_time::ProfilerCategory::LayoutTextShaping, profile_time::ProfilerCategory::LayoutTextShaping,
self.profiler_metadata(), self.profiler_metadata(),
&self.time_profiler_chan, &self.time_profiler_chan,
0, 0,
text_shaping_time, text_shaping_time as u64,
0, 0,
0, 0,
); );
@ -1742,12 +1720,16 @@ impl LayoutThread {
|| { || {
let profiler_metadata = self.profiler_metadata(); let profiler_metadata = self.profiler_metadata();
if let (true, Some(traversal)) = let thread_pool = if self.parallel_flag {
(self.parallel_flag, self.parallel_traversal.as_ref()) STYLE_THREAD_POOL.style_thread_pool.as_ref()
{ } else {
None
};
if let Some(pool) = thread_pool {
// Parallel mode. // Parallel mode.
LayoutThread::solve_constraints_parallel( LayoutThread::solve_constraints_parallel(
traversal, pool,
FlowRef::deref_mut(root_flow), FlowRef::deref_mut(root_flow),
profiler_metadata, profiler_metadata,
self.time_profiler_chan.clone(), self.time_profiler_chan.clone(),
@ -1913,7 +1895,8 @@ fn get_ua_stylesheets() -> Result<UserAgentStylesheets, &'static str> {
)))) ))))
} }
let shared_lock = SharedRwLock::new(); let shared_lock = &GLOBAL_STYLE_DATA.shared_lock;
// FIXME: presentational-hints.css should be at author origin with zero specificity. // FIXME: presentational-hints.css should be at author origin with zero specificity.
// (Does it make a difference?) // (Does it make a difference?)
let mut user_or_user_agent_stylesheets = vec![ let mut user_or_user_agent_stylesheets = vec![
@ -1958,7 +1941,7 @@ fn get_ua_stylesheets() -> Result<UserAgentStylesheets, &'static str> {
)?; )?;
Ok(UserAgentStylesheets { Ok(UserAgentStylesheets {
shared_lock: shared_lock, shared_lock: shared_lock.clone(),
user_or_user_agent_stylesheets: user_or_user_agent_stylesheets, user_or_user_agent_stylesheets: user_or_user_agent_stylesheets,
quirks_mode_stylesheet: quirks_mode_stylesheet, quirks_mode_stylesheet: quirks_mode_stylesheet,
}) })

View file

@ -43,7 +43,6 @@ pub trait LayoutThreadFactory {
content_process_shutdown_chan: Option<IpcSender<()>>, content_process_shutdown_chan: Option<IpcSender<()>>,
webrender_api_sender: webrender_api::RenderApiSender, webrender_api_sender: webrender_api::RenderApiSender,
webrender_document: webrender_api::DocumentId, webrender_document: webrender_api::DocumentId,
layout_threads: usize,
paint_time_metrics: PaintTimeMetrics, paint_time_metrics: PaintTimeMetrics,
); );
} }

View file

@ -38,7 +38,6 @@ use script_traits::{
WindowSizeData, WindowSizeData,
}; };
use script_traits::{NewLayoutInfo, ScriptMsg}; use script_traits::{NewLayoutInfo, ScriptMsg};
use servo_config::prefs::PREFS;
use servo_url::ServoUrl; use servo_url::ServoUrl;
use std::cell::Cell; use std::cell::Cell;
use style::attr::{AttrValue, LengthOrPercentageOrAuto}; use style::attr::{AttrValue, LengthOrPercentageOrAuto};
@ -204,7 +203,6 @@ impl HTMLIFrameElement {
}, },
device_pixel_ratio: window.device_pixel_ratio(), device_pixel_ratio: window.device_pixel_ratio(),
}, },
layout_threads: PREFS.get("layout.threads").as_u64().expect("count") as usize,
}; };
self.pipeline_id.set(Some(new_pipeline_id)); self.pipeline_id.set(Some(new_pipeline_id));

View file

@ -45,7 +45,6 @@ use msg::constellation_msg::BrowsingContextId;
use msg::constellation_msg::PipelineId; use msg::constellation_msg::PipelineId;
use msg::constellation_msg::TopLevelBrowsingContextId; use msg::constellation_msg::TopLevelBrowsingContextId;
use script_traits::{AuxiliaryBrowsingContextLoadInfo, LoadData, NewLayoutInfo, ScriptMsg}; use script_traits::{AuxiliaryBrowsingContextLoadInfo, LoadData, NewLayoutInfo, ScriptMsg};
use servo_config::prefs::PREFS;
use servo_url::ServoUrl; use servo_url::ServoUrl;
use std::cell::Cell; use std::cell::Cell;
use std::ptr; use std::ptr;
@ -290,7 +289,6 @@ impl WindowProxy {
pipeline_port: pipeline_receiver, pipeline_port: pipeline_receiver,
content_process_shutdown_chan: None, content_process_shutdown_chan: None,
window_size: window.window_size(), window_size: window.window_size(),
layout_threads: PREFS.get("layout.threads").as_u64().expect("count") as usize,
}; };
let constellation_msg = ScriptMsg::ScriptNewAuxiliary(load_info, pipeline_sender); let constellation_msg = ScriptMsg::ScriptNewAuxiliary(load_info, pipeline_sender);
window.send_to_constellation(constellation_msg); window.send_to_constellation(constellation_msg);

View file

@ -1910,7 +1910,6 @@ impl ScriptThread {
window_size, window_size,
pipeline_port, pipeline_port,
content_process_shutdown_chan, content_process_shutdown_chan,
layout_threads,
} = new_layout_info; } = new_layout_info;
let layout_pair = unbounded(); let layout_pair = unbounded();
@ -1927,7 +1926,6 @@ impl ScriptThread {
script_chan: self.control_chan.clone(), script_chan: self.control_chan.clone(),
image_cache: self.image_cache.clone(), image_cache: self.image_cache.clone(),
content_process_shutdown_chan: content_process_shutdown_chan, content_process_shutdown_chan: content_process_shutdown_chan,
layout_threads: layout_threads,
paint_time_metrics: PaintTimeMetrics::new( paint_time_metrics: PaintTimeMetrics::new(
new_pipeline_id, new_pipeline_id,
self.time_profiler_chan.clone(), self.time_profiler_chan.clone(),

View file

@ -220,6 +220,5 @@ pub struct NewLayoutThreadInfo {
pub script_chan: IpcSender<ConstellationControlMsg>, pub script_chan: IpcSender<ConstellationControlMsg>,
pub image_cache: Arc<dyn ImageCache>, pub image_cache: Arc<dyn ImageCache>,
pub content_process_shutdown_chan: Option<IpcSender<()>>, pub content_process_shutdown_chan: Option<IpcSender<()>>,
pub layout_threads: usize,
pub paint_time_metrics: PaintTimeMetrics, pub paint_time_metrics: PaintTimeMetrics,
} }

View file

@ -195,8 +195,6 @@ pub struct NewLayoutInfo {
pub pipeline_port: IpcReceiver<LayoutControlMsg>, pub pipeline_port: IpcReceiver<LayoutControlMsg>,
/// A shutdown channel so that layout can tell the content process to shut down when it's done. /// A shutdown channel so that layout can tell the content process to shut down when it's done.
pub content_process_shutdown_chan: Option<IpcSender<()>>, pub content_process_shutdown_chan: Option<IpcSender<()>>,
/// Number of threads to use for layout.
pub layout_threads: usize,
} }
/// When a pipeline is closed, should its browsing context be discarded too? /// When a pipeline is closed, should its browsing context be discarded too?

View file

@ -16,8 +16,7 @@ path = "lib.rs"
doctest = false doctest = false
[features] [features]
gecko = ["num_cpus", gecko = ["style_traits/gecko", "fallible/known_system_malloc"]
"style_traits/gecko", "fallible/known_system_malloc"]
use_bindgen = ["bindgen", "regex", "toml"] use_bindgen = ["bindgen", "regex", "toml"]
servo = ["serde", "style_traits/servo", "servo_atoms", "servo_config", "html5ever", servo = ["serde", "style_traits/servo", "servo_atoms", "servo_config", "html5ever",
"cssparser/serde", "encoding_rs", "malloc_size_of/servo", "arrayvec/use_union", "cssparser/serde", "encoding_rs", "malloc_size_of/servo", "arrayvec/use_union",
@ -47,7 +46,7 @@ log = "0.4"
malloc_size_of = { path = "../malloc_size_of" } malloc_size_of = { path = "../malloc_size_of" }
malloc_size_of_derive = { path = "../malloc_size_of_derive" } malloc_size_of_derive = { path = "../malloc_size_of_derive" }
matches = "0.1" matches = "0.1"
num_cpus = {version = "1.1.0", optional = true} num_cpus = {version = "1.1.0"}
num-integer = "0.1" num-integer = "0.1"
num-traits = "0.2" num-traits = "0.2"
num-derive = "0.2" num-derive = "0.2"

View file

@ -10,7 +10,6 @@ mod non_ts_pseudo_class_list;
pub mod arc_types; pub mod arc_types;
pub mod conversions; pub mod conversions;
pub mod data; pub mod data;
pub mod global_style_data;
pub mod media_features; pub mod media_features;
pub mod media_queries; pub mod media_queries;
pub mod pseudo_element; pub mod pseudo_element;

View file

@ -5,15 +5,13 @@
//! Global style data //! Global style data
use crate::context::StyleSystemOptions; use crate::context::StyleSystemOptions;
#[cfg(feature = "gecko")]
use crate::gecko_bindings::bindings; use crate::gecko_bindings::bindings;
use crate::parallel::STYLE_THREAD_STACK_SIZE_KB; use crate::parallel::STYLE_THREAD_STACK_SIZE_KB;
use crate::shared_lock::SharedRwLock; use crate::shared_lock::SharedRwLock;
use crate::thread_state; use crate::thread_state;
use num_cpus;
use rayon; use rayon;
use std::cmp;
use std::env; use std::env;
use std::ffi::CString;
/// Global style data /// Global style data
pub struct GlobalStyleData { pub struct GlobalStyleData {
@ -37,20 +35,22 @@ fn thread_name(index: usize) -> String {
format!("StyleThread#{}", index) format!("StyleThread#{}", index)
} }
fn thread_startup(index: usize) { fn thread_startup(_index: usize) {
thread_state::initialize_layout_worker_thread(); thread_state::initialize_layout_worker_thread();
#[cfg(feature = "gecko")]
unsafe { unsafe {
use std::ffi::CString;
bindings::Gecko_SetJemallocThreadLocalArena(true); bindings::Gecko_SetJemallocThreadLocalArena(true);
} let name = thread_name(_index);
let name = thread_name(index); let name = CString::new(name).unwrap();
let name = CString::new(name).unwrap();
unsafe {
// Gecko_RegisterProfilerThread copies the passed name here. // Gecko_RegisterProfilerThread copies the passed name here.
bindings::Gecko_RegisterProfilerThread(name.as_ptr()); bindings::Gecko_RegisterProfilerThread(name.as_ptr());
} }
} }
fn thread_shutdown(_: usize) { fn thread_shutdown(_: usize) {
#[cfg(feature = "gecko")]
unsafe { unsafe {
bindings::Gecko_UnregisterProfilerThread(); bindings::Gecko_UnregisterProfilerThread();
bindings::Gecko_SetJemallocThreadLocalArena(false); bindings::Gecko_SetJemallocThreadLocalArena(false);
@ -64,12 +64,26 @@ lazy_static! {
.map(|s| s.parse::<usize>().expect("invalid STYLO_THREADS value")); .map(|s| s.parse::<usize>().expect("invalid STYLO_THREADS value"));
let mut num_threads = match stylo_threads { let mut num_threads = match stylo_threads {
Ok(num) => num, Ok(num) => num,
// The default heuristic is num_virtual_cores * .75. This gives us #[cfg(feature = "servo")]
// three threads on a hyper-threaded dual core, and six threads on _ => {
// a hyper-threaded quad core. The performance benefit of additional // We always set this pref on startup, before layout or script
// threads seems to level off at around six, so we cap it there on // have had a chance of accessing (and thus creating) the
// many-core machines (see bug 1431285 comment 14). // thread-pool.
_ => cmp::min(cmp::max(num_cpus::get() * 3 / 4, 1), 6), use servo_config::prefs::PREFS;
PREFS.get("layout.threads").as_u64().unwrap() as usize
}
#[cfg(feature = "gecko")]
_ => {
// The default heuristic is num_virtual_cores * .75. This gives
// us three threads on a hyper-threaded dual core, and six
// threads on a hyper-threaded quad core. The performance
// benefit of additional threads seems to level off at around
// six, so we cap it there on many-core machines
// (see bug 1431285 comment 14).
use num_cpus;
use std::cmp;
cmp::min(cmp::max(num_cpus::get() * 3 / 4, 1), 6)
}
}; };
// If num_threads is one, there's no point in creating a thread pool, so // If num_threads is one, there's no point in creating a thread pool, so

View file

@ -133,6 +133,7 @@ pub mod font_metrics;
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
#[allow(unsafe_code)] #[allow(unsafe_code)]
pub mod gecko_bindings; pub mod gecko_bindings;
pub mod global_style_data;
pub mod hash; pub mod hash;
pub mod invalidation; pub mod invalidation;
#[allow(missing_docs)] // TODO. #[allow(missing_docs)] // TODO.