mirror of
https://github.com/servo/servo.git
synced 2025-07-23 15:23:42 +01:00
Auto merge of #12862 - servo:layout-new, r=emilio
added dom obj counting to decide sequential/parallel layout (#10110) This is a rebased version of #11713 --- - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes fix #10110 (github issue number if applicable). - [X] There are no tests for these changes because it's an optimization with no visible behavioral changes <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/12862) <!-- Reviewable:end -->
This commit is contained in:
commit
0fe94a6724
7 changed files with 76 additions and 35 deletions
|
@ -175,6 +175,9 @@ pub struct LayoutThread {
|
||||||
/// The workers that we use for parallel operation.
|
/// The workers that we use for parallel operation.
|
||||||
parallel_traversal: Option<rayon::ThreadPool>,
|
parallel_traversal: Option<rayon::ThreadPool>,
|
||||||
|
|
||||||
|
/// Flag to indicate whether to use parallel operations
|
||||||
|
parallel_flag: bool,
|
||||||
|
|
||||||
/// Starts at zero, and increased by one every time a layout completes.
|
/// Starts at zero, and increased by one every time a layout completes.
|
||||||
/// This can be used to easily check for invalid stale data.
|
/// This can be used to easily check for invalid stale data.
|
||||||
generation: u32,
|
generation: u32,
|
||||||
|
@ -389,13 +392,11 @@ impl LayoutThread {
|
||||||
let device = Device::new(
|
let device = Device::new(
|
||||||
MediaType::Screen,
|
MediaType::Screen,
|
||||||
opts::get().initial_window_size.to_f32() * ScaleFactor::new(1.0));
|
opts::get().initial_window_size.to_f32() * ScaleFactor::new(1.0));
|
||||||
let parallel_traversal = if layout_threads != 1 {
|
|
||||||
let configuration =
|
let configuration =
|
||||||
rayon::Configuration::new().set_num_threads(layout_threads);
|
rayon::Configuration::new().set_num_threads(layout_threads);
|
||||||
rayon::ThreadPool::new(configuration).ok()
|
let parallel_traversal = rayon::ThreadPool::new(configuration).ok();
|
||||||
} else {
|
debug!("Possible layout Threads: {}", layout_threads);
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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) = channel();
|
let (new_animations_sender, new_animations_receiver) = channel();
|
||||||
|
@ -441,6 +442,7 @@ impl LayoutThread {
|
||||||
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_traversal: parallel_traversal,
|
||||||
|
parallel_flag: true,
|
||||||
generation: 0,
|
generation: 0,
|
||||||
new_animations_sender: new_animations_sender,
|
new_animations_sender: new_animations_sender,
|
||||||
new_animations_receiver: new_animations_receiver,
|
new_animations_receiver: new_animations_receiver,
|
||||||
|
@ -979,6 +981,13 @@ impl LayoutThread {
|
||||||
(data.reflow_info.goal == ReflowGoal::ForScriptQuery &&
|
(data.reflow_info.goal == ReflowGoal::ForScriptQuery &&
|
||||||
data.query_type != ReflowQueryType::NoQuery));
|
data.query_type != ReflowQueryType::NoQuery));
|
||||||
|
|
||||||
|
// Parallelize if there's more than 750 objects based on rzambre's suggestion
|
||||||
|
// https://github.com/servo/servo/issues/10110
|
||||||
|
self.parallel_flag = self.layout_threads > 1 && data.dom_count > 750;
|
||||||
|
debug!("layout: received layout request for: {}", self.url);
|
||||||
|
debug!("Number of objects in DOM: {}", data.dom_count);
|
||||||
|
debug!("layout: parallel? {}", self.parallel_flag);
|
||||||
|
|
||||||
let mut rw_data = possibly_locked_rw_data.lock();
|
let mut rw_data = possibly_locked_rw_data.lock();
|
||||||
|
|
||||||
let element: ServoLayoutElement = match document.root_node() {
|
let element: ServoLayoutElement = match document.root_node() {
|
||||||
|
@ -1140,18 +1149,16 @@ impl LayoutThread {
|
||||||
self.time_profiler_chan.clone(),
|
self.time_profiler_chan.clone(),
|
||||||
|| {
|
|| {
|
||||||
// Perform CSS selector matching and flow construction.
|
// Perform CSS selector matching and flow construction.
|
||||||
match self.parallel_traversal {
|
if let (true, Some(traversal)) = (self.parallel_flag, self.parallel_traversal.as_mut()) {
|
||||||
None => {
|
// Parallel mode
|
||||||
|
parallel::traverse_dom::<ServoLayoutNode, RecalcStyleAndConstructFlows>(
|
||||||
|
element.as_node(), dom_depth, &shared_layout_context, traversal);
|
||||||
|
} else {
|
||||||
|
// Sequential mode
|
||||||
sequential::traverse_dom::<ServoLayoutNode, RecalcStyleAndConstructFlows>(
|
sequential::traverse_dom::<ServoLayoutNode, RecalcStyleAndConstructFlows>(
|
||||||
element.as_node(), &shared_layout_context);
|
element.as_node(), &shared_layout_context);
|
||||||
}
|
}
|
||||||
Some(ref mut traversal) => {
|
|
||||||
parallel::traverse_dom::<ServoLayoutNode, RecalcStyleAndConstructFlows>(
|
|
||||||
element.as_node(), dom_depth, &shared_layout_context, traversal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO(pcwalton): Measure energy usage of text shaping, perhaps?
|
// TODO(pcwalton): Measure energy usage of text shaping, perhaps?
|
||||||
let text_shaping_time =
|
let text_shaping_time =
|
||||||
(font::get_and_reset_text_shaping_performance_counter() as u64) /
|
(font::get_and_reset_text_shaping_performance_counter() as u64) /
|
||||||
|
@ -1402,19 +1409,17 @@ impl LayoutThread {
|
||||||
self.time_profiler_chan.clone(),
|
self.time_profiler_chan.clone(),
|
||||||
|| {
|
|| {
|
||||||
let profiler_metadata = self.profiler_metadata();
|
let profiler_metadata = self.profiler_metadata();
|
||||||
match self.parallel_traversal {
|
|
||||||
None => {
|
if let (true, Some(traversal)) = (self.parallel_flag, self.parallel_traversal.as_mut()) {
|
||||||
// Sequential mode.
|
|
||||||
LayoutThread::solve_constraints(FlowRef::deref_mut(&mut root_flow), &layout_context)
|
|
||||||
}
|
|
||||||
Some(ref mut parallel) => {
|
|
||||||
// Parallel mode.
|
// Parallel mode.
|
||||||
LayoutThread::solve_constraints_parallel(parallel,
|
LayoutThread::solve_constraints_parallel(traversal,
|
||||||
FlowRef::deref_mut(&mut root_flow),
|
FlowRef::deref_mut(&mut root_flow),
|
||||||
profiler_metadata,
|
profiler_metadata,
|
||||||
self.time_profiler_chan.clone(),
|
self.time_profiler_chan.clone(),
|
||||||
&*layout_context);
|
&*layout_context);
|
||||||
}
|
} else {
|
||||||
|
//Sequential mode
|
||||||
|
LayoutThread::solve_constraints(FlowRef::deref_mut(&mut root_flow), &layout_context)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -284,6 +284,12 @@ pub struct Document {
|
||||||
last_click_info: DOMRefCell<Option<(Instant, Point2D<f32>)>>,
|
last_click_info: DOMRefCell<Option<(Instant, Point2D<f32>)>>,
|
||||||
/// https://html.spec.whatwg.org/multipage/#ignore-destructive-writes-counter
|
/// https://html.spec.whatwg.org/multipage/#ignore-destructive-writes-counter
|
||||||
ignore_destructive_writes_counter: Cell<u32>,
|
ignore_destructive_writes_counter: Cell<u32>,
|
||||||
|
/// Track the total number of elements in this DOM's tree.
|
||||||
|
/// This is sent to the layout thread every time a reflow is done;
|
||||||
|
/// layout uses this to determine if the gains from parallel layout will be worth the overhead.
|
||||||
|
///
|
||||||
|
/// See also: https://github.com/servo/servo/issues/10110
|
||||||
|
dom_count: Cell<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(JSTraceable, HeapSizeOf)]
|
#[derive(JSTraceable, HeapSizeOf)]
|
||||||
|
@ -455,6 +461,22 @@ impl Document {
|
||||||
self.base_element.set(base.r());
|
self.base_element.set(base.r());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn dom_count(&self) -> u32 {
|
||||||
|
self.dom_count.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is called by `bind_to_tree` when a node is added to the DOM.
|
||||||
|
/// The internal count is used by layout to determine whether to be sequential or parallel.
|
||||||
|
/// (it's sequential for small DOMs)
|
||||||
|
pub fn increment_dom_count(&self) {
|
||||||
|
self.dom_count.set(self.dom_count.get() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is called by `unbind_from_tree` when a node is removed from the DOM.
|
||||||
|
pub fn decrement_dom_count(&self) {
|
||||||
|
self.dom_count.set(self.dom_count.get() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn quirks_mode(&self) -> QuirksMode {
|
pub fn quirks_mode(&self) -> QuirksMode {
|
||||||
self.quirks_mode.get()
|
self.quirks_mode.get()
|
||||||
}
|
}
|
||||||
|
@ -1884,6 +1906,7 @@ impl Document {
|
||||||
target_element: MutNullableHeap::new(None),
|
target_element: MutNullableHeap::new(None),
|
||||||
last_click_info: DOMRefCell::new(None),
|
last_click_info: DOMRefCell::new(None),
|
||||||
ignore_destructive_writes_counter: Default::default(),
|
ignore_destructive_writes_counter: Default::default(),
|
||||||
|
dom_count: Cell::new(1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2151,10 +2151,12 @@ impl VirtualMethods for Element {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ref value) = *self.id_attribute.borrow() {
|
|
||||||
let doc = document_from_node(self);
|
let doc = document_from_node(self);
|
||||||
|
if let Some(ref value) = *self.id_attribute.borrow() {
|
||||||
doc.register_named_element(self, value.clone());
|
doc.register_named_element(self, value.clone());
|
||||||
}
|
}
|
||||||
|
// This is used for layout optimization.
|
||||||
|
doc.increment_dom_count();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unbind_from_tree(&self, context: &UnbindContext) {
|
fn unbind_from_tree(&self, context: &UnbindContext) {
|
||||||
|
@ -2164,10 +2166,12 @@ impl VirtualMethods for Element {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ref value) = *self.id_attribute.borrow() {
|
|
||||||
let doc = document_from_node(self);
|
let doc = document_from_node(self);
|
||||||
|
if let Some(ref value) = *self.id_attribute.borrow() {
|
||||||
doc.unregister_named_element(self, value.clone());
|
doc.unregister_named_element(self, value.clone());
|
||||||
}
|
}
|
||||||
|
// This is used for layout optimization.
|
||||||
|
doc.decrement_dom_count();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn children_changed(&self, mutation: &ChildrenMutation) {
|
fn children_changed(&self, mutation: &ChildrenMutation) {
|
||||||
|
|
|
@ -71,7 +71,10 @@ impl VirtualMethods for HTMLHeadElement {
|
||||||
fn super_type(&self) -> Option<&VirtualMethods> {
|
fn super_type(&self) -> Option<&VirtualMethods> {
|
||||||
Some(self.upcast::<HTMLElement>() as &VirtualMethods)
|
Some(self.upcast::<HTMLElement>() as &VirtualMethods)
|
||||||
}
|
}
|
||||||
fn bind_to_tree(&self, _tree_in_doc: bool) {
|
fn bind_to_tree(&self, tree_in_doc: bool) {
|
||||||
|
if let Some(ref s) = self.super_type() {
|
||||||
|
s.bind_to_tree(tree_in_doc);
|
||||||
|
}
|
||||||
load_script(self);
|
load_script(self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,9 +71,12 @@ impl VirtualMethods for HTMLTitleElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bind_to_tree(&self, is_in_doc: bool) {
|
fn bind_to_tree(&self, tree_in_doc: bool) {
|
||||||
|
if let Some(ref s) = self.super_type() {
|
||||||
|
s.bind_to_tree(tree_in_doc);
|
||||||
|
}
|
||||||
let node = self.upcast::<Node>();
|
let node = self.upcast::<Node>();
|
||||||
if is_in_doc {
|
if tree_in_doc {
|
||||||
node.owner_doc().title_changed();
|
node.owner_doc().title_changed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1084,6 +1084,7 @@ impl Window {
|
||||||
window_size: window_size,
|
window_size: window_size,
|
||||||
script_join_chan: join_chan,
|
script_join_chan: join_chan,
|
||||||
query_type: query_type,
|
query_type: query_type,
|
||||||
|
dom_count: self.Document().dom_count(),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.layout_chan.send(Msg::Reflow(reflow)).unwrap();
|
self.layout_chan.send(Msg::Reflow(reflow)).unwrap();
|
||||||
|
|
|
@ -126,6 +126,8 @@ pub struct ScriptReflow {
|
||||||
pub script_join_chan: Sender<()>,
|
pub script_join_chan: Sender<()>,
|
||||||
/// The type of query if any to perform during this reflow.
|
/// The type of query if any to perform during this reflow.
|
||||||
pub query_type: ReflowQueryType,
|
pub query_type: ReflowQueryType,
|
||||||
|
/// The number of objects in the dom #10110
|
||||||
|
pub dom_count: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for ScriptReflow {
|
impl Drop for ScriptReflow {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue