JavaScript: `delete` 关键字的性能限制

JavaScript: performance constraints of `delete` keyword

我正在尝试更好地了解 JS 的工作原理,我过去听说 delete 关键字(特别是 node.js 或使用 V8 的浏览器)会导致性能不佳,所以我想看看我是否可以弄清楚 benefits/detriments 是什么用于使用该关键字。

我认为不使用 delete 的原因是删除 属性 会导致重建隐藏的 class 转换,从而重新编译内联缓存。但是,我相信对象原型将不再枚举 属性 也是事实,因此如果对象被大量使用,前期成本最终可能会得到回报。

所以:

  1. 我关于权衡的假设是否正确?
  2. 如果它们是正确的,一个因素是否比另一个因素更重要(例如,重建 IC 是否比许多原型枚举贵得多)?

这里是 V8 开发人员。简短回答:"it depends".

有一个未使用的 属性 没有坏处;除非您实际执行显式枚举,否则没有通用的 "enumeration cost"。换句话说,"enumeration cost" 只有在你发现自己在做这样的事情时才会存在:

for (var p in object) {
  if (p === old_property_that_I_could_have_deleted) continue;
  /* process other properties... */
}

很难给出具体答案(或提供一个效果可衡量的典型示例)的关键原因是效果是非本地的:它们都取决于您正在做什么有问题的对象,以及您的应用程序的其余部分正在做什么。从一个对象中删除 属性 可能会导致对其他对象的操作变慢。或者更快。视情况而定。

退后一步,看看高级情况:JavaScript 作为一种语言,假定对象表示为字典。删除字典中的条目应该完全没问题,这就是 delete 运算符存在的原因。在实践中,事实证明引擎可以为读取密集型应用程序实现巨大的性能改进,这是迄今为止最常见的情况,如果它 将对象存储为字典,而是更像是类似于 C/C++ 结构的东西。但是,这样的对象表示是 (1) 通常 hard/inefficient 在属性被删除时执行,并且 (2) 引擎甚至可以将 属性 的第一次删除解释为程序员想要的提示这个特定对象的行为就像一个字典,所以它可能会切换内部表示。如果快速修改字典是您想要的,那很好(它甚至会提供好处);但是,如果您希望对象保持 slow-to-modify/fast-to-read 模式,您会认为转换到 fast-to-modify/slow-to-read 字典模式是一个性能问题。

谢天谢地,现在有一个很好的解决方案:当你需要字典时,使用 MapSet。引擎可以(并且通常会)假设您想要从这些条目中删除条目,因此优化了实现以使其成为可能而没有负面影响;特别是不涉及隐藏的 classes。

关于您的假设的几点说明:删除 属性 会使对象(大部分)离开隐藏的 class 转换系统,不会重建任何转换。没有单一的全局 "inline cache",有许多内联缓存散布在您的函数中。它们不会被重建,它们只是过渡到越来越慢的模式,因为它们必须处理更多不同的情况。 (这通常是缓存的工作原理:缓存单个案例可以提供巨大的加速;另一方面,如果您有与执行一样多的不同案例,那么缓存只会浪费时间和内存,而不会提供任何好处。)字典模式对象取决于整体情况:处理(大部分)字典模式对象的内联缓存通常表现出介于(1)之间的某处性能内联缓存只需要处理共享单个相同隐藏的对象class,以及 (2) 一个内联缓存,它必须处理成百上千个不同的隐藏 classes.