Cache the parent CV identity on ValidationData.

This will make the linear probing faster. If we end up implementing the two-tier
cache setup, this code will be unnecessary and can go away.

MozReview-Commit-ID: BRfV5ump34n
This commit is contained in:
Bobby Holley 2017-09-12 12:29:29 -07:00
parent 7b019f807b
commit 729db5ccec
2 changed files with 60 additions and 24 deletions

View file

@ -10,26 +10,30 @@ use Atom;
use bloom::StyleBloom; use bloom::StyleBloom;
use context::{SelectorFlagsMap, SharedStyleContext}; use context::{SelectorFlagsMap, SharedStyleContext};
use dom::TElement; use dom::TElement;
use servo_arc::Arc;
use sharing::{StyleSharingCandidate, StyleSharingTarget}; use sharing::{StyleSharingCandidate, StyleSharingTarget};
/// Whether styles may be shared across the children of the given parent elements. /// Determines whether a target and a candidate have compatible parents for sharing.
/// This is used to share style across cousins. pub fn parents_allow_sharing<E>(
/// target: &mut StyleSharingTarget<E>,
/// Both elements need to be styled already. candidate: &mut StyleSharingCandidate<E>
pub fn can_share_style_across_parents<E>(first: Option<E>, second: Option<E>) -> bool ) -> bool
where E: TElement, where E: TElement,
{ {
let (first, second) = match (first, second) { // If the identity of the parent style isn't equal, we can't share. We check
(Some(f), Some(s)) => (f, s), // this first, because the result is cached.
_ => return false, if target.parent_style_identity() != candidate.parent_style_identity() {
}; return false;
}
debug_assert_ne!(first, second); // Siblings can always share.
let parent = target.inheritance_parent().unwrap();
let first_data = first.borrow_data().unwrap(); let candidate_parent = candidate.element.inheritance_parent().unwrap();
let second_data = second.borrow_data().unwrap(); if parent == candidate_parent {
return true;
}
// Cousins are a bit more complicated.
//
// If a parent element was already styled and we traversed past it without // If a parent element was already styled and we traversed past it without
// restyling it, that may be because our clever invalidation logic was able // restyling it, that may be because our clever invalidation logic was able
// to prove that the styles of that element would remain unchanged despite // to prove that the styles of that element would remain unchanged despite
@ -40,14 +44,14 @@ pub fn can_share_style_across_parents<E>(first: Option<E>, second: Option<E>) ->
// //
// This is a somewhat conservative check. We could tighten it by having the // This is a somewhat conservative check. We could tighten it by having the
// invalidation logic explicitly flag elements for which it ellided styling. // invalidation logic explicitly flag elements for which it ellided styling.
if first_data.traversed_without_styling() || second_data.traversed_without_styling() { let parent_data = parent.borrow_data().unwrap();
let candidate_parent_data = candidate_parent.borrow_data().unwrap();
if parent_data.traversed_without_styling() ||
candidate_parent_data.traversed_without_styling() {
return false; return false;
} }
let same_computed_values = true
Arc::ptr_eq(first_data.styles.primary(), second_data.styles.primary());
same_computed_values
} }
/// Whether two elements have the same same style attribute (by pointer identity). /// Whether two elements have the same same style attribute (by pointer identity).

View file

@ -76,7 +76,7 @@ use matching::MatchMethods;
use owning_ref::OwningHandle; use owning_ref::OwningHandle;
use properties::ComputedValues; use properties::ComputedValues;
use selectors::matching::{ElementSelectorFlags, VisitedHandlingMode}; use selectors::matching::{ElementSelectorFlags, VisitedHandlingMode};
use servo_arc::Arc; use servo_arc::{Arc, NonZeroPtrMut};
use smallbitvec::SmallBitVec; use smallbitvec::SmallBitVec;
use smallvec::SmallVec; use smallvec::SmallVec;
use std::marker::PhantomData; use std::marker::PhantomData;
@ -108,6 +108,16 @@ pub enum StyleSharingBehavior {
Disallow, Disallow,
} }
/// Opaque pointer type to compare ComputedValues identities.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct OpaqueComputedValues(NonZeroPtrMut<()>);
impl OpaqueComputedValues {
fn from(cv: &ComputedValues) -> Self {
let p = NonZeroPtrMut::new(cv as *const ComputedValues as *const () as *mut ());
OpaqueComputedValues(p)
}
}
/// Some data we want to avoid recomputing all the time while trying to share /// Some data we want to avoid recomputing all the time while trying to share
/// style. /// style.
#[derive(Debug, Default)] #[derive(Debug, Default)]
@ -121,6 +131,9 @@ pub struct ValidationData {
/// The list of presentational attributes of the element. /// The list of presentational attributes of the element.
pres_hints: Option<SmallVec<[ApplicableDeclarationBlock; 5]>>, pres_hints: Option<SmallVec<[ApplicableDeclarationBlock; 5]>>,
/// The pointer identity of the parent ComputedValues.
parent_style_identity: Option<OpaqueComputedValues>,
/// The cached result of matching this entry against the revalidation /// The cached result of matching this entry against the revalidation
/// selectors. /// selectors.
revalidation_match_results: Option<SmallBitVec>, revalidation_match_results: Option<SmallBitVec>,
@ -167,6 +180,18 @@ impl ValidationData {
&*self.class_list.as_ref().unwrap() &*self.class_list.as_ref().unwrap()
} }
/// Get or compute the parent style identity.
pub fn parent_style_identity<E>(&mut self, el: E) -> OpaqueComputedValues
where E: TElement,
{
if self.parent_style_identity.is_none() {
let parent = el.inheritance_parent().unwrap();
self.parent_style_identity =
Some(OpaqueComputedValues::from(parent.borrow_data().unwrap().styles.primary()));
}
self.parent_style_identity.as_ref().unwrap().clone()
}
/// Computes the revalidation results if needed, and returns it. /// Computes the revalidation results if needed, and returns it.
/// Inline so we know at compile time what bloom_known_valid is. /// Inline so we know at compile time what bloom_known_valid is.
#[inline] #[inline]
@ -250,6 +275,11 @@ impl<E: TElement> StyleSharingCandidate<E> {
self.validation_data.pres_hints(self.element) self.validation_data.pres_hints(self.element)
} }
/// Get the parent style identity.
fn parent_style_identity(&mut self) -> OpaqueComputedValues {
self.validation_data.parent_style_identity(self.element)
}
/// Compute the bit vector of revalidation selector match results /// Compute the bit vector of revalidation selector match results
/// for this candidate. /// for this candidate.
fn revalidation_match_results( fn revalidation_match_results(
@ -304,6 +334,11 @@ impl<E: TElement> StyleSharingTarget<E> {
self.validation_data.pres_hints(self.element) self.validation_data.pres_hints(self.element)
} }
/// Get the parent style identity.
fn parent_style_identity(&mut self) -> OpaqueComputedValues {
self.validation_data.parent_style_identity(self.element)
}
fn revalidation_match_results( fn revalidation_match_results(
&mut self, &mut self,
stylist: &Stylist, stylist: &Stylist,
@ -615,10 +650,7 @@ impl<E: TElement> StyleSharingCache<E> {
// share styles and permit sharing across their children. The latter // share styles and permit sharing across their children. The latter
// check allows us to share style between cousins if the parents // check allows us to share style between cousins if the parents
// shared style. // shared style.
let parent = target.inheritance_parent(); if !checks::parents_allow_sharing(target, candidate) {
let candidate_parent = candidate.element.inheritance_parent();
if parent != candidate_parent &&
!checks::can_share_style_across_parents(parent, candidate_parent) {
trace!("Miss: Parent"); trace!("Miss: Parent");
return false; return false;
} }