Why Google's V8 JavaScript engine in my C++ addon works significantly slower than in Node.JS?
I've tried to write some stupidly simple code for generating prime numbers in the JavaScript and ran it in V8 via my C++ addon and directly in Node.JS.
I was quite shocked, because both should be using the same JavaScript engine and both have executed the same code (time in milliseconds, less is better):
V8 in Node.JS: 495517
V8 in Node.JS C++ Addon: 623598
Here is the source of JavaScript module and source of C++ addon that runs same JavaScript code (and I think problem not in the interop, because measuring of time works directly in JS):
index.js:
var jsInNodeJsPrimeGeneratorBenchmark = require("./javascript.js");
var jsInNativePrimeGeneratorBenchmark = require("./native");
console.log("V8 in Node.JS: ", jsInNodeJsPrimeGeneratorBenchmark.primeGeneratorBenchmark());
console.log("V8 in Node.JS C++ Addon: ", jsInNativePrimeGeneratorBenchmark.primeGeneratorBenchmark());
javascript.js:
function primeGeneratorBenchmark() {
var result, primeNumberCounter, i, j, isPrime, start, end;
i = 3;
primeNumberCounter = 1;
start = Date.now();
while (primeNumberCounter < 100000) {
isPrime = true;
for (j = 2; j < i; j++) {
if (i % j === 0) {
isPrime = false;
break;
}
}
if (isPrime) {
result = i;
primeNumberCounter++;
}
i++;
}
end = Date.now();
return end - start;
}
exports.primeGeneratorBenchmark = primeGeneratorBenchmark;
native.cpp:
#include <node.h>
v8::Handle<v8::Value> primeGeneratorBenchmark(const v8::Arguments &arguments);
void registerModule(v8::Handle<v8::Object> target);
v8::Handle<v8::Value> primeGeneratorBenchmark(const v8::Arguments &arguments) {
v8::HandleScope handleScope;
v8::Local<v8::Context> context = arguments.Holder()->CreationContext();
v8::Context::Scope scope(context);
const char *sourceStringC =
"var result, primeNumberCounter, i, j, isPrime, start, end, time;\n"
"i = 3;\n"
"primeNumberCounter = 1;\n"
"start = Date.now();\n"
"while (primeNumberCounter < 100000) {\n"
" isPrime = true;\n"
" for (j = 2; j < i; j++) {\n"
" if (i % j === 0) {\n"
" isPrime = false;\n"
" break;\n"
" }\n"
" }\n"
" if (isPrime) {\n"
" result = i;\n"
" primeNumberCounter++;\n"
" }\n"
" i++;\n"
"}\n"
"end = Date.now();\n"
"time = end - start;\n";
v8::Local<v8::String> sourceStringV8 = v8::String::New(sourceStringC);
v8::Local<v8::Script> script = v8::Script::Compile(sourceStringV8);
script->Run();
v8::Local<v8::Value> timeResult = v8::Context::GetCurrent()->Global()->Get(v8::String::New("time"));
return handleScope.Close(timeResult);
}
void registerModule(v8::Handle<v8::Object> target) {
target->Set(v8::String::NewSymbol("primeGeneratorBenchmark"), v8::FunctionTemplate::New(primeGeneratorBenchmark)->GetFunction());
}
NODE_MODULE(native, registerModule);
c to use a double instead of a long in the loop, the time C took was even longer! Not trying to start a flame war, but why is Node. JS (116 ms.) so much faster than native C (198 ms.)
V8 is required for the current Node. js engine to function. In the absence of V8, it wouldn't have a JavaScript engine, and thus wouldn't be able to run JavaScript code. The V8 interface between C++ and JavaScript is used by the native code bindings that come with Node.
It enables in load balancing over several CPU cores which makes it simpler to deliver desired outcomes via smaller modules without depleting the RAM process. Not only that, Node. js also employs a non-blocking event-loop mechanism which enables the servers in seamless processing of requests.
Node. js is just a runtime environment that supports the execution of a program. V8 is at the core of Node.
In the C++ version all variables declared in the script source (result
, primeNumberCounter
, i
, j
, isPrime
, start
, end, time
) are global because script's top level scope is global scope.
For optimizing compiler it is easy to allocate local variables into machine registers (or spill slots on stack) and keep track of their type. Working with global variables on the other hand requires constant memory accesses and type checks because V8 does not (currently) perform a register promotion optimization.
If you wrap the source into an immediately called function difference should go away.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With