Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign updocument.createElement very slow. #12354
Comments
|
I can repro these numbers |
|
Since we create a reflector eagerly and Firefox does it lazily, I would like to see numbers where you modify the benchmark such that the reflector would be created in Firefox. Then at least we will know whether this is the cost of eager creation or if there is another problem hiding here. @bzbarsky What is the simplest way to ensure a reflector is created? Just access some property of the element? |
Not relevant here. Note that it would be interesting to test the no-reflector-creation case too (using cloneNode(true) on a tree of nontrivial size). I seem to recall @wycats saying that skipping reflector creation in Firefox helps a lot there, and we were wondering what the Servo numbers would look like. But that's a separate issue, obviously. |
|
So far profiling has revealed a stray memmove in the div element constructor, but it's been difficult to pin down what's causing it. |
|
Inlining the box expression in HTMLDivElement::new into the Node::reflect_node call made the memmove go away, but the timing did not noticeably improve. Current profile shows 25% of time spent under HTMLDivElement::new (14% under HTMLElement::new, 10% under Element::new_inherited, 3.5% under EventTarget::new_inherited), but no smoking guns :( |
|
Following @bzbarsky's advice, I ran the following test in both servo and gecko (with some modifications to the TextEncoder interface): <script>
var t = 'TestBinding' in window ? (new TestBinding()) : (new TextEncoder());
var start = new Date();
var count = 1000000;
for (var i = 0; i < count; i++) {
var a = t.receiveVoid();
}
var stop = new Date();
console.log('void method: ' + ((stop - start) / count * 1e6) + 'ns');
var start = new Date();
var count = 1000000;
for (var i = 0; i < count; i++) {
var a = t.longAttribute;
}
var stop = new Date();
console.log('int getter: ' + ((stop - start) / count * 1e6) + 'ns');
var n = document.firstElement;
var start = new Date();
var count = 1000000;
for (var i = 0; i < count; i++) {
var a = n.firstChild;
}
var stop = new Date();
console.log('firstChild getter: ' + ((stop - start) / count * 1e6) + 'ns');
</script>Results:
|
|
With https://gist.github.com/jdm/4755af850d2d718aac3fc95da456556f applied to rust-mozjs I get void method timings of 18-20ns. The profile shows 30% of execution time is spent code related to catching panics, courtesy of my changes in #11803. I guess we need to talk to the Rust team about what can be done to reduce that. |
|
I filed rust-lang/rust#34727 about catching panics hurting our performance. |
|
I tried removing the catch_unwind stuff and measuring again, and I get timings around 10ns. |
|
Curiously enough, I tried measuring the firstChild getter with the inlining changes and catch_unwind removed - the first two runs averaged 4ns and 2ns, and every single run after that averaged 42-48ns. I have no idea what to make of that. This is consistent across restarts of Servo and page reloads, too. |
An easy way to test this is to compare constructing a tree using |
|
No surprise given #12358, the profile for the firstChild getter is dominated by JSCompartment::wrap and related SM wrapping functions. |
Enable cross-crate inlining for JSVal conversion methods. Make it possible for Rust methods which are called in the generated bindings to be inlined. This shaved off 1/3-1/2 of the total time in the measurements in servo/servo#12354. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/rust-mozjs/276) <!-- Reviewable:end -->
|
So half the problem is insufficient inlining (a solved problem), and the other half is panic catching? |
|
And the third half is the slow compartment wrapping. There might be other things too; those were just the biggest offenders so far... |
Improve performance of HTMLDivElement constructor These changes address two sources of performance loss seen while profiling in #12354. #12358 and rust-lang/rust#34727 are still the biggest offenders, however. --- - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes do not require tests because we don't have performance tests and these are only optimizations <!-- 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/12374) <!-- Reviewable:end -->
|
FTR, servo/rust-mozjs#276 landed |
More inlining possibilities These methods were showing up in profiles in servo/servo#12354. This change reduced testcase time by 100ms. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/rust-mozjs/277) <!-- Reviewable:end -->
Add a manual test for measuring DOM binding performance This is the test harness I've been using for profiling and measuring in #12354. <!-- 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/12449) <!-- Reviewable:end -->
|
Servo master reports ~1.0 for me now. Progress! |
|
@jdm So that's still just over 3x slower? |
|
Firefox gives me ~0.4, so 2-3x. |
|
Correction, with LTO enabled I get ~0.9. |
|
As far as the timings for tests/html/bindings_perf.html, the situation compared with Gecko is much better:
This might only need #12357 to make a big difference for the testcase here. |
|
Unfortunately, I still get 0.8-0.9 with #12980 applied. |
|
My servo time is now ~0.6. Progress! |
|
What changed? |
|
The various changes to rustc linked from rust-lang/rust#34727. |
|
This still seems to be an issue. |
document.createElementappears to be much slower than in FF/Chrome.The core issue is shown with this code:
FF/Chrome avg. time around
0.317465..Servo avg. time around
1.3Full testcase here.