node.js为什么用c++插件迭代数组效率变差了
Why is the efficiency worse when I use c++ addon to iterate an array in node.js
我是 node.js c++ 插件的初学者,我正在尝试实现一个与 Array.prototype.map 功能相同的 c++ 插件。
但是在我完成这个之后,我对我的插件进行了基准测试,发现它比 Array.prototype.map 功能差 100 倍。而且比我直接在js里用for循环还差。
这是我的代码:
// addon.cc
#include <napi.h>
Napi::Value myFunc(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
Napi::Array arr = info[0].As<Napi::Array>();
Napi::Function cb = info[1].As<Napi::Function>();
// iterate over array and call callback for each element
for (uint32_t i = 0; i < arr.Length(); i++) {
arr[i] = cb.Call(env.Global(), {arr.Get(i)});
}
return arr;
}
Napi::Object Init(Napi::Env env, Napi::Object exports) {
return Napi::Function::New(env, myFunc);
}
NODE_API_MODULE(addon, Init)
var addon = require("bindings")("addon")
for (let i = 0; i < 10; i++) {
const arr1 = [];
while (arr1.length < 10000000) {
arr1.push(Math.random());
}
const arr2 = [];
while (arr2.length < 10000000) {
arr2.push(Math.random());
}
const arr3 = [];
while (arr3.length < 10000000) {
arr3.push(Math.random());
}
console.time("map");
const a = arr1.map((cur) => cur * 2);
console.timeEnd("map");
console.time("myAddon");
const b = addon(arr2, (i) => i * 2);
console.timeEnd("myAddon");
const c = [];
console.time('for');
for (let i = 0; i < arr3.length; i++) {
c.push(arr3[i] * 2);
}
console.timeEnd('for');
console.log('--------------')
}
- 我的基准测试结果:
map: 411.9ms
myAddon: 3.220s
for: 218.143ms
--------------
map: 363.966ms
myAddon: 2.841s
for: 86.077ms
--------------
map: 481.605ms
myAddon: 2.819s
for: 75.333ms
--------------
- 我尝试在 c++ 代码的
for
循环期间对我的插件和 return 执行时间进行计时,return 它到 js 并输出值。
- 确实
for
循环耗时很长,但是为什么呢?c++不应该比js快吗?
- 有什么方法可以提高我的插件效率吗?
(此处为 V8 开发人员。)
这是意料之中的。原因是跨越 C++ 和 JavaScript 之间的边界(在任一方向)都比较昂贵。这就是为什么在 V8 中,我们不在 C++ 中实现 Array.map
和类似的 built-in 函数;然而,我们用来实现这一点的内部技术(“CodeStubAssembler”、“Torque”)不适用于 Node 插件。
It's really that the for loop cost a long time, but why?
这不是 for
循环本身,而是 cb.Call(...)
表达式。
Shouldn't c++ faster than js?
不,不一定。优化后的 JS 可以一样快。在极少数情况下,当您创建一个动态优化比静态优化更强大的场景时,它甚至可以更快。在手头的例子中,这与哪种语言更快无关,而是关于 cross-language 函数调用。
And is there any way to improve my addon efficiency?
写在JavaScript。或者找到一种方法来减少任一方向上任何回调的调用(即既不从 C++ 为每个数组元素调用 JS,也不从 JS 为每个数组元素调用 C++)。对于数字代码,TypedArrays 有时对此很有用,具体取决于您的用例。
旁注:为了更公平的比较,基于普通旧 JavaScript for
循环的第三种情况应该预分配结果数组,即将 const c = []
替换为 const c = new Array(arr3.length)
,以及 c.push
和 c[i] =
。这样速度会快一倍。
我是 node.js c++ 插件的初学者,我正在尝试实现一个与 Array.prototype.map 功能相同的 c++ 插件。
但是在我完成这个之后,我对我的插件进行了基准测试,发现它比 Array.prototype.map 功能差 100 倍。而且比我直接在js里用for循环还差。
这是我的代码:
// addon.cc
#include <napi.h>
Napi::Value myFunc(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
Napi::Array arr = info[0].As<Napi::Array>();
Napi::Function cb = info[1].As<Napi::Function>();
// iterate over array and call callback for each element
for (uint32_t i = 0; i < arr.Length(); i++) {
arr[i] = cb.Call(env.Global(), {arr.Get(i)});
}
return arr;
}
Napi::Object Init(Napi::Env env, Napi::Object exports) {
return Napi::Function::New(env, myFunc);
}
NODE_API_MODULE(addon, Init)
var addon = require("bindings")("addon")
for (let i = 0; i < 10; i++) {
const arr1 = [];
while (arr1.length < 10000000) {
arr1.push(Math.random());
}
const arr2 = [];
while (arr2.length < 10000000) {
arr2.push(Math.random());
}
const arr3 = [];
while (arr3.length < 10000000) {
arr3.push(Math.random());
}
console.time("map");
const a = arr1.map((cur) => cur * 2);
console.timeEnd("map");
console.time("myAddon");
const b = addon(arr2, (i) => i * 2);
console.timeEnd("myAddon");
const c = [];
console.time('for');
for (let i = 0; i < arr3.length; i++) {
c.push(arr3[i] * 2);
}
console.timeEnd('for');
console.log('--------------')
}
- 我的基准测试结果:
map: 411.9ms
myAddon: 3.220s
for: 218.143ms
--------------
map: 363.966ms
myAddon: 2.841s
for: 86.077ms
--------------
map: 481.605ms
myAddon: 2.819s
for: 75.333ms
--------------
- 我尝试在 c++ 代码的
for
循环期间对我的插件和 return 执行时间进行计时,return 它到 js 并输出值。 - 确实
for
循环耗时很长,但是为什么呢?c++不应该比js快吗? - 有什么方法可以提高我的插件效率吗?
(此处为 V8 开发人员。)
这是意料之中的。原因是跨越 C++ 和 JavaScript 之间的边界(在任一方向)都比较昂贵。这就是为什么在 V8 中,我们不在 C++ 中实现 Array.map
和类似的 built-in 函数;然而,我们用来实现这一点的内部技术(“CodeStubAssembler”、“Torque”)不适用于 Node 插件。
It's really that the for loop cost a long time, but why?
这不是 for
循环本身,而是 cb.Call(...)
表达式。
Shouldn't c++ faster than js?
不,不一定。优化后的 JS 可以一样快。在极少数情况下,当您创建一个动态优化比静态优化更强大的场景时,它甚至可以更快。在手头的例子中,这与哪种语言更快无关,而是关于 cross-language 函数调用。
And is there any way to improve my addon efficiency?
写在JavaScript。或者找到一种方法来减少任一方向上任何回调的调用(即既不从 C++ 为每个数组元素调用 JS,也不从 JS 为每个数组元素调用 C++)。对于数字代码,TypedArrays 有时对此很有用,具体取决于您的用例。
旁注:为了更公平的比较,基于普通旧 JavaScript for
循环的第三种情况应该预分配结果数组,即将 const c = []
替换为 const c = new Array(arr3.length)
,以及 c.push
和 c[i] =
。这样速度会快一倍。