Node C Extension

A Sample node.js C/C++ Addons module by using N-API & node-addon-api along with CMake.js to build the native module. Also a SneakPeek into google V8 engine optimization while executing JavaScript (JS) code.

If you are new to node.js native (C/C++) Addons and starting the groundwork for creating one then you may come across many approaches such as V8, NAN, node-addon-api, N-API, node-gyp, CMake.js etc. For a new module the best choice could be to use N-API (or its C++ wrapper class node-addon-api).

Glossary

Why N-API ?

Node.js is a JavaScript library, yet its core is writ in C/C++ (V8, Libuv and many other such modules). The easy and popular way of writing a node.js modules is also by using JavaScript. Such a modules are mostly platform independent and easy to manage. There are times when the performance of JavaScript is not enough, then we may have to consider native Node.js Addon modules. There are many advantages with native Addon node modules, efficient and excellent runtime performance, and the list go on. One of the major downside of native node module is the overhead of maintenance. This overhead alone is good enough to keep us think twice before introducing a native module. Till the arrival of N-API none of the abstraction API used to provide ABI stability across node.js major releases, then we may have to recompile our native module with each major node.js release that often come with build breakage. There are many effort in the past to eliminate or minimize such difficulties, NAN is the most popular among that. Though NAN did a reasonably good job, still not addressed all possible pain points.

The N-API is expected to address this problem to a new level, it is expected to be Application Binary Interface (ABI) stable across versions of Node.js release. This will be a big relief for those who maintain native node.js Addon modules. Though the N-API was available with two past LTS node releases, it was experimental at that time. The current node.js LTS release (v10) has marked it as stable. I feel then it is right time to jump in and start using it. So far I am impressed with the concept and design because of its simplicity and easy to use than NAN.

The N-API is C language API and it is part of node.js core itself, then no external module dependency. To provide support for C++, the node.js team is also maintains a C++ wrapper module (created on top of N-API) called node-addon-api. This wrapper is not part of node.js core, still it is maintained by the node.js team.

The node team has done a good job by creating a set of N-API and node-addon-api examples in a single repository, and it can be accessed from node-addon-examples. It has helped me a lot when I started learning N-API; in fact some of the examples in this repository are either a direct copy or enhance by deriving the concept from it.

Native addon functionalities coved in this example

This example try to mix and match both N-API and node-addon-api in the same module, such approach provides better flexibility for a native addons project.

Prerequisite

# install CMake.js
npm install -g cmake-js
cmake-js    --help

Clone the repository

git clone https://github.com/msatyan/MyNodeC.git
cd MyNodeC

# Install dependency and build
npm install

Build native module

# build native module with cmake-js
cmake-js compile

# or
cmake-js build

# for debug build
cmake-js build --debug

FYI: cmake-generators for VS

Example:

FYI: CMake 3.7.2 or higher needed for VS 2017

cmake-js -G “Visual Studio 15 2017 Win64” cmake-js build –release -G “Visual Studio 15 2017 Win64” cmake-js build –debug -G “Visual Studio 15 2017 Win64”

FYI: CMake 3.14 or higher needed for VS 2019

cmake-js -G “Visual Studio 16 2019” -A Win32 cmake-js -G “Visual Studio 16 2019” -A x64 cmake-js -G “Visual Studio 16 2019” -A ARM cmake-js -G “Visual Studio 16 2019” -A ARM64

or if debug then use -D flag, for example

cmake-js -D -G “Visual Studio 16 2019” -A x64


### Run some sample
The following **SpeedTest.js** sample program execute two functions one is a native function **SpeedTest_CPrimeCount()** and the other one is a pure JavaScript function **SpeedTest_JSPrimeCount()**. Both the functions are doing the same operation, calculating number of prime numbers between a given two numbers (say X=2 and Y=1000). Then the sample module compare the time taken by this two functions.

```bash
node test/SpeedTest.js
# try with different range
node test/SpeedTest.js  800

# Other examples
node test/EventEmit.js
node test/TestExtensions.js

Try a simple JavaScript vs C/C++ profiling by using the SpeedTest.js

Try to run the SpeedTest.js with different value range; for a moment we may get puzzled with the performance comparison output. What we are seeing is perfectly a normal behavior, if we dig a bit deeper then we may find very interesting discovery.

node test/SpeedTest.js  3
node test/SpeedTest.js  8
node test/SpeedTest.js  50
node test/SpeedTest.js  100
node test/SpeedTest.js  500
node test/SpeedTest.js  1000
node test/SpeedTest.js  5000
node test/SpeedTest.js  10000
node test/SpeedTest.js  25000
node test/SpeedTest.js  50000

We may see the following interesting behaviors

The performance variation we are seeing is because the V8 engine compilers (Ignition & TurboFan) started getting engaged and disengaged at various stages of JS code efficient execution. Two major module involved in this are:

1) At near lower value ranges
function Add( a, b ) {
    return( a + b );
}

// These are perfectly a valid JavaScript scenario
// Not recommended such usage for better code optimization.
console.log( Add(3, 5) );
console.log( Add("Hello ", "World !") );
2) Slightly above lower value ranges
3) Somewhat mid to upper value ranges
4) Upper value ranges

SpeedTest.js profile output