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

selectors: Check the bloom filter at most once per complex selector. #15890

Merged
merged 1 commit into from
Mar 20, 2017

Conversation

emilio
Copy link
Member

@emilio emilio commented Mar 9, 2017

Fixes servo/rust-selectors#107


This change is Reviewable

@highfive highfive added the S-awaiting-review There is new code that needs to be reviewed. label Mar 9, 2017
@emilio
Copy link
Member Author

emilio commented Mar 9, 2017

r? @SimonSapin

@highfive highfive assigned SimonSapin and unassigned KiChjang Mar 9, 2017
@emilio
Copy link
Member Author

emilio commented Mar 9, 2017

Also, cc @bholley

@emilio
Copy link
Member Author

emilio commented Mar 9, 2017

@bors-servo try

@bors-servo
Copy link
Contributor

⌛ Trying commit 0a102d5 with merge 7c60e66...

bors-servo pushed a commit that referenced this pull request Mar 9, 2017
selectors: Check the bloom filter at most once per complex selector.

Fixes servo/rust-selectors#107

<!-- 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/15890)
<!-- Reviewable:end -->
@bors-servo
Copy link
Contributor

💔 Test failed - linux-rel-css

@highfive highfive added the S-tests-failed The changes caused existing tests to fail. label Mar 9, 2017
@emilio
Copy link
Member Author

emilio commented Mar 9, 2017

That one is an intermittent it seems.

@emilio
Copy link
Member Author

emilio commented Mar 18, 2017

r? @heycam

Simon said he doesn't have the bloom filter paged in right now, and he's got a bunch to do for #15998.

@highfive highfive assigned heycam and unassigned SimonSapin Mar 18, 2017
@emilio
Copy link
Member Author

emilio commented Mar 18, 2017

(Though @bzbarsky could be a good reviewer for this one too, actually).

Which remembers we should probably limit the amount of selectors we test against the filter somehow, IIRC the gecko limit was four or something like that. Still I'd want to land this relatively soon.

selector = &**cs;
continue;
}
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still I haven't got all the details, but look like this prevents checking the first selector(the rightmost one) in the bloom filter. Maybe move this match to the end of the loop?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's intended. The bloom filter contains only data from the ancestors, which are the expensive selectors to check. It's quite tricky, I admit.

@@ -466,7 +452,10 @@ fn matches_simple_selector<E>(
}
SimpleSelector::Negation(ref negated) => {
!negated.iter().all(|s| {
matches_complex_selector(s, element, parent_bf, relations, flags)
match matches_complex_selector_internal(s, element, relations, flags) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might have descendant combinators inside the :not(...). Don't we want to be able to use the ancestor filter to quickly return false for them too?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's not a super-clear win, and I wanted to prevent the filter from being misused, but I guess I can do that too.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I meant that because :not is already quite expensive, but sure, will do that).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@highfive highfive removed the S-tests-failed The changes caused existing tests to fail. label Mar 20, 2017
@emilio
Copy link
Member Author

emilio commented Mar 20, 2017

@bors-servo try

@bors-servo
Copy link
Contributor

⌛ Trying commit ed6351c with merge f3c4ccd...

bors-servo pushed a commit that referenced this pull request Mar 20, 2017
selectors: Check the bloom filter at most once per complex selector.

Fixes servo/rust-selectors#107

<!-- 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/15890)
<!-- Reviewable:end -->
@heycam
Copy link
Contributor

heycam commented Mar 20, 2017

Hmm, I think I made a mistake. The selector inside :not(...) isn't allowed to take a selector with combinators. Is that the same in Servo? If so, then I guess we don't need to pass the Bloom filter through, sorry.

@emilio
Copy link
Member Author

emilio commented Mar 20, 2017

So, servo implements :not per the selectors 4 spec, which does allow combinators. I'll file a bug against the "don't ship new CSS features" bug we have for that, but I'd like to land this for now.

@emilio
Copy link
Member Author

emilio commented Mar 20, 2017

@heycam
Copy link
Contributor

heycam commented Mar 20, 2017

OK, sounds good, thanks!

@emilio
Copy link
Member Author

emilio commented Mar 20, 2017

@bors-servo r=heycam

@bors-servo
Copy link
Contributor

📌 Commit 2772419 has been approved by heycam

@highfive highfive added S-awaiting-merge The PR is in the process of compiling and running tests on the automated CI. and removed S-awaiting-review There is new code that needs to be reviewed. labels Mar 20, 2017
@bors-servo
Copy link
Contributor

⌛ Testing commit 2772419 with merge 4fa40c7...

bors-servo pushed a commit that referenced this pull request Mar 20, 2017
selectors: Check the bloom filter at most once per complex selector.

Fixes servo/rust-selectors#107

<!-- 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/15890)
<!-- Reviewable:end -->
@bors-servo
Copy link
Contributor

💔 Test failed - linux-rel-wpt

@highfive highfive added S-tests-failed The changes caused existing tests to fail. and removed S-awaiting-merge The PR is in the process of compiling and running tests on the automated CI. labels Mar 20, 2017
@emilio
Copy link
Member Author

emilio commented Mar 20, 2017

@bors-servo retry

#14323

@bors-servo
Copy link
Contributor

⚡ Previous build results for android, arm32, arm64, linux-dev, linux-rel-css, mac-dev-unit, mac-rel-css, mac-rel-wpt1, mac-rel-wpt2, windows-msvc-dev are reusable. Rebuilding only linux-rel-wpt...

@bors-servo
Copy link
Contributor

☀️ Test successful - android, arm32, arm64, linux-dev, linux-rel-css, linux-rel-wpt, mac-dev-unit, mac-rel-css, mac-rel-wpt1, mac-rel-wpt2, windows-msvc-dev
Approved by: heycam
Pushing 4fa40c7 to master...

@bors-servo bors-servo merged commit 2772419 into servo:master Mar 20, 2017
@bzbarsky
Copy link
Contributor

IIRC the gecko limit was four or something like that.

The Gecko limit is 4 because gecko does the following:

  1. Precomputes hashes for the things it wants to test against the bloom filter, because hash functions are slow.
  2. Stores those precomputed hashes in fixed-length allocations fused into another data structure, because malloc is slow.

Given that, we needed to have some fixed length we picked, and 4 was a compromise between bloating the data structure too much and having enough slots to cover common selectors reasonably.

@emilio emilio deleted the bloom branch March 21, 2017 16:19
@bholley
Copy link
Contributor

bholley commented Mar 21, 2017

Hm, does that setup predate the existing of precomputed hashes in nsIAtom?

@bzbarsky
Copy link
Contributor

Hm, does that setup predate the existing of precomputed hashes in nsIAtom?

No. In fact, I implemented precomputed hashes in nsIAtom as a prereq for the Bloom filter in Gecko, precisely so we could build the cascade faster, instead of having to hash even then.

But even just getting to the atoms still involves a bunch of linked-list walking and whatnot in Gecko. I seem to recall that I tried measuring just looking for them every time, and it was slower than the precached approach, with lots of cache misses and whatnot.

It might be worth remeasuring various stuff around this, in general.

@bholley
Copy link
Contributor

bholley commented Mar 22, 2017

In Servo we have a Vec, so the only cache miss will be on the nsIAtoms themselves. Hopefully that's acceptable.

@bzbarsky
Copy link
Contributor

In Servo we have a Vec

No, you have a linked list of Vecs.

Specifically, a ComplexSelector is a Vec of SimpleSelectors. But ComplexSelectors form a linked list. So given a selector like "#foo.bar > *" you have a linked list with two elements. The first element has probably an empty Vec or so, depending on default namespaces; the second element has a Vec with two items (one for the id selector, one for the class selector).

Any case in which the Bloom filter helps at all will involve a linked list of length more than 1, and hence pointer-chasing.

@bzbarsky
Copy link
Contributor

Oh, and instead of hoping we should obviously measure. What the priority of that measurement should be is hard to say.

@bholley
Copy link
Contributor

bholley commented Mar 22, 2017

Oh interesting - is there a reason it actually needs to be a linked list rather than a Vec of Vecs? That would certainly make #16019 easier.

@bzbarsky
Copy link
Contributor

I have no idea. I'm still in the "butterfly collecting" stage of understanding, slowly moving towards the "make predictions" stage. ;)

I should also note that even with Vec you can get cache misses on both the pointer to the buffer and the contents to the buffer, since I don't think Vec ever uses an inline buffer. Again, this is something that should be measured. I worry about it because profiles of Gecko's style system long ago showed L2/L3 misses being a significant part of the performance drag, as we walk around selectors, DOM nodes, etc, etc... But at least I had data then.

@mbrubeck
Copy link
Contributor

I should also note that even with Vec you can get cache misses on both the pointer to the buffer and the contents to the buffer, since I don't think Vec ever uses an inline buffer.

We use https://crates.io/crates/smallvec in cases where we want a possibly-inline buffer.

@SimonSapin
Copy link
Member

is there a reason it actually needs to be a linked list rather than a Vec of Vecs?

Each "link" also stores a combinator. With a Vec we’d need to have a dummy/unused combinator in the first or last item of the top-level vector, or have a separate vector of combinators whose length is 1 less than the other top-level vector.

@bholley
Copy link
Contributor

bholley commented Mar 28, 2017

Ok - a dummy combinator seems like a small price to pay for better cache locality. But we should measure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-tests-failed The changes caused existing tests to fail.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants