V8 如何为 Map 实现原始值键?
How does V8 implement primitive-valued keys for `Map`s?
我假设 V8 使用散列来使用对象作为 Map
s 中的键。
const a = {};
new Map([[a, "a"]]);
问题第 1 部分:V8 如何使用原始值键实现映射,如下所示?
const a = {};
new Map([[a, "a"], [1, 100], [2, 200])
问题第 2 部分:
像这样的地图怎么样,它只包含 个原始值键,这些键都是同一类型?
new Map([[0, 0], [1, 100], [2, 200])
据我所知,将数字映射到数字的最有效方法是使用向量,因此上面的映射可以像这样实现:
[0, 100, 200]
V8 是否进行任何类型的专业化
按照这些思路?
我进行了基准测试,将 Map 与数字数组进行比较,发现 number[] 快了 37%(大致符合预期),但我不确定到底发生了什么:https://jsperf.com/map-number-number-vs-array。
请注意,在基准测试中,数组和映射是动态构建的,而不是像上面示例中那样一次性构建。
这里是 V8 开发人员。 V8 以相同的方式实现所有 Map
:它们是散列映射,并且计算键的散列。为 0
、1
等数字计算哈希值非常简单,因此没有特别的挑战。
V8 不会根据仅查看特定类型的键来执行任何特殊外壳。这个想法是:如果你使用 Map
,你就是在向引擎发出你想要哈希映射实现的信号,因为你将拥有任意键并希望它们都得到同样的处理。另一方面,如果你知道你的密钥总是一组密集的小数字,你可以使用 Array
而不是 Map
,这确实会更有效(就特定情况下的内存和性能)。给你更多的力量!
简短的回答是 V8 没有专门针对原始值的映射(或集合)。现在让我们深入探讨一下。
首先,我们需要了解Maps使用了什么样的算法。 “经典”散列 tables 不会这样做,因为它们不提供任何迭代顺序保证,而 ES6 规范要求插入按迭代顺序进行。因此,V8 中的地图构建于 so-called deterministic hash tables 之上。这个想法类似于经典算法,但是桶还有另一层间接层,所有条目都被插入并存储在一个固定大小的连续数组中。
考虑到这一点,对于 Smis V8 可能存储 key/value in-place,但对于更大的数字、字符串、对象和其他东西,哈希 table 条目将存储指针.
至于基准,这是一个有争议的话题,但我希望与 Map 相比,普通 fixed-size 数组在读取方面效率更高。那是因为它不涉及散列的开销 table,如散列码计算和在桶链上移动。
最后,如果您仍然对 V8 地图的幕后工作方式感到好奇,您可能会找到更多详细信息 here。前一段时间我对这个话题很感兴趣,并试图以一种可读的方式分享我的发现。
我假设 V8 使用散列来使用对象作为 Map
s 中的键。
const a = {};
new Map([[a, "a"]]);
问题第 1 部分:V8 如何使用原始值键实现映射,如下所示?
const a = {};
new Map([[a, "a"], [1, 100], [2, 200])
问题第 2 部分:
像这样的地图怎么样,它只包含 个原始值键,这些键都是同一类型?
new Map([[0, 0], [1, 100], [2, 200])
据我所知,将数字映射到数字的最有效方法是使用向量,因此上面的映射可以像这样实现:
[0, 100, 200]
V8 是否进行任何类型的专业化 按照这些思路?
我进行了基准测试,将 Map 与数字数组进行比较,发现 number[] 快了 37%(大致符合预期),但我不确定到底发生了什么:https://jsperf.com/map-number-number-vs-array。
请注意,在基准测试中,数组和映射是动态构建的,而不是像上面示例中那样一次性构建。
这里是 V8 开发人员。 V8 以相同的方式实现所有 Map
:它们是散列映射,并且计算键的散列。为 0
、1
等数字计算哈希值非常简单,因此没有特别的挑战。
V8 不会根据仅查看特定类型的键来执行任何特殊外壳。这个想法是:如果你使用 Map
,你就是在向引擎发出你想要哈希映射实现的信号,因为你将拥有任意键并希望它们都得到同样的处理。另一方面,如果你知道你的密钥总是一组密集的小数字,你可以使用 Array
而不是 Map
,这确实会更有效(就特定情况下的内存和性能)。给你更多的力量!
简短的回答是 V8 没有专门针对原始值的映射(或集合)。现在让我们深入探讨一下。
首先,我们需要了解Maps使用了什么样的算法。 “经典”散列 tables 不会这样做,因为它们不提供任何迭代顺序保证,而 ES6 规范要求插入按迭代顺序进行。因此,V8 中的地图构建于 so-called deterministic hash tables 之上。这个想法类似于经典算法,但是桶还有另一层间接层,所有条目都被插入并存储在一个固定大小的连续数组中。
考虑到这一点,对于 Smis V8 可能存储 key/value in-place,但对于更大的数字、字符串、对象和其他东西,哈希 table 条目将存储指针.
至于基准,这是一个有争议的话题,但我希望与 Map 相比,普通 fixed-size 数组在读取方面效率更高。那是因为它不涉及散列的开销 table,如散列码计算和在桶链上移动。
最后,如果您仍然对 V8 地图的幕后工作方式感到好奇,您可能会找到更多详细信息 here。前一段时间我对这个话题很感兴趣,并试图以一种可读的方式分享我的发现。