Over a million developers have joined DZone.

Optimizing Bound Functions Further

DZone's Guide to

Optimizing Bound Functions Further

In this article, a DZone MVB and Google engineer is able to reduce the load speed a JavaScript-based web app by several times using just 19 lines of code.

· Performance Zone ·
Free Resource

Learn how error monitoring with Sentry closes the gap between the product team and your customers. With Sentry, you can focus on what you do best: building and scaling software that makes your users’ lives better.

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. The 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 Call builtin (these steps match the ECMAScript specification for [[Call]] on Bound Function Exotic Objects).

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 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.

"use strict";
function foo(x, y, z) { return this + x + y + z }
var foo1 = foo.bind(1, 0);
var foo2 = foo.bind(2, 1);
function test() {  // Performance test.
  var sum = 0;
  const limit = 1000;
  for (var y = 0; y < limit; ++y) {
    for (var z = 0; z < limit; ++z) {
      sum += foo1(y, z) + foo2(y, z);
  return sum;
for (let i = 0; i < 10; ++i) test(); // Warmup first.
var startTime = Date.now();
for (let i = 0; i < 10; ++i) test();
var endTime = Date.now();
print("Time: " + (endTime - startTime) + "ms.");

Looking at my Function.prototype.bind micro-benchmark again, and running it with TurboFan (passing the --turbo flag to d8or 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 Function.prototype.bind 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).

What’s the best way to boost the efficiency of your product team and ship with confidence? Check out this ebook to learn how Sentry's real-time error monitoring helps developers stay in their workflow to fix bugs before the user even knows there’s a problem.

code optimization ,conversion optimization ,index optimization ,performance

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}