// fabonacci.js
function fabonacciNodeJS(n) {
if (n === 0) {
return 0;
}
if (n === 1) {
return 1;
}
return fabonacciNodeJS(n - 1) + fabonacciNodeJS(n - 2);
}
function TestFabonnacci(func, env, n) {
const start = (new Date()).getTime();
const result = func(n);
const end = (new Date()).getTime();
console.log(`fabonacci(${n}) run in ${env} result is ${result}, cost time is ${end - start} ms.`);
}
TestFabonnacci(fabonacciNodeJS, 'Native Node.js', 40);
可以在命令行中运行这一段程序,结果如下:
fabonacci(40) run in Native Node.js result is 102334155, cost time is 1125 ms.
// fabonacci.cc
#include <node.h>
namespace fabonacci {
using namespace v8;
static inline size_t runFabonacci(size_t n) {
if (n == 0)
{
return 0;
}
if (n == 1)
{
return 1;
}
return runFabonacci(n - 1) + runFabonacci(n - 2);
}
static void Fabonacci(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
// 检查参数类型
if (!args[0]->IsNumber())
{
isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate, "argument type must be Number")));
}
size_t n = args[0]->NumberValue();
Local<Number> num = Number::New(isolate, runFabonacci(n));
args.GetReturnValue().Set(num);
}
void init(Local<Object> exports, Local<Object> module) {
NODE_SET_METHOD(module, "exports", Fabonacci);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, init)
}
修改之前的fabonacci.js,测试以上C++拓展程序:
// fabonacci.js
const fabonacciCPP = require('./build/Release/fabonacci');
function fabonacciNodeJS(n) {
if (n === 0) {
return 0;
}
if (n === 1) {
return 1;
}
return fabonacciNodeJS(n - 1) + fabonacciNodeJS(n - 2);
}
function TestFabonnacci(func, env, n) {
const start = (new Date()).getTime();
const result = func(n);
const end = (new Date()).getTime();
console.log(`fabonacci(${n}) run in ${env} result is ${result}, cost time is ${end - start} ms.`);
}
TestFabonnacci(fabonacciNodeJS, 'Native Node.js', 40);
TestFabonnacci(fabonacciCPP, 'C++ Addon', 40);
运行上述程序,结果如下:
fabonacci(40) run in Native Node.js result is 102334155, cost time is 1120 ms.
fabonacci(40) run in C++ Addon result is 102334155, cost time is 587 ms.
对
JavaScript
程序员来说,Node.js
确实是我们作为服务端开发的首选语言。Node.js
的性能优势源于其使用Google
的V8引擎
,使用非阻塞式的I / O模型
,依靠事件驱动。但涉及密集型计算的场景时,Node.js
不一定能够有很优秀的表现。还好有C++ Addons的机制,能够使得我们编写原生的C++
模块,并且能够在Node.js
中调用它。为何要使用
C++
模块C++
社区庞大,我想在我们现成的Node.js
应用中使用某个C++
模块。举个例子:
Fabonacci
斐波那契数列通常解法是以递归地方式来完成,在这里,为了体现
Node.js
中调用C++
模块的优势,我们并不在Fabonacci
中使用缓存的机制。在
Node.js
中,根据Fabonacci
定义,我们编写了如下代码,fabonacci.js
:可以在命令行中运行这一段程序,结果如下:
为了体现密集型计算场景时在
Node.js
中使用C++
拓展模块的优势,我根据C++ Addons编写了如下代码,fabonacci.cc
:修改之前的
fabonacci.js
,测试以上C++
拓展程序:运行上述程序,结果如下:
可以看到,在
Node.js
中调用C++
拓展模块,计算n = 40
的斐波那契数,速度快了接近一倍。从
Hello World
开始现在,我们可以从书写一个
Hello World
来介绍如何编写一个C++
拓展,并在Node.js
模块中调用:以下是一个使用C++ Addons编写的一个
Hello World
模块,我们可以在Node.js
代码中调用这一个模块。以上
C++
代码相当于以下JavaScript
代码:首先,在工程根目录下创建一个名为
binding.gyp
的文件,如下:binding.gyp
使用一个类似JSON
的格式来描述模块的构建配置。然后使用node-gyp把我们书写的C++
模块源码编译为二进制模块,我们可以使用sudo npm install -g node-gyp
全局安装node-gyp
:在项目根目录下执行:
编译构建成功之后,可执行文件
fabonacci.node
会在项目根目录下的/build/Release
目录下,我们可以在Node.js
引入该模块:V8数据类型和JavaScript数据类型的转换
V8数据类型转换为JavaScript数据类型
根据v8文档使用
v8::Local<v8::Value>
声明的数据将会被V8
的Garbage Collector
管理。我们书写如下的C++
模块示例,在C++
模块中声明如下的V8
类型的变量,并导出给JavaScript
模块使用:使用
node-gyp
工具构建上述模块,在Node.js
模块中引入:运行结果:
JavaScript数据类型转换为V8数据类型
例如我们在参数中传入了一个
Number
数据类型的JavaScript
变量,可以使用v8::Number::Cast
方法将JavaScript
数据类型转换为V8
数据类型,我们创建了如下模块factory.cc
,一个工厂模式创建对象的示例:调用上述模块: