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.b,
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::WebGL(_) => "WebGL".to_owned(),
DisplayItem::Border(_) => "Border".to_owned(),

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

@ -266,7 +266,9 @@ impl LineBreaker {
}
/// 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();
// Create our fragment iterator.
@ -274,10 +276,11 @@ impl LineBreaker {
let mut old_fragments = mem::replace(&mut flow.fragments, InlineFragments::new());
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
// could resynchronize if we discover during reflow that all subsequent fragments must have
// the same position as they had in the previous reflow. I don't know how common this case
// really is in practice, but it's probably worth handling.
// TODO(pcwalton): This would likely be better as a list of dirty line
// indices. That way we could resynchronize if we discover during reflow
// that all subsequent fragments must have the same position as they had
// 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();
// Do the reflow.
@ -286,9 +289,10 @@ impl LineBreaker {
// Perform unicode bidirectional layout.
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
// fragments on level run boundaries during flow construction), so we can build a level
// array with just one entry per fragment.
// The text within a fragment is at a single bidi embedding level
// (because we split fragments on level run boundaries during flow
// 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 {
SpecificFragmentInfo::ScannedText(ref info) => info.run.bidi_level,
_ => para_level
@ -300,7 +304,8 @@ impl LineBreaker {
let has_rtl = levels.iter().cloned().any(unicode_bidi::is_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 {
let range = line.range.begin().to_usize()..line.range.end().to_usize();
let runs = unicode_bidi::visual_runs(range, &levels);
@ -323,7 +328,8 @@ impl LineBreaker {
mut old_fragment_iter: I,
flow: &'a InlineFlow,
layout_context: &LayoutContext)
where I: Iterator<Item=Fragment> {
where I: Iterator<Item=Fragment>,
{
loop {
// Acquire the next fragment to lay out from the work list or fragment list, as
// appropriate.
@ -355,17 +361,24 @@ impl LineBreaker {
/// 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-
/// reflow-safe; try `next_unbroken_fragment` instead.
fn next_fragment<I>(&mut self, old_fragment_iter: &mut I) -> Option<Fragment>
where I: Iterator<Item=Fragment> {
fn next_fragment<I>(&mut self,
old_fragment_iter: &mut I)
-> Option<Fragment>
where I: Iterator<Item=Fragment>,
{
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
/// subsequent fragments as appropriate. In effect, what this method does is to return the next
/// fragment to lay out, undoing line break operations that any previous reflows may have
/// performed. You probably want to be using this method instead of `next_fragment`.
fn next_unbroken_fragment<I>(&mut self, old_fragment_iter: &mut I) -> Option<Fragment>
where I: Iterator<Item=Fragment> {
/// Acquires a new fragment to lay out from the work list or fragment list,
/// merging it with any subsequent fragments as appropriate. In effect, what
/// this method does is to return the next fragment to lay out, undoing line
/// break operations that any previous reflows may have performed. You
/// probably want to be using this method instead of `next_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) {
None => return None,
Some(fragment) => fragment,
@ -536,7 +549,8 @@ impl LineBreaker {
// 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() {
fragment.strip_leading_whitespace_if_necessary();
let (line_bounds, _) = self.initial_line_placement(flow, &fragment, self.cur_b);
@ -547,15 +561,16 @@ impl LineBreaker {
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(),
fragment.border_box.size,
self.pending_line.green_zone,
fragment);
// NB: At this point, if `green_zone.inline < self.pending_line.bounds.size.inline` or
// `green_zone.block < self.pending_line.bounds.size.block`, then we committed a line that
// NB: At this point, if `green_zone.inline <
// 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.
let green_zone = self.pending_line.green_zone;
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) {
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;
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.
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.
//
// TODO(pcwalton, #226): Get the CSS `line-height` property from the style of the
// containing block to determine the minimum line block size.
// TODO(pcwalton, #226): Get the CSS `line-height` property from the
// 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
// element to determine its block-size for computing the line's own block-size.
// TODO(pcwalton, #226): Get the CSS `line-height` property from each
// non-replaced inline element to determine its block-size for computing
// the line's own block-size.
//
// TODO(pcwalton): Cache the line scanner?
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.
let line_count = self.lines.len();
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,
line,
self.base.flags.text_align(),
@ -1439,8 +1459,9 @@ impl Flow for InlineFlow {
&self.minimum_line_metrics,
layout_context);
// This is used to set the block-start position of the next line in the next iteration
// of the loop. We're no longer on the first line, so set indentation to zero.
// This is used to set the block-start position of the next line in
// the next iteration of the loop. We're no longer on the first
// line, so set indentation to zero.
indentation = Au(0)
}
@ -1768,7 +1789,7 @@ impl InlineFragmentContext {
return false
}
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
}
}

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

@ -6,7 +6,7 @@
#![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.
///
@ -16,6 +16,12 @@ pub struct LRUCache<K> {
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> {
/// Create a new LRU cache with `size` elements at most.
pub fn new(size: usize) -> Self {
@ -35,14 +41,15 @@ impl<K: PartialEq> LRUCache<K> {
}
}
/// Iterate over the contents of this cache.
pub fn iter(&self) -> Iter<K> {
self.entries.iter()
/// Iterate over the contents of this cache, from more to less recently
/// used.
pub fn iter(&self) -> LRUCacheIterator<K> {
self.entries.iter().rev()
}
/// Iterate mutably over the contents of this cache.
pub fn iter_mut(&mut self) -> IterMut<K> {
self.entries.iter_mut()
pub fn iter_mut(&mut self) -> LRUCacheMutIterator<K> {
self.entries.iter_mut().rev()
}
/// Insert a given key in the cache.

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

@ -28,6 +28,7 @@ use stylist::Stylist;
use thread_state;
use time;
use timer::Timer;
use traversal::DomTraversal;
/// This structure is used to create a local style context from a shared one.
pub struct ThreadLocalStyleContextCreationInfo {
@ -118,6 +119,8 @@ pub struct TraversalStatistics {
pub styles_shared: u32,
/// Time spent in the traversal, in milliseconds.
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.
@ -132,6 +135,7 @@ impl<'a> Add for &'a TraversalStatistics {
elements_matched: self.elements_matched + other.elements_matched,
styles_shared: self.styles_shared + other.styles_shared,
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 {
debug_assert!(self.traversal_time_ms != 0.0, "should have set traversal time");
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_styled,{}", self.elements_styled));
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.
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;
}
}

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

@ -10,7 +10,7 @@
use {Atom, LocalName};
use animation::{self, Animation, PropertyAnimation};
use atomic_refcell::AtomicRefMut;
use cache::LRUCache;
use cache::{LRUCache, LRUCacheMutIterator};
use cascade_info::CascadeInfo;
use context::{SequentialTask, SharedStyleContext, StyleContext};
use data::{ComputedStyle, ElementData, ElementStyles, RestyleData};
@ -27,7 +27,6 @@ use selectors::matching::AFFECTED_BY_PSEUDO_ELEMENTS;
use servo_config::opts;
use sink::ForgetfulSink;
use std::collections::hash_map::Entry;
use std::slice::IterMut;
use std::sync::Arc;
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()
}

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

@ -86,7 +86,7 @@ pub fn traverse_dom<E, D>(traversal: &D,
Some(ref cx) => &cx.borrow().statistics + &acc,
}
});
aggregate.compute_traversal_time(start_time.unwrap());
aggregate.finish(traversal, start_time.unwrap());
println!("{}", aggregate);
}
}

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

@ -68,7 +68,7 @@ pub fn traverse_dom<E, D>(traversal: &D,
// Dump statistics to stdout if requested.
if dump_stats {
let tlsc = tlc.borrow_mut();
tlsc.statistics.compute_traversal_time(start_time.unwrap());
tlsc.statistics.finish(traversal, start_time.unwrap());
println!("{}", tlsc.statistics);
}
}