diff options
Diffstat (limited to 'components/layout/parallel.rs')
-rw-r--r-- | components/layout/parallel.rs | 232 |
1 files changed, 86 insertions, 146 deletions
diff --git a/components/layout/parallel.rs b/components/layout/parallel.rs index a51f585eb3c..6128abbdae6 100644 --- a/components/layout/parallel.rs +++ b/components/layout/parallel.rs @@ -8,7 +8,7 @@ #![allow(unsafe_code)] -use context::{LayoutContext, SharedLayoutContext}; +use context::LayoutContext; use flow::{self, Flow, MutableFlowUtils, PostorderFlowTraversal, PreorderFlowTraversal}; use flow_ref::FlowRef; use profile_traits::time::{self, TimerMetadata, profile}; @@ -49,11 +49,6 @@ pub fn borrowed_flow_to_unsafe_flow(flow: &Flow) -> UnsafeFlow { } } -pub type ChunkedFlowTraversalFunction<'scope> = - extern "Rust" fn(Box<[UnsafeFlow]>, &rayon::Scope<'scope>, &'scope SharedLayoutContext); - -pub type FlowTraversalFunction = extern "Rust" fn(UnsafeFlow, &LayoutContext); - /// Information that we need stored in each flow. pub struct FlowParallelInfo { /// The number of children that still need work done. @@ -71,183 +66,128 @@ impl FlowParallelInfo { } } -/// A parallel bottom-up flow traversal. -trait ParallelPostorderFlowTraversal : PostorderFlowTraversal { - /// Process current flow and potentially traverse its ancestors. - /// - /// If we are the last child that finished processing, recursively process - /// our parent. Else, stop. Also, stop at the root. - /// - /// Thus, if we start with all the leaves of a tree, we end up traversing - /// the whole tree bottom-up because each parent will be processed exactly - /// once (by the last child that finishes processing). - /// - /// The only communication between siblings is that they both - /// fetch-and-subtract the parent's children count. - fn run_parallel(&self, mut unsafe_flow: UnsafeFlow) { - loop { - // Get a real flow. - let flow: &mut Flow = unsafe { - mem::transmute(unsafe_flow) - }; - - // Perform the appropriate traversal. - if self.should_process(flow) { - self.process(flow); - } +/// Process current flow and potentially traverse its ancestors. +/// +/// If we are the last child that finished processing, recursively process +/// our parent. Else, stop. Also, stop at the root. +/// +/// Thus, if we start with all the leaves of a tree, we end up traversing +/// the whole tree bottom-up because each parent will be processed exactly +/// once (by the last child that finishes processing). +/// +/// The only communication between siblings is that they both +/// fetch-and-subtract the parent's children count. +fn buttom_up_flow(mut unsafe_flow: UnsafeFlow, + assign_bsize_traversal: &AssignBSizes) { + loop { + // Get a real flow. + let flow: &mut Flow = unsafe { + mem::transmute(unsafe_flow) + }; + + // Perform the appropriate traversal. + if assign_bsize_traversal.should_process(flow) { + assign_bsize_traversal.process(flow); + } - let base = flow::mut_base(flow); + let base = flow::mut_base(flow); - // Reset the count of children for the next layout traversal. - base.parallel.children_count.store(base.children.len() as isize, - Ordering::Relaxed); + // Reset the count of children for the next layout traversal. + base.parallel.children_count.store(base.children.len() as isize, + Ordering::Relaxed); - // Possibly enqueue the parent. - let unsafe_parent = base.parallel.parent; - if unsafe_parent == null_unsafe_flow() { - // We're done! - break - } + // Possibly enqueue the parent. + let unsafe_parent = base.parallel.parent; + if unsafe_parent == null_unsafe_flow() { + // We're done! + break + } - // No, we're not at the root yet. Then are we the last child - // of our parent to finish processing? If so, we can continue - // on with our parent; otherwise, we've gotta wait. - let parent: &mut Flow = unsafe { - mem::transmute(unsafe_parent) - }; - let parent_base = flow::mut_base(parent); - if parent_base.parallel.children_count.fetch_sub(1, Ordering::Relaxed) == 1 { - // We were the last child of our parent. Reflow our parent. - unsafe_flow = unsafe_parent - } else { - // Stop. - break - } + // No, we're not at the root yet. Then are we the last child + // of our parent to finish processing? If so, we can continue + // on with our parent; otherwise, we've gotta wait. + let parent: &mut Flow = unsafe { + mem::transmute(unsafe_parent) + }; + let parent_base = flow::mut_base(parent); + if parent_base.parallel.children_count.fetch_sub(1, Ordering::Relaxed) == 1 { + // We were the last child of our parent. Reflow our parent. + unsafe_flow = unsafe_parent + } else { + // Stop. + break } } } -/// A parallel top-down flow traversal. -trait ParallelPreorderFlowTraversal : PreorderFlowTraversal { - fn run_parallel<'scope>(&self, - unsafe_flows: &[UnsafeFlow], - scope: &rayon::Scope<'scope>, - shared: &'scope SharedLayoutContext); - - fn should_record_thread_ids(&self) -> bool; - - #[inline(always)] - fn run_parallel_helper<'scope>(&self, - unsafe_flows: &[UnsafeFlow], - scope: &rayon::Scope<'scope>, - shared: &'scope SharedLayoutContext, - top_down_func: ChunkedFlowTraversalFunction<'scope>, - bottom_up_func: FlowTraversalFunction) - { - let mut discovered_child_flows = vec![]; - let context = LayoutContext::new(&shared); - - for unsafe_flow in unsafe_flows { - let mut had_children = false; - unsafe { - // Get a real flow. - let flow: &mut Flow = mem::transmute(*unsafe_flow); - - if self.should_record_thread_ids() { - // 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(); - } - - if self.should_process(flow) { - // Perform the appropriate traversal. - self.process(flow); - } - - // Possibly enqueue the children. - for kid in flow::child_iter_mut(flow) { - had_children = true; - discovered_child_flows.push(borrowed_flow_to_unsafe_flow(kid)); - } +fn top_down_flow<'scope>(unsafe_flows: &[UnsafeFlow], + scope: &rayon::Scope<'scope>, + assign_isize_traversal: &'scope AssignISizes, + assign_bsize_traversal: &'scope AssignBSizes) +{ + 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); + + // 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(); + + if assign_isize_traversal.should_process(flow) { + // Perform the appropriate traversal. + assign_isize_traversal.process(flow); } - // If there were no more children, start assigning block-sizes. - if !had_children { - bottom_up_func(*unsafe_flow, &context) + // Possibly enqueue the children. + for kid in flow::child_iter_mut(flow) { + had_children = true; + discovered_child_flows.push(borrowed_flow_to_unsafe_flow(kid)); } } - for chunk in discovered_child_flows.chunks(CHUNK_SIZE) { - let nodes = chunk.iter().cloned().collect::<Vec<_>>().into_boxed_slice(); - - scope.spawn(move |scope| { - top_down_func(nodes, scope, shared); - }); + // If there were no more children, start assigning block-sizes. + if !had_children { + buttom_up_flow(*unsafe_flow, &assign_bsize_traversal) } } -} -impl<'a> ParallelPreorderFlowTraversal for AssignISizes<'a> { - fn run_parallel<'scope>(&self, - unsafe_flows: &[UnsafeFlow], - scope: &rayon::Scope<'scope>, - shared: &'scope SharedLayoutContext) - { - self.run_parallel_helper(unsafe_flows, - scope, - shared, - assign_inline_sizes, - assign_block_sizes_and_store_overflow) - } + for chunk in discovered_child_flows.chunks(CHUNK_SIZE) { + let nodes = chunk.iter().cloned().collect::<Vec<_>>().into_boxed_slice(); - fn should_record_thread_ids(&self) -> bool { - true + scope.spawn(move |scope| { + top_down_flow(&nodes, scope, &assign_isize_traversal, &assign_bsize_traversal); + }); } } -impl<'a> ParallelPostorderFlowTraversal for AssignBSizes<'a> {} - -fn assign_inline_sizes<'scope>(unsafe_flows: Box<[UnsafeFlow]>, - scope: &rayon::Scope<'scope>, - shared: &'scope SharedLayoutContext) { - let assign_inline_sizes_traversal = AssignISizes { - shared_context: &shared.style_context, - }; - assign_inline_sizes_traversal.run_parallel(&unsafe_flows, scope, shared) -} - -fn assign_block_sizes_and_store_overflow( - unsafe_flow: UnsafeFlow, - context: &LayoutContext) { - let assign_block_sizes_traversal = AssignBSizes { - layout_context: context, - }; - assign_block_sizes_traversal.run_parallel(unsafe_flow) -} - pub fn traverse_flow_tree_preorder( root: &mut Flow, profiler_metadata: Option<TimerMetadata>, time_profiler_chan: time::ProfilerChan, - shared: &SharedLayoutContext, + context: &LayoutContext, queue: &rayon::ThreadPool) { if opts::get().bubble_inline_sizes_separately { - let context = LayoutContext::new(shared); let bubble_inline_sizes = BubbleISizes { layout_context: &context }; root.traverse_postorder(&bubble_inline_sizes); } + let assign_isize_traversal = &AssignISizes { layout_context: &context }; + let assign_bsize_traversal = &AssignBSizes { layout_context: &context }; 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, scope, &shared); + top_down_flow(&nodes, scope, assign_isize_traversal, assign_bsize_traversal); }); }); }); |