mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +01:00
Reuse style sharing cache across traversals.
MozReview-Commit-ID: 9wCJtciqs6K
This commit is contained in:
parent
0614010552
commit
ec6cca6d08
1 changed files with 68 additions and 9 deletions
|
@ -66,6 +66,7 @@
|
||||||
|
|
||||||
use Atom;
|
use Atom;
|
||||||
use applicable_declarations::ApplicableDeclarationBlock;
|
use applicable_declarations::ApplicableDeclarationBlock;
|
||||||
|
use atomic_refcell::{AtomicRefCell, AtomicRefMut};
|
||||||
use bit_vec::BitVec;
|
use bit_vec::BitVec;
|
||||||
use bloom::StyleBloom;
|
use bloom::StyleBloom;
|
||||||
use cache::{LRUCache, LRUCacheMutIterator};
|
use cache::{LRUCache, LRUCacheMutIterator};
|
||||||
|
@ -73,12 +74,15 @@ use context::{SelectorFlagsMap, SharedStyleContext, StyleContext};
|
||||||
use data::{ElementData, ElementStyles};
|
use data::{ElementData, ElementStyles};
|
||||||
use dom::{TElement, SendElement};
|
use dom::{TElement, SendElement};
|
||||||
use matching::{ChildCascadeRequirement, MatchMethods};
|
use matching::{ChildCascadeRequirement, MatchMethods};
|
||||||
|
use owning_ref::OwningHandle;
|
||||||
use properties::ComputedValues;
|
use properties::ComputedValues;
|
||||||
use selector_parser::RestyleDamage;
|
use selector_parser::RestyleDamage;
|
||||||
use selectors::matching::{ElementSelectorFlags, VisitedHandlingMode};
|
use selectors::matching::{ElementSelectorFlags, VisitedHandlingMode};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
use std::marker::PhantomData;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
use stylearc::Arc;
|
||||||
use stylist::Stylist;
|
use stylist::Stylist;
|
||||||
|
|
||||||
mod checks;
|
mod checks;
|
||||||
|
@ -93,7 +97,8 @@ mod checks;
|
||||||
/// improvements (e.g. 3x fewer styles having to be resolved than at size 8) and
|
/// improvements (e.g. 3x fewer styles having to be resolved than at size 8) and
|
||||||
/// slight performance improvements. Sizes larger than 32 haven't really been
|
/// slight performance improvements. Sizes larger than 32 haven't really been
|
||||||
/// tested.
|
/// tested.
|
||||||
pub const STYLE_SHARING_CANDIDATE_CACHE_SIZE: usize = 31;
|
pub const SHARING_CACHE_SIZE: usize = 31;
|
||||||
|
const SHARING_CACHE_BACKING_STORE_SIZE: usize = SHARING_CACHE_SIZE + 1;
|
||||||
|
|
||||||
/// Controls whether the style sharing cache is used.
|
/// Controls whether the style sharing cache is used.
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
|
@ -211,6 +216,9 @@ impl ValidationData {
|
||||||
/// Note that this information is stored in TLS and cleared after the traversal,
|
/// Note that this information is stored in TLS and cleared after the traversal,
|
||||||
/// and once here, the style information of the element is immutable, so it's
|
/// and once here, the style information of the element is immutable, so it's
|
||||||
/// safe to access.
|
/// safe to access.
|
||||||
|
///
|
||||||
|
/// Important: If you change the members/layout here, You need to do the same for
|
||||||
|
/// FakeCandidate below.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct StyleSharingCandidate<E: TElement> {
|
pub struct StyleSharingCandidate<E: TElement> {
|
||||||
/// The element. We use SendElement here so that the cache may live in
|
/// The element. We use SendElement here so that the cache may live in
|
||||||
|
@ -219,6 +227,11 @@ pub struct StyleSharingCandidate<E: TElement> {
|
||||||
validation_data: ValidationData,
|
validation_data: ValidationData,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct FakeCandidate {
|
||||||
|
_element: usize,
|
||||||
|
_validation_data: ValidationData,
|
||||||
|
}
|
||||||
|
|
||||||
impl<E: TElement> Deref for StyleSharingCandidate<E> {
|
impl<E: TElement> Deref for StyleSharingCandidate<E> {
|
||||||
type Target = E;
|
type Target = E;
|
||||||
|
|
||||||
|
@ -457,35 +470,81 @@ pub enum StyleSharingResult {
|
||||||
StyleWasShared(usize, ChildCascadeRequirement),
|
StyleWasShared(usize, ChildCascadeRequirement),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Style sharing caches are are large allocations, so we store them in thread-local
|
||||||
|
/// storage such that they can be reused across style traversals. Ideally, we'd just
|
||||||
|
/// stack-allocate these buffers with uninitialized memory, but right now rustc can't
|
||||||
|
/// avoid memmoving the entire cache during setup, which gets very expensive. See
|
||||||
|
/// issues like [1] and [2].
|
||||||
|
///
|
||||||
|
/// Given that the cache stores entries of type TElement, we transmute to usize
|
||||||
|
/// before storing in TLS. This is safe as long as we make sure to empty the cache
|
||||||
|
/// before we let it go.
|
||||||
|
///
|
||||||
|
/// [1] https://github.com/rust-lang/rust/issues/42763
|
||||||
|
/// [2] https://github.com/rust-lang/rust/issues/13707
|
||||||
|
type SharingCacheBase<Candidate> = LRUCache<[Candidate; SHARING_CACHE_BACKING_STORE_SIZE]>;
|
||||||
|
type SharingCache<E> = SharingCacheBase<StyleSharingCandidate<E>>;
|
||||||
|
type TypelessSharingCache = SharingCacheBase<FakeCandidate>;
|
||||||
|
type StoredSharingCache = Arc<AtomicRefCell<TypelessSharingCache>>;
|
||||||
|
|
||||||
|
thread_local!(static SHARING_CACHE_KEY: StoredSharingCache =
|
||||||
|
Arc::new(AtomicRefCell::new(LRUCache::new())));
|
||||||
|
|
||||||
/// An LRU cache of the last few nodes seen, so that we can aggressively try to
|
/// An LRU cache of the last few nodes seen, so that we can aggressively try to
|
||||||
/// reuse their styles.
|
/// reuse their styles.
|
||||||
///
|
///
|
||||||
/// Note that this cache is flushed every time we steal work from the queue, so
|
/// Note that this cache is flushed every time we steal work from the queue, so
|
||||||
/// storing nodes here temporarily is safe.
|
/// storing nodes here temporarily is safe.
|
||||||
pub struct StyleSharingCandidateCache<E: TElement> {
|
pub struct StyleSharingCandidateCache<E: TElement> {
|
||||||
cache: LRUCache<[StyleSharingCandidate<E>; STYLE_SHARING_CANDIDATE_CACHE_SIZE + 1]>,
|
/// The LRU cache, with the type cast away to allow persisting the allocation.
|
||||||
|
cache_typeless: OwningHandle<StoredSharingCache, AtomicRefMut<'static, TypelessSharingCache>>,
|
||||||
|
/// Bind this structure to the lifetime of E, since that's what we effectively store.
|
||||||
|
marker: PhantomData<SendElement<E>>,
|
||||||
/// The DOM depth we're currently at. This is used as an optimization to
|
/// The DOM depth we're currently at. This is used as an optimization to
|
||||||
/// clear the cache when we change depths, since we know at that point
|
/// clear the cache when we change depths, since we know at that point
|
||||||
/// nothing in the cache will match.
|
/// nothing in the cache will match.
|
||||||
dom_depth: usize,
|
dom_depth: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<E: TElement> Drop for StyleSharingCandidateCache<E> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<E: TElement> StyleSharingCandidateCache<E> {
|
impl<E: TElement> StyleSharingCandidateCache<E> {
|
||||||
|
fn cache(&self) -> &SharingCache<E> {
|
||||||
|
let base: &TypelessSharingCache = &*self.cache_typeless;
|
||||||
|
unsafe { mem::transmute(base) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cache_mut(&mut self) -> &mut SharingCache<E> {
|
||||||
|
let base: &mut TypelessSharingCache = &mut *self.cache_typeless;
|
||||||
|
unsafe { mem::transmute(base) }
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new style sharing candidate cache.
|
/// Create a new style sharing candidate cache.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
|
assert_eq!(mem::size_of::<SharingCache<E>>(), mem::size_of::<TypelessSharingCache>());
|
||||||
|
assert_eq!(mem::align_of::<SharingCache<E>>(), mem::align_of::<TypelessSharingCache>());
|
||||||
|
let cache_arc = SHARING_CACHE_KEY.with(|c| c.clone());
|
||||||
|
let cache = OwningHandle::new_with_fn(cache_arc, |x| unsafe { x.as_ref() }.unwrap().borrow_mut());
|
||||||
|
debug_assert_eq!(cache.num_entries(), 0);
|
||||||
|
|
||||||
StyleSharingCandidateCache {
|
StyleSharingCandidateCache {
|
||||||
cache: LRUCache::new(),
|
cache_typeless: cache,
|
||||||
|
marker: PhantomData,
|
||||||
dom_depth: 0,
|
dom_depth: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of entries in the cache.
|
/// Returns the number of entries in the cache.
|
||||||
pub fn num_entries(&self) -> usize {
|
pub fn num_entries(&self) -> usize {
|
||||||
self.cache.num_entries()
|
self.cache().num_entries()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn iter_mut(&mut self) -> LRUCacheMutIterator<StyleSharingCandidate<E>> {
|
fn iter_mut(&mut self) -> LRUCacheMutIterator<StyleSharingCandidate<E>> {
|
||||||
self.cache.iter_mut()
|
self.cache_mut().iter_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to insert an element in the style sharing cache.
|
/// Tries to insert an element in the style sharing cache.
|
||||||
|
@ -530,7 +589,7 @@ impl<E: TElement> StyleSharingCandidateCache<E> {
|
||||||
self.clear();
|
self.clear();
|
||||||
self.dom_depth = dom_depth;
|
self.dom_depth = dom_depth;
|
||||||
}
|
}
|
||||||
self.cache.insert(StyleSharingCandidate {
|
self.cache_mut().insert(StyleSharingCandidate {
|
||||||
element: unsafe { SendElement::new(*element) },
|
element: unsafe { SendElement::new(*element) },
|
||||||
validation_data: validation_data,
|
validation_data: validation_data,
|
||||||
});
|
});
|
||||||
|
@ -538,12 +597,12 @@ impl<E: TElement> StyleSharingCandidateCache<E> {
|
||||||
|
|
||||||
/// Touch a given index in the style sharing candidate cache.
|
/// Touch a given index in the style sharing candidate cache.
|
||||||
pub fn touch(&mut self, index: usize) {
|
pub fn touch(&mut self, index: usize) {
|
||||||
self.cache.touch(index);
|
self.cache_mut().touch(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear the style sharing candidate cache.
|
/// Clear the style sharing candidate cache.
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.cache.evict_all()
|
self.cache_mut().evict_all()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to share a style with another node.
|
/// Attempts to share a style with another node.
|
||||||
|
@ -610,7 +669,7 @@ impl<E: TElement> StyleSharingCandidateCache<E> {
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("{:?} Cannot share style: {} cache entries", target.element,
|
debug!("{:?} Cannot share style: {} cache entries", target.element,
|
||||||
self.cache.num_entries());
|
self.cache().num_entries());
|
||||||
|
|
||||||
StyleSharingResult::CannotShare
|
StyleSharingResult::CannotShare
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue