As mentioned in my previous blog post about a new approach to Function.prototype.bind there’s more potential for optimizing bound functions in V8, especially in the new optimizing compiler TurboFan. One obvious thing here is to further reduce the overhead for calling into bound functions, which was traditionally very high.
So what usually happens when you call a bound function is you push the receiver and all the arguments onto the machine stack and call into the
Call builtin, which dispatches to the
CallBoundFunction builtin since we are attempting to call a bound function.
CallBoundFunction builtin then patches the receiver to the
boundThis parameter of
Function.prototype.bind, pushes the additional arguments
onto the stack (below the arguments that are already present), and transfers control to the
targetFunction by tail calling back into the
(these steps match the ECMAScript specification for [[Call]] on Bound Function Exotic
But if you already know that you are calling a certain bound function, then these steps above are unnecessary, and you could instead push the bound receiver plus the bound arguments directly and call to the actual target function because they may be known during compilation time (the VM collects this information as part of the type feedback that is gathered in various places during warmup). If you call a certain bound functions very often, then this greatly reduces the call overhead. And thanks to the new architecture in TurboFan, this also enables the compiler to inline the target function into the caller. So I just landed a change in V8 that does exactly that, and achieved an amazing speedup.
Looking at my
Function.prototype.bind microbenchmark again, and running it with TurboFan (passing the
--turbo flag to
d8 or via
--js-flags=--turbo to Chrome), the time goes down to 31ms, compared to the 240ms it takes with Crankshaft or 360ms with TurboFan before
my change. That’s roughly another 12x improvement compared to baseline TurboFan or 8x improvement compared to Crankshaft (our current
shipping configuration). So that’ll be an overall 400x-600x improvement compared to before we started looking into
once we ship TurboFan by default (I was actually looking into also making this available in Crankshaft, but the inlining machinery in Crankshaft
is extremely brittle and the plan is to replace Crankshaft anyway this year).