node.js拼接速度太慢,超过70000个项目

时间:2017-09-27 15:06:38

标签: arrays node.js v8

我是node.js的新手。

我试图将70000个项目插入数组,然后删除所有项目:

var Stopwatch = require("node-stopwatch").Stopwatch;
var stopwatch = Stopwatch.create();


var a = []
stopwatch.start();

for (var i = 1 ; i < 70000 ; i++){
    a.push((parseInt(Math.random() * 10000)) + "test");
}

for (var i = 1 ; i < 70000 ; i++){
    a.splice(0,1);
}

stopwatch.stop();

console.log("End: " + stopwatch.elapsedMilliseconds + " : " + a.length);

工作正常,输出为:

PS C:\Users\Documents\VSCode> node test.js
End: 51 : 0

但是当我将项目数量增加到72000时,结束需要花费太多时间:

var Stopwatch = require("node-stopwatch").Stopwatch;
var stopwatch = Stopwatch.create();


var a = []
stopwatch.start();

for (var i = 1 ; i < 72000 ; i++){
    a.push((parseInt(Math.random() * 10000)) + "test");
}

for (var i = 1 ; i < 72000 ; i++){
    a.splice(0,1);
}

stopwatch.stop();

console.log("End: " + stopwatch.elapsedMilliseconds + " : " + a.length);

输出是:

End: 9554 : 0

为什么会这样?只增加了2000个项目,但需要花费太多时间。

Node.js版本是:v6.11.3

2 个答案:

答案 0 :(得分:3)

V8开发者在这里。在开始时(array[0])删除(或插入)数组元素通常非常昂贵,因为必须移动所有剩余的元素。基本上,对于这些.splice(0, 1)操作中的每一个,引擎必须做的事情是:

for (var j = 0; j < a.length - 1; j++) {
  a[j] = a[j+1];
}
a.length = a.length - 1`

在某些情况下,V8可以在引擎盖的底部使用一个技巧 - 而在快速的情况下,你可以看到这个技巧提供的惊人的加速。但是,由于技术原因,此技巧不能应用于超出特定大小的阵列。由此产生的&#34;减速&#34;实际上是&#34; true&#34;这种非常昂贵的操作的速度。

如果您想快速删除数组元素,请从最后删除它们(array[array.length - 1]),例如使用Array.pop()。如果您想一次性删除所有元素,只需设置array.length = 0即可。如果你需要快速FIFO /&#34;队列&#34;语义,考虑从环形缓冲区中获取灵感:有一个&#34;光标&#34;对于要读取/返回的下一个元素,只有当有大量元素被释放时才收缩数组。大致是:

function Queue() {
  this.enqueue = function(x) {
    this.array_.push(x);
  }
  this.dequeue = function() {
    var x = this.array_[this.cursor_++];
    // Free up space if half the array is unused.
    if (this.cursor_ > this.array_.length / 2) {
      this.array_.splice(0, this.cursor_);
      this.cursor_ = 0;
    }
    return x;
  }
  this.array_ = [];
  this.cursor_ = 0;
}

旁注:这并不重要,但为了记录,要将70,000个元素推入数组,您的循环应从0开始:for (var i = 0; i < 70000; i++) {...}。如上所述,你只推动了69,999个元素。

附注2:通过&#34; parseInt&#34;将double舍入为整数非常慢,因为它首先将double格式化为字符串,然后将该字符串作为整数读回。更快的方法是Math.floor(Math.random() * 10000))。 (出于本次测试的目的,您也可以简单地推送i。)

答案 1 :(得分:0)

有趣的是,我做了类似

的事情
if (i % 1000 === 0) {
    console.log(i + " " + stopwatch.elapsedMilliseconds + " : " + a.length);
}

在第二个循环内。 (这很难计算,但它有助于诊断问题)

我没有遭受性能损失。但是,我认为,我找到了为什么&#34;只有&#34; 2000更多的事情要做的就是节点很难。首先 - 我的区别: [loop max num,unit,3 benchmarks results]

70k:[ms] ~26k,~25.7k,~26k 72k:[ms] ~25.6k,27k,25.7k

好的,当我看到日志记录时,我看到,最后的10k记录就像瞬间计算一样。我认为splice从前面移除1个项目,然后 - 逐个移动数组1索引&#34;到开始&#34;,让我们将测试更改为10个数组10k记录,看它是否会好得多。我会以最懒惰的方式做到这一点:

var Stopwatch = require("node-stopwatch").Stopwatch;
var stopwatch = Stopwatch.create();

var a1 = [], a2 = [], a3 = [], a4 = [], a5 = [];
var a6 = [], a7 = [], a8 = [], a9 = [], a10 = [];

stopwatch.start();

function fill (arr) {
    for (var i = 1 ; i < 10000 ; i++){
        arr.push((parseInt(Math.random() * 10000)) + "test");
    }
}

fill(a1); fill(a2); fill(a3); fill(a4); fill(a5);
fill(a6); fill(a7); fill(a8); fill(a9); fill(a10);

let removeCount = 0;
function unfill(arr) {
    for (var i = 1 ; i < 10000 ; i++){
        arr.splice(0,1);
        removeCount++;

        if (i % 1000 === 0) {
            console.log(i + " " + stopwatch.elapsedMilliseconds + " : " + arr.length);
        }
    }
}

unfill(a1); unfill(a2); unfill(a3); unfill(a4); unfill(a5);
unfill(a6); unfill(a7); unfill(a8); unfill(a9); unfill(a10);

stopwatch.stop();

console.log("End: " + stopwatch.elapsedMilliseconds + " removeCount " + removeCount);

而且,是的...我没有回答为什么你的电脑在70k到72k的记录之间有这样的性能损失 - 我相信它是依赖于机器的......可能缺少RAM,但是没有得到它错了 - 我不知道。

我解决了如何改善这一点。 10个阵列上的100k(-10)执行时间约为73-74毫秒。我认为,您可以将其写入2d数组并修改逻辑以根据需要计算长度和剩余部分。

感谢您的关注。