Node.js vốn là một nền tảng chạy trên môi trường V8 JavaScript, một trình thông dịch JavaScript vốn nằm trong trình duyệt Chrome, engine này vốn được viết từ C++, nó sẽ compile JavaScript trực tiếp thành native code thay vì interpreting bytecode, điều này cho ta tốc độ nhanh hơn kha khá, nhưng ta vẫn có thể tăng tốc và tối ưu ứng dụng nhanh hơn nữa bằng việc dùng C++.
Thư viện
Ta cần sử dụng node-gyp, đây là một build tool dùng để compile native addon modules cho Nodejs:
npm install -g node-gyp
Bắt đầu
Init một cái project mới:
npm init -y
Ta sẽ thử viết một cái addon tên là
hw.cc
để in ra dòng chữ "Hello world" đơn giản xem sao:#include <node.h>
namespace hw {
using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::String;
using v8::Value;
void Method(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
args.GetReturnValue().Set(String::NewFromUtf8(isolate, "Hello World"));
}
void init(Local<Object> exports) {
NODE_SET_METHOD(exports, "hw", Method);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, init)
}
Lưu ý rằng tất cả các addon đều phải tuân theo cái pattern này:
void Initialize(Local<Object> exports);
NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)
Giống như trên thì đoạn:
void init(Local<Object> exports) {
NODE_SET_METHOD(exports, "hw", Method);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, init)
Cũng giống như:
module.exports = hw;
trong JavasScript vậy.
Build
Nodejs không thể require() file .cc vào file javascript được, mà nó phải dịch sang mã máy (cái đống 01100101011001010...) thành file có cái đuôi .node đã.
Đầu tiên, ta tạo file
binding.gyp
ở root folder của project, nó chính là 1 file dạng như JSON vậy, toàn bộ thông tin trong này sẽ là config cho node-gyp
mà mình vừa install trên compile{
"targets": [
{
"target_name": "addon",
"sources": [ "hw.cc" ]
}
]
}
Như ở trên, mình taọ một target với target name là "addon" (tên file sẽ được compile ra) và sources là file
hw.cc
nãy mình vừa mới code.Sau đó chạy:
node-gyp configure build
Mặc định của node-gyp là addon của mình sẽ nằm trong folder
build/Release
Ta tạo file index.js và require cái addon ấy vào:
var addon = require('./build/Release/addon')
console.log(addon.hw())
Và đây là kết quả:
Agrument
Ở trên ta đã viết được một addon đơn giản, nhưng nếu ta muốn truyền argument từ JavaScript truyền sang C++ thì sao, điều này khá dễ dàng, các argument đều được truyền từ const
FunctionCallbackInfo<Value>& args
, ta thử một ví dụ đơn giản để tính tổng hai số truyền vào:#include <node.h>
namespace hw {
using v8::Exception;
using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::Number;
using v8::Object;
using v8::String;
using v8::Value;
void Sum(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
// Cộng 2 argument lại với nhau
double total = args[0]->NumberValue() + args[1]->NumberValue();
Local<Number> num = Number::New(isolate, total);
args.GetReturnValue().Set(num);
}
void Init(Local<Object> exports) {
NODE_SET_METHOD(exports, "sum", Sum);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, Init)
}
Build:
Ta sẽ thử truyền 2 số vào:
var addon = require('./build/Release/addon')
console.log(addon.sum(6, 9))
Và đây là kết quả:
Tốc độ giữa C++ và Javascript
Mặc dù 2 thằng này, một thằng là Static Language, thằng kia là Dynamic Language thì ai cũng biết rõ rồi nhưng mình vẫn muốn thử xem tốc độ của 2 thằng như thế nào, bằng cách lặp từ 0 -> 100000000:
C++:
#include <node.h>
namespace sum {
using v8::Exception;
using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::Number;
using v8::Object;
using v8::String;
using v8::Value;
void Sum(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
int a = 1, b = 2;
for (int i = 0; i < 100000000; i++) {
a += b;
}
Local<Number> total = Number::New(isolate, a);
args.GetReturnValue().Set(total);
}
void Init(Local<Object> exports) {
NODE_SET_METHOD(exports, "cpp", Sum);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, Init)
}
Javascript:
var addon = require('./build/Release/addon')
function js () {
let a = 1, b = 2;
for (let i = 0; i < 100000000; i++) {
a += b;
}
return a;
}
console.time('c++')
let a = addon.cpp()
console.log(a)
console.timeEnd('c++')
console.time('js')
let b = js()
console.log(b)
console.timeEnd('js')
Kết quả:
Ta thấy C++ nhanh hơn gần 50 lần so với JavaScript.
Tổng kết
Còn rất nhiều thứ như Callbacks, Object factory,... mình sẽ giới thiệu sau. Mặc dù sử dụng C++ khá là phức tạp vì nó khá khó so với JavaScript nhưng đối với ai muốn tối ưu tốc độ app của mình thì dùng C++ sẽ là một giải pháp khá tốt để giải quyết vấn đề này.