When comparing this benchmark with chrome 16 vs opera 11.6 we find that
- in chrome native bind is almost 5 times slower then an emulated version of bind
- in opera native bind is almost 4 times faster then an emulated version of bind
Where an emulated version of bind in this case is
var emulatebind = function (f, context) {
return function () {
f.apply(context, arguments);
};
};
Are there good reasons why there is such a difference or is this just a matter of v8 not optimizing enough?
Note: that emulatebind
only implements a subset but that isn't really relevant. If you have a fully featured and optimised emulated bind the performance difference in the benchmark still exists.
It's impossible to implement a fully-featured
bind
in ES5 alone. In particular sections 15.3.4.5.1 through 15.3.4.5.3 of the spec cannot be emulated.15.3.4.5.1, in particular, seems like a possible performance burden: in short bound functions have different
[[Call]]
internal properties, so calling them is likely to take an unusual and possibly more complicated code path.Various other specific un-emulatable features of a bound function (such as
arguments
/caller
poisoning, and possibly the customlength
independent of original signature) could possibly add overhead to each call, although I admit it's a bit unlikely. Although it looks like V8 doesn't even implement the poisoning at the moment.EDIT this answer is speculation, but my other answer has something more approaching evidence. I still think this is valid speculation, but it's a separate answer, so I'll leave it as such and just refer you to the other one.
The V8 source code for bind is implemented in JS.
The OP doesn't emulate
bind
because it doesn't curry arguments the waybind
does. Here is a fully featuredbind
:Obviously, a quick optimization would be to do
instead if
curriedArgs.length == 0
, because otherwise you have two unnecessary array creations, and an unnecessary copy, but perhaps the native version is really implemented in JS and does not do that optimization.Caveat: This fully featured
bind
does not correctly handle some corner cases aroundthis
argument coercion in strict mode. That might be another source of overhead.Based on http://jsperf.com/bind-vs-emulate/6, which adds the es5-shim version for comparison, it looks like the culprit is the extra branch and
instanceof
that the bound version has to perform to test if it's being called as a constructor.Each time the bound version is run, the code that gets executed is essentially:
In the V8 source code, this check appears (inside
boundFunction
) as(Plaintext link to v8natives.js for when Google Code Search dies.)
It is a bit puzzling that, for Chrome 16 at least, the es5-shim version is still faster than the native version. And that other browsers have rather varying results for es5-shim vs. native. Speculation: maybe
%_IsConstructCall()
is even slower thanthis instanceof bound
, perhaps due to crossing native/JS code boundaries. And perhaps other browsers have a much faster way of checking for a[[Construct]]
call.