<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Nodejs 的 C++ 拓展開發(fā)

          共 5131字,需瀏覽 11分鐘

           ·

          2021-10-26 01:55

          大廠技術(shù)??高級前端??Node進(jìn)階

          點(diǎn)擊上方?程序員成長指北,關(guān)注公眾號

          回復(fù)1,加入高級Node交流群

          Nodejs 模塊機(jī)制

          首先在開始之前先簡單介紹一下 Nodejs 里面的模塊引入機(jī)制。

          1. Node.js 核心模塊

          例如 fs、net、path 這樣的模塊,代碼在 nodejs 源碼(lib目錄下)中,通過 API 來暴露給開發(fā)者,這些核心模塊都有自己的預(yù)留標(biāo)識,當(dāng) require() 函數(shù)傳入的標(biāo)識和核心模塊相同時(shí),就會返回核心模塊的 API。

          const fs = require('fs');

          2. 文件模塊

          文件模塊則分為兩種方式:

          2.1 第三方模塊

          這些模塊以 Nodejs 依賴包的形式存在。例如一些常見的 npm 包 axios、webpack等等。

          Nodejs require 這一類模塊的話是會去找該模塊項(xiàng)目下面的 package.json 文件,如果 package.json 文件合法,則會解析 main 字段的那個(gè)路徑。

          當(dāng) require() 函數(shù)中傳入一個(gè)第三方模塊,例如 axios,那么 Nodejs 對于尋找這個(gè) axios 目錄的路徑的過程是這樣的:

          1. 去當(dāng)前文件目錄下 node_modules 中找

          2. 沒找到就去當(dāng)前文件父目錄下的 node_modules 中找

          3. 還沒找到就再往上一層

          4. 還沒找到就重復(fù)3,直到找到符合的模塊或者根目錄為止

          以一個(gè) monorepo 項(xiàng)目為例子,一般在 monorepo 中一些包管理工具例如 yarn workspace 下會把一些依賴提升到外層的目錄中來,那么子項(xiàng)目就是這樣去尋找外層的依賴的:

          node_modules axios here

          packages

          package-a

          node_modules axios not found here

          index.js -> const axios = require('axios');
          2.2 項(xiàng)目模塊

          在項(xiàng)目中執(zhí)行 require() 來載入 "/"、"./" 或者 "../" 開頭的模塊就是項(xiàng)目模塊。這里根據(jù)相對路徑或者絕對路徑所指向的模塊去進(jìn)行加載。通過加載模塊的時(shí)候如果不指定后綴名,Nodejs 則會通過枚舉去嘗試后綴名。后綴名依次是 .js 、.json 和 .node ,其中 .node后綴的文件就是 C++ 拓展。

          例如目錄下有個(gè) addon.node 文件,我們可以 require 去加載(nodejs 是默認(rèn)支持的):

          const addon = require('./addon');

          什么是 Nodejs C++ 拓展

          本質(zhì)

          Node.js 是基于 C++ 開發(fā)的(底層用 chrome v8 做 js 引擎 && libuv 完成事件循環(huán)機(jī)制),因此它的所有底層頭文件暴露的 API 也都是適用于 C++ 的。

          上一節(jié)中提到 nodejs 模塊尋徑的時(shí)候會默認(rèn)找 .node 為后綴名的模塊,實(shí)際上這是個(gè) C++ 模塊的二進(jìn)制文件,即編譯好之后的 C++ 模塊,本質(zhì)上是個(gè)動態(tài)鏈接庫。例如 (Windows dll/Linux so/Unix dylib)

          在 Nodejs 在調(diào)用原生的 C++ 函數(shù)和調(diào)用 C++ 拓展函數(shù)的本質(zhì)區(qū)別在于前者的代碼會直接編譯成 Node.js 可執(zhí)行文件,而后者則是在動態(tài)鏈接庫中。

          C++ 拓展加載方式

          C++ 拓展的加載過程源碼可以參考:

          https://github.com/nodejs/node/blob/master/src/node_binding.cc#L415


          通過 uv_dlopen 這個(gè)方法去加載動態(tài)鏈接庫文件來完成

          C++ 拓展模塊(.node二進(jìn)制鏈接庫文件)的具體加載過程:

          • 在用戶首次執(zhí)行 require 時(shí)使用 uv_dlopen 來加載cpp addon 的 .node 鏈接庫文件

          • 鏈接庫內(nèi)部把模塊注冊函數(shù)賦值給 mp

          • 將執(zhí)行 require 時(shí)傳入的 module 和 exports 兩個(gè)對象傳入模塊注冊函數(shù)(mp 實(shí)例)進(jìn)行導(dǎo)出

          相關(guān)加載代碼參考:

          void DLOpen(const FunctionCallbackInfo& args) {

          Environment* env = Environment::GetCurrent(args);

          uv_lib_t lib;



          ...



          Local<Object> module = args[0]->ToObject(env->isolate());

          node::Utf8Value filename(env->isolate(), args[1]);



          // 使用 uv_dlopen 函數(shù)打開 .node 動態(tài)鏈接庫

          const bool is_dlopen_error = uv_dlopen(*filename, &lib);



          // 將加載出來的動態(tài)鏈接庫的句柄轉(zhuǎn)移給 node_module 的實(shí)例對象上來

          node_module* const mp = modpending;

          modpending = nullptr;



          ...



          // 最后把一些

          mp->nm_dso_handle = lib.handle;

          mp->nm_link = modlist_addon;

          modlist_addon = mp;



          Local<String> exports_string = env->exports_string();



          // exports_string 其實(shí)就是 `"exports"`

          // 這句的意思是 `exports = module.exports`

          Local<Object> exports = module->Get(exports_string)->ToObject(env->isolate());



          // exports 和 module 傳給模塊注冊函數(shù)導(dǎo)出出去

          if (mp->nm_context_register_func != nullptr) {

          mp->nm_context_register_func(exports, module, env->context(), mp->nm_priv);

          } else if (mp->nm_register_func != nullptr) {

          mp->nm_register_func(exports, module, mp->nm_priv);

          } else {

          uv_dlclose(&lib);

          env->ThrowError("Module has no declared entry point.");

          return;

          }

          }


          為什么要寫 C++ 拓展

          • C++ 比 js 高效

            • 相同意思的代碼,在 js 解釋器中執(zhí)行 js 代碼效率比直接執(zhí)行一個(gè) Cpp 編譯好后的二進(jìn)制文件要低(后續(xù)會用 demo 驗(yàn)證)

          • 一些已有的 C++ 輪子可以拿來用

            • 例如一些常用的算法市面上只有 Cpp 實(shí)現(xiàn)且代碼太過復(fù)雜,用 JS 實(shí)現(xiàn)不現(xiàn)實(shí)(例如 Bling Hashes 字符串 hash 摘要算法、Open SDK)

          • 一些系統(tǒng)底層 API 或者 V8 API 沒法通過 js 調(diào)用,可以封裝一個(gè) cpp addon 出來(例如:?緩解Node.js因生成heap snapshot導(dǎo)致進(jìn)程退出的一種辦法)

          缺點(diǎn):

          • 開發(fā)維護(hù)成本比較高,需要掌握一門 native 語言

          • 增加了 native addon 的編譯流程以及拓展發(fā)布流程

          發(fā)展歷史

          這里介紹幾種開發(fā) Nodejs 拓展的方式:

          原始方式

          這種方式比較暴力,直接使用 nodejs 提供的原生模塊來開發(fā)頭文件,例如在 C++ 代碼中直接使用 Nodejs 相關(guān)的各種 API 以及 V8 的各種 API。需要開發(fā)者對 nodejs 以及 v8 文檔比較熟悉。而且隨著相關(guān) API 迭代導(dǎo)致無法跨版本去進(jìn)行使用。

          NAN

          Native Abstractions for Node.js,即 Node.js 原生模塊抽象接口集

          本質(zhì)上是一堆宏判斷,在上層針對 libuv 和 v8 的 API 做了一些兼容性的處理,對用戶側(cè)而言是比較穩(wěn)定的 API 使用,缺點(diǎn)是不符合 ABI(二進(jìn)制應(yīng)用接口) 穩(wěn)定,對于不同版本的 Node.js 每次即使每次重新安裝了 node_modules 之后還需要對 C++ 代碼進(jìn)行重新編譯以適應(yīng)不同版本的Nodejs,即代碼只需要編寫一次,但需要使用者去到處編譯。

          N-API

          N-API 相比于 NAN 則是將 Nodejs 中底層所有的數(shù)據(jù)結(jié)構(gòu)都黑盒處理了,抽象成 N-API 中的接口。

          不同版本的 Node.js 去使用這些接口,都是穩(wěn)定的、ABI 化的。使得在不同的 Node.js 版本下,代碼只需要編譯一次就可以直接使用,不需要去重新進(jìn)行編譯。在 Nodev8.x 時(shí)發(fā)布。

          • 以 C 語言風(fēng)格提供穩(wěn)定的 ABI 接口

          • 消除 Node.js 版本差異

          • 消除 js 引擎差異(例如 Chrome v8、Microsoft ChakraCore 等)

          Node-Addon-API

          目前 Node.js 社區(qū)推崇的寫 Cpp addon 的方式,實(shí)際上是基于 N-API 的一層 C++ 封裝(本質(zhì)上還是 N-API)。

          支持的最早版本是 Nodev10.x(在 v10.x 之后逐步穩(wěn)定)。

          • API 更簡單

          • 文檔良心,編寫和測試都更方便

          • 官方維護(hù)

          今天介紹的也是這種方式來編寫 C++ 拓展。

          準(zhǔn)備工作

          • 安裝 node-gyp

          npm i node-gyp -g

          node-gyp 這里是個(gè) nodejs 官方維護(hù)的 C++ 的構(gòu)建工具,幾乎所有的 Nodejs C++ 拓展都是由它來構(gòu)建。基于 GYP (generate your project,谷歌的一個(gè)構(gòu)建工具)進(jìn)行工作,簡單來說,可以想象成面向 C++ 的 Webpack。

          作用是將 C++ 文件編譯成二進(jìn)制文件(即前面提到的后綴名為 .node 的文件)。

          • node-gyp 附帶的一些依賴環(huán)境(參考官方文檔,以 macos 為例子)

            • Python(一般 unix 系統(tǒng)都會自帶)

          • Xcode

          同時(shí) node-gyp 也需要在項(xiàng)目下有個(gè) binding.gyp 的文件去進(jìn)行配置,寫法上和 json 類似,不過可以在里面寫注釋。

          例如:

          {

          "targets": [

          {

          # 編譯之后的拓展文件名稱,例如這里就是 addon.node

          "target_name": "addon",

          # 待編譯的原 cpp 文件

          "sources": [ "src/addon.cpp" ]

          }

          ]

          }

          一些 demo

          這一節(jié)主要是通過一些簡單的 demo 來入門 C++ Addon 的開發(fā):

          Hello World

          在做好一些準(zhǔn)備工作之后,我們可以先來利用 node-addon-api 開發(fā)一個(gè)簡單的 helloworld

          1. 初始化

          mkdir hello-world && cd hello-world

          npm init -y

          # 安裝 node-addon-api 依賴

          npm i node-addon-api

          # 新建一個(gè) cpp 文件 && js 文件

          touch addon.cpp index.js
          1. 配置 binding.gyp

          {

          "targets": [

          {

          # 編譯出來的 xxx.node 文件名稱,這里是 addon.node

          "target_name": "addon",

          # 被編譯的 cpp 源文件

          "sources": [

          "addon.cpp"

          ],





          # 為了簡便,忽略掉編譯過程中的一些報(bào)錯(cuò)

          "cflags!": [ "-fno-exceptions"],

          "cflags_cc!": ["-fno-exceptions"],



          # cpp 文件調(diào)用 n-api 的頭文件的時(shí)候能找到對應(yīng)的目錄

          # 增加一個(gè)頭文件搜索路徑

          "include_dirs": [

          "require('node-addon-api').include")"

          ],



          # 添加一個(gè)預(yù)編譯宏,避免編譯的時(shí)候并行拋錯(cuò)

          'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ],

          }

          ]

          }
          1. 寫原生的 cpp 拓展

          這里貼兩份代碼,為了便于去做個(gè)區(qū)分比較:

          原生 Node Cpp Addon ?版本:

          // 引用 node.js 中的 node.h 頭文件

          #include



          namespace demo {

          using v8::FunctionCallbackInfo;

          using v8::Isolate;

          using v8::Local;

          using v8::Object;

          using v8::String;

          using v8::Value;



          void Method(const FunctionCallbackInfo& args) {

          // 通過 v8 中的隔離實(shí)例(v8的引擎實(shí)例,有各種獨(dú)立的狀態(tài), 包括推管理、垃圾回收等)

          // 存取 Nodejs 環(huán)境的實(shí)例

          Isolate* isolate = args.GetIsolate();

          // 返回一個(gè) v8 的 string 類型,值為 "hello world"

          args.GetReturnValue().Set(String::NewFromUtf8(ioslate, "hello world"));

          }





          void init(Local<Object> exports) {

          // nodejs 內(nèi)部宏,用于導(dǎo)出一個(gè) function

          // 這里類似于 exports = { "hello": Method }

          NODE_SET_METHOD(exports, "hello", Method);

          }



          // 來自 nodejs 內(nèi)部的一個(gè)宏: https://github.com/nodejs/node/blob/master/src/node.h#L839

          // 用于注冊 addon 的回調(diào)函數(shù)

          NODE_MODULE(addon, init);

          }

          Node-addon-api 版本:

          // 引用 node-addon-api 的 頭文件

          #include



          // Napi 這個(gè)實(shí)際上封裝的是 v8 里面的一些數(shù)據(jù)結(jié)構(gòu),搭建了一個(gè)從 JS 到 V8 的橋梁



          // 定義一個(gè)返回值為 Napi::String 的 函數(shù)

          // CallbackInfo 是個(gè)回調(diào)函數(shù)類型 info 里面存的是 JS 調(diào)用這個(gè)函數(shù)時(shí)的一些信息

          Napi::String Method(const Napi::CallbackInfo& info) {

          // env 是個(gè)環(huán)境變量,提供一些執(zhí)行上下文的環(huán)境

          Napi::Env env = info.Env();



          // 返回一個(gè)構(gòu)造好的 Napi::String 類型的值

          // New是個(gè)靜態(tài)方法,一般第一個(gè)參數(shù)是當(dāng)前執(zhí)行環(huán)境的上下變量,第二個(gè)是對應(yīng)的值

          // 其他參數(shù)不做過多介紹

          return Napi::String::New(env, "hello world~");

          }



          // 導(dǎo)出注冊函數(shù)

          // 這里其實(shí)等同于 exports = { hello: Method }

          Napi::Object Init(Napi::Env env, Napi::Object exports) {

          exports.Set(

          Napi::String::New(env, "hello"),

          Napi::Function::New(env, Method)

          );

          return exports;

          }



          // node-addon-api 中用于注冊函數(shù)的宏

          // hello 為 key, 可以是任意變量

          // Init 則會注冊的函數(shù)

          NODE_API_MODULE(hello, Init);

          這里代碼里面的 Napi:: 命名空間里面的一些類型實(shí)際上是對 v8 原生的一些數(shù)據(jù)結(jié)構(gòu)做了包裝,調(diào)用的時(shí)候更簡單,數(shù)據(jù)結(jié)構(gòu)相關(guān)的文檔可以參考:https://github.com/nodejs/node-addon-api API 文檔那一節(jié)。

          這里的 Napi 本質(zhì)上就是 C++ 和 JS 之間的一座相互溝通的橋梁。

          這里拆分講解一下這些函數(shù)的作用, Method 函數(shù)是我們的一個(gè)執(zhí)行函數(shù),執(zhí)行該函數(shù)會返回一個(gè) "hello world" 的字符串值。

          CallBackInfo 對應(yīng) v8 里面的 FunctionCallbackInfo 類型(里面有一些函數(shù)回調(diào)信息,存在 info 這個(gè)地址里面),里面包含了 JS 函數(shù)調(diào)用這個(gè)方法的時(shí)候需要的一些信息。

          1. 在 js 代碼中調(diào)用 cpp addon

          我們通過對上面的 cpp 進(jìn)行進(jìn)行 node-gyp 的編譯,得到一個(gè) build 的目錄里面存放的是編譯產(chǎn)物,里面會有編譯出來的 二進(jìn)制動態(tài)鏈接文件(后綴名為 .node):

          $ node-gyp configure build



          # 或者為了更簡便一點(diǎn)會直接使用 node-gyp rebuild,這個(gè)命令包含了清除緩存并重新打包的功能

          $ node-gyo rebuild

          編譯之后我們直接在 js 代碼中引入即可:

          // hello-world/index.js

          const { hello } = require('./build/Release/addon');



          console.log(hello());

          A + B

          在上一節(jié)我們講到了 Napi::CallbackInfo& info info 中會存 JS 調(diào)用該函數(shù)時(shí)的一些上下文信息,因此我們在 js 中給 cpp 函數(shù)傳參數(shù)也可以在 info 中獲取到,于是可以寫出下面一個(gè)簡單的 a + b 的 cpp addon demo:

          #include



          // 這里為了做演示,把 Napi 直接通過 using namespace 聲明了

          // 只要該文件不被其他的 cpp 文件引用就不會出現(xiàn) namespace 污染 這里主要為了簡潔

          using namespace Napi;



          // 因?yàn)檫@里可能會遇到拋 error 的情況,因此返回值類型設(shè)置為 Value

          // Value 包含了 Napi 里面的所有數(shù)據(jù)結(jié)構(gòu)

          Value Add(const CallBackInfo& info) {

          Env env = info.Env();



          if (info.Length() < 2) {

          // 異常處理相關(guān)的 API 可以參考

          // 不過這里可以看到 cpp 里面拋異常代碼很麻煩... 建議這里可以在 js 端就處理好

          // https://github.com/nodejs/node-addon-api/blob/main/doc/error_handling.md

          TypeError::New(env, "Number of arg wrong").ThrowAsJavaScriptException();

          return env.Nulll();

          }



          double a = info[0].As<Number>().Doublevalue();

          double b = info[1].As<Number>().DoubleValue();



          Number num = Number::new(env, a + b);

          return num;

          }



          // exports = { add: Add };

          Object Init(Env env, Object exports) {

          exports.Set(String::New(env, "add"), Function::new(env, Add));

          }



          NODE_API_MODULE(addon, Init);

          Js 調(diào)用只需要:

          const { add } = require('./build/Release/addon');



          // output is 5.2

          console.log(add(2, 3.2));

          callback

          回調(diào)函數(shù)也是一樣,通過 info 這個(gè)也可以拿到,再貼個(gè) cpp addon 的 demo:

          // addon.cpp

          #include



          // 這一節(jié)用 namespace 包裹一下,提前聲明一些數(shù)據(jù)結(jié)構(gòu)

          // 省得調(diào)用的時(shí)候一直 Napi::xxx ...

          namespace CallBackDemo {

          using Napi::Value;

          using Napi::CallbackInfo;

          using Napi::Env;

          using Napi::TypeError;

          using Napi::Number;

          using Napi::Object;

          using Napi::String;

          using Napi::Function;



          void RunCallBack(const CallbackInfo &info) {

          Env env = info.Env();

          Function cb = info[0].As<Function>();

          cb.Call(env.Global(), { String::New(env, "hello world") } );

          }



          Object Init(Env env, Object exports) {

          return Function::New(env, RunCallback);

          }



          NODE_API_MODULE(addon, Init);

          }

          實(shí)戰(zhàn) demo

          上面簡單講了一些 node native addon 的簡單 API 使用,算是做了個(gè)簡單的入門教學(xué),下面選了個(gè)簡單的實(shí)際 demo 來看一下 node-addon-api 在具體項(xiàng)目中起到的作用:

          案例展開講一下,封裝了 v8 的 API 用于 debug

          參考案例:緩解Node.js因生成heap snapshot導(dǎo)致進(jìn)程退出的一種辦法

          代碼地址:https://github.com/bytedance/diat/tree/master/packages/addon


          更多 demo

          可以參考:

          Nodejs 官方的 addon examples:?https://github.com/nodejs/node-addon-examples

          Nodejs 來一打 cpp 拓展隨書代碼:?https://github.com/XadillaX/nyaa-nodejs-demo

          性能對比

          可以通過一個(gè)簡單的 Demo 去做一下對比:

          quickSort (O(nlogn))

          我們可以手寫個(gè)快排分別在 JS 或者 CPP 兩邊去 run 一下來對比性能:

          首先我們的 cpp addon 代碼可以這樣寫:

          #include

          #include

          #include



          // 快排 時(shí)間復(fù)雜度 O(nlogn) 空間復(fù)雜度 O(1)

          void quickSort(int a[], int l, int r) {

          if (l >= r) return;

          int x = a[(l + r) >> 1], i = l -1, j = r + 1;

          while (i < j) {

          while (a[++i] < x);

          while (a[--j] > x);

          if (i < j) {

          std::swap(a[i], a[j]);

          }

          }

          quickSort(a, l, j);

          quickSort(a, j + 1, r);

          }



          Napi::Value Main(const Napi::CallbackInfo& info) {

          Napi::Env env = info.Env();



          Napi::Array arr = info[0].AsArray>();



          int len = arr.Length();



          // 存返回值

          Napi::Array res = Napi::Array::New(env, len);



          int* arr2 = new int[len];



          // 轉(zhuǎn)化一下數(shù)據(jù)結(jié)構(gòu)

          for (int i = 0; i < len; i++) {

          Napi::Value value = arr[i];



          arr2[i] = value.ToNumber().Int64Value();

          }



          quickSort(arr2, 0, len - 1);



          // for (int i = 0; i < len; i ++) {

          // std::cout << arr2[i] << " ";

          // }

          // std::cout << std::endl;



          // 轉(zhuǎn)回 JS 的數(shù)據(jù)結(jié)構(gòu)

          for (int i = 0; i < len; i ++) {

          res[i] = Napi::Number::New(env, arr2[i]);

          }

          return res;

          }



          Napi::Object Init(Napi::Env env, Napi::Object exports) {

          exports.Set(

          Napi::String::New(env, "quicksortCpp"),

          Napi::Function::New(env, Main)

          );

          return exports;

          }



          NODE_API_MODULE(addon, Init);

          JS側(cè)的代碼可以這樣寫:

          // 這里使用 bindings 這個(gè)庫,他會幫我們自動去尋找 addon.node 對應(yīng)目錄

          // 不需要再去指定對應(yīng)的 build 目錄了

          const { quicksortCpp } = require('bindings')('addon.node');



          // 構(gòu)造一個(gè)函數(shù)出來

          const arr = Array.from(new Array(1e3), () => Math.random() * 1e4 | 0);



          let arr1 = JSON.parse(JSON.stringify(arr));

          let arr2 = JSON.parse(JSON.stringify(arr));





          console.time('JS');

          const solve = (arr) => {

          let n = arr.length;

          const quickSortJS = (arr, l, r) => {

          if (l >= r) {

          return;

          }

          let x = arr[Math.floor((l + r) >> 1)], i = l - 1, j = r + 1;

          while (i < j) {

          while(arr[++i] < x);

          while(arr[--j] > x);

          if (i < j) {

          [arr[i], arr[j]] = [arr[j], arr[i]];

          }

          }

          quickSortJS(arr, l, j);

          quickSortJS(arr, j + 1, r);

          }

          quickSortJS(arr, 0, n - 1);

          }

          solve(arr2);

          console.timeEnd('JS');





          console.time('C++');

          const a = quicksortCpp(arr1);

          console.timeEnd('C++');

          這里兩側(cè)代碼基本上從實(shí)現(xiàn)上來說都是一模一樣的,在實(shí)際運(yùn)行中,通過去修改數(shù)組的長度對比兩者的效率,我們可以得到如下的數(shù)據(jù):

          數(shù)組長度/時(shí)間1e21e31e41e51e6
          JS0.255ms4.391ms10.810ms26.004ms116.914ms
          C++0.065ms0.347ms2.908ms23.637ms234.757ms

          那么我們可以看到在數(shù)組長度相對而言比較低的時(shí)候,C++ Addon 的快排效率是要完爆 JS 的,但隨著數(shù)組長度的增長,C++ 就呈現(xiàn)一種被完爆的趨勢。

          導(dǎo)致這種情況的原因是因?yàn)?V8 的數(shù)據(jù)結(jié)構(gòu)與 C++ 里面原生的數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)換所帶來的消耗:


          1e5 的數(shù)據(jù)規(guī)模下,實(shí)際上 cpp 的 quickSort 算法只跑了大概 6.9ms,而算上數(shù)據(jù)轉(zhuǎn)換的時(shí)間,一共就跑了 28.9ms......

          隨著數(shù)據(jù)規(guī)模的增大這種轉(zhuǎn)換帶來的開銷就越來越大,因此在這種時(shí)候如果使用 C++ 的話,可能會得不償失。

          綜上來看,有時(shí)候 C++ 寫出來的包確實(shí)會在性能上稍微高于 Nodejs 的 JS 代碼,但如果高出來的這部分性能還比不過 Nodejs 打開并且執(zhí)行 C++ Addon 所消耗掉的 I/O 時(shí)間或者在 v8 數(shù)據(jù)結(jié)構(gòu)與 C++ 數(shù)據(jù)結(jié)構(gòu)之前進(jìn)行轉(zhuǎn)換的所消耗的時(shí)間(例如上面的 Case) ,這個(gè)時(shí)候用 C++ 可能就得不償失了。

          不過一般情況下,針對并非并行 && 計(jì)算密集型代碼來說,C++ 效率還是會好于 Nodejs 的。

          總結(jié)

          隨著 N_API 體系的發(fā)展以及 nodejs 開發(fā)團(tuán)隊(duì)的不斷迭代更新,未來開發(fā) native addon 的成本也會越來越低,在一些特定的場景里面(例如需要用到一些 v8 的 API 場景或者 electron + openCV 場景),nodejs addon 可能會變得極其重要,未來使用場景也會不斷的提高。

          參考資料

          • 《Nodejs,來一打 C++ 拓展》原書

          • https://github.com/nodejs/node-addon-api

          • https://github.com/nodejs/node-addon-examples

          Node 社群


          我組建了一個(gè)氛圍特別好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你對Node.js學(xué)習(xí)感興趣的話(后續(xù)有計(jì)劃也可以),我們可以一起進(jìn)行Node.js相關(guān)的交流、學(xué)習(xí)、共建。下方加 考拉 好友回復(fù)「Node」即可。


          ???“分享、點(diǎn)贊、在看” 支持一波??

          瀏覽 53
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产免费一区二区三区在线 | 嫩草网址 | 青青青草av | 久久婷婷亚洲AV无码专区 | 国产内射无码 |