зеркало из https://github.com/mozilla/gecko-dev.git
servo: Merge #15891 - style: Iterate the LRU cache contents from back to front (from emilio:lru-back-to-front); r=bholley,mbrubeck
This is on top of #15888. Only the second commit needs review. We put the more recently used item last, so iterating then from left to right is pointless. Source-Repo: https://github.com/servo/servo Source-Revision: 7fa4a94bb154449702e5ae9422c3008a5195c714 --HG-- extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear extra : subtree_revision : fbf4742c4186ad27bd4d5ccd3e2319a5b0748b55
This commit is contained in:
Родитель
e98f75a57a
Коммит
99fa3bca94
|
@ -1222,7 +1222,11 @@ impl fmt::Debug for DisplayItem {
|
||||||
solid_color.color.g,
|
solid_color.color.g,
|
||||||
solid_color.color.b,
|
solid_color.color.b,
|
||||||
solid_color.color.a),
|
solid_color.color.a),
|
||||||
DisplayItem::Text(_) => "Text".to_owned(),
|
DisplayItem::Text(ref text) => {
|
||||||
|
format!("Text ({:?})",
|
||||||
|
&text.text_run.text[
|
||||||
|
text.range.begin().0 as usize..(text.range.begin().0 + text.range.length().0) as usize])
|
||||||
|
}
|
||||||
DisplayItem::Image(_) => "Image".to_owned(),
|
DisplayItem::Image(_) => "Image".to_owned(),
|
||||||
DisplayItem::WebGL(_) => "WebGL".to_owned(),
|
DisplayItem::WebGL(_) => "WebGL".to_owned(),
|
||||||
DisplayItem::Border(_) => "Border".to_owned(),
|
DisplayItem::Border(_) => "Border".to_owned(),
|
||||||
|
|
|
@ -266,7 +266,9 @@ impl LineBreaker {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reflows fragments for the given inline flow.
|
/// Reflows fragments for the given inline flow.
|
||||||
fn scan_for_lines(&mut self, flow: &mut InlineFlow, layout_context: &LayoutContext) {
|
fn scan_for_lines(&mut self,
|
||||||
|
flow: &mut InlineFlow,
|
||||||
|
layout_context: &LayoutContext) {
|
||||||
self.reset_scanner();
|
self.reset_scanner();
|
||||||
|
|
||||||
// Create our fragment iterator.
|
// Create our fragment iterator.
|
||||||
|
@ -274,10 +276,11 @@ impl LineBreaker {
|
||||||
let mut old_fragments = mem::replace(&mut flow.fragments, InlineFragments::new());
|
let mut old_fragments = mem::replace(&mut flow.fragments, InlineFragments::new());
|
||||||
let old_fragment_iter = old_fragments.fragments.into_iter();
|
let old_fragment_iter = old_fragments.fragments.into_iter();
|
||||||
|
|
||||||
// TODO(pcwalton): This would likely be better as a list of dirty line indices. That way we
|
// TODO(pcwalton): This would likely be better as a list of dirty line
|
||||||
// could resynchronize if we discover during reflow that all subsequent fragments must have
|
// indices. That way we could resynchronize if we discover during reflow
|
||||||
// the same position as they had in the previous reflow. I don't know how common this case
|
// that all subsequent fragments must have the same position as they had
|
||||||
// really is in practice, but it's probably worth handling.
|
// in the previous reflow. I don't know how common this case really is
|
||||||
|
// in practice, but it's probably worth handling.
|
||||||
self.lines = Vec::new();
|
self.lines = Vec::new();
|
||||||
|
|
||||||
// Do the reflow.
|
// Do the reflow.
|
||||||
|
@ -286,9 +289,10 @@ impl LineBreaker {
|
||||||
// Perform unicode bidirectional layout.
|
// Perform unicode bidirectional layout.
|
||||||
let para_level = flow.base.writing_mode.to_bidi_level();
|
let para_level = flow.base.writing_mode.to_bidi_level();
|
||||||
|
|
||||||
// The text within a fragment is at a single bidi embedding level (because we split
|
// The text within a fragment is at a single bidi embedding level
|
||||||
// fragments on level run boundaries during flow construction), so we can build a level
|
// (because we split fragments on level run boundaries during flow
|
||||||
// array with just one entry per fragment.
|
// construction), so we can build a level array with just one entry per
|
||||||
|
// fragment.
|
||||||
let levels: Vec<u8> = self.new_fragments.iter().map(|fragment| match fragment.specific {
|
let levels: Vec<u8> = self.new_fragments.iter().map(|fragment| match fragment.specific {
|
||||||
SpecificFragmentInfo::ScannedText(ref info) => info.run.bidi_level,
|
SpecificFragmentInfo::ScannedText(ref info) => info.run.bidi_level,
|
||||||
_ => para_level
|
_ => para_level
|
||||||
|
@ -300,7 +304,8 @@ impl LineBreaker {
|
||||||
let has_rtl = levels.iter().cloned().any(unicode_bidi::is_rtl);
|
let has_rtl = levels.iter().cloned().any(unicode_bidi::is_rtl);
|
||||||
|
|
||||||
if has_rtl {
|
if has_rtl {
|
||||||
// Compute and store the visual ordering of the fragments within the line.
|
// Compute and store the visual ordering of the fragments within the
|
||||||
|
// line.
|
||||||
for line in &mut lines {
|
for line in &mut lines {
|
||||||
let range = line.range.begin().to_usize()..line.range.end().to_usize();
|
let range = line.range.begin().to_usize()..line.range.end().to_usize();
|
||||||
let runs = unicode_bidi::visual_runs(range, &levels);
|
let runs = unicode_bidi::visual_runs(range, &levels);
|
||||||
|
@ -323,7 +328,8 @@ impl LineBreaker {
|
||||||
mut old_fragment_iter: I,
|
mut old_fragment_iter: I,
|
||||||
flow: &'a InlineFlow,
|
flow: &'a InlineFlow,
|
||||||
layout_context: &LayoutContext)
|
layout_context: &LayoutContext)
|
||||||
where I: Iterator<Item=Fragment> {
|
where I: Iterator<Item=Fragment>,
|
||||||
|
{
|
||||||
loop {
|
loop {
|
||||||
// Acquire the next fragment to lay out from the work list or fragment list, as
|
// Acquire the next fragment to lay out from the work list or fragment list, as
|
||||||
// appropriate.
|
// appropriate.
|
||||||
|
@ -355,17 +361,24 @@ impl LineBreaker {
|
||||||
/// Acquires a new fragment to lay out from the work list or fragment list as appropriate.
|
/// Acquires a new fragment to lay out from the work list or fragment list as appropriate.
|
||||||
/// Note that you probably don't want to call this method directly in order to be incremental-
|
/// Note that you probably don't want to call this method directly in order to be incremental-
|
||||||
/// reflow-safe; try `next_unbroken_fragment` instead.
|
/// reflow-safe; try `next_unbroken_fragment` instead.
|
||||||
fn next_fragment<I>(&mut self, old_fragment_iter: &mut I) -> Option<Fragment>
|
fn next_fragment<I>(&mut self,
|
||||||
where I: Iterator<Item=Fragment> {
|
old_fragment_iter: &mut I)
|
||||||
|
-> Option<Fragment>
|
||||||
|
where I: Iterator<Item=Fragment>,
|
||||||
|
{
|
||||||
self.work_list.pop_front().or_else(|| old_fragment_iter.next())
|
self.work_list.pop_front().or_else(|| old_fragment_iter.next())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Acquires a new fragment to lay out from the work list or fragment list, merging it with any
|
/// Acquires a new fragment to lay out from the work list or fragment list,
|
||||||
/// subsequent fragments as appropriate. In effect, what this method does is to return the next
|
/// merging it with any subsequent fragments as appropriate. In effect, what
|
||||||
/// fragment to lay out, undoing line break operations that any previous reflows may have
|
/// this method does is to return the next fragment to lay out, undoing line
|
||||||
/// performed. You probably want to be using this method instead of `next_fragment`.
|
/// break operations that any previous reflows may have performed. You
|
||||||
fn next_unbroken_fragment<I>(&mut self, old_fragment_iter: &mut I) -> Option<Fragment>
|
/// probably want to be using this method instead of `next_fragment`.
|
||||||
where I: Iterator<Item=Fragment> {
|
fn next_unbroken_fragment<I>(&mut self,
|
||||||
|
old_fragment_iter: &mut I)
|
||||||
|
-> Option<Fragment>
|
||||||
|
where I: Iterator<Item=Fragment>,
|
||||||
|
{
|
||||||
let mut result = match self.next_fragment(old_fragment_iter) {
|
let mut result = match self.next_fragment(old_fragment_iter) {
|
||||||
None => return None,
|
None => return None,
|
||||||
Some(fragment) => fragment,
|
Some(fragment) => fragment,
|
||||||
|
@ -536,7 +549,8 @@ impl LineBreaker {
|
||||||
|
|
||||||
// Determine initial placement for the fragment if we need to.
|
// Determine initial placement for the fragment if we need to.
|
||||||
//
|
//
|
||||||
// Also, determine whether we can legally break the line before, or inside, this fragment.
|
// Also, determine whether we can legally break the line before, or
|
||||||
|
// inside, this fragment.
|
||||||
let fragment_is_line_break_opportunity = if self.pending_line_is_empty() {
|
let fragment_is_line_break_opportunity = if self.pending_line_is_empty() {
|
||||||
fragment.strip_leading_whitespace_if_necessary();
|
fragment.strip_leading_whitespace_if_necessary();
|
||||||
let (line_bounds, _) = self.initial_line_placement(flow, &fragment, self.cur_b);
|
let (line_bounds, _) = self.initial_line_placement(flow, &fragment, self.cur_b);
|
||||||
|
@ -547,15 +561,16 @@ impl LineBreaker {
|
||||||
fragment.white_space().allow_wrap()
|
fragment.white_space().allow_wrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("LineBreaker: trying to append to line {} (fragment size: {:?}, green zone: {:?}): \
|
debug!("LineBreaker: trying to append to line {} \
|
||||||
{:?}",
|
(fragment size: {:?}, green zone: {:?}): {:?}",
|
||||||
self.lines.len(),
|
self.lines.len(),
|
||||||
fragment.border_box.size,
|
fragment.border_box.size,
|
||||||
self.pending_line.green_zone,
|
self.pending_line.green_zone,
|
||||||
fragment);
|
fragment);
|
||||||
|
|
||||||
// NB: At this point, if `green_zone.inline < self.pending_line.bounds.size.inline` or
|
// NB: At this point, if `green_zone.inline <
|
||||||
// `green_zone.block < self.pending_line.bounds.size.block`, then we committed a line that
|
// self.pending_line.bounds.size.inline` or `green_zone.block <
|
||||||
|
// self.pending_line.bounds.size.block`, then we committed a line that
|
||||||
// overlaps with floats.
|
// overlaps with floats.
|
||||||
let green_zone = self.pending_line.green_zone;
|
let green_zone = self.pending_line.green_zone;
|
||||||
let new_line_metrics = self.pending_line.new_metrics_for_fragment(&fragment,
|
let new_line_metrics = self.pending_line.new_metrics_for_fragment(&fragment,
|
||||||
|
@ -1263,7 +1278,8 @@ impl Flow for InlineFlow {
|
||||||
fn bubble_inline_sizes(&mut self) {
|
fn bubble_inline_sizes(&mut self) {
|
||||||
self.update_restyle_damage();
|
self.update_restyle_damage();
|
||||||
|
|
||||||
let _scope = layout_debug_scope!("inline::bubble_inline_sizes {:x}", self.base.debug_id());
|
let _scope = layout_debug_scope!("inline::bubble_inline_sizes {:x}",
|
||||||
|
self.base.debug_id());
|
||||||
|
|
||||||
let writing_mode = self.base.writing_mode;
|
let writing_mode = self.base.writing_mode;
|
||||||
for kid in self.base.child_iter_mut() {
|
for kid in self.base.child_iter_mut() {
|
||||||
|
@ -1386,15 +1402,18 @@ impl Flow for InlineFlow {
|
||||||
|
|
||||||
/// Calculate and set the block-size of this flow. See CSS 2.1 § 10.6.1.
|
/// Calculate and set the block-size of this flow. See CSS 2.1 § 10.6.1.
|
||||||
fn assign_block_size(&mut self, layout_context: &LayoutContext) {
|
fn assign_block_size(&mut self, layout_context: &LayoutContext) {
|
||||||
let _scope = layout_debug_scope!("inline::assign_block_size {:x}", self.base.debug_id());
|
let _scope = layout_debug_scope!("inline::assign_block_size {:x}",
|
||||||
|
self.base.debug_id());
|
||||||
|
|
||||||
// Divide the fragments into lines.
|
// Divide the fragments into lines.
|
||||||
//
|
//
|
||||||
// TODO(pcwalton, #226): Get the CSS `line-height` property from the style of the
|
// TODO(pcwalton, #226): Get the CSS `line-height` property from the
|
||||||
// containing block to determine the minimum line block size.
|
// style of the containing block to determine the minimum line block
|
||||||
|
// size.
|
||||||
//
|
//
|
||||||
// TODO(pcwalton, #226): Get the CSS `line-height` property from each non-replaced inline
|
// TODO(pcwalton, #226): Get the CSS `line-height` property from each
|
||||||
// element to determine its block-size for computing the line's own block-size.
|
// non-replaced inline element to determine its block-size for computing
|
||||||
|
// the line's own block-size.
|
||||||
//
|
//
|
||||||
// TODO(pcwalton): Cache the line scanner?
|
// TODO(pcwalton): Cache the line scanner?
|
||||||
debug!("assign_block_size_inline: floats in: {:?}", self.base.floats);
|
debug!("assign_block_size_inline: floats in: {:?}", self.base.floats);
|
||||||
|
@ -1426,7 +1445,8 @@ impl Flow for InlineFlow {
|
||||||
// Now, go through each line and lay out the fragments inside.
|
// Now, go through each line and lay out the fragments inside.
|
||||||
let line_count = self.lines.len();
|
let line_count = self.lines.len();
|
||||||
for (line_index, line) in self.lines.iter_mut().enumerate() {
|
for (line_index, line) in self.lines.iter_mut().enumerate() {
|
||||||
// Lay out fragments in the inline direction, and justify them if necessary.
|
// Lay out fragments in the inline direction, and justify them if
|
||||||
|
// necessary.
|
||||||
InlineFlow::set_inline_fragment_positions(&mut self.fragments,
|
InlineFlow::set_inline_fragment_positions(&mut self.fragments,
|
||||||
line,
|
line,
|
||||||
self.base.flags.text_align(),
|
self.base.flags.text_align(),
|
||||||
|
@ -1439,8 +1459,9 @@ impl Flow for InlineFlow {
|
||||||
&self.minimum_line_metrics,
|
&self.minimum_line_metrics,
|
||||||
layout_context);
|
layout_context);
|
||||||
|
|
||||||
// This is used to set the block-start position of the next line in the next iteration
|
// This is used to set the block-start position of the next line in
|
||||||
// of the loop. We're no longer on the first line, so set indentation to zero.
|
// the next iteration of the loop. We're no longer on the first
|
||||||
|
// line, so set indentation to zero.
|
||||||
indentation = Au(0)
|
indentation = Au(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1768,7 +1789,7 @@ impl InlineFragmentContext {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for (this_node, other_node) in self.nodes.iter().zip(&other.nodes) {
|
for (this_node, other_node) in self.nodes.iter().zip(&other.nodes) {
|
||||||
if !arc_ptr_eq(&this_node.style, &other_node.style) {
|
if this_node.address != other_node.address {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
use std::slice::{Iter, IterMut};
|
use std::{iter, slice};
|
||||||
|
|
||||||
/// A LRU cache used to store a set of at most `n` elements at the same time.
|
/// A LRU cache used to store a set of at most `n` elements at the same time.
|
||||||
///
|
///
|
||||||
|
@ -16,6 +16,12 @@ pub struct LRUCache<K> {
|
||||||
cache_size: usize,
|
cache_size: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A iterator over the items of the LRU cache.
|
||||||
|
pub type LRUCacheIterator<'a, K> = iter::Rev<slice::Iter<'a, K>>;
|
||||||
|
|
||||||
|
/// A iterator over the mutable items of the LRU cache.
|
||||||
|
pub type LRUCacheMutIterator<'a, K> = iter::Rev<slice::IterMut<'a, K>>;
|
||||||
|
|
||||||
impl<K: PartialEq> LRUCache<K> {
|
impl<K: PartialEq> LRUCache<K> {
|
||||||
/// Create a new LRU cache with `size` elements at most.
|
/// Create a new LRU cache with `size` elements at most.
|
||||||
pub fn new(size: usize) -> Self {
|
pub fn new(size: usize) -> Self {
|
||||||
|
@ -35,14 +41,15 @@ impl<K: PartialEq> LRUCache<K> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterate over the contents of this cache.
|
/// Iterate over the contents of this cache, from more to less recently
|
||||||
pub fn iter(&self) -> Iter<K> {
|
/// used.
|
||||||
self.entries.iter()
|
pub fn iter(&self) -> LRUCacheIterator<K> {
|
||||||
|
self.entries.iter().rev()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterate mutably over the contents of this cache.
|
/// Iterate mutably over the contents of this cache.
|
||||||
pub fn iter_mut(&mut self) -> IterMut<K> {
|
pub fn iter_mut(&mut self) -> LRUCacheMutIterator<K> {
|
||||||
self.entries.iter_mut()
|
self.entries.iter_mut().rev()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a given key in the cache.
|
/// Insert a given key in the cache.
|
||||||
|
|
|
@ -28,6 +28,7 @@ use stylist::Stylist;
|
||||||
use thread_state;
|
use thread_state;
|
||||||
use time;
|
use time;
|
||||||
use timer::Timer;
|
use timer::Timer;
|
||||||
|
use traversal::DomTraversal;
|
||||||
|
|
||||||
/// This structure is used to create a local style context from a shared one.
|
/// This structure is used to create a local style context from a shared one.
|
||||||
pub struct ThreadLocalStyleContextCreationInfo {
|
pub struct ThreadLocalStyleContextCreationInfo {
|
||||||
|
@ -118,6 +119,8 @@ pub struct TraversalStatistics {
|
||||||
pub styles_shared: u32,
|
pub styles_shared: u32,
|
||||||
/// Time spent in the traversal, in milliseconds.
|
/// Time spent in the traversal, in milliseconds.
|
||||||
pub traversal_time_ms: f64,
|
pub traversal_time_ms: f64,
|
||||||
|
/// Whether this was a parallel traversal.
|
||||||
|
pub is_parallel: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implementation of Add to aggregate statistics across different threads.
|
/// Implementation of Add to aggregate statistics across different threads.
|
||||||
|
@ -132,6 +135,7 @@ impl<'a> Add for &'a TraversalStatistics {
|
||||||
elements_matched: self.elements_matched + other.elements_matched,
|
elements_matched: self.elements_matched + other.elements_matched,
|
||||||
styles_shared: self.styles_shared + other.styles_shared,
|
styles_shared: self.styles_shared + other.styles_shared,
|
||||||
traversal_time_ms: 0.0,
|
traversal_time_ms: 0.0,
|
||||||
|
is_parallel: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -142,6 +146,11 @@ impl fmt::Display for TraversalStatistics {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
debug_assert!(self.traversal_time_ms != 0.0, "should have set traversal time");
|
debug_assert!(self.traversal_time_ms != 0.0, "should have set traversal time");
|
||||||
try!(writeln!(f, "[PERF] perf block start"));
|
try!(writeln!(f, "[PERF] perf block start"));
|
||||||
|
try!(writeln!(f, "[PERF],traversal,{}", if self.is_parallel.unwrap() {
|
||||||
|
"parallel"
|
||||||
|
} else {
|
||||||
|
"sequential"
|
||||||
|
}));
|
||||||
try!(writeln!(f, "[PERF],elements_traversed,{}", self.elements_traversed));
|
try!(writeln!(f, "[PERF],elements_traversed,{}", self.elements_traversed));
|
||||||
try!(writeln!(f, "[PERF],elements_styled,{}", self.elements_styled));
|
try!(writeln!(f, "[PERF],elements_styled,{}", self.elements_styled));
|
||||||
try!(writeln!(f, "[PERF],elements_matched,{}", self.elements_matched));
|
try!(writeln!(f, "[PERF],elements_matched,{}", self.elements_matched));
|
||||||
|
@ -170,7 +179,11 @@ impl TraversalStatistics {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes the traversal time given the start time in seconds.
|
/// Computes the traversal time given the start time in seconds.
|
||||||
pub fn compute_traversal_time(&mut self, start: f64) {
|
pub fn finish<E, D>(&mut self, traversal: &D, start: f64)
|
||||||
|
where E: TElement,
|
||||||
|
D: DomTraversal<E>,
|
||||||
|
{
|
||||||
|
self.is_parallel = Some(traversal.is_parallel());
|
||||||
self.traversal_time_ms = (time::precise_time_s() - start) * 1000.0;
|
self.traversal_time_ms = (time::precise_time_s() - start) * 1000.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
use {Atom, LocalName};
|
use {Atom, LocalName};
|
||||||
use animation::{self, Animation, PropertyAnimation};
|
use animation::{self, Animation, PropertyAnimation};
|
||||||
use atomic_refcell::AtomicRefMut;
|
use atomic_refcell::AtomicRefMut;
|
||||||
use cache::LRUCache;
|
use cache::{LRUCache, LRUCacheMutIterator};
|
||||||
use cascade_info::CascadeInfo;
|
use cascade_info::CascadeInfo;
|
||||||
use context::{SequentialTask, SharedStyleContext, StyleContext};
|
use context::{SequentialTask, SharedStyleContext, StyleContext};
|
||||||
use data::{ComputedStyle, ElementData, ElementStyles, RestyleData};
|
use data::{ComputedStyle, ElementData, ElementStyles, RestyleData};
|
||||||
|
@ -27,7 +27,6 @@ use selectors::matching::AFFECTED_BY_PSEUDO_ELEMENTS;
|
||||||
use servo_config::opts;
|
use servo_config::opts;
|
||||||
use sink::ForgetfulSink;
|
use sink::ForgetfulSink;
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
use std::slice::IterMut;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use stylist::ApplicableDeclarationBlock;
|
use stylist::ApplicableDeclarationBlock;
|
||||||
|
|
||||||
|
@ -366,7 +365,7 @@ impl<E: TElement> StyleSharingCandidateCache<E> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn iter_mut(&mut self) -> IterMut<StyleSharingCandidate<E>> {
|
fn iter_mut(&mut self) -> LRUCacheMutIterator<StyleSharingCandidate<E>> {
|
||||||
self.cache.iter_mut()
|
self.cache.iter_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -86,7 +86,7 @@ pub fn traverse_dom<E, D>(traversal: &D,
|
||||||
Some(ref cx) => &cx.borrow().statistics + &acc,
|
Some(ref cx) => &cx.borrow().statistics + &acc,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
aggregate.compute_traversal_time(start_time.unwrap());
|
aggregate.finish(traversal, start_time.unwrap());
|
||||||
println!("{}", aggregate);
|
println!("{}", aggregate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,7 @@ pub fn traverse_dom<E, D>(traversal: &D,
|
||||||
// Dump statistics to stdout if requested.
|
// Dump statistics to stdout if requested.
|
||||||
if dump_stats {
|
if dump_stats {
|
||||||
let tlsc = tlc.borrow_mut();
|
let tlsc = tlc.borrow_mut();
|
||||||
tlsc.statistics.compute_traversal_time(start_time.unwrap());
|
tlsc.statistics.finish(traversal, start_time.unwrap());
|
||||||
println!("{}", tlsc.statistics);
|
println!("{}", tlsc.statistics);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче