In 2010 , a package manager by the name of npm was introduced for Node.js environment for the purpose of simplifying publishing & sharing of source code of Node.js libraries while later in June 2011, support for Windows was also introduced.
In this article, we are only going to cover the core of Node.js i.e. Google’s V8 engine.
Why Understanding of Node.js Internals Are Necessary ?
Below diagram denotes a simplified version of Node.js architecture.
Following are the 3 main parts
- V8 Engine
- Node.js Bindings (Node API)
- An event loop
As discussed earlier, this article is only about V8 engine, more of it we’ll be covering in the next episodes of this series.
- Rhino from Mozilla
- JerryScript a lightweight engine for Internet of things
- Chakra (JScript9) for Internet Explorer
- V8 from Google and so on.
So without further ado, let’s start looking into Google V8.
Inside, the V8 engine, consists of several threads.
- A thread for fetching JS code, compiling it & then executing it.
- A separate thread for compiling, so that the main thread can keep executing while the former is optimizing the code .
- A Profiler thread which tells the runtime on which methods we spend more time so that Crankshaft can try to optimize them.
- Garbage Collection threads
Now another thread starts using another compiler “Crankshaft” . Its main role is optimization .
Inlining replaces the line of code where the function is called with the body of the called function for faster execution. C++ developers are used to with this concept.
Inline caching relies on the observation that repeated calls to the same method tend to occur on the same type of object.
V8 uses a cache for the type of objects that were passed as a parameter in recent method calls and uses this knowledge to make an assumption about the type of object that will be passed as a parameter in the future. If this assumption is right, it can skip the process of figuring out how to access the object’s properties and can use the stored information from cache .
Newest Improvements in 2017, Ignition & TurboFan
Since V8 version 5.9 , (from Node.js version 8.3.0 onwards) , V8 uses a new execution pipeline to achieve significantly better performance and memory optimization. Ignition is V8 interpreter and Turbofan is a new optimizing compiler.
After inclusion of ignition and Turbofan , Full-codegen and Crankshaft are no longer in use by V8. Below graph shows significant speed improvement between V8 version 5.8 and 5.9.
Here is a summary of his guidelines.
- Try to instantiate your object properties in the same order so that hidden classes, and subsequently optimized code, can be shared.
- Assign all properties of an object in its constructor to avoid hidden class change and hence to get better optimization.
- Due to inline caching, code that executes the same method repeatedly will run faster than code that executes many different methods only once.
- Avoid sparse arrays where keys are not incremental numbers. Elements in such arrays are more expensive to access. Also, avoid pre-allocating large arrays instead grow as you go. Finally, do not delete elements in arrays. It causes the keys to sparse.
- V8 represents objects and numbers with 32 bits. It uses a bit to find out if it is an object (flag = 1) or an integer (flag = 0) called SMI (SMall Integer) because of its 31 bits. If a numeric value is bigger than 31 bits, V8 will box the number, turning it into a double .
var i = 52; // this is a 31-bit signed integer
var j = 5.2; // this is a double-precision floating point number
Hence, prefer numeric values that can be represented as 31-bit signed integers to avoid the expensive boxing operation into a JS object.