Reputation: 1393
I'm trying to write a very simple nodejs c++ addon and compare its usage with usual nodejs module usage (written on CoffeeScript). I wrote a test that creates a million object instances of AddonClass from addon and CoffeeClass from module. The source code is:
addon.cpp
#define BUILDING_NODE_EXTENSION
#include <node.h>
#include "addon_class.hpp"
using namespace v8;
static void InitAll(Handle<Object> target) {
Addon::Init(target);
}
NODE_MODULE(addon, InitAll)
addon_class.hpp
#ifndef _ADDON_CLASS_HPP_
#define _ADDON_CLASS_HPP_
#include <node.h>
class Addon : public node::ObjectWrap {
public:
static void Init(v8::Handle<v8::Object> target);
private:
Addon(int value) : value_(value) {};
~Addon() {};
static v8::Handle<v8::Value> New(const v8::Arguments& args);
int value_;
};
#endif
addon_class.cpp
#define BUILDING_NODE_EXTENSION
#include <cstdio>
#include <node.h>
#include "addon_class.hpp"
using namespace v8;
Persistent<FunctionTemplate> tpl;
void Addon::Init(Handle<Object> target) {
Local<FunctionTemplate> t = FunctionTemplate::New(New);
tpl = Persistent<FunctionTemplate>::New(t);
tpl->SetClassName(String::NewSymbol("AddonClass"));
tpl->InstanceTemplate()->SetInternalFieldCount(1);
target->Set(String::NewSymbol("AddonClass"), tpl->GetFunction());
}
Handle<Value> Addon::New(const Arguments& args) {
HandleScope scope;
Addon* obj = new Addon(0);
if (args[0]->IsNumber()) {
obj->value_ = args[0]->NumberValue();
}
obj->Wrap(args.This());
return args.This();
}
CoffeeClass.coffee
class CoffeeClass
value: undefined
constructor: (@value) ->
if !@value? then @value = 0
exports.CoffeeClass = CoffeeClass
benchmark.coffee
CoffeeClass = (require './CoffeeClass.coffee').CoffeeClass
AddonClass = (require './build/Release/addon.node').AddonClass
N = 1e6
calculateDiff = (d1, d2) ->
h = parseInt d2.getHours()
h -= parseInt d1.getHours()
m = parseInt d2.getMinutes() + h * 60
m -= parseInt d1.getMinutes()
s = parseInt d2.getSeconds() + m * 60
s -= parseInt d1.getSeconds()
ms = parseInt d2.getMilliseconds() + s * 1000
ms -= parseInt d1.getMilliseconds()
return ms
testCreate = (LC) ->
d1 = new Date()
for i in [1..N]
l = new LC
d2 = new Date()
console.log LC.name, calculateDiff(d1, d2), "ms"
testCreate(CoffeeClass)
testCreate(AddonClass)
The result of this benchmark is very, very strange for me:
CoffeeClass 34 ms
AddonClass 487 ms
So, the question is: why is this simple addon so slow? And, more importantly, is it possible to do something with it or not?
Upvotes: 3
Views: 2240
Reputation: 1638
Your test case here is so small that the overhead of interfacing between V8 and your addon is greater than any performance savings of the compiled c++.
A good use for an addon is something that is not chatty, for example performing a complex and optimized algorithm on the input parameter or interacting with a complex c++ data structure. You want to minimize the frequency of calls to the addon and maximize the work that it performs with each call. You may even consider batching what would be many separate calls together into an array.
Upvotes: 3
Reputation: 3444
Going between V8 C++ and JavaScript is expensive. I would imagine that if you moved the for loop into the C++ class and the CoffeeScript class, you'd probably see that C++ outperforms the CoffeeScript.
Upvotes: 2