mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
Auto merge of #13108 - emilio:stupidest-opt-ever, r=bholley
style: Don't loop over all the set of dependencies always. <!-- Please describe your changes on the following line: --> Instead, divide which kind of dependencies could match a mutation. This cuts down incremental restyle time in BrowserHTML quite a bit. --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors <!-- Either: --> - [x] There are tests for these changes OR <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> The dependency count is not at all minor, and this way we avoid looping through all of them in the common cases, mainly either changing state, or attributes. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/13108) <!-- Reviewable:end -->
This commit is contained in:
commit
6dfb5f0572
1 changed files with 94 additions and 22 deletions
|
@ -5,6 +5,8 @@
|
|||
//! Restyle hints: an optimization to avoid unnecessarily matching selectors.
|
||||
|
||||
use element_state::*;
|
||||
#[cfg(feature = "servo")]
|
||||
use heapsize::HeapSizeOf;
|
||||
use selector_impl::{ElementExt, TheSelectorImpl, NonTSPseudoClass, AttrValue};
|
||||
use selectors::matching::StyleRelations;
|
||||
use selectors::matching::matches_complex_selector;
|
||||
|
@ -33,6 +35,11 @@ bitflags! {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "servo")]
|
||||
impl HeapSizeOf for RestyleHint {
|
||||
fn heap_size_of_children(&self) -> usize { 0 }
|
||||
}
|
||||
|
||||
/// In order to compute restyle hints, we perform a selector match against a
|
||||
/// list of partial selectors whose rightmost simple selector may be sensitive
|
||||
/// to the thing being changed. We do this matching twice, once for the element
|
||||
|
@ -334,23 +341,50 @@ impl Sensitivities {
|
|||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
struct Dependency {
|
||||
selector: Arc<ComplexSelector<TheSelectorImpl>>,
|
||||
combinator: Option<Combinator>,
|
||||
hint: RestyleHint,
|
||||
sensitivities: Sensitivities,
|
||||
}
|
||||
|
||||
/// A set of dependencies for a given stylist.
|
||||
///
|
||||
/// Note that there are measurable perf wins from storing them separately
|
||||
/// depending on what kind of change they affect, and its also not a big deal to
|
||||
/// do it, since the dependencies are per-document.
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
pub struct DependencySet {
|
||||
deps: Vec<Dependency>,
|
||||
/// Dependencies only affected by state.
|
||||
state_deps: Vec<Dependency>,
|
||||
/// Dependencies only affected by attributes.
|
||||
attr_deps: Vec<Dependency>,
|
||||
/// Dependencies affected by both.
|
||||
common_deps: Vec<Dependency>,
|
||||
}
|
||||
|
||||
impl DependencySet {
|
||||
fn add_dependency(&mut self, dep: Dependency) {
|
||||
let affects_attrs = dep.sensitivities.attrs;
|
||||
let affects_states = !dep.sensitivities.states.is_empty();
|
||||
|
||||
if affects_attrs && affects_states {
|
||||
self.common_deps.push(dep)
|
||||
} else if affects_attrs {
|
||||
self.attr_deps.push(dep)
|
||||
} else {
|
||||
self.state_deps.push(dep)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
DependencySet { deps: Vec::new() }
|
||||
DependencySet {
|
||||
state_deps: vec![],
|
||||
attr_deps: vec![],
|
||||
common_deps: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.deps.len()
|
||||
self.common_deps.len() + self.attr_deps.len() + self.state_deps.len()
|
||||
}
|
||||
|
||||
pub fn note_selector(&mut self, selector: &Arc<ComplexSelector<TheSelectorImpl>>) {
|
||||
|
@ -365,9 +399,9 @@ impl DependencySet {
|
|||
}
|
||||
}
|
||||
if !sensitivities.is_empty() {
|
||||
self.deps.push(Dependency {
|
||||
self.add_dependency(Dependency {
|
||||
selector: cur.clone(),
|
||||
combinator: combinator,
|
||||
hint: combinator_to_restyle_hint(combinator),
|
||||
sensitivities: sensitivities,
|
||||
});
|
||||
}
|
||||
|
@ -383,38 +417,76 @@ impl DependencySet {
|
|||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.deps.clear();
|
||||
self.common_deps.clear();
|
||||
self.attr_deps.clear();
|
||||
self.state_deps.clear();
|
||||
}
|
||||
}
|
||||
|
||||
impl DependencySet {
|
||||
pub fn compute_hint<E>(&self, el: &E,
|
||||
snapshot: &E::Snapshot,
|
||||
current_state: ElementState)
|
||||
-> RestyleHint
|
||||
where E: ElementExt + Clone
|
||||
where E: ElementExt + Clone
|
||||
{
|
||||
debug!("About to calculate restyle hint for element. Deps: {}",
|
||||
self.deps.len());
|
||||
self.len());
|
||||
|
||||
let state_changes = snapshot.state().map_or_else(ElementState::empty, |old_state| current_state ^ old_state);
|
||||
let state_changes = snapshot.state()
|
||||
.map_or_else(ElementState::empty, |old_state| current_state ^ old_state);
|
||||
let attrs_changed = snapshot.has_attrs();
|
||||
|
||||
if state_changes.is_empty() && !attrs_changed {
|
||||
return RestyleHint::empty();
|
||||
}
|
||||
|
||||
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 snapshot = ElementWrapper::new_with_snapshot(el.clone(), snapshot);
|
||||
|
||||
Self::compute_partial_hint(&self.common_deps, el, &snapshot,
|
||||
&state_changes, attrs_changed, &mut hint);
|
||||
|
||||
if !state_changes.is_empty() {
|
||||
Self::compute_partial_hint(&self.state_deps, el, &snapshot,
|
||||
&state_changes, attrs_changed, &mut hint);
|
||||
}
|
||||
|
||||
if attrs_changed {
|
||||
Self::compute_partial_hint(&self.attr_deps, el, &snapshot,
|
||||
&state_changes, attrs_changed, &mut hint);
|
||||
}
|
||||
|
||||
hint
|
||||
}
|
||||
|
||||
fn compute_partial_hint<E>(deps: &[Dependency],
|
||||
element: &E,
|
||||
snapshot: &ElementWrapper<E>,
|
||||
state_changes: &ElementState,
|
||||
attrs_changed: bool,
|
||||
hint: &mut RestyleHint)
|
||||
where E: ElementExt
|
||||
{
|
||||
if hint.is_all() {
|
||||
return;
|
||||
}
|
||||
for dep in deps {
|
||||
debug_assert!(state_changes.intersects(dep.sensitivities.states) ||
|
||||
attrs_changed && dep.sensitivities.attrs,
|
||||
"Testing a completely ineffective dependency?");
|
||||
if !hint.intersects(dep.hint) {
|
||||
let matched_then =
|
||||
matches_complex_selector(&*dep.selector, &old_el, None, &mut StyleRelations::empty());
|
||||
matches_complex_selector(&dep.selector, snapshot, None,
|
||||
&mut StyleRelations::empty());
|
||||
let matches_now =
|
||||
matches_complex_selector(&*dep.selector, el, None, &mut StyleRelations::empty());
|
||||
matches_complex_selector(&dep.selector, element, None,
|
||||
&mut StyleRelations::empty());
|
||||
if matched_then != matches_now {
|
||||
hint.insert(combinator_to_restyle_hint(dep.combinator));
|
||||
if hint.is_all() {
|
||||
break
|
||||
}
|
||||
hint.insert(dep.hint);
|
||||
}
|
||||
if hint.is_all() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
hint
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue