diff options
-rw-r--r-- | components/layout_thread/lib.rs | 65 | ||||
-rw-r--r-- | components/script/dom/document.rs | 23 | ||||
-rw-r--r-- | components/script/dom/element.rs | 8 | ||||
-rw-r--r-- | components/script/dom/htmlheadelement.rs | 5 | ||||
-rw-r--r-- | components/script/dom/htmltitleelement.rs | 7 | ||||
-rw-r--r-- | components/script/dom/window.rs | 1 | ||||
-rw-r--r-- | components/script_layout_interface/message.rs | 2 |
7 files changed, 76 insertions, 35 deletions
diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index 19593c72d77..04f35183ff4 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -175,6 +175,9 @@ pub struct LayoutThread { /// The workers that we use for parallel operation. 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. /// This can be used to easily check for invalid stale data. generation: u32, @@ -389,13 +392,11 @@ impl LayoutThread { let device = Device::new( MediaType::Screen, opts::get().initial_window_size.to_f32() * ScaleFactor::new(1.0)); - let parallel_traversal = if layout_threads != 1 { - let configuration = - rayon::Configuration::new().set_num_threads(layout_threads); - rayon::ThreadPool::new(configuration).ok() - } else { - None - }; + + let configuration = + rayon::Configuration::new().set_num_threads(layout_threads); + let parallel_traversal = rayon::ThreadPool::new(configuration).ok(); + debug!("Possible layout Threads: {}", layout_threads); // Create the channel on which new animations can be sent. let (new_animations_sender, new_animations_receiver) = channel(); @@ -441,6 +442,7 @@ impl LayoutThread { font_cache_receiver: font_cache_receiver, font_cache_sender: ipc_font_cache_sender, parallel_traversal: parallel_traversal, + parallel_flag: true, generation: 0, new_animations_sender: new_animations_sender, new_animations_receiver: new_animations_receiver, @@ -979,6 +981,13 @@ impl LayoutThread { (data.reflow_info.goal == ReflowGoal::ForScriptQuery && 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 element: ServoLayoutElement = match document.root_node() { @@ -1140,18 +1149,16 @@ impl LayoutThread { self.time_profiler_chan.clone(), || { // Perform CSS selector matching and flow construction. - match self.parallel_traversal { - None => { - sequential::traverse_dom::<ServoLayoutNode, RecalcStyleAndConstructFlows>( - element.as_node(), &shared_layout_context); - } - Some(ref mut traversal) => { - parallel::traverse_dom::<ServoLayoutNode, RecalcStyleAndConstructFlows>( - element.as_node(), dom_depth, &shared_layout_context, traversal); - } + if let (true, Some(traversal)) = (self.parallel_flag, self.parallel_traversal.as_mut()) { + // Parallel mode + parallel::traverse_dom::<ServoLayoutNode, RecalcStyleAndConstructFlows>( + element.as_node(), dom_depth, &shared_layout_context, traversal); + } else { + // Sequential mode + sequential::traverse_dom::<ServoLayoutNode, RecalcStyleAndConstructFlows>( + element.as_node(), &shared_layout_context); } }); - // TODO(pcwalton): Measure energy usage of text shaping, perhaps? let text_shaping_time = (font::get_and_reset_text_shaping_performance_counter() as u64) / @@ -1402,19 +1409,17 @@ impl LayoutThread { self.time_profiler_chan.clone(), || { let profiler_metadata = self.profiler_metadata(); - match self.parallel_traversal { - None => { - // Sequential mode. - LayoutThread::solve_constraints(FlowRef::deref_mut(&mut root_flow), &layout_context) - } - Some(ref mut parallel) => { - // Parallel mode. - LayoutThread::solve_constraints_parallel(parallel, - FlowRef::deref_mut(&mut root_flow), - profiler_metadata, - self.time_profiler_chan.clone(), - &*layout_context); - } + + if let (true, Some(traversal)) = (self.parallel_flag, self.parallel_traversal.as_mut()) { + // Parallel mode. + LayoutThread::solve_constraints_parallel(traversal, + FlowRef::deref_mut(&mut root_flow), + profiler_metadata, + self.time_profiler_chan.clone(), + &*layout_context); + } else { + //Sequential mode + LayoutThread::solve_constraints(FlowRef::deref_mut(&mut root_flow), &layout_context) } }); } diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 68777b70d39..468f127f7ad 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -284,6 +284,12 @@ pub struct Document { last_click_info: DOMRefCell<Option<(Instant, Point2D<f32>)>>, /// https://html.spec.whatwg.org/multipage/#ignore-destructive-writes-counter 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)] @@ -455,6 +461,22 @@ impl Document { 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 { self.quirks_mode.get() } @@ -1884,6 +1906,7 @@ impl Document { target_element: MutNullableHeap::new(None), last_click_info: DOMRefCell::new(None), ignore_destructive_writes_counter: Default::default(), + dom_count: Cell::new(1), } } diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index c384001e33a..6df2122e7b3 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -2151,10 +2151,12 @@ impl VirtualMethods for Element { return; } + let doc = document_from_node(self); if let Some(ref value) = *self.id_attribute.borrow() { - let doc = document_from_node(self); doc.register_named_element(self, value.clone()); } + // This is used for layout optimization. + doc.increment_dom_count(); } fn unbind_from_tree(&self, context: &UnbindContext) { @@ -2164,10 +2166,12 @@ impl VirtualMethods for Element { return; } + let doc = document_from_node(self); if let Some(ref value) = *self.id_attribute.borrow() { - let doc = document_from_node(self); doc.unregister_named_element(self, value.clone()); } + // This is used for layout optimization. + doc.decrement_dom_count(); } fn children_changed(&self, mutation: &ChildrenMutation) { diff --git a/components/script/dom/htmlheadelement.rs b/components/script/dom/htmlheadelement.rs index 419644c5bb3..fc6e888d015 100644 --- a/components/script/dom/htmlheadelement.rs +++ b/components/script/dom/htmlheadelement.rs @@ -71,7 +71,10 @@ impl VirtualMethods for HTMLHeadElement { fn super_type(&self) -> Option<&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); } } diff --git a/components/script/dom/htmltitleelement.rs b/components/script/dom/htmltitleelement.rs index c124a4a3604..f3ec357cb56 100644 --- a/components/script/dom/htmltitleelement.rs +++ b/components/script/dom/htmltitleelement.rs @@ -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>(); - if is_in_doc { + if tree_in_doc { node.owner_doc().title_changed(); } } diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 31daeec1f9d..77a569ef65a 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -1084,6 +1084,7 @@ impl Window { window_size: window_size, script_join_chan: join_chan, query_type: query_type, + dom_count: self.Document().dom_count(), }; self.layout_chan.send(Msg::Reflow(reflow)).unwrap(); diff --git a/components/script_layout_interface/message.rs b/components/script_layout_interface/message.rs index 6abbfcb3e3b..581adbb45bc 100644 --- a/components/script_layout_interface/message.rs +++ b/components/script_layout_interface/message.rs @@ -126,6 +126,8 @@ pub struct ScriptReflow { pub script_join_chan: Sender<()>, /// The type of query if any to perform during this reflow. pub query_type: ReflowQueryType, + /// The number of objects in the dom #10110 + pub dom_count: u32, } impl Drop for ScriptReflow { |