mirror of
https://github.com/servo/servo.git
synced 2025-07-23 15:23:42 +01:00
Cache the results of cache entry revalidation and use the bloom filter.
This commit is contained in:
parent
d51d95d0c7
commit
1663bf6e5d
7 changed files with 100 additions and 49 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -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)",
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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(),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue