为什么对于整数键,“Map”操作比 JavaScript (v8) 中的“Object”慢得多?

Can*_*ure 8 javascript performance dictionary v8 object

我很高兴Map在我的 JavaScript 代码库中使用索引访问,但我刚刚偶然发现了这个基准:https ://stackoverflow.com/a/54385459/365104

我也在这里重新创建了它: https: //jsben.ch/HOU3g

benchmark 所做的基本上就是用 1M 个元素填充一个映射,然后迭代它们。

我希望 Map 和 Object 的结果是相同的,但它们有很大不同 - 有利于 Object。

这是预期的行为吗?可以解释一下吗?是因为订购要求吗?或者因为map正在做一些键散列?或者只是因为 Map 允许任何对象作为键(我希望它使用指针地址作为键,这不需要任何哈希)?Map 和 Object 索引算法有什么区别?

这是非常出乎意料和令人沮丧的 - 基本上我将不得不恢复到老式的“对象作为地图”编码风格。

更新#1

正如评论中所建议的,对象可能会优化为数组(因为它是从零开始的整数索引)。

将迭代顺序从sizeto 0-Object更改为快 2 倍。当使用字符串作为索引时,Map 的性能提高了 2 倍。

jmr*_*mrk 29

(V8 开发人员在此。)

我将不得不恢复到老式的“对象作为地图”编码风格。

如果您这样做,您将成为误导性微基准的受害者。

在使用连续整数作为键的非常特殊的情况下,普通的Object会更快,是的。在这种情况下,没有什么比连续数组更好的了。因此,如果您提到的“代码库中随处可见的索引访问”确实使用索引集(例如从 0 到 1M 的整数),那么使用对象或数组是一个好主意。但这是一个特殊情况。如果索引空间稀疏,事情看起来就会有所不同。

在以随机顺序使用任意字符串的一般情况下, a 的Map性能明显优于Object。更重要的是,处理此类对象属性访问的方式(在 V8 中,很可能也在其他引擎中)具有非局部影响:如果一个函数对对象属性查找处理系统的缓慢路径施加过大的压力,那么可能会减慢一些依赖相同缓慢路径进行属性访问的其他功能。

根本原因是引擎针对不同的使用模式优化了不同的东西。引擎可以在底层实现几乎相同的对象和地图;但这并不是理想的行为,因为不同的使用模式受益于不同的内部表示和实现选择。因此,引擎允许您向他们提供提示:如果您使用 a Map,引擎会知道您计划将其用作地图(废话!),其中随机键会来来去去。如果您使用Object,那么引擎将(至少在开始时)假设您想要最适合您的平均对象的优化集,其中属性集相当小并且是静态的。如果您使用 an Array(或Object仅使用整数属性,这在 JS 中几乎是相同的),那么您就可以让引擎轻松地为您提供快速的整数索引访问。

使用"x" + ias key 是一个很好的建议,可以证明微基准可以多快地更改,从而产生相反的结果。但这里有一个剧透:如果您(仅)进行此修改,那么您测量的很大一部分将是数字到字符串的转换和字符串内部化,而不是映射/对象访问性能本身。

谨防微基准;它们具有误导性。您确实必须非常深入地分析它们(通过分析,和/或通过检查生成的代码,和/或通过跟踪其他引擎内部)以确保它们正在测量您认为它们正在测量的内容,从而产生结果他们正在告诉你你认为他们正在告诉你的事情。

一般来说,强烈建议使用代表性测试用例进行性能测量。理想情况下,您的应用程序本身;或者通过将其真实部分提取到对真实数据进行操作的测试用例中。如果您无法通过对整个生产应用程序进行压力测试来衡量两种实现选择之间的差异,那么就不值得担心这种差异。通过微基准(即几条人工制作的线),我可以“证明”几乎任何不适用于一般情况的东西。

  • 很高兴知道对于整数来说,还有更快的 Map - Object :) 对于现在 key 是整数的情况,我将使用 Objects 。 (2认同)