Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

reuse the bloom filter and style sharing cache across traversals #17701

Merged
merged 4 commits into from Jul 13, 2017
Merged
Changes from 1 commit
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

Reuse style sharing cache across traversals.

MozReview-Commit-ID: 9wCJtciqs6K
  • Loading branch information
bholley committed Jul 12, 2017
commit ec6cca6d08d3f8599016aa10988f3a377ad21207
@@ -66,19 +66,23 @@

use Atom;
use applicable_declarations::ApplicableDeclarationBlock;
use atomic_refcell::{AtomicRefCell, AtomicRefMut};
use bit_vec::BitVec;
use bloom::StyleBloom;
use cache::{LRUCache, LRUCacheMutIterator};
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<E: TElement> {
/// 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,
}

struct FakeCandidate {
_element: usize,
_validation_data: ValidationData,
}

impl<E: TElement> Deref for StyleSharingCandidate<E> {
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<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
/// 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<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
/// clear the cache when we change depths, since we know at that point
/// nothing in the cache will match.
dom_depth: usize,
}

impl<E: TElement> Drop for StyleSharingCandidateCache<E> {
fn drop(&mut self) {
self.clear();
}
}

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.
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 {
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<StyleSharingCandidate<E>> {
self.cache.iter_mut()
self.cache_mut().iter_mut()
}

/// Tries to insert an element in the style sharing cache.
@@ -530,20 +589,20 @@ impl<E: TElement> StyleSharingCandidateCache<E> {
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,
});
}

/// 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<E: TElement> StyleSharingCandidateCache<E> {
}

debug!("{:?} Cannot share style: {} cache entries", target.element,
self.cache.num_entries());
self.cache().num_entries());

StyleSharingResult::CannotShare
}
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.