Javascript V8中JS中对象属性的缓慢删除

Javascript V8中JS中对象属性的缓慢删除,javascript,performance,v8,Javascript,Performance,V8,为了训练自己打字,我写了一篇文章。它只适用于原语键,所以没有bucket,没有散列码,等等。我遇到的问题是实现delete方法。使用纯delete速度慢得令人无法接受。对于大型地图,它比ES6地图删除慢大约300-400倍。我注意到,如果对象的大小很大,性能会大大降低。在节点JS 7.9.0(例如Chrome 57)上,如果对象具有50855个属性delete性能与ES6 Map相同。但对于50856属性,ES6映射在两个数量级上更快。下面是要复制的简单代码: //对于节点6:76300 //

为了训练自己打字,我写了一篇文章。它只适用于原语键,所以没有bucket,没有散列码,等等。我遇到的问题是实现delete方法。使用纯
delete
速度慢得令人无法接受。对于大型地图,它比ES6地图删除慢大约300-400倍。我注意到,如果对象的大小很大,性能会大大降低。在节点JS 7.9.0(例如Chrome 57)上,如果对象具有50855个属性
delete
性能与ES6 Map相同。但对于50856属性,ES6映射在两个数量级上更快。下面是要复制的简单代码:

//对于节点6:76300
//节点7:50855
常数N0=50855;
函数fast(){
常数N=N0
常数o={}
for(设i=0;i
回答“为什么向N添加1会减慢删除操作”的问题

我的猜测是:慢的原因是内存分配给
对象的方式

尝试将代码更改为:

(() => {
    const N = 50855

    const o = {}
    for ( let i = 0; i < N; i++ ) {
        o[i] = i
    }

    // Show the heap memory allocated
    console.log(process.memoryUsage().heapTotal);
    const t1 = Date.now()
    for ( let i = 0; i < N; i++ ) {
        delete o[i]
    }
    const t2 = Date.now()

    console.log( N / (t2 - t1) + ' OP/S' )
})();
(()=>{
常数N=50855
常数o={}
for(设i=0;i
现在,当您使用
N=50855
运行时,分配的内存是:“8306688字节”(8.3MB)

使用
N=50856
运行时,分配的内存为:“8929280字节”(8.9MB)

因此,仅通过向对象添加一个键,分配的内存大小就增加了600kb

现在,我说我“猜测”这就是慢度的来源,但我认为删除函数随着对象大小的增加而变慢是有意义的

如果您尝试使用
N=70855
,您仍然会使用相同的8.9MB。这是因为内存分配器通常以固定的“批”分配内存,同时增加数组/对象的大小以减少内存分配的数量

现在,同样的事情可能发生在
delete
GC
上。您删除的内存必须由GC拾取,如果对象大小更大,
GC
将更慢。此外,如果钥匙的数量低于特定的数字,内存可能会被释放

(如果您想了解更多,您应该阅读有关动态阵列的内存分配的内容;有一篇关于内存分配应该使用什么增长率的文章很酷,我找不到它:()

PS:
delete
不是“非常慢”,你只是计算出了错误的运算速度。所经过的时间是以毫秒为单位的,而不是以秒为单位的,因此你必须乘以
1000
(这里是V8开发者。)是的,这是一个已知的问题。潜在的问题是,当对象变得太稀疏时,对象应该将其元素从平面数组切换到字典,而历史上的实现方式是每次
delete
操作都检查是否有足够的元素存在,以避免发生转换然而,数组越大,此检查所用的时间就越长。在某些情况下(最近创建的对象小于某个大小),此检查被跳过——由此产生的令人印象深刻的加速就是您在
fast()
案例中观察到的

我已经利用这个机会修复了常规/慢速路径的行为(坦率地说相当愚蠢)。偶尔检查一下就足够了,而不是在每个
delete
。修复将在V8 6.0中进行,Node应该在几个月内完成(我相信Node 8应该会在某个时候完成)


也就是说,在许多情况下,使用
delete
会导致各种形式和程度的减速,因为这会使事情变得更复杂,迫使发动机(任何发动机)要执行更多的检查和/或从各种快速路径上掉下来。通常建议尽可能避免使用
delete
。因为您有ES6映射/集,请使用它们!:-)

我在操作系统X上的Chrome中看到了相同的效果。相关:是的,
delete
被认为是一种不同的测量工具:@Mike McCaughan谢谢,看起来很惊奇,谢谢,编辑代码snipplet OP/S=>KOP/S。但这并不重要。x300-400的性能下降很严重。我认为问题在于V8中对象的智能实现中的bug。或者,V8作者可能认为不再使用对象作为哈希表,而是已经使用ES6映射了!是2K17:)谢谢!我认为这澄清了整件事:)@jmrk感谢您的回复。你能澄清你对使用地图/集合的最后评论吗?我有几个对象一直在更改,代码使用delete。我最好使用地图和地图。删除(项目)?@leonormes:是的,这是建议。使用
Map
会告诉引擎“我将把这个东西作为一个Map来使用,所以不要费心去检测什么用例来优化它的内部表示”。那么,您是说我们应该避免
delete
来节省CPU资源吗?但是我们不是失去了内存资源吗?或者是因为在这种特殊情况下,由于某些内部引擎的行为,CPU的节省是如此之大,以至于我们应该牺牲内存。对于其他非数组情况,可以使用
delete
释放内存吗?我会非常感谢你的