yeti logo icon
Close Icon
contact us
Yeti postage stamp
We'll reply within 24 hours.
Thank you! Your message has been received!
A yeti hand giving a thumb's up
Oops! Something went wrong while submitting the form.

Optimizing JavaScript for runtime speed

By
-
November 3, 2012

This summer Brandon Jones of Motorola Mobility presented a talk about efficient JavaScript vector math [1]. The concepts presented in this JavaScript meetup talk are beneficial to make JavaScript run faster in any codebase. Here are some of the principles that a developer should take:

1. Cache variable calls instead of making multiple separate calls. For example,

someObject.innerObject.doSomething();
someObject.innerObject.doSomethingElse();

should be written as:

var a = someObject.innerObject;
a.doSomething();
a.doSomethingElse();

This code is faster because the method lookup on the object only has to be performed once.

2. Write your APIs (application programming interfaces) as function methods not objects.

var theObject = new SomeObject();
theObject.peformOpA();
theObject.peformOpB(paramA, paramB);

should be written as:

var theObject = SomeModule.create(); // create a hashtable of only values
SomeModule.performOpA(theObject);
SomeModule.performOpB(theObject, paramA, paramB);

This code is faster because we are not copying all the function pointers in the object hashtable. Only the values that change between each instance are copied.

3. Inline code rather than writing function calls. Compilers in the early days weren't good at optimizing certain function calls, but they've improved over time now. JavaScript runtime compilers, however, aren't at that level yet.

function pow2(a) {
   return a * a;
}
var c = pow2(some_var)

should be written as:

var c = (some_var * some_var);

This code is faster because a function call requires its own execution context (stack frame). Without a function call this extra stack frame isn't needed.

4. Unroll your loops. This concept refers to pipelining instructions without branch conditions. Branch conditions often appear in loops, which prevents the compiler from predicting the code path of execution. Have a look at the example from Brandon (below).

mat3.multiply = function (mat, mat2, dest) {
   dest[0] = mat2[0] * mat[0] + mat2[1] * mat[3] + mat2[2] * mat[6];
   dest[1] = mat2[0] * mat[1] + mat2[1] * mat[4] + mat2[2] * mat[7];
   dest[2] = mat2[0] * mat[2] + mat2[1] * mat[5] + mat2[2] * mat[8];

   dest[3] = mat2[3] * mat[0] + mat2[4] * mat[3] + mat2[5] * mat[6];
   dest[4] = mat2[3] * mat[1] + mat2[4] * mat[4] + mat2[5] * mat[7];
   dest[5] = mat2[3] * mat[2] + mat2[4] * mat[5] + mat2[5] * mat[8];

   dest[6] = mat2[6] * mat[0] + mat2[7] * mat[3] + mat2[8] * mat[6];
   dest[7] = mat2[6] * mat[1] + mat2[7] * mat[4] + mat2[8] * mat[7];
   dest[8] = mat2[6] * mat[2] + mat2[7] * mat[5] + mat2[8] * mat[8];

   return dest;
};

This code should be written unrolled.

mat3.multiply = function (mat, mat2, dest) {
   var a00 = mat[0], a01 = mat[1], a02 = mat[2],
       a10 = mat[3], a11 = mat[4], a12 = mat[5],
       a20 = mat[6], a21 = mat[7], a22 = mat[8],

       b00 = mat2[0], b01 = mat2[1], b02 = mat2[2],
       b10 = mat2[3], b11 = mat2[4], b12 = mat2[5],
       b20 = mat2[6], b21 = mat2[7], b22 = mat2[8];

   dest[0] = b00 * a00 + b01 * a10 + b02 * a20;
   dest[1] = b00 * a01 + b01 * a11 + b02 * a21;
   dest[2] = b00 * a02 + b01 * a12 + b02 * a22;

   dest[3] = b10 * a00 + b11 * a10 + b12 * a20;
   dest[4] = b10 * a01 + b11 * a11 + b12 * a21;
   dest[5] = b10 * a02 + b11 * a12 + b12 * a22;

   dest[6] = b20 * a00 + b21 * a10 + b22 * a20;
   dest[7] = b20 * a01 + b21 * a11 + b22 * a21;
   dest[8] = b20 * a02 + b21 * a12 + b22 * a22;

   return dest;
};

The code is much faster because bounds checking (i.e., the branch condition) is checked once at the beginning rather than on every call.

5. Use native arrays when performing computation-intensive operations on floating-point numbers. Float32Array is very fast to use but incredibly expensive to create. In other words, reuse and cache floating-point array objects and don't create them inside a loop.

In summary these optimizations are workarounds to JavaScript's non-optimizing compiler. Modern C and Java compilers are able to circumvent some of these issues but web browser JavaScript engines aren't as smart yet. Happy optimizing!

[1] http://media.tojicode.com/sfjs-vectors/

You Might also like...

Shopify Checkout Using UI Extensions

At Yeti we recently created a custom Shopify App using Checkout UI extensions to pull real-time data from a client’s Enterprise Resource Planning (ERP) system into Shopify. By the end of this article you will better understand what Shopify Checkout UI extensions are, how to approach building them, and some of the things to keep in mind during the implementation process.

colorful swirlsAn Introduction to Neural Networks

Join James McNamara in this insightful talk as he navigates the intricate world of neural networks, deep learning, and artificial intelligence. From the evolution of architectures like CNNs and RNNs to groundbreaking techniques like word embeddings and transformers, discover the transformative impact of AI in image recognition, natural language processing, and even coding assistance.

A keyboardThe Symbolicon: My Journey to an Ineffective 10-key Keyboard

Join developer Jonny in exploring the Symbolicon, a unique 10-key custom keyboard inspired by the Braille alphabet. Delve into the conceptualization, ideas, and the hands-on process of building this unique keyboard!

Browse all Blog Articles

Ready for your new product adventure?

Let's Get Started