Rewrite the style sharing candidate cache.

The style candidate cache had regressed a few times (see #12534), and my
intuition is that being able to disable all style sharing with a single rule in
the page is really unfortunate.

This commit redesigns the style sharing cache in order to be a optimistic cache,
but then reject candidates if they match different sibling-affecting selectors
in the page, for example.

So far the numbers have improved, but not so much as I'd wanted (~10%/20% of
non-incremental restyling time in general). The current implementation is really
dumb though (we recompute and re-match a lot of stuff), so we should be able to
optimise it quite a bit.

I have different ideas for improving it (that may or may not work), apart of the
low-hanging fruit like don't re-matching candidates all the time but I have to
measure the real impact.

Also, I need to verify it against try.
This commit is contained in:
Emilio Cobos Álvarez 2016-07-29 17:24:12 -07:00
parent ec53136863
commit 3af774bd75
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
23 changed files with 467 additions and 236 deletions

View file

@ -6,6 +6,7 @@
use element_state::*;
use selector_impl::{ElementExt, TheSelectorImpl, NonTSPseudoClass, AttrValue};
use selectors::matching::StyleRelations;
use selectors::matching::matches_compound_selector;
use selectors::parser::{AttrSelector, Combinator, CompoundSelector, SimpleSelector, SelectorImpl};
use selectors::{Element, MatchAttr};
@ -348,7 +349,11 @@ impl DependencySet {
DependencySet { deps: Vec::new() }
}
pub fn note_selector(&mut self, selector: Arc<CompoundSelector<TheSelectorImpl>>) {
pub fn len(&self) -> usize {
self.deps.len()
}
pub fn note_selector(&mut self, selector: &Arc<CompoundSelector<TheSelectorImpl>>) {
let mut cur = selector;
let mut combinator: Option<Combinator> = None;
loop {
@ -370,7 +375,7 @@ impl DependencySet {
cur = match cur.next {
Some((ref sel, comb)) => {
combinator = Some(comb);
sel.clone()
sel
}
None => break,
}
@ -389,14 +394,19 @@ impl DependencySet {
-> RestyleHint
where E: ElementExt + Clone
{
debug!("About to calculate restyle hint for element. Deps: {}",
self.deps.len());
let state_changes = snapshot.state().map_or_else(ElementState::empty, |old_state| current_state ^ old_state);
let attrs_changed = snapshot.has_attrs();
let mut hint = RestyleHint::empty();
for dep in &self.deps {
if state_changes.intersects(dep.sensitivities.states) || (attrs_changed && dep.sensitivities.attrs) {
let old_el: ElementWrapper<E> = ElementWrapper::new_with_snapshot(el.clone(), snapshot);
let matched_then = matches_compound_selector(&*dep.selector, &old_el, None, &mut false);
let matches_now = matches_compound_selector(&*dep.selector, el, None, &mut false);
let matched_then =
matches_compound_selector(&*dep.selector, &old_el, None, &mut StyleRelations::empty());
let matches_now =
matches_compound_selector(&*dep.selector, el, None, &mut StyleRelations::empty());
if matched_then != matches_now {
hint.insert(combinator_to_restyle_hint(dep.combinator));
if hint.is_all() {