Cache the results of cache entry revalidation and use the bloom filter.

This commit is contained in:
Bobby Holley 2017-04-11 14:23:35 +08:00
parent d51d95d0c7
commit 1663bf6e5d
7 changed files with 100 additions and 49 deletions

1
Cargo.lock generated
View file

@ -2733,6 +2733,7 @@ dependencies = [
"app_units 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bindgen 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bit-vec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",

View file

@ -25,6 +25,7 @@ testing = []
app_units = "0.4"
atomic_refcell = "0.1"
bitflags = "0.7"
bit-vec = "0.4.3"
byteorder = "1.0"
cfg-if = "0.1.0"
cssparser = "0.12.1"

View file

@ -7,6 +7,7 @@
use animation::{Animation, PropertyAnimation};
use app_units::Au;
use bit_vec::BitVec;
use bloom::StyleBloom;
use data::ElementData;
use dom::{OpaqueNode, TNode, TElement, SendElement};
@ -111,6 +112,9 @@ pub struct CurrentElementInfo {
element: OpaqueNode,
/// Whether the element is being styled for the first time.
is_initial_style: bool,
/// Lazy cache of the result of matching the current element against the
/// revalidation selectors.
pub revalidation_match_results: Option<BitVec>,
/// A Vec of possibly expired animations. Used only by Servo.
#[allow(dead_code)]
pub possibly_expired_animations: Vec<PropertyAnimation>,
@ -317,6 +321,7 @@ impl<E: TElement> ThreadLocalStyleContext<E> {
self.current_element_info = Some(CurrentElementInfo {
element: element.as_node().opaque(),
is_initial_style: !data.has_styles(),
revalidation_match_results: None,
possibly_expired_animations: Vec::new(),
});
}

View file

@ -39,6 +39,7 @@
extern crate app_units;
extern crate atomic_refcell;
extern crate bit_vec;
#[macro_use]
extern crate bitflags;
#[allow(unused_extern_crates)] extern crate byteorder;

View file

@ -10,9 +10,10 @@
use Atom;
use animation::{self, Animation, PropertyAnimation};
use atomic_refcell::AtomicRefMut;
use bit_vec::BitVec;
use cache::{LRUCache, LRUCacheMutIterator};
use cascade_info::CascadeInfo;
use context::{SequentialTask, SharedStyleContext, StyleContext};
use context::{CurrentElementInfo, SequentialTask, SharedStyleContext, StyleContext};
use data::{ComputedStyle, ElementData, ElementStyles, RestyleData};
use dom::{AnimationRules, SendElement, TElement, TNode};
use font_metrics::FontMetricsProvider;
@ -53,6 +54,8 @@ struct StyleSharingCandidate<E: TElement> {
element: SendElement<E>,
/// The cached class names.
class_attributes: Option<Vec<Atom>>,
/// The cached result of matching this entry against the revalidation selectors.
revalidation_match_results: Option<BitVec>,
}
impl<E: TElement> PartialEq<StyleSharingCandidate<E>> for StyleSharingCandidate<E> {
@ -103,7 +106,9 @@ pub enum CacheMiss {
fn element_matches_candidate<E: TElement>(element: &E,
candidate: &mut StyleSharingCandidate<E>,
candidate_element: &E,
shared_context: &SharedStyleContext)
shared: &SharedStyleContext,
bloom: &BloomFilter,
info: &mut CurrentElementInfo)
-> Result<ComputedStyle, CacheMiss> {
macro_rules! miss {
($miss: ident) => {
@ -151,7 +156,7 @@ fn element_matches_candidate<E: TElement>(element: &E,
miss!(PresHints)
}
if !revalidate(element, candidate_element, shared_context) {
if !revalidate(element, candidate, candidate_element, shared, bloom, info) {
miss!(Revalidation)
}
@ -194,9 +199,37 @@ fn have_same_class<E: TElement>(element: &E,
#[inline]
fn revalidate<E: TElement>(element: &E,
candidate: &E,
ctx: &SharedStyleContext) -> bool {
ctx.stylist.revalidate_entry_for_cache(element, candidate)
candidate: &mut StyleSharingCandidate<E>,
candidate_element: &E,
shared: &SharedStyleContext,
bloom: &BloomFilter,
info: &mut CurrentElementInfo)
-> bool {
// NB: We could avoid matching ancestor selectors entirely (rather than
// just depending on the bloom filter), at the expense of some complexity.
// Gecko bug 1354965 tracks this.
//
// We could also be even more careful about only matching the minimal number
// of revalidation selectors until we find a mismatch. Gecko bug 1355668
// tracks this.
//
// These potential optimizations may not be worth the complexity.
let stylist = &shared.stylist;
if info.revalidation_match_results.is_none() {
info.revalidation_match_results =
Some(stylist.match_revalidation_selectors(element, bloom));
}
if candidate.revalidation_match_results.is_none() {
candidate.revalidation_match_results =
Some(stylist.match_revalidation_selectors(candidate_element, bloom));
}
let for_element = info.revalidation_match_results.as_ref().unwrap();
let for_candidate = candidate.revalidation_match_results.as_ref().unwrap();
debug_assert!(for_element.len() == for_candidate.len());
for_element == for_candidate
}
static STYLE_SHARING_CANDIDATE_CACHE_SIZE: usize = 8;
@ -219,7 +252,8 @@ impl<E: TElement> StyleSharingCandidateCache<E> {
pub fn insert_if_possible(&mut self,
element: &E,
style: &Arc<ComputedValues>,
relations: StyleRelations) {
relations: StyleRelations,
revalidation_match_results: Option<BitVec>) {
let parent = match element.parent_element() {
Some(element) => element,
None => {
@ -252,6 +286,7 @@ impl<E: TElement> StyleSharingCandidateCache<E> {
self.cache.insert(StyleSharingCandidate {
element: unsafe { SendElement::new(*element) },
class_attributes: None,
revalidation_match_results: revalidation_match_results,
});
}
@ -632,11 +667,14 @@ trait PrivateMatchMethods: TElement {
}
fn share_style_with_candidate_if_possible(&self,
shared_context: &SharedStyleContext,
candidate: &mut StyleSharingCandidate<Self>)
candidate: &mut StyleSharingCandidate<Self>,
shared: &SharedStyleContext,
bloom: &BloomFilter,
info: &mut CurrentElementInfo)
-> Result<ComputedStyle, CacheMiss> {
let candidate_element = *candidate.element;
element_matches_candidate(self, candidate, &candidate_element, shared_context)
element_matches_candidate(self, candidate, &candidate_element,
shared, bloom, info)
}
}
@ -699,11 +737,23 @@ pub trait MatchMethods : TElement {
// If the style is shareable, add it to the LRU cache.
if sharing == StyleSharingBehavior::Allow && relations_are_shareable(&primary_relations) {
// If we previously tried to match this element against the cache,
// the revalidation match results will already be cached. Otherwise
// we'll have None, and compute them later on-demand.
//
// If we do have the results, grab them here to satisfy the borrow
// checker.
let revalidation_match_results = context.thread_local
.current_element_info
.as_mut().unwrap()
.revalidation_match_results
.take();
context.thread_local
.style_sharing_candidate_cache
.insert_if_possible(self,
data.styles().primary.values(),
primary_relations);
primary_relations,
revalidation_match_results);
}
}
@ -951,9 +1001,7 @@ pub trait MatchMethods : TElement {
/// live nodes in it, and we have no way to guarantee that at the type
/// system level yet.
unsafe fn share_style_if_possible(&self,
style_sharing_candidate_cache:
&mut StyleSharingCandidateCache<Self>,
shared_context: &SharedStyleContext,
context: &mut StyleContext<Self>,
data: &mut AtomicRefMut<ElementData>)
-> StyleSharingResult {
if is_share_style_cache_disabled() {
@ -972,11 +1020,17 @@ pub trait MatchMethods : TElement {
return StyleSharingResult::CannotShare
}
let cache = &mut context.thread_local.style_sharing_candidate_cache;
let current_element_info =
&mut context.thread_local.current_element_info.as_mut().unwrap();
let bloom = context.thread_local.bloom_filter.filter();
let mut should_clear_cache = false;
for (i, candidate) in style_sharing_candidate_cache.iter_mut().enumerate() {
for (i, candidate) in cache.iter_mut().enumerate() {
let sharing_result =
self.share_style_with_candidate_if_possible(shared_context,
candidate);
self.share_style_with_candidate_if_possible(candidate,
&context.shared,
bloom,
current_element_info);
match sharing_result {
Ok(shared_style) => {
// Yay, cache hit. Share the style.
@ -986,7 +1040,8 @@ pub trait MatchMethods : TElement {
let old_values = data.get_styles_mut()
.and_then(|s| s.primary.values.take());
if let Some(old) = old_values {
self.accumulate_damage(shared_context, data.restyle_mut(), &old,
self.accumulate_damage(&context.shared,
data.restyle_mut(), &old,
shared_style.values(), None);
}
@ -1021,7 +1076,7 @@ pub trait MatchMethods : TElement {
}
}
if should_clear_cache {
style_sharing_candidate_cache.clear();
cache.clear();
}
StyleSharingResult::CannotShare

View file

@ -7,6 +7,7 @@
#![deny(missing_docs)]
use {Atom, LocalName};
use bit_vec::BitVec;
use data::ComputedStyle;
use dom::{AnimationRules, PresentationalHintsSynthetizer, TElement};
use error_reporting::StdoutErrorReporter;
@ -765,41 +766,30 @@ impl Stylist {
self.rule_tree.root()
}
/// Returns whether two elements match the same set of revalidation-
/// requiring rules.
///
/// This is also for the style sharing candidate cache.
pub fn revalidate_entry_for_cache<E>(&self,
/// Computes the match results of a given element against the set of
/// revalidation selectors.
pub fn match_revalidation_selectors<E>(&self,
element: &E,
candidate: &E) -> bool
bloom: &BloomFilter)
-> BitVec
where E: TElement,
{
use selectors::matching::StyleRelations;
use selectors::matching::matches_complex_selector;
// TODO(emilio): we can probably do better, the candidate should already
// know what rules it matches.
//
// TODO(emilio): Use the bloom filter, since they contain the element's
// ancestor chain and it's correct for the candidate too.
for ref selector in self.selectors_for_cache_revalidation.iter() {
let element_matches =
matches_complex_selector(&selector.complex_selector, element,
None, &mut StyleRelations::empty(),
&mut |_, _| {});
let candidate_matches =
matches_complex_selector(&selector.complex_selector, candidate,
None, &mut StyleRelations::empty(),
&mut |_, _| {});
let len = self.selectors_for_cache_revalidation.len();
let mut results = BitVec::from_elem(len, false);
if element_matches != candidate_matches {
debug!("match_same_sibling_affecting_rules: Failure due to {:?}",
selector.complex_selector);
return false;
}
for (i, ref selector) in self.selectors_for_cache_revalidation
.iter().enumerate() {
results.set(i, matches_complex_selector(&selector.complex_selector,
element,
Some(bloom),
&mut StyleRelations::empty(),
&mut |_, _| {}));
}
true
results
}
/// Given an element, and a snapshot that represents a previous state of the

View file

@ -680,15 +680,13 @@ fn compute_style<E, D>(_traversal: &D,
use matching::StyleSharingResult::*;
context.thread_local.statistics.elements_styled += 1;
let shared_context = context.shared;
let kind = data.restyle_kind();
// First, try the style sharing cache. If we get a match we can skip the rest
// of the work.
if let MatchAndCascade = kind {
let sharing_result = unsafe {
let cache = &mut context.thread_local.style_sharing_candidate_cache;
element.share_style_if_possible(cache, shared_context, &mut data)
element.share_style_if_possible(context, &mut data)
};
if let StyleWasShared(index) = sharing_result {
context.thread_local.statistics.styles_shared += 1;