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

我在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

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可以利用引擎盖下的对象移动开始 – 在快速的情况下,你可以看到这个技巧提供了惊人的加速。 但是,由于技术原因,这个技巧不能应用于超过一定大小的数组。 由此产生的“放缓”实际上是这个非常昂贵的操作的“真正的”速度。

如果要快速删除数组元素,请从末尾( array[array.length - 1] )删除它们,例如使用Array.pop() 。 如果要一次删除所有元素,只需设置array.length = 0 。 如果您需要快速FIFO /“队列”语义,请考虑从环形缓冲区获取灵感:为要读取/返回的下一个元素提供一个“光标”,并且只有在有大量元素被释放时才缩小arrays。 大致:

 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; } 

注意:这里没关系,但是为了logging,把70,000个元素放到你的数组中,你的循环应该从0开始: for (var i = 0; i < 70000; i++) {...} 正如所写,你只推69,999个元素。

附注2:通过“parseInt”将double舍入为整数非常慢,因为它首先将double设置为string格式,然后将该string作为整数读取。 更快的方法是Math.floor(Math.random() * 10000)) 。 (为了这个testing的目的,你也可以简单地推i 。)

有趣的是,我做了类似的事情

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

在第二循环内。 (这是计算的痛苦,但它会帮助诊断问题)

我没有遭受性能损失。 但是,我认为,我发现为什么“只有”2000更多的事情是难以节点。 首先 – 我的区别:[循环最大数量,单位,3基准结果]

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

好吧,当我看到日志logging时,我看到,最后的10k条logging就像瞬间计算一样。 我认为splice从前面删除了1个项目,然后 – 逐个将array 1的索引移动到“开始”,让我们将testing更改为每10个logging10个数组,以查看它是否会好得多。 我会以最懒惰的方式做到这一点:

 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); 

而且,是的,我没有回答为什么exacly你的电脑有这样的性能损失70k和72klogging之间 – 我相信这是依赖于机器…可能缺乏内存,但不要误解 – 我不'不知道。

我解决了如何改善。 10个arrays执行时间的100k(-10)大约是73-74毫秒。 我想,你可以把它写入二维数组,并修改逻辑来计算长度和其他东西,如你所愿。

感谢关注。

Interesting Posts