Tìm hiểu về C++ addon trong Nodejs
nodejs
65
C++
27
White

Thành Minh viết ngày 23/12/2017

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 (mình vẫn thích SpiderMonkey của Firefox hơn <(") ), 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

alt text

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ả:

alt text

Argument

Ở 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

alt text

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ả:

alt text

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ả:
alt text

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.

Bài viết gốc: Tìm hiểu về C++ addon trong Nodejs.

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Thành Minh

7 bài viết.
37 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
28 20
1. Các thư viện cần dùng Thư viện (Link) để xử lí file Zip. Thư viện (Link) để xử lí file Rar. Thư viện (Link), thư viện rarfile bắt buộc đi kèm v...
Thành Minh viết 9 tháng trước
28 20
White
18 3
V8 hay còn gọi là Chrome V8, là một Javascript engine được phát triển bởi Chromium Project, mục đích ban đầu là dành cho Google Chrome nói riêng và...
Thành Minh viết 2 tháng trước
18 3
White
7 2
Mỗi năm thì lại có một bản ECMAScript được thêm vào, cùng với đó là thêm kha khá tính năng mới, bài viết này sẽ kể đến một vài tính năng nhỏ nhưng ...
Thành Minh viết 29 ngày trước
7 2
Bài viết liên quan
White
0 0
Lâu lâu không động vào nodejs không biết mấy ông tool tiếc này đi đâu về đâu rồi. Trước đây thì mình vẫn có thể dùng istanbul với mocha đơn giản th...
Hoàng Duy viết hơn 1 năm trước
0 0
White
1 0
Xử lý đồng bộ một mảng bằng Promise thay cho async.eachSeries Tựa Đang muốn chạy một hàm trong đó xử lý đồng bộ từng phần tử trong một mảng, do g...
Cuong Pham viết hơn 1 năm trước
1 0
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
7 bài viết.
37 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!