Closed Bug 1128157 Opened 11 years ago Closed 9 years ago

Getters don't get inlined in a testcase involving nodelists

Categories

(Core :: JavaScript Engine: JIT, defect)

32 Branch
x86
Linux
defect
Not set
normal

Tracking

()

RESOLVED DUPLICATE of bug 965992

People

(Reporter: michalwadas, Unassigned)

References

Details

(Keywords: perf)

User Agent: Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:32.0) Gecko/20100101 Firefox/32.0 Build ID: 20140830211128 Steps to reproduce: I have compared cost of reading `.length` for following objects: var boostedNodeList = document.querySelectorAll('#wow > span'); (function(){ var n = boostedNodeList.length; Object.defineProperty(boostedNodeList, 'length', { get: function(){return n;} }) }()); var defaultNodeList = document.querySelectorAll('#wow > span'); Related jsperf - http://jsperf.com/nodelistoptimisation/2 Actual results: reading `bostedNodeList.length` is 10 times slower than reading `defaultNodeList.length` It's also **1000 times slower** than the same code in Chromium. Expected results: Reading `bostedNodeList.length` should be as fast as directly reading `n` variable.
Summary: Getters doesn't get inlined → Getters don't get inlined
> Related jsperf - http://jsperf.com/nodelistoptimisation/2 On this page, I see the "boostedNodeList" case also much slower than the other one in Chrome, for what it's worth. At first glance what things look like to me is that the function is in fact compiled into a constant return value. However the .length on the nodelist ends up using an inline cache to call the function, again, afaict, which is not all that cheap...
Summary: Getters don't get inlined → Getters don't get inlined in a testcase involving nodelists
In any case, what's happening here is that since nodelist has to be a proxy the "length" getter here is defined on the expando object, not the nodelist directly. So the baseline IC stub is of type GetProp_DOMProxyShadowed, which isn't a type that BaselineInspector::commonGetPropFunction knows about, so IonBuilder::getPropTryCommonGetter does nothing useful and we end up falling through to an Ion IC. We _could_ try to do something better for non-overridebuiltins DOM proxies for which we discover that they're shadowing, because that means the property lives on the expando object and we can in fact try to do something smart with that... But we'd need to either add some shape guards on the expando object or something (e.g. in this testcase the property is non-configurable so we might be able to eliminate the shape guards).
Status: UNCONFIRMED → NEW
Ever confirmed: true
DOM performance is ridiculosly slow in all browsers. I think it can be improved by directly creating .length property on immutable collections (like NodeList returned by querySelectorAll) and omitting even getter. It can potetially break only (?) hasOwnProperty and getOwnPropertyDescriptor (but it was never guaranted to work consistently across browsers).
Flags: needinfo?(jdemooij)
And just to prove that the issue here is in fact the nodelist proxy, compare http://jsperf.com/nodelistoptimisation/11 where both we and Chrome do way better on the "boosted" version (though it's still not nearly as fast in Firefox as an actual constant value would be, afaict; compare <http://jsperf.com/nodelistoptimisation/12>). > I think it can be improved by directly creating .length property on immutable collections That wouldn't be any better in this case; it would still end up going through the proxy machinery. There's literally no place on the actual NodeList object to store the property. What would actually make things sane for the immutable-collections case is to stop the NodeList insanity and return an Array subclass. That's what callers typically want anyway. This is what the updated query APIs do, but Array subclassing is not supported very well in JS engines yet, so those APIs haven't been implemented yet.
> compare http://jsperf.com/nodelistoptimisation/11 This was buggy. The right thing is http://jsperf.com/nodelistoptimisation/13 which shows the getter in fact not getting inlined. Filed bug 1128646 on the obvious issue Jan noticed there, but fixing that will not be sufficient for nodelists.
Depends on: 1128646
Depends on: 965992
Unfortunately the jsperf tests here have disappeared :( I wanted to see how bug 1128646 affected things...
Following code can be run in console: var boostedNodeList = document.querySelectorAll('*'); (function(){ var n = boostedNodeList.length; Object.defineProperty(boostedNodeList, 'length', { get: function(){return n;} }) }()); var defaultNodeList = document.querySelectorAll('*'); console.time('Normal node list'); for(var j=0; j < 100; j++) for(var i=0; i < defaultNodeList.length; i++); console.timeEnd('Normal node list') console.time('Getter node list'); for(var j=0; j < 100; j++) for(var i=0; i < boostedNodeList.length; i++); console.timeEnd('Getter node list') Now it seems to have similar time (FF36).
The test in comment 7 is quite different from the original jsperf testcases because it doesn't run enough iterations to actually end up in the JIT fast paths. But yes, doing something like that with a much higher iteration count (or just recreating the jsperf; I wonder why that went away anyway) would work.
Thanks. As expected, not much change, since the new ICs know nothing about DOM proxy expando objects...
Clearing needinfo; I'm working on other (perf) issues right now. Maybe we can get to this later this year.
Flags: needinfo?(jdemooij)
Keywords: perf
Flags: needinfo?(jdemooij)
Flags: needinfo?(jdemooij)
\o/
Status: NEW → RESOLVED
Closed: 9 years ago
Resolution: --- → DUPLICATE
You need to log in before you can comment on or make changes to this bug.