# 代码优化:Node.js C++插件

Node.js 插件是用 C++ 编写的动态链接共享对象,require 函数可以将插件加载为普通的 Node.js 模块。插件提供了 JavaScript 和 C/C++ 库之间的接口。

基础文档:http://nodejs.cn/api/addons.html

进阶文档:https://nodeaddons.com/

将计算量转移到 C++ 进行:

  • 收益:C++ 运算比 Javascript 更快的部分
  • 成本:C++ 变量与 V8 变量的转换

# 大致流程

  • 编写 C++ 脚本
  • 编写 binding.gyp 构建配置文件
  • 使用 node-gyp 编译 C++ 脚本
  • 在JS中引用 C++ 插件

# node-gyp

Javascript是解释型语言,运行时通过V8编译就可以执行。

当 C++ 源代码已被编写,它必须被编译成二进制 addon.node 文件。要做到这点,需在项目的顶层创建一个名为 binding.gyp 的文件,它使用一个类似 JSON 的格式来描述模块的构建配置。 该文件会被 node-gyp(一个用于编译 Node.js 插件的工具)使用。

# binding.gyp

binding.gyp 文件以类似于JSON的格式描述了构建模块的配置。该文件与 package.json 一起放在包的根目录中。

{
  "targets": [
    {
      "target_name": "binding",
      "sources": [ "src/binding.cc" ]
    }
  ]
}

# 示例

binding.gyp 文件的一些示例的链接:https://github.com/nodejs/node-gyp/wiki/%22binding.gyp%22-files-out-in-the-wild

# Node.js头文件

node-gyp 编译时还需要依赖 Node.js头文件,安装 Node.js头文件包后,node-gyp 才能编译对应版本的Node.js的 C++ 插件。

# 强关联

C++ 模块编译成的二进制 addon.node 文件,是与平台强关联的。在Unix下编译的 addon.node 文件只能在Unix下使用。在macOS下编译的 addon.node 文件只能在macOS下使用。windows也是同理。通常情况下,在Linux目标服务器下安装编译环境就行了。只是会带来不好的开发体验。

此外,C++ 模块编译成的二进制 addon.node 文件与Node.js的版本是强关联的。如果在Linux下编译的是v12版本的插件,那么如果Node.js环境是v10,那么该 C++ 插件编译后的代码是无法正常运行的。因此,在编译时需要通过 Node.js头文件 指定合适的 Node.js 版本。

# N-API

N-API在node8.0以后开始加入支持,目的在于解决node跨版本之间原生模块的兼容问题,原生模块无法兼容不同版本的Node,需要重新编译。N-API做了一层抽象,实现了跨版本支持。

# C++ 扩展一定比 JavaScript 快吗?

Node.js 非常适合 IO 密集型的应用,而对于 计算密集 的业务,很多人都会想到用编写 C++ Addon 的方式来优化性能。但实际上 C++ 扩展并不是灵丹妙药,V8 的性能也没有想象的那么差。

JavaScript 在 V8 上跑得比 C++ 扩展还快,这种情况多半发生在与 字符串正则表达式 相关的场景,因为 V8 内部使用的正则表达式引擎是 irregexp,这个正则表达式引擎比 boost 中自带的引擎(boost::regex)要快得多。

还有一处值得注意的就是,Node.js 的 C++ 扩展在进行类型转换的时候,可能会消耗非常多的性能,如果不注意 C++ 代码的细节,性能会很大地下降。

换句话说,C++ 是否比 JavaScript 更加高效需要具体问题具体分析,某些情况下,C++ 扩展不一定就会比原生 JavaScript 更高效。

# 参考

更新时间: 6/29/2020, 6:30:01 PM