Reputation: 105449
Angular has the following interesting function implementation which instead of looping through an array of values takes them as parameters and performs if
checks inline. I assumed it was done for performance purposes so I created this simple performance test:
function checkAndUpdateTextInline(v, def, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9) {
let changed = false;
const bindings = def.bindings;
const bindLen = bindings.length;
if (bindLen > 0 && checkAndUpdateBinding(v, def, 0, v0)) changed = true;
if (bindLen > 1 && checkAndUpdateBinding(v, def, 1, v1)) changed = true;
if (bindLen > 2 && checkAndUpdateBinding(v, def, 2, v2)) changed = true;
if (bindLen > 3 && checkAndUpdateBinding(v, def, 3, v3)) changed = true;
if (bindLen > 4 && checkAndUpdateBinding(v, def, 4, v4)) changed = true;
if (bindLen > 5 && checkAndUpdateBinding(v, def, 5, v5)) changed = true;
if (bindLen > 6 && checkAndUpdateBinding(v, def, 6, v6)) changed = true;
if (bindLen > 7 && checkAndUpdateBinding(v, def, 7, v7)) changed = true;
if (bindLen > 8 && checkAndUpdateBinding(v, def, 8, v8)) changed = true;
if (bindLen > 9 && checkAndUpdateBinding(v, def, 9, v9)) changed = true;
return changed;
}
versus dynamic version:
function checkAndUpdateTextDynamic(v, def, values) {
const bindings = def.bindings;
let changed = false;
for (let i = 0; i < values.length; i++) {
if (checkAndUpdateBinding(v, def, i, values[i])) {
changed = true;
}
}
}
And inline version seems to be 25% faster than dynamic. What could be the reason for that? Is inline caching in play here? How can I debug it using d8
?
Upvotes: 0
Views: 202
Reputation: 36088
First, the two versions are not equivalent: the number of iterations is bounded by bindings.length
in the first and by values.length
in the second. If they differ, you may get a different number of iterations.
Second, there are several operations that the unrolled version avoids:
values.length
on every iteration.values[i]
on every iteration.i
on every iteration.i
on every iteration.The first two in particular can be expensive, depending on the amount of dynamic optimisations and specialisation the compiler is able to perform. And if checkAndUpdateBinding
doesn't do much, that might well be a noticeable overhead. You could try to change the original version to use some of these operations as well, and see where that puts it in the comparison.
Third, this is a micro benchmark. Performance in JavaScript involves so many hidden variables and dependencies on static and dynamic context that micro benchmarks are even more pointless to measure than for other languages. In general, I estimate that 90% of what people measure on jsperf is useless or even misleading.
Fourth, note that loop unrolling is a fairly common optimisation performed by compilers. In some cases it can be a significant win, even in languages that are more efficient than JavaScript.
Upvotes: 4
Reputation: 190907
Without the loop, there's no need to update or compare i
(it doesn't exist). Some implementations can be slow with Array.prototype.length
.
The optimizer could be smart with the unwound version too, because its of a fixed number of steps. The optimizer doesn't know whether values
is 1 item, 10 items, 100 items or 10 million items.
Upvotes: 1