diff options
-rw-r--r-- | Cargo.lock | 1 | ||||
-rw-r--r-- | components/compositing/compositor.rs | 3 | ||||
-rw-r--r-- | components/gfx/render_task.rs | 5 | ||||
-rw-r--r-- | components/layout/layout_task.rs | 30 | ||||
-rw-r--r-- | components/layout/parallel.rs | 11 | ||||
-rw-r--r-- | components/script/layout_interface.rs | 2 | ||||
-rw-r--r-- | components/script/page.rs | 1 | ||||
-rw-r--r-- | components/util/Cargo.toml | 2 | ||||
-rw-r--r-- | components/util/lib.rs | 1 | ||||
-rw-r--r-- | components/util/time.rs | 136 |
10 files changed, 123 insertions, 69 deletions
diff --git a/Cargo.lock b/Cargo.lock index c096c14e6ad..0536f1cf9bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -461,6 +461,7 @@ dependencies = [ "geom 0.1.0 (git+https://github.com/servo/rust-geom#2982b770db6e5e3270305e0fd6b8068f6f80a489)", "string_cache 0.0.0 (git+https://github.com/servo/string-cache?ref=pre-rustup#37a5869d4130bf75e2f082bab54767d56d4ba63a)", "task_info 0.0.1", + "url 0.1.0 (git+https://github.com/servo/rust-url#678bb4d52638b1cfdab78ef8e521566c9240fb1a)", ] [[package]] diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index f17954245d5..08be501cff0 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -922,7 +922,7 @@ impl IOCompositor { } fn composite(&mut self) { - profile(time::CompositingCategory, self.time_profiler_chan.clone(), || { + profile(time::CompositingCategory, None, self.time_profiler_chan.clone(), || { debug!("compositor: compositing"); // Adjust the layer dimensions as necessary to correspond to the size of the window. self.scene.size = self.window_size.as_f32().to_untyped(); @@ -993,4 +993,3 @@ impl IOCompositor { self.recomposite = result || self.recomposite; } } - diff --git a/components/gfx/render_task.rs b/components/gfx/render_task.rs index 8c41d8d1d26..6c4075cbd65 100644 --- a/components/gfx/render_task.rs +++ b/components/gfx/render_task.rs @@ -287,7 +287,7 @@ impl<C:RenderListener + Send> RenderTask<C> { tiles: Vec<BufferRequest>, scale: f32, layer_id: LayerId) { - time::profile(time::RenderingCategory, self.time_profiler_chan.clone(), || { + time::profile(time::RenderingCategory, None, self.time_profiler_chan.clone(), || { // FIXME: Try not to create a new array here. let mut new_buffers = vec!(); @@ -350,7 +350,7 @@ impl<C:RenderListener + Send> RenderTask<C> { ctx.clear(); // Draw the display list. - profile(time::RenderingDrawingCategory, self.time_profiler_chan.clone(), || { + profile(time::RenderingDrawingCategory, None, self.time_profiler_chan.clone(), || { display_list.draw_into_context(&mut ctx, &matrix); ctx.draw_target.flush(); }); @@ -439,4 +439,3 @@ impl<C:RenderListener + Send> RenderTask<C> { }) } } - diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs index bed96555e4b..fa42cb04f59 100644 --- a/components/layout/layout_task.rs +++ b/components/layout/layout_task.rs @@ -58,6 +58,7 @@ use servo_util::time::{TimeProfilerChan, profile}; use servo_util::time; use servo_util::task::spawn_named_with_send_on_failure; use servo_util::workqueue::WorkQueue; +use std::cell::Cell; use std::comm::{channel, Sender, Receiver, Select}; use std::mem; use std::ptr; @@ -127,6 +128,9 @@ pub struct LayoutTask { /// The command-line options. pub opts: Opts, + /// Is this the first reflow in this LayoutTask? + pub first_reflow: Cell<bool>, + /// A mutex to allow for fast, read-only RPC of layout's internal data /// structures, while still letting the LayoutTask modify them. /// @@ -400,6 +404,7 @@ impl LayoutTask { image_cache_task: image_cache_task.clone(), font_cache_task: font_cache_task, opts: opts.clone(), + first_reflow: Cell::new(true), rw_data: Arc::new(Mutex::new( LayoutTaskData { local_image_cache: local_image_cache, @@ -511,7 +516,8 @@ impl LayoutTask { self.rw_data.clone()) as Box<LayoutRPC + Send>); }, ReflowMsg(data) => { - profile(time::LayoutPerformCategory, self.time_profiler_chan.clone(), || { + profile(time::LayoutPerformCategory, Some((&data.url, data.iframe, self.first_reflow.get())), + self.time_profiler_chan.clone(), || { self.handle_reflow(&*data, possibly_locked_rw_data); }); }, @@ -670,6 +676,7 @@ impl LayoutTask { /// benchmarked against those two. It is marked `#[inline(never)]` to aid profiling. #[inline(never)] fn solve_constraints_parallel(&self, + data: &Reflow, rw_data: &mut LayoutTaskData, layout_root: &mut FlowRef, shared_layout_context: &SharedLayoutContext) { @@ -686,6 +693,9 @@ impl LayoutTask { // NOTE: this currently computes borders, so any pruning should separate that // operation out. parallel::traverse_flow_tree_preorder(layout_root, + &data.url, + data.iframe, + self.first_reflow.get(), self.time_profiler_chan.clone(), shared_layout_context, traversal); @@ -755,6 +765,9 @@ impl LayoutTask { &data.url); let mut layout_root = profile(time::LayoutStyleRecalcCategory, + Some((&data.url, + data.iframe, + self.first_reflow.get())), self.time_profiler_chan.clone(), || { // Perform CSS selector matching and flow construction. @@ -789,7 +802,8 @@ impl LayoutTask { } // Propagate damage. - profile(time::LayoutDamagePropagateCategory, self.time_profiler_chan.clone(), || { + profile(time::LayoutDamagePropagateCategory, Some((&data.url, data.iframe, self.first_reflow.get())), + self.time_profiler_chan.clone(), || { layout_root.get_mut().traverse_preorder(&mut PropagateDamageTraversal { all_style_damage: all_style_damage }); @@ -798,7 +812,8 @@ impl LayoutTask { // Perform the primary layout passes over the flow tree to compute the locations of all // the boxes. - profile(time::LayoutMainCategory, self.time_profiler_chan.clone(), || { + profile(time::LayoutMainCategory, Some((&data.url, data.iframe, self.first_reflow.get())), + self.time_profiler_chan.clone(), || { let rw_data = rw_data.deref_mut(); match rw_data.parallel_traversal { None => { @@ -808,7 +823,7 @@ impl LayoutTask { } Some(_) => { // Parallel mode. - self.solve_constraints_parallel(rw_data, &mut layout_root, &mut shared_layout_ctx) + self.solve_constraints_parallel(data, rw_data, &mut layout_root, &mut shared_layout_ctx) } } }); @@ -816,7 +831,7 @@ impl LayoutTask { // Build the display list if necessary, and send it to the renderer. if data.goal == ReflowForDisplay { let writing_mode = flow::base(layout_root.get()).writing_mode; - profile(time::LayoutDispListBuildCategory, self.time_profiler_chan.clone(), || { + profile(time::LayoutDispListBuildCategory, Some((&data.url, data.iframe, self.first_reflow.get())), self.time_profiler_chan.clone(), || { shared_layout_ctx.dirty = flow::base(layout_root.get()).position.to_physical( writing_mode, rw_data.screen_size); flow::mut_base(layout_root.get_mut()).abs_position = @@ -833,6 +848,9 @@ impl LayoutTask { } Some(ref mut traversal) => { parallel::build_display_list_for_subtree(&mut layout_root, + &data.url, + data.iframe, + self.first_reflow.get(), self.time_profiler_chan.clone(), &mut shared_layout_ctx, traversal); @@ -901,6 +919,8 @@ impl LayoutTask { }); } + self.first_reflow.set(false); + if self.opts.trace_layout { layout_debug::end_trace(); } diff --git a/components/layout/parallel.rs b/components/layout/parallel.rs index fbf85053a4f..f24b4b10e0c 100644 --- a/components/layout/parallel.rs +++ b/components/layout/parallel.rs @@ -15,6 +15,7 @@ use flow; use flow_ref::FlowRef; use layout_task::{AssignBSizesAndStoreOverflowTraversal, AssignISizesTraversal}; use layout_task::{BubbleISizesTraversal}; +use url::Url; use util::{LayoutDataAccess, LayoutDataWrapper, OpaqueNodeMethods}; use wrapper::{layout_node_to_unsafe_layout_node, layout_node_from_unsafe_layout_node, LayoutNode, PostorderNodeMutTraversal}; use wrapper::{ThreadSafeLayoutNode, UnsafeLayoutNode}; @@ -624,12 +625,15 @@ pub fn recalc_style_for_subtree(root_node: &LayoutNode, } pub fn traverse_flow_tree_preorder(root: &mut FlowRef, + url: &Url, + iframe: bool, + first_reflow: bool, time_profiler_chan: TimeProfilerChan, shared_layout_context: &SharedLayoutContext, queue: &mut WorkQueue<*const SharedLayoutContext,UnsafeFlow>) { queue.data = shared_layout_context as *const _; - profile(time::LayoutParallelWarmupCategory, time_profiler_chan, || { + profile(time::LayoutParallelWarmupCategory, Some((url, iframe, first_reflow)), time_profiler_chan, || { queue.push(WorkUnit { fun: assign_inline_sizes, data: mut_owned_flow_to_unsafe_flow(root), @@ -642,12 +646,15 @@ pub fn traverse_flow_tree_preorder(root: &mut FlowRef, } pub fn build_display_list_for_subtree(root: &mut FlowRef, + url: &Url, + iframe: bool, + first_reflow: bool, time_profiler_chan: TimeProfilerChan, shared_layout_context: &SharedLayoutContext, queue: &mut WorkQueue<*const SharedLayoutContext,UnsafeFlow>) { queue.data = shared_layout_context as *const _; - profile(time::LayoutParallelWarmupCategory, time_profiler_chan, || { + profile(time::LayoutParallelWarmupCategory, Some((url, iframe, first_reflow)), time_profiler_chan, || { queue.push(WorkUnit { fun: compute_absolute_position, data: mut_owned_flow_to_unsafe_flow(root), diff --git a/components/script/layout_interface.rs b/components/script/layout_interface.rs index 0c82c61447d..18c98dc50e6 100644 --- a/components/script/layout_interface.rs +++ b/components/script/layout_interface.rs @@ -142,6 +142,8 @@ pub struct Reflow { pub goal: ReflowGoal, /// The URL of the page. pub url: Url, + /// Is the current reflow of an iframe, as opposed to a root window? + pub iframe: bool, /// The channel through which messages can be sent back to the script task. pub script_chan: ScriptControlChan, /// The current window size. diff --git a/components/script/page.rs b/components/script/page.rs index eca24e6d814..7aa882cebd7 100644 --- a/components/script/page.rs +++ b/components/script/page.rs @@ -347,6 +347,7 @@ impl Page { let reflow = box Reflow { document_root: root.to_trusted_node_address(), url: self.get_url(), + iframe: self.subpage_id.is_some(), goal: goal, window_size: window_size, script_chan: script_chan, diff --git a/components/util/Cargo.toml b/components/util/Cargo.toml index 1378fc1a051..0bbc9581349 100644 --- a/components/util/Cargo.toml +++ b/components/util/Cargo.toml @@ -20,3 +20,5 @@ path = "../../support/rust-task_info" git = "https://github.com/servo/string-cache" branch = "pre-rustup" +[dependencies.url] +git = "https://github.com/servo/rust-url"
\ No newline at end of file diff --git a/components/util/lib.rs b/components/util/lib.rs index b543df42e80..9e6bddb9fab 100644 --- a/components/util/lib.rs +++ b/components/util/lib.rs @@ -27,6 +27,7 @@ extern crate sync; extern crate task_info; extern crate std_time = "time"; extern crate string_cache; +extern crate url; pub mod atom; pub mod bloom; diff --git a/components/util/time.rs b/components/util/time.rs index bb483ca2a99..cb283ba3e2f 100644 --- a/components/util/time.rs +++ b/components/util/time.rs @@ -11,6 +11,7 @@ use std::f64; use std::iter::AdditiveIterator; use std::io::timer::sleep; use task::{spawn_named}; +use url::Url; // front-end representation of the profiler used to communicate with the profiler #[deriving(Clone)] @@ -23,9 +24,37 @@ impl TimeProfilerChan { } } +#[deriving(PartialEq, Clone, PartialOrd, Eq, Ord)] +pub struct TimerMetadata { + url: String, + iframe: bool, + first_reflow: bool, +} + +pub trait Formatable { + fn format(&self) -> String; +} + +impl Formatable for Option<TimerMetadata> { + fn format(&self) -> String { + match self { + // TODO(cgaebel): Center-align in the format strings as soon as rustc supports it. + &Some(ref meta) => { + let url = meta.url.as_slice(); + let first_reflow = if meta.first_reflow { " yes" } else { " no " }; + let iframe = if meta.iframe { " yes" } else { " no " }; + format!(" {:14} {:9} {:30}", first_reflow, iframe, url) + }, + &None => + format!(" {:14} {:9} {:30}", " N/A", " N/A", " N/A") + } + } +} + +#[deriving(Clone)] pub enum TimeProfilerMsg { /// Normal message used for reporting time - TimeMsg(TimeProfilerCategory, f64), + TimeMsg((TimeProfilerCategory, Option<TimerMetadata>), f64), /// Message used to force print the profiling metrics PrintMsg, /// Tells the profiler to shut down. @@ -36,9 +65,7 @@ pub enum TimeProfilerMsg { #[deriving(PartialEq, Clone, PartialOrd, Eq, Ord)] pub enum TimeProfilerCategory { CompositingCategory, - LayoutQueryCategory, LayoutPerformCategory, - LayoutMaxSelectorMatchesCategory, LayoutStyleRecalcCategory, LayoutSelectorMatchCategory, LayoutTreeBuilderCategory, @@ -47,47 +74,16 @@ pub enum TimeProfilerCategory { LayoutParallelWarmupCategory, LayoutShapingCategory, LayoutDispListBuildCategory, - GfxRegenAvailableFontsCategory, RenderingDrawingCategory, RenderingPrepBuffCategory, RenderingCategory, - // FIXME(rust#8803): workaround for lack of CTFE function on enum types to return length - NumBuckets, } -impl TimeProfilerCategory { - // convenience function to not have to cast every time - pub fn num_buckets() -> uint { - NumBuckets as uint - } - - // enumeration of all TimeProfilerCategory types - fn empty_buckets() -> TimeProfilerBuckets { - let mut buckets = TreeMap::new(); - buckets.insert(CompositingCategory, vec!()); - buckets.insert(LayoutQueryCategory, vec!()); - buckets.insert(LayoutPerformCategory, vec!()); - buckets.insert(LayoutMaxSelectorMatchesCategory, vec!()); - buckets.insert(LayoutStyleRecalcCategory, vec!()); - buckets.insert(LayoutSelectorMatchCategory, vec!()); - buckets.insert(LayoutTreeBuilderCategory, vec!()); - buckets.insert(LayoutMainCategory, vec!()); - buckets.insert(LayoutParallelWarmupCategory, vec!()); - buckets.insert(LayoutShapingCategory, vec!()); - buckets.insert(LayoutDamagePropagateCategory, vec!()); - buckets.insert(LayoutDispListBuildCategory, vec!()); - buckets.insert(GfxRegenAvailableFontsCategory, vec!()); - buckets.insert(RenderingDrawingCategory, vec!()); - buckets.insert(RenderingPrepBuffCategory, vec!()); - buckets.insert(RenderingCategory, vec!()); - - buckets - } - +impl Formatable for TimeProfilerCategory { // some categories are subcategories of LayoutPerformCategory // and should be printed to indicate this - pub fn format(self) -> String { - let padding = match self { + fn format(&self) -> String { + let padding = match *self { LayoutStyleRecalcCategory | LayoutMainCategory | LayoutDispListBuildCategory | @@ -98,11 +94,26 @@ impl TimeProfilerCategory { LayoutTreeBuilderCategory => "| + ", _ => "" }; - format!("{:s}{:?}", padding, self) + let name = match *self { + CompositingCategory => "Compositing", + LayoutPerformCategory => "Layout", + LayoutStyleRecalcCategory => "Style Recalc", + LayoutSelectorMatchCategory => "Selector Matching", + LayoutTreeBuilderCategory => "Tree Building", + LayoutDamagePropagateCategory => "Damage Propagation", + LayoutMainCategory => "Primary Layout Pass", + LayoutParallelWarmupCategory => "Parallel Warmup", + LayoutShapingCategory => "Shaping", + LayoutDispListBuildCategory => "Display List Construction", + RenderingDrawingCategory => "Draw", + RenderingPrepBuffCategory => "Buffer Prep", + RenderingCategory => "Rendering", + }; + format!("{:s}{}", padding, name) } } -type TimeProfilerBuckets = TreeMap<TimeProfilerCategory, Vec<f64>>; +type TimeProfilerBuckets = TreeMap<(TimeProfilerCategory, Option<TimerMetadata>), Vec<f64>>; // back end of the profiler that handles data aggregation and performance metrics pub struct TimeProfiler { @@ -151,7 +162,7 @@ impl TimeProfiler { pub fn new(port: Receiver<TimeProfilerMsg>) -> TimeProfiler { TimeProfiler { port: port, - buckets: TimeProfilerCategory::empty_buckets(), + buckets: TreeMap::new(), last_msg: None, } } @@ -170,9 +181,18 @@ impl TimeProfiler { } } + fn find_or_insert(&mut self, k: (TimeProfilerCategory, Option<TimerMetadata>), t: f64) { + match self.buckets.find_mut(&k) { + None => {}, + Some(v) => { v.push(t); return; }, + } + + self.buckets.insert(k, vec!(t)); + } + fn handle_msg(&mut self, msg: TimeProfilerMsg) -> bool { - match msg { - TimeMsg(category, t) => self.buckets.find_mut(&category).unwrap().push(t), + match msg.clone() { + TimeMsg(k, t) => self.find_or_insert(k, t), PrintMsg => match self.last_msg { // only print if more data has arrived since the last printout Some(TimeMsg(..)) => self.print_buckets(), @@ -185,10 +205,11 @@ impl TimeProfiler { } fn print_buckets(&mut self) { - println!("{:39s} {:15s} {:15s} {:15s} {:15s} {:15s}", - "_category_", "_mean (ms)_", "_median (ms)_", - "_min (ms)_", "_max (ms)_", "_bucket size_"); - for (category, data) in self.buckets.mut_iter() { + println!("{:35s} {:14} {:9} {:30} {:15s} {:15s} {:-15s} {:-15s} {:-15s}", + "_category_", "_incremental?_", "_iframe?_", + " _url_", " _mean (ms)_", " _median (ms)_", + " _min (ms)_", " _max (ms)_", " _events_"); + for (&(ref category, ref meta), ref mut data) in self.buckets.mut_iter() { data.sort_by(|a, b| { if a < b { Less @@ -200,11 +221,11 @@ impl TimeProfiler { if data_len > 0 { let (mean, median, min, max) = (data.iter().map(|&x|x).sum() / (data_len as f64), - (*data)[data_len / 2], + data.as_slice()[data_len / 2], data.iter().fold(f64::INFINITY, |a, &b| a.min(b)), data.iter().fold(-f64::INFINITY, |a, &b| a.max(b))); - println!("{:-35s}: {:15.4f} {:15.4f} {:15.4f} {:15.4f} {:15u}", - category.format(), mean, median, min, max, data_len); + println!("{:-35s}{} {:15.4f} {:15.4f} {:15.4f} {:15.4f} {:15u}", + category.format(), meta.format(), mean, median, min, max, data_len); } } println!(""); @@ -213,6 +234,8 @@ impl TimeProfiler { pub fn profile<T>(category: TimeProfilerCategory, + // url, iframe?, first reflow? + meta: Option<(&Url, bool, bool)>, time_profiler_chan: TimeProfilerChan, callback: || -> T) -> T { @@ -220,7 +243,13 @@ pub fn profile<T>(category: TimeProfilerCategory, let val = callback(); let end_time = precise_time_ns(); let ms = (end_time - start_time) as f64 / 1000000f64; - time_profiler_chan.send(TimeMsg(category, ms)); + let meta = meta.map(|(url, iframe, first_reflow)| + TimerMetadata { + url: url.serialize(), + iframe: iframe, + first_reflow: first_reflow, + }); + time_profiler_chan.send(TimeMsg((category, meta), ms)); return val; } @@ -234,10 +263,3 @@ pub fn time<T>(msg: &str, callback: || -> T) -> T{ } return val; } - -// ensure that the order of the buckets matches the order of the enum categories -#[test] -fn check_order() { - let buckets = TimeProfilerCategory::empty_buckets(); - assert!(buckets.len() == NumBuckets as uint); -} |