From ec6cca6d08d3f8599016aa10988f3a377ad21207 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Tue, 11 Jul 2017 16:34:11 -0700 Subject: [PATCH] Reuse style sharing cache across traversals. MozReview-Commit-ID: 9wCJtciqs6K --- components/style/sharing/mod.rs | 77 +++++++++++++++++++++++++++++---- 1 file changed, 68 insertions(+), 9 deletions(-) diff --git a/components/style/sharing/mod.rs b/components/style/sharing/mod.rs index c7c2c946cc8..2d540c5f436 100644 --- a/components/style/sharing/mod.rs +++ b/components/style/sharing/mod.rs @@ -66,6 +66,7 @@ use Atom; use applicable_declarations::ApplicableDeclarationBlock; +use atomic_refcell::{AtomicRefCell, AtomicRefMut}; use bit_vec::BitVec; use bloom::StyleBloom; use cache::{LRUCache, LRUCacheMutIterator}; @@ -73,12 +74,15 @@ use context::{SelectorFlagsMap, SharedStyleContext, StyleContext}; use data::{ElementData, ElementStyles}; use dom::{TElement, SendElement}; use matching::{ChildCascadeRequirement, MatchMethods}; +use owning_ref::OwningHandle; use properties::ComputedValues; use selector_parser::RestyleDamage; use selectors::matching::{ElementSelectorFlags, VisitedHandlingMode}; use smallvec::SmallVec; +use std::marker::PhantomData; use std::mem; use std::ops::Deref; +use stylearc::Arc; use stylist::Stylist; mod checks; @@ -93,7 +97,8 @@ mod checks; /// 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 /// 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. #[derive(Clone, Copy, PartialEq)] @@ -211,6 +216,9 @@ impl ValidationData { /// 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 /// safe to access. +/// +/// Important: If you change the members/layout here, You need to do the same for +/// FakeCandidate below. #[derive(Debug)] pub struct StyleSharingCandidate { /// The element. We use SendElement here so that the cache may live in @@ -219,6 +227,11 @@ pub struct StyleSharingCandidate { validation_data: ValidationData, } +struct FakeCandidate { + _element: usize, + _validation_data: ValidationData, +} + impl Deref for StyleSharingCandidate { type Target = E; @@ -457,35 +470,81 @@ pub enum StyleSharingResult { 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 = LRUCache<[Candidate; SHARING_CACHE_BACKING_STORE_SIZE]>; +type SharingCache = SharingCacheBase>; +type TypelessSharingCache = SharingCacheBase; +type StoredSharingCache = Arc>; + +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 /// reuse their styles. /// /// Note that this cache is flushed every time we steal work from the queue, so /// storing nodes here temporarily is safe. pub struct StyleSharingCandidateCache { - cache: LRUCache<[StyleSharingCandidate; STYLE_SHARING_CANDIDATE_CACHE_SIZE + 1]>, + /// The LRU cache, with the type cast away to allow persisting the allocation. + cache_typeless: OwningHandle>, + /// Bind this structure to the lifetime of E, since that's what we effectively store. + marker: PhantomData>, /// 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 /// nothing in the cache will match. dom_depth: usize, } +impl Drop for StyleSharingCandidateCache { + fn drop(&mut self) { + self.clear(); + } +} + impl StyleSharingCandidateCache { + fn cache(&self) -> &SharingCache { + let base: &TypelessSharingCache = &*self.cache_typeless; + unsafe { mem::transmute(base) } + } + + fn cache_mut(&mut self) -> &mut SharingCache { + let base: &mut TypelessSharingCache = &mut *self.cache_typeless; + unsafe { mem::transmute(base) } + } + /// Create a new style sharing candidate cache. pub fn new() -> Self { + assert_eq!(mem::size_of::>(), mem::size_of::()); + assert_eq!(mem::align_of::>(), mem::align_of::()); + 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 { - cache: LRUCache::new(), + cache_typeless: cache, + marker: PhantomData, dom_depth: 0, } } /// Returns the number of entries in the cache. pub fn num_entries(&self) -> usize { - self.cache.num_entries() + self.cache().num_entries() } fn iter_mut(&mut self) -> LRUCacheMutIterator> { - self.cache.iter_mut() + self.cache_mut().iter_mut() } /// Tries to insert an element in the style sharing cache. @@ -530,7 +589,7 @@ impl StyleSharingCandidateCache { self.clear(); self.dom_depth = dom_depth; } - self.cache.insert(StyleSharingCandidate { + self.cache_mut().insert(StyleSharingCandidate { element: unsafe { SendElement::new(*element) }, validation_data: validation_data, }); @@ -538,12 +597,12 @@ impl StyleSharingCandidateCache { /// Touch a given index in the style sharing candidate cache. pub fn touch(&mut self, index: usize) { - self.cache.touch(index); + self.cache_mut().touch(index); } /// Clear the style sharing candidate cache. pub fn clear(&mut self) { - self.cache.evict_all() + self.cache_mut().evict_all() } /// Attempts to share a style with another node. @@ -610,7 +669,7 @@ impl StyleSharingCandidateCache { } debug!("{:?} Cannot share style: {} cache entries", target.element, - self.cache.num_entries()); + self.cache().num_entries()); StyleSharingResult::CannotShare }