节点。在填充一个int32数组时,JS性能与本机c++ addon相对应。

时间:2022-09-01 19:15:57

I've been experimenting with Node.JS and C++ addons and found that populating an Int32Array is considerably slower when using the C++ addon rather than directly doing so in Node.JS / JavaScript.

我一直在用Node做实验。JS和c++ addons发现,在使用c++ addon时填充Int32Array的速度要比直接在节点中填充要慢得多。JS / JavaScript。

Node.JS: 133 ~ ms
C++: 1103 ~ ms

节点。133 ~ ms c++: 1103 ~ ms。

Does anyone know why this is? My test code consists of a fairly large array and for loops containing if statements.

有人知道为什么吗?我的测试代码由一个相当大的数组和包含if语句的for循环组成。

I suspect I'm populating the array incorrectly in my C++ addon. (?)

我怀疑我在c++ addon中不正确地填充数组。(?)

JavaScript:

JavaScript:

var testArray = new Int32Array(36594368);

var i = 0;
for (var xi = 0; xi < 332; xi++) {
    for (var yi = 0; yi < 332; yi++) {
        for (var zi = 0; zi < 332; zi++) {
            if ((xi + yi + zi) % 62 == 0) testArray[i] = 2;
            else if (yi < 16) testArray[i] = 2;
            else if (yi == 16) testArray[i] = 1;
            else testArray[i] = 0;

            i++;
        }
    }
}

C++ Addon:

c++插件:

Local<Int32Array> testArray = Int32Array::New(ArrayBuffer::New(isolate, 4 * 36594368), 0, 36594368);

int i = 0;
for (int xi = 0; xi < 332; xi++) {
    for (int yi = 0; yi < 332; yi++) {
        for (int zi = 0; zi < 332; zi++) {
            if ((xi + yi + zi) % 62 == 0) testArray->Set(i, Integer::New(isolate, 2));
            else if (yi < 16) testArray->Set(i, Integer::New(isolate, 2));
            else if (yi == 16) testArray->Set(i, Integer::New(isolate, 1));
            else testArray->Set(i, Integer::New(isolate, 0));

            i++;
        }
    }
}

EDIT: Just to add, the functions I'm using in my C++ code are V8 functions and weren't defined by myself. Is there another way to set values in an Int32Array without using these?

编辑:补充一下,我在c++代码中使用的函数是V8函数,不是我自己定义的。是否有另一种方法可以在int32数组中设置值而不使用这些值?

1 个解决方案

#1


8  

Maxing out Typed Array Performance using C++

I'm not surprised this is slow as written, but there is a lot you can do to speed it up. The critical insight is that when dealing with JavaScript typed arrays in node, you can gain access to the memory buffer and operate on that directly.

我并不奇怪这写得这么慢,但是你可以做很多事情来加速它。关键的见解是,当在node中处理JavaScript类型数组时,您可以获得对内存缓冲区的访问并直接对其进行操作。

The main source of slowness

Though when dealing with normal JavaScript arrays/objects, the following are necessary

虽然在处理正常的JavaScript数组/对象时,下面是必要的。

Integer::New(isolate,value)

整数::新(隔离,值)

and

testArray->Set(value)

testArray - >设置(值)

So for example the following line

举个例子。

testArray->Set(i, Integer::New(isolate, 0));

creates a new Number object, converts the integer 0 to a double since all JavaScript numbers are double, calls Set with the Number object, then converts the double back to an integer because it's storing the value in a Int32 typed array, and then destructs the Number object. This happens 3 million times.

创建一个新的数字对象,将整数0转换为double,因为所有的JavaScript数字都是double,用Number对象设置调用,然后将double转换为integer,因为它将值存储在Int32类型化数组中,然后销毁Number对象。这发生了300万次。

An improvement

But typed arrays are different, and the call GetIndexedPropertiesExternalArrayData gives one access to the underlying buffer, which for a Int32Array is a buffer of int. This allows the C++ function to be re-written to avoid all of those allocations and casts:

但是类型化数组是不同的,GetIndexedPropertiesExternalArrayData调用提供了一个对底层缓冲区的访问,对于Int32Array,它是int的一个缓冲区。

void doMkArray(const FunctionCallbackInfo<Value> &args)
{
   v8::Isolate *I = v8::Isolate::GetCurrent();
   Local<Int32Array> testArray = Int32Array::New(ArrayBuffer::New(I, 4 * 36594368),0,36594368);
   int *dptr = (int*)testArray->GetIndexedPropertiesExternalArrayData();

   int i = 0;
   for (int xi = 0; xi < 332; xi++)
   {
      for (int yi = 0; yi < 332; yi++)
      {
         for (int zi = 0; zi < 332; zi++)
         {
            if ((xi + yi + zi) % 62 == 0) dptr[i] = 2;
            else if (yi < 16) dptr[i] = 2;
            else if (yi == 16) dptr[i] = 1;
            else dptr[i] = 0;

            i++;
         }
      }
   }

   args.GetReturnValue().Set(testArray);
}

Some measurements

Replacing with the above makes things faster, but how much faster needs a test. The following package can be cloned and when run (using node 0.12.5) results in the following

用上面的替换可以使事情更快,但是多快需要测试。可以克隆以下包,运行时(使用节点0.12.5)会产生以下结果

  Performance Tests
    ✓ via javascript (169ms)
    ✓ via c++ (141ms)

So that alone makes using C++ faster, but maybe not all that amazing, but if both the Javascript and the C++ loops (see the src) are commented out, and when only the array allocation is included:

仅这一点就使使用c++变得更快,但也许并不是那么令人惊奇,但是如果Javascript和c++循环(参见src)都被注释掉了,并且只包含数组分配:

    void doMkArray(const FunctionCallbackInfo<Value> &args)
    {
       v8::Isolate *I = v8::Isolate::GetCurrent();
       Local<Int32Array> testArray = Int32Array::New(ArrayBuffer::New(I, 4 
/*
...

Then the time changes to

然后时间就变了

  Performance Tests
    ✓ via javascript (62ms)
    ✓ via c++ (80ms)

In other words, simply allocating the array takes approximately 60ms in JavaScript, and 80ms in the C++ module. But that means that the rest of the time is the time spent in the loop, which is approximately 60ms in C++ and 110ms in Javascript. And so for actions that are predominately loops and calculations using direct buffer access, C++ is preferred.

换句话说,简单地分配数组在JavaScript中需要大约60ms,在c++模块中需要80ms。但这意味着剩下的时间是在循环中花费的时间,在c++中大约是60ms, Javascript是110毫秒。因此,对于主要使用直接缓冲区访问进行循环和计算的操作,最好使用c++。

#1


8  

Maxing out Typed Array Performance using C++

I'm not surprised this is slow as written, but there is a lot you can do to speed it up. The critical insight is that when dealing with JavaScript typed arrays in node, you can gain access to the memory buffer and operate on that directly.

我并不奇怪这写得这么慢,但是你可以做很多事情来加速它。关键的见解是,当在node中处理JavaScript类型数组时,您可以获得对内存缓冲区的访问并直接对其进行操作。

The main source of slowness

Though when dealing with normal JavaScript arrays/objects, the following are necessary

虽然在处理正常的JavaScript数组/对象时,下面是必要的。

Integer::New(isolate,value)

整数::新(隔离,值)

and

testArray->Set(value)

testArray - >设置(值)

So for example the following line

举个例子。

testArray->Set(i, Integer::New(isolate, 0));

creates a new Number object, converts the integer 0 to a double since all JavaScript numbers are double, calls Set with the Number object, then converts the double back to an integer because it's storing the value in a Int32 typed array, and then destructs the Number object. This happens 3 million times.

创建一个新的数字对象,将整数0转换为double,因为所有的JavaScript数字都是double,用Number对象设置调用,然后将double转换为integer,因为它将值存储在Int32类型化数组中,然后销毁Number对象。这发生了300万次。

An improvement

But typed arrays are different, and the call GetIndexedPropertiesExternalArrayData gives one access to the underlying buffer, which for a Int32Array is a buffer of int. This allows the C++ function to be re-written to avoid all of those allocations and casts:

但是类型化数组是不同的,GetIndexedPropertiesExternalArrayData调用提供了一个对底层缓冲区的访问,对于Int32Array,它是int的一个缓冲区。

void doMkArray(const FunctionCallbackInfo<Value> &args)
{
   v8::Isolate *I = v8::Isolate::GetCurrent();
   Local<Int32Array> testArray = Int32Array::New(ArrayBuffer::New(I, 4 * 36594368),0,36594368);
   int *dptr = (int*)testArray->GetIndexedPropertiesExternalArrayData();

   int i = 0;
   for (int xi = 0; xi < 332; xi++)
   {
      for (int yi = 0; yi < 332; yi++)
      {
         for (int zi = 0; zi < 332; zi++)
         {
            if ((xi + yi + zi) % 62 == 0) dptr[i] = 2;
            else if (yi < 16) dptr[i] = 2;
            else if (yi == 16) dptr[i] = 1;
            else dptr[i] = 0;

            i++;
         }
      }
   }

   args.GetReturnValue().Set(testArray);
}

Some measurements

Replacing with the above makes things faster, but how much faster needs a test. The following package can be cloned and when run (using node 0.12.5) results in the following

用上面的替换可以使事情更快,但是多快需要测试。可以克隆以下包,运行时(使用节点0.12.5)会产生以下结果

  Performance Tests
    ✓ via javascript (169ms)
    ✓ via c++ (141ms)

So that alone makes using C++ faster, but maybe not all that amazing, but if both the Javascript and the C++ loops (see the src) are commented out, and when only the array allocation is included:

仅这一点就使使用c++变得更快,但也许并不是那么令人惊奇,但是如果Javascript和c++循环(参见src)都被注释掉了,并且只包含数组分配:

    void doMkArray(const FunctionCallbackInfo<Value> &args)
    {
       v8::Isolate *I = v8::Isolate::GetCurrent();
       Local<Int32Array> testArray = Int32Array::New(ArrayBuffer::New(I, 4 
/*
...

Then the time changes to

然后时间就变了

  Performance Tests
    ✓ via javascript (62ms)
    ✓ via c++ (80ms)

In other words, simply allocating the array takes approximately 60ms in JavaScript, and 80ms in the C++ module. But that means that the rest of the time is the time spent in the loop, which is approximately 60ms in C++ and 110ms in Javascript. And so for actions that are predominately loops and calculations using direct buffer access, C++ is preferred.

换句话说,简单地分配数组在JavaScript中需要大约60ms,在c++模块中需要80ms。但这意味着剩下的时间是在循环中花费的时间,在c++中大约是60ms, Javascript是110毫秒。因此,对于主要使用直接缓冲区访问进行循环和计算的操作,最好使用c++。