初探Nodejs的c++ addon

初探Nodejs的c++ addon

最近我打算把公司的接口服务前端换成node,可以减少负载服务器的数量,节约成本,同时也想把node作为公司的一门重要语言加入到项目中,以前都是.net和php。

node已经很快了,但是我想榨干它的极限,想让它更快一些,所以就想到了c++模块。于是对照着node官网的手册一步步的开始摸索c++模块和V8。

要写C++模块,必须先搭建一个环境,node-gyp命令是必须。所以我们得先安装node-gyp

npm install -g node-gyp  //当然要保证你的node版本是0.8.x

装好node-gyp后,我发现死活跑不起来官网的hello world例子,于是打开node-gyp的github页面,赫然印有需要 python 2.7.3 的要求,于是继续折腾py,具体py的安装例子参考: CentOs 安装python python 2.7.3 下载 装好py之后,我们就完成了环境搭建工作了,可以跑官网的hello world例子了。 创建node-gyp文件:

{
  "targets": [
    {
      "target_name": "hello",
      "sources": [ "hello.cc" ]
    }
  ]
}

接着执行node-gyp configure然后执行node-gyp build如果报找不到binding.gyp的错误,只要把binding.gyp拷贝到build文件目录下即可

hello.cc:

#include <node.h>
#include <v8.h>
using namespace v8;  
Handle<Value> Method(const Arguments& args) {
  HandleScope scope;
  return scope.Close(String::New("world"));
}

void init(Handle<Object> target) { target->Set(String::NewSymbol(“hello”), FunctionTemplate::New(Method)->GetFunction()); } NODE_MODULE(hello, init)

这是官网一个简单的helloworld的示例,node端调用方法为:

var hello = require('./addon/build/Release/hello.node').hello();
console.log(hello); //这里打印world字符串

成功跑起来例子之后,我打算从一个简单的模块入手,慢慢的熟悉如何写C++的addon,我们开发一个简单的但是实用的node验证模块,比如验证参数是否是数组,数字,字符串,大于0等等。因为这个模块在我项目中可能被大量用到,需要验证接口参数的大量合法性数据。

我们构建一个verify文件夹,用来做开发verify验证模块用,目录结构如下:

verify
/index.js
/package.json
/readme.md
/lib
    /verify.js
/addon
    /binding.gyp
    /SimpleVerify.cc
    /SimpleVerify.h
    /verify.cc

一个简单的模块目录构成,不知道写的对不对,反正我就这么弄了,哈哈。

我们直接看下3个c++代码:(依葫芦画瓢的,不要取笑啊)

verify.cc:

#define BUILDING_NODE_EXTENSION
#include <node.h>
#include "SimpleVerify.h"
using namespace v8;

void Init(Handle<Object> target) {

target->Set(String::NewSymbol(“isArray”), FunctionTemplate::New(SimpleV::isArray)->GetFunction());

target->Set(String::NewSymbol(“isNumber”), FunctionTemplate::New(SimpleV::isNumber)->GetFunction()); }

NODE_MODULE(verify, Init)

SimpleVerify.h

#ifndef SV_H
#define SV_H
#include <node.h>

class SimpleV {
 public:
  static v8::Handle<v8::Value> isArray(const v8::Arguments& args);
  static v8::Handle<v8::Value> isNumber(const v8::Arguments& args);

 private:
  SimpleV();
  ~SimpleV();
};

#endif

SimpleVerify.cc

#define BUILDING_NODE_EXTENSION
#include <node.h>
#include "SimpleVerify.h"

using namespace v8;

SimpleV::SimpleV() {};
SimpleV::~SimpleV() {};

Handle<Value> SimpleV::isArray(const Arguments& args) {//定义是否是数组
  HandleScope scope;
  return scope.Close(Boolean::New(args[0]->IsArray()));
}

Handle<Value> SimpleV::isNumber(const Arguments& args) {//定义是否是数字
  HandleScope scope;
  return scope.Close(Boolean::New(args[0]->IsNumber()));
}

代码很简单,看完官网的例子和初步了解V8手册以后很容易看懂,不多解释了。

目前SimpleV这个类只有判断是否是数组和是否是数字2个方法,这2个方法都可以在v8手册上看到,当然我打算继续完善这个模块,根据node-validate的功能山寨一把,全部搬成C++的。 估计有同学说,你费这么大劲搞c++的验证模块,到底效果如何?有用吗? 数据说明一切,下面我们针对这2种情况做一下简单的测试,测试代码如下:

var sv = module.exports = require('./lib/verify.js');

//利用纯js语法进行判断
var ia = Array.isArray;
console.time('js');
for(var j=0;j<1000;j++){
 ia([]);
 ia('111');
 isNaN(123);
 isNaN('abc');
}
console.timeEnd('js')


//利用c++模块进行判断
var ia2 = sv.isArray;
var in2 = sv.isNumber;
console.time('c++');
for(var j=0;j<1000;j++){
 ia2([]);
 ia2('111');
 in2(123);
 in2('abc');
}
console.timeEnd('c++');

测试环境:linux redhat虚拟机,不多介绍了,具体测试结果可能跟机器配置有关系

我们看一下多次测试的结果:

js: 10ms
c++: 3ms


js: 9ms
c++: 1ms


js: 7ms
c++: 2ms


js: 8ms
c++: 0ms


js: 6ms
c++: 0ms

我们可以看到尽管我们利用了js原生的isNaN和isArray这2个方法,但是从性能上来说,无疑C++验证的更快一些。

可能有同学说循环1000次是否有些过了,但是我最近打算用node做接口服务,所以同时有100个接口被查询,每个接口有5-10个参数被验证的情况是可能存在的,如果我们使用c++的addon验证模块可以至少快5ms,当然如果一些正则等等判断可能差距的更多。

最后补一个小教训,我在一开始先创建了binding.gyp,没有把SimpleVerify.cc文件包含进去,导致每次编译都通过,但是调用一直报错,搞了好一阵子才发现,给大家提个醒啊,不要像我这么笨了,哎~

感谢小型笨蛋的系列文章让我学到很多,javascript里有个C google v8开发手册地址 博客原文,用C++写node(一) 博客原文,用C++写node(四) 里面有一些官网实例的代码分析,不知道对不对,希望指教啊 最后发现,不知觉我的粉丝有100啦,哈哈,大家多多关注啊


8 回复

初探Nodejs的c++ addon

最近我打算把公司的接口服务前端换成Node.js,以减少负载服务器的数量并节约成本。同时,我也想把Node.js作为公司的重要技术之一引入到项目中。之前我们主要使用.NET和PHP。

Node.js本身已经非常高效了,但我还想进一步提升其性能。因此,我考虑使用C++模块来增强Node.js的性能。为了实现这一目标,我首先需要设置好开发环境,并安装node-gyp工具。

安装node-gyp

首先确保Node.js版本在0.8.x以上,然后安装node-gyp

npm install -g node-gyp

接着安装Python 2.7.3,因为Node-gyp依赖于Python 2.7.3:

# CentOS 安装Python
sudo yum install python27

# 或者直接下载Python 2.7.3
wget http://www.python.org/download/releases/2.7.3/

完成这些步骤后,我们的开发环境就搭建好了,可以开始编写C++模块了。

编写C++模块

接下来,我们创建一个简单的C++模块,用于验证参数类型。假设我们有一个目录结构如下:

verify
/index.js
/package.json
/readme.md
/lib
    /verify.js
/addon
    /binding.gyp
    /SimpleVerify.cc
    /SimpleVerify.h
    /verify.cc

我们将编写几个C++文件来实现验证功能。

verify.cc

#define BUILDING_NODE_EXTENSION
#include <node.h>
#include "SimpleVerify.h"

using namespace v8;

void Init(Handle<Object> target) {
  target->Set(String::NewSymbol("isArray"),
              FunctionTemplate::New(SimpleV::isArray)->GetFunction());

  target->Set(String::NewSymbol("isNumber"),
              FunctionTemplate::New(SimpleV::isNumber)->GetFunction());
}

NODE_MODULE(verify, Init)

SimpleVerify.h

#ifndef SV_H
#define SV_H

#include <node.h>

class SimpleV {
public:
  static v8::Handle<v8::Value> isArray(const v8::Arguments& args);
  static v8::Handle<v8::Value> isNumber(const v8::Arguments& args);

private:
  SimpleV();
  ~SimpleV();
};

#endif

SimpleVerify.cc

#define BUILDING_NODE_EXTENSION
#include <node.h>
#include "SimpleVerify.h"

using namespace v8;

SimpleV::SimpleV() {}
SimpleV::~SimpleV() {}

Handle<Value> SimpleV::isArray(const Arguments& args) {
  HandleScope scope;
  return scope.Close(Boolean::New(args[0]->IsArray()));
}

Handle<Value> SimpleV::isNumber(const Arguments& args) {
  HandleScope scope;
  return scope.Close(Boolean::New(args[0]->IsNumber()));
}

测试性能

现在我们有了一个简单的C++模块,可以通过以下JavaScript代码进行测试:

var sv = require('./lib/verify.js');

// 利用纯JS语法进行判断
console.time('js');
for (var j = 0; j < 1000; j++) {
  Array.isArray([]);
  Array.isArray('111');
  isNaN(123);
  isNaN('abc');
}
console.timeEnd('js');

// 利用C++模块进行判断
console.time('c++');
for (var j = 0; j < 1000; j++) {
  sv.isArray([]);
  sv.isArray('111');
  sv.isNumber(123);
  sv.isNumber('abc');
}
console.timeEnd('c++');

运行这段代码后,可以看到C++模块的性能明显优于纯JavaScript。例如:

js: 10ms
c++: 3ms

尽管循环1000次可能显得有些极端,但考虑到实际应用场景中的高并发请求,这种性能提升还是非常有意义的。例如,在处理大量接口请求时,每秒节省几毫秒的时间可能会显著提高整体性能。

希望这篇文章能帮助你更好地理解和使用Node.js的C++ addon。


哇0.0

谁说v8的js堪比二进制的,还是慢很多嘛。

我擦,牛掰啊

好文章,作为一个起步,值得研究

初学好文,赞一个

初学好文,赞一个

5年后我重新测试了以上代码, 由于的v8的缓存机制, js_c++ - isNum loop 100000 js: 1.367ms c++: 2.890ms

因为上述判断也是用的v8的库,所用到的IsNumber等方法没有意义,比v8直接调用还要慢。 如果把循环写到C++扩展模块内部 js: 4.367ms c++: 1.200 ms

所以说,写底层模块用v8的方法判断类型是没有意义的,因为本质上还是通过v8管理内存的。 那么C++模块用作什么呢?一定是脱离了v8虚拟机方法的计算密集任务。

初探Nodejs的c++ addon

如果你希望进一步提升Node.js的性能,并且想要更高效地处理某些计算密集型任务,那么编写C++插件(addons)是一个不错的选择。C++插件能够直接与Node.js绑定,并利用V8引擎的高性能。

1. 环境搭建

首先,你需要确保已安装Node.js和Node-gyp:

npm install -g node-gyp

接着,安装Python 2.7.3,这是Node-gyp所必需的:

# CentOS 安装 Python 2.7.3 示例
sudo yum install gcc gcc-c++ openssl-devel python27

# 设置默认Python版本为2.7.3
sudo alternatives --set python /usr/bin/python2.7

2. 创建C++插件

假设我们要创建一个名为verify的插件,该插件用于验证传入参数的基本类型,如数组、数字等。

文件结构
verify
├── binding.gyp
├── addon
│   ├── SimpleVerify.cc
│   ├── SimpleVerify.h
│   └── verify.cc
└── lib
    └── verify.js
binding.gyp
{
  "targets": [
    {
      "target_name": "verify",
      "sources": [ "addon/SimpleVerify.cc", "addon/SimpleVerify.h", "addon/verify.cc" ]
    }
  ]
}
SimpleVerify.h
#ifndef SV_H
#define SV_H
#include <node.h>

class SimpleV {
public:
  static v8::Local<v8::Value> isArray(const v8::FunctionCallbackInfo<v8::Value>& args);
  static v8::Local<v8::Value> isNumber(const v8::FunctionCallbackInfo<v8::Value>& args);
private:
  SimpleV();
  ~SimpleV();
};
#endif
SimpleVerify.cc
#include "SimpleVerify.h"

using namespace v8;

SimpleV::SimpleV() {}
SimpleV::~SimpleV() {}

void SimpleV::isArray(const FunctionCallbackInfo<v8::Value>& args) {
  Isolate* isolate = args.GetIsolate();
  args.GetReturnValue().Set(args[0]->IsArray());
}

void SimpleV::isNumber(const FunctionCallbackInfo<v8::Value>& args) {
  Isolate* isolate = args.GetIsolate();
  args.GetReturnValue().Set(args[0]->IsNumber());
}
verify.cc
#include <node.h>
#include "SimpleVerify.h"

using namespace v8;

void Init(Handle<Object> exports) {
  NODE_SET_METHOD(exports, "isArray", SimpleV::isArray);
  NODE_SET_METHOD(exports, "isNumber", SimpleV::isNumber);
}

NODE_MODULE(verify, Init)

3. 使用插件

在Node.js中加载并使用这个插件:

const verify = require('./build/Release/verify');

console.log(verify.isArray([]));  // true
console.log(verify.isNumber(123)); // true

通过上述步骤,你可以开始尝试编写自己的C++插件。这种方式虽然复杂,但可以显著提高程序性能,尤其是在处理大量数据时。

回到顶部