Optimization Techniques in V8 engine in javascript

Writing a code is not difficult task in javascript, but optimized code. Well, it is also not difficult. All it requires is knowledge of certain things that we can imbibe in our code while writing it. Here we will discuss how V8 engine works in little detail. While discussing features like hidden classes, tagging, unboxing, full compiler and optimising compiler, we will also discuss common pitfalls and what we can do to optimise our code.

Hidden Classes

  • Since javascript is a loosely typed language, we have limited compile time information.
  • It is expensive to reason about js types at compile time.

  • Hidden classes help V8 run js faster

  • v8 internally create hidden classes for objects at runtime.

  • Objects with same hidden class can use same optimised generated code.

  • Object used by same constructors keep on reusing same hidden classes

Pitfalls

  • Suppose we have object and object b.
  • Both are constructed using the same constructor.
  • If we assign a property to object b outside of constructor and not to a, it will create a new hidden class.

What to do?

  • Initialise all the object members in constructor functions.

  • Always initialise object members in the same order. This is because if we add members in different orders, we create a different tree of hidden classes. And in the end, we will have the objects that will use 2 different hidden classes that can’t use the same optimised code.

Numbers

  • How does V8 represents values efficiently if types are changed?

  • V8 uses tagging.

  • It uses internally as objects or small integer.

  • It keeps last bit of 32 bit to differentiate. If it is 1, it is object pointer, and if it is 0, it is 31 bit signed integer.

  • For larger numbers that don’t fit in 31 bit(signed), we box the number.We turn it into double, and we create a new object to put that number inside it.

What to do?

  • Prefer numeric values that can be used as 31 bit signed numbers.

Arrays

  • In js, array can be sparse, i.e. elements absent in between.

  • V8 uses two methods to handle arrays: fast elements  and dictionary elements.
  • fast elements: it uses linear storage buffer. V8 uses them if set of keys is very compact.

  • dictionary elements: it is a hash table which is much more compact but also more expensive at run time to access.

What to do?

  • Make sure v8 uses fast elements, i.e.  make sure that key starts at 0 and is contiguous.

  • Don’t preallocate large arrays (i.e. > 64k elements) to their maximum size, instead grow as you go.

  • Don’t delete elements inside of array, especially numeric arrays.

  • Don’t load uninitialised or deleted elements. e.g.:

    • a=new Array(); for(var i=0;i<10;i++){   a[0] |= b; //No!  }

    • a=new Array(); a[0]=0; for(var i=0;i<10;i++){   a[0] |= b; //2x faster  }

 

But if we talk about tagging, arrays of double should be slow?

we have mechanism called unboxing. When we unbox a double, we take 64 bit IEEE number inside of this double object, take it out and put it into a linear buffer.

  • Array’s hidden class tracks element types.

  • Arrays containing only doubles are unboxed.

  • Unboxing causes hidden class change.

Careless manipulation of arrays can lead to a lot of extra work due to boxing/unboxing.

var a = new Array();
a[0] = 77; //allocates
a[1] = 88;
a[2] = 0.5; //allocates, converts i.e. 77.0, 88.0, 0.5 => also, hidden class change
a[3] = true; //allocates, converts

because true can’t be stored in unboxed double array. So, array is converted back to its tagged format i.e. converting elements that can be converted to smis back to smis. and reboxing the double value. And now, we have a backing store, where we can store true value in. 4 allocations, 2 hidden class changes => expensive

We can use a array literal. This is a hit to v8 upfront.

var a = [77, 88, 0.5, true]; // 1 allocation. correct backing store representation at compile time.

What to do?

  • Initialise using array literals for small fixed size arrays.
  • Preallocate small arrays to correct size before using them. Because for small arrays, even if they are sparse, V8 will use fast arrays for them.

  • Don’t store non-numeric values in numeric arrays. For double, V8 uses unboxing which is good. But for objects, it has to box everything again which is bad.

Compiler

  • V8 has 2 compilers: Full Compiler and Optimising Compiler.

  • “Full” compiler can generate good code for any javascript.

  • Optimising compiler produces great code for most javascript.

  • “Full compiler starts executing code “ASAP”.

  • Quickly generates good but not great JIT code.

  • Assumes (almost) nothing about the types at the compilation time.

  • Uses Inline Caches(or ICs) to refine knowledge about types while program runs.
    • IC is Type dependent code for operations(given inputs of specific hidden class).
    • Validate type assumptions first, then do work.

    • Change at run time via back patching as more types are discovered.

  • ICs improve speed upto 20times.

  • Operations are monomorphic if hidden classes are always the same. Otherwise, they are polymorphic. Monomorphic operations are better than polymorphic.

  • Monomorphic operations are easier to optimise. 
    function add(x, y){
      return x+y;
    }
    add(2, 3); //5 + in add is monomorphic.
    add(“2”, “3”); //23 + in add becomes polymorphic.

What to do?

  • Don’t try to mix operations or objects of different types in operations at a particular place in our code.

  • Prefer monomorphic code to polymorphic code.

 

Summary

  • Ensure problem is js problem(Use Dev Tools in chrome to figure out we are spending time in our web application.)

  • Reduce to pure js(No DOM! solve js problem, DOM interaction problem is different.)
  • Collect metrics.

  • Locate bottlenecks.

  • Fix them.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s