diff options
author | Boris Zbarsky <bzbarsky@mit.edu> | 2017-06-08 12:35:06 -0400 |
---|---|---|
committer | Boris Zbarsky <bzbarsky@mit.edu> | 2017-06-08 13:02:36 -0400 |
commit | 93271e199b8b7f6ec20d1546b384ea7ab9155759 (patch) | |
tree | fdeffa703729cc91a6bbd2f651315a1289e0e2e2 | |
parent | 3616b8f0c3c7c1beba4d0977ea3a4fb2a5f9c835 (diff) | |
download | servo-93271e199b8b7f6ec20d1546b384ea7ab9155759.tar.gz servo-93271e199b8b7f6ec20d1546b384ea7ab9155759.zip |
Reduce the size of top_down_dom stackframes.
The idea is to put the memmoving of the ThreadLocalContext out of line from the point of view of top_down_dom so we don't need to allocate a large chunk of stack space for it.
-rw-r--r-- | components/style/parallel.rs | 16 | ||||
-rw-r--r-- | components/style/scoped_tls.rs | 12 |
2 files changed, 24 insertions, 4 deletions
diff --git a/components/style/parallel.rs b/components/style/parallel.rs index 859548d3c8b..fecdff844a6 100644 --- a/components/style/parallel.rs +++ b/components/style/parallel.rs @@ -125,6 +125,19 @@ pub fn traverse_dom<E, D>(traversal: &D, } } +/// A callback to create our thread local context. This needs to be +/// out of line so we don't allocate stack space for the entire struct +/// in the caller. +#[inline(never)] +fn create_thread_local_context<'scope, E, D>( + traversal: &'scope D, + slot: &mut Option<D::ThreadLocalContext>) + where E: TElement + 'scope, + D: DomTraversal<E> +{ + *slot = Some(traversal.create_thread_local_context()) +} + /// A parallel top-down DOM traversal. /// /// This algorithm traverses the DOM in a breadth-first, top-down manner. The @@ -153,7 +166,8 @@ fn top_down_dom<'a, 'scope, E, D>(nodes: &'a [SendNode<E::ConcreteNode>], { // Scope the borrow of the TLS so that the borrow is dropped before // a potential recursive call when we pass TailCall. - let mut tlc = tls.ensure(|| traversal.create_thread_local_context()); + let mut tlc = tls.ensure( + |slot: &mut Option<D::ThreadLocalContext>| create_thread_local_context(traversal, slot)); for n in nodes { // If the last node we processed produced children, spawn them off diff --git a/components/style/scoped_tls.rs b/components/style/scoped_tls.rs index 78537bce5f9..b1ccb4c7179 100644 --- a/components/style/scoped_tls.rs +++ b/components/style/scoped_tls.rs @@ -9,6 +9,7 @@ use rayon; use std::cell::{Ref, RefCell, RefMut}; +use std::ops::DerefMut; /// A scoped TLS set, that is alive during the `'scope` lifetime. /// @@ -52,11 +53,16 @@ impl<'scope, T: Send> ScopedTLS<'scope, T> { } /// Ensure that the current data this thread owns is initialized, or - /// initialize it using `f`. - pub fn ensure<F: FnOnce() -> T>(&self, f: F) -> RefMut<T> { + /// initialize it using `f`. We want ensure() to be fast and inline, and we + /// want to inline the memmove that initializes the Option<T>. But we don't + /// want to inline space for the entire large T struct in our stack frame. + /// That's why we hand `f` a mutable borrow to write to instead of just + /// having it return a T. + #[inline(always)] + pub fn ensure<F: FnOnce(&mut Option<T>)>(&self, f: F) -> RefMut<T> { let mut opt = self.borrow_mut(); if opt.is_none() { - *opt = Some(f()); + f(opt.deref_mut()); } RefMut::map(opt, |x| x.as_mut().unwrap()) |