在V8中的JS中缓慢删除对象属性 [英] Slow delete of object properties in JS in V8

查看:36
本文介绍了在V8中的JS中缓慢删除对象属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

只是为了训练自己一些Typescript,我写了一个基于普通JS对象的类似于ES6 Map + Set的简单实现.它仅适用于原始键,因此没有存储桶,没有哈希码等.我遇到的问题是实现delete方法.使用普通的 delete 速度慢得令人无法接受.对于大型地图,它比ES6地图删除要慢300-400倍.我注意到,如果对象的大小很大,性能将会大大降低.在Node JS 7.9.0(例如Chrome 57)上,如果对象具有50855个属性,则 delete 的性能与ES6 Map相同.但是对于50856个属性,ES6 Map的速度要快2个数量级.这是要重现的简单代码:

Just to train myself a bit of Typescript I wrote a simple ES6 Map+Set-like implementation based on plain JS Object. It works only for primitive keys, so no buckets, no hash-codes, etc. The problem I encountered is implementing delete method. Using plain delete is just unacceptably slow. For large maps it's about 300-400x slower than ES6 Map delete. I noticed the huge performance degradation if size of the object is large. On Node JS 7.9.0 (and Chrome 57 for example) if object has 50855 properties delete performance is the same as ES6 Map. But for 50856 properties the ES6 Map is faster on 2 orders of magnitude. Here is the simple code to reproduce:

// for node 6: 76300
// for node 7: 50855
const N0 = 50855;

function fast() {
	const N = N0

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

	const t1 = Date.now()
	for ( let i = 0; i < N; i++ ) {
		delete o[i]
	}
	const t2 = Date.now()

	console.log( N / (t2 - t1) + ' KOP/S' )
}

function slow() {
	const N = N0 + 1 // adding just 1

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

	const t1 = Date.now()
	for ( let i = 0; i < N; i++ ) {
		delete o[i]
	}
	const t2 = Date.now()

	console.log( N / (t2 - t1) + ' KOP/S' )
}

fast()

slow()

我想我可以代替 delete 属性,而是将它们设置为 undefined 或一些保护对象,但这会弄乱代码,因为 hasOwnProperty 将无法正常工作, for ... in 循环将需要进行其他检查,依此类推.还有更好的解决方案吗?

I guess I could instead of delete properties just set them to undefined or some guard object, but this will mess the code, because hasOwnProperty will not work correctly, for...in loops will need additional check and so on. Are there more nice solutions?

P.S.我正在OSX Sierra上使用节点7.9.0

P.S. I'm using node 7.9.0 on OSX Sierra

已编辑感谢您的评论,我修复了OP/S => KOP/S.我想我问了一个错误指定的问题,所以我更改了标题.经过一番调查,我发现例如在Firefox中不存在此类问题-删除成本呈线性增长.因此这是超级智能V8的问题.而且我认为这只是一个错误:(

Edited Thanks for comments guys, I fixed OP/S => KOP/S. I think I asked rather badly specified question, so I changed the title. After some investigation I found out that for example in Firefox there is no such problems -- deleting cost grows linearly. So it's problem of super smart V8. And I think it's just a bug:(

推荐答案

(此处为V8开发人员.)是的,这是一个已知问题.潜在的问题是,当对象太稀疏时,应将其元素后备存储从平面数组切换到字典,并且这种实现方式的历史记录是针对每个 delete 操作检查是否有足够的元素仍然存在,以使 not 尚未发生.数组越大,此检查花费的时间越多.在某些条件下(最近创建的对象在一定大小以下),该检查被跳过了-结果令人印象深刻的加速就是您在 fast()情况下所观察到的.

(V8 developer here.) Yes, this is a known issue. The underlying problem is that objects should switch their elements backing store from a flat array to a dictionary when they become too sparse, and the way this has historically been implemented was for every delete operation to check if enough elements were still present for that transition not to happen yet. The bigger the array, the more time this check took. Under certain conditions (recently created objects below a certain size), the check was skipped -- the resulting impressive speedup is what you're observing in the fast() case.

我已经利用这个机会来修复常规/慢速路径的(坦率地说很愚蠢)行为.时常检查一次,而不是对每个 delete 进行一次检查就足够了.该修补程序将在V8 6.0中发布,Node应该在几个月后使用它(我相信Node 8应该会在某个时候得到它).

I've taken this opportunity to fix the (frankly quite silly) behavior of the regular/slow path. It should be sufficient to check every now and then, not on every single delete. The fix will be in V8 6.0, which should be picked up by Node in a few months (I believe Node 8 is supposed to get it at some point).

也就是说,在许多情况下使用 delete 会导致各种形式和程度的减速,因为它会使事情变得更加复杂,从而迫使引擎( any 引擎)执行更多检查和/或脱离各种快速路径.通常建议尽可能避免使用 delete .由于您具有ES6映射/集,请使用它们!:-)

That said, using delete causes various forms and magnitudes of slowdown in many situations, because it tends to make things more complicated, forcing the engine (any engine) to perform more checks and/or fall off various fast paths. It is generally recommended to avoid using delete whenever possible. Since you have ES6 maps/sets, use them! :-)

这篇关于在V8中的JS中缓慢删除对象属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆