JavaScript/V8 中的整数数组实际上是作为整数数组实现的吗?
Is an array of ints actually implemented as an array of ints in JavaScript / V8?
this article 中声称 JavaScript 中的整数数组是由 C++ 整数数组实现的。
但是;根据 MDN 除非你专门使用 BigInts,在 JavaScript 中所有数字都被抑制为双精度。
如果我这样做:
cont arr = [0, 1, 2, 3];
V8 引擎中的实际表示是什么?
V8 的代码是here on github,但我不知道去哪里找:
(此处为 V8 开发人员。)
“C++ array of ints”有点简化,但那篇文章描述的关键思想是正确的,数组[0, 1, 2, 3]
将被存储为“Smis”的数组。
什么是“Smi”?虽然 JavaScript 中的每个数字都必须表现得像 IEEE754 double,但 V8 在内部将数字表示为“小整数”(31 位带符号整数值 + 1 位标记),即当数字在范围 -2**30
到 2**30-1
,以提高效率。引擎通常可以在引擎盖下做任何他们想做的事情,只要事情表现得好像实现严格遵守规范。因此,当规范(或 MDN 文档)说“所有数字都是双精度数”时,从引擎(或引擎开发人员)的角度来看,它的真正含义是“所有数字的行为都必须像双精度数一样”。
当数组仅包含 Smis 时,数组本身会跟踪这一事实,因此从此类数组加载的值无需检查即可知道其类型。这很重要,例如对于 a[i] + 1
,当 a
是一个 Smi 数组时,+
的实现不必检查 a[i]
是否是一个 Smi。
当第一个不符合 Smi 范围的数字存储在数组中时,它将转换为双精度数组(严格来说仍然不是 "C++ 数组",而是垃圾收集堆上的自定义数组,但它类似于 C++ 数组,因此这是解释它的好方法。
当第一个非 Number 存储在数组中时,会发生什么取决于数组之前处于什么状态:如果它是一个“Smi 数组”,那么它只需要忘记它只包含 Smis 的事实。不需要重写,因为 Smis 是有效的对象指针,这要归功于它们的标记位。如果数组之前是“双数组”,那么它确实要重写,让每个元素都是一个有效的对象指针。此时,所有双精度值都将被“装箱”为所谓的“堆编号”(托管堆上仅包装双精度值的对象)。
总而言之,我想指出,在绝大多数情况下,无需担心任何这些内部实现技巧,甚至无需注意它们。我当然理解你的好奇心!此外,数组表示是不考虑实现细节的微基准测试很容易产生误导的更常见原因之一,因为它会建议不会转移到更大应用程序的结果。
处理评论:
V8 does sometimes even use int16 or lower.
不,不是。将来可能会也可能不会开始这样做;尽管如果有任何变化,我猜未标记的 int32 比 int16 更有可能被引入;此外,如果实施有任何变化,那么当然可观察到的行为将 不会 改变。
如果您认为您的应用程序会受益于 int16 存储,您可以使用 Int16Array
来强制执行它,但一定要衡量这是否真的对您有利,因为它很可能不会,甚至可能会降低性能,具体取决于关于您的应用对其数组执行的操作。
It may start to be a double when you make it a decimal
稍微准确一点:smis数组需要转为double数组有几个原因,比如:
- 在其中存储一个小数值,例如
0.5
- 在其中存储很大的值,例如
2**34
- 在其中存储
NaN
或 Infinity
或 -0
this article 中声称 JavaScript 中的整数数组是由 C++ 整数数组实现的。
但是;根据 MDN 除非你专门使用 BigInts,在 JavaScript 中所有数字都被抑制为双精度。
如果我这样做:
cont arr = [0, 1, 2, 3];
V8 引擎中的实际表示是什么?
V8 的代码是here on github,但我不知道去哪里找:
(此处为 V8 开发人员。)
“C++ array of ints”有点简化,但那篇文章描述的关键思想是正确的,数组[0, 1, 2, 3]
将被存储为“Smis”的数组。
什么是“Smi”?虽然 JavaScript 中的每个数字都必须表现得像 IEEE754 double,但 V8 在内部将数字表示为“小整数”(31 位带符号整数值 + 1 位标记),即当数字在范围 -2**30
到 2**30-1
,以提高效率。引擎通常可以在引擎盖下做任何他们想做的事情,只要事情表现得好像实现严格遵守规范。因此,当规范(或 MDN 文档)说“所有数字都是双精度数”时,从引擎(或引擎开发人员)的角度来看,它的真正含义是“所有数字的行为都必须像双精度数一样”。
当数组仅包含 Smis 时,数组本身会跟踪这一事实,因此从此类数组加载的值无需检查即可知道其类型。这很重要,例如对于 a[i] + 1
,当 a
是一个 Smi 数组时,+
的实现不必检查 a[i]
是否是一个 Smi。
当第一个不符合 Smi 范围的数字存储在数组中时,它将转换为双精度数组(严格来说仍然不是 "C++ 数组",而是垃圾收集堆上的自定义数组,但它类似于 C++ 数组,因此这是解释它的好方法。
当第一个非 Number 存储在数组中时,会发生什么取决于数组之前处于什么状态:如果它是一个“Smi 数组”,那么它只需要忘记它只包含 Smis 的事实。不需要重写,因为 Smis 是有效的对象指针,这要归功于它们的标记位。如果数组之前是“双数组”,那么它确实要重写,让每个元素都是一个有效的对象指针。此时,所有双精度值都将被“装箱”为所谓的“堆编号”(托管堆上仅包装双精度值的对象)。
总而言之,我想指出,在绝大多数情况下,无需担心任何这些内部实现技巧,甚至无需注意它们。我当然理解你的好奇心!此外,数组表示是不考虑实现细节的微基准测试很容易产生误导的更常见原因之一,因为它会建议不会转移到更大应用程序的结果。
处理评论:
V8 does sometimes even use int16 or lower.
不,不是。将来可能会也可能不会开始这样做;尽管如果有任何变化,我猜未标记的 int32 比 int16 更有可能被引入;此外,如果实施有任何变化,那么当然可观察到的行为将 不会 改变。
如果您认为您的应用程序会受益于 int16 存储,您可以使用 Int16Array
来强制执行它,但一定要衡量这是否真的对您有利,因为它很可能不会,甚至可能会降低性能,具体取决于关于您的应用对其数组执行的操作。
It may start to be a double when you make it a decimal
稍微准确一点:smis数组需要转为double数组有几个原因,比如:
- 在其中存储一个小数值,例如
0.5
- 在其中存储很大的值,例如
2**34
- 在其中存储
NaN
或Infinity
或-0