o.f() and (0, o.f)() have different semantics - the latter is closer to temp = o.f; temp().
For that reason the optimisers see the code being function-call vs. method-call. Given the usage of function and method calls are generally different the compilers make different optimising decisions.
Yes, you are absolutely right - semantics is quite different. But that's not what changes perf here - the difference comes from whether there is an explicit property load or a property load is an implicit part of the method call. This change is also made in the code that never executes during the benchmark run - which adds to a conundrum.
There is a very V8 / Crankshaft specific thing going on here - slides try to explain it.
Why does this require JS (to the point of giving a blank page!) for something that can be trivially done without? A bit of tweaking to the source later, and I have something readable. Now, about the actual article:
Also:
Can someone explain to me why it has to assume that arr.length could change? It states it does, but I don't see why. That seems like a pretty obvious optimization flaw: you should be able to safely assume that something won't change if the only cases where it could change is if it already has changed. In other words, why is this:
var sum = 0;
for (var i = 0, L = arr.length;
i < arr.length;
++i) {
if (arr.length !== L)
H.throwConcurrentModificationError(arr);
var item = list[i];
sum += item;
}
not optimizable to remove the whole business with L and the if check?
Also, this is one of my frustrations with optimizers in general. Far too many of them end up with global (or at the very least) side-effects of trivial changes (or worse, changes that shouldn't do anything!), to the point of making optimization a fool's game. (I optimized this method, but now the change in function alignment changes cache aliasing such that the program runs slower. Hey look: A runs substantially faster than B. But if you add a comment here, B runs faster than A.)
> Why does this require JS (to the point of giving a blank page!) for something that can be trivially done without?
This is a slide deck (not a blog post or article) hence its dependence on JS.
(though even blog posts I write usually depend on JS to provide proper syntax highlighting and diagrams)
> Can someone explain to me why it has to assume that arr.length could change? It states it does, but I don't see why.
If you don't know where `throwConcurrentModificationError(arr)` goes then you don't know what it does, e.g. it can do something like this:
H.throwConcurrentModificationError = function (arr) {
arr.push(10);
};
(contrary to its name - but JITs don't optimize based on names)
That's why JIT has to assume global side effects from the `throwConcurrentModificationError` call - unless of course it emits an unconditional deoptimization right before it which is what happens in the (0, o.f)() case.
The only case that arr.length could change, and hence the if statement taken, is if the if statement had already been taken - i.e. arr.length had already changed. This seems safe to optimize.
It doesn't matter what H.throwConcurrentModificationError is - it can only be called iff it has already been called. Which means that as soon as you're through the first loop iteration, you know that arr.length hasn't changed that loop, and it won't change any other iterations through the loop.
And as for JS - there are plenty of slide decks that work perfectly fine without, or at the very least degrade gracefully. So that's not a good excuse.
> The only case that arr.length could change, and hence the if statement taken, is if the if statement had already been taken - i.e. arr.length had already changed.
Fair point. I misread your original comment. To see this you need a more sophisticated analysis pass than V8 is capable of performing - something similar in spirit to sparse conditional constant propagation, V8's LICM just assumes that all blocks are reachable so it takes the union of all possible side-effects.
> So that's not a good excuse.
I am not making an excuse, I am simply stating a fact: this is a slide deck, it needs JS to operate. As simple as that. If there is an easy fix - please send me a PR[1], I will gladly take it
13 comments
[ 3.4 ms ] story [ 31.3 ms ] threadFor that reason the optimisers see the code being function-call vs. method-call. Given the usage of function and method calls are generally different the compilers make different optimising decisions.
There is a very V8 / Crankshaft specific thing going on here - slides try to explain it.
Also:
Can someone explain to me why it has to assume that arr.length could change? It states it does, but I don't see why. That seems like a pretty obvious optimization flaw: you should be able to safely assume that something won't change if the only cases where it could change is if it already has changed. In other words, why is this:
not optimizable to remove the whole business with L and the if check?Also, this is one of my frustrations with optimizers in general. Far too many of them end up with global (or at the very least) side-effects of trivial changes (or worse, changes that shouldn't do anything!), to the point of making optimization a fool's game. (I optimized this method, but now the change in function alignment changes cache aliasing such that the program runs slower. Hey look: A runs substantially faster than B. But if you add a comment here, B runs faster than A.)
This is a slide deck (not a blog post or article) hence its dependence on JS.
(though even blog posts I write usually depend on JS to provide proper syntax highlighting and diagrams)
> Can someone explain to me why it has to assume that arr.length could change? It states it does, but I don't see why.
If you don't know where `throwConcurrentModificationError(arr)` goes then you don't know what it does, e.g. it can do something like this:
(contrary to its name - but JITs don't optimize based on names)That's why JIT has to assume global side effects from the `throwConcurrentModificationError` call - unless of course it emits an unconditional deoptimization right before it which is what happens in the (0, o.f)() case.
That makes it acceptable to not fail gracefully?
I get it, whining about graceful degradation and being anti-Javascript can be annoying, but your response is even more so.
Do the right thing for now, not some mythical "what if" situation later that adds premature complification.
One should argue that OP should re-write the whole thing in non-slide deck format - TTRWTF
There are many CSS and JS-based slide decks that degrade nicely. e.g. deck.js (http://imakewebthings.com/deck.js/introduction/deck.html).
The only case that arr.length could change, and hence the if statement taken, is if the if statement had already been taken - i.e. arr.length had already changed. This seems safe to optimize.
It doesn't matter what H.throwConcurrentModificationError is - it can only be called iff it has already been called. Which means that as soon as you're through the first loop iteration, you know that arr.length hasn't changed that loop, and it won't change any other iterations through the loop.
And as for JS - there are plenty of slide decks that work perfectly fine without, or at the very least degrade gracefully. So that's not a good excuse.
Fair point. I misread your original comment. To see this you need a more sophisticated analysis pass than V8 is capable of performing - something similar in spirit to sparse conditional constant propagation, V8's LICM just assumes that all blocks are reachable so it takes the union of all possible side-effects.
> So that's not a good excuse.
I am not making an excuse, I am simply stating a fact: this is a slide deck, it needs JS to operate. As simple as that. If there is an easy fix - please send me a PR[1], I will gladly take it
[1] https://github.com/mraleph/mraleph.github.com/tree/master/ta...
The angry face you use reminded me a lot of Ally Brosh's style and made me laugh out loud.
Love this style.