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:
Emilio Cobos Álvarez 2017-03-11 02:31:25 -08:00
Родитель e98f75a57a
Коммит 99fa3bca94
7 изменённых файлов: 90 добавлений и 46 удалений

Просмотреть файл

@ -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);
} }
} }