如何在 V8 中针对访问时间优化一小部分对象引用?

Jos*_*eph 1 javascript arrays optimization v8 ecmascript-6

我正在开发一款游戏,因此必须考虑小的性能提升和大的性能提升。

我正在阅读 V8 中的数组初始化,特别是这篇文章,它表明使用该格式var myArray = new Array(100)最终比var myArray = []使用小数组更快(作者建议使用少于 ~1000 个元素作为小数组的阈值)。

鉴于一些假设,不难看出原因。特别是,如果数组的元素在优化后都是相同类型的,比如都是整数,它们应该被预先分配在连续的内存中,并且不会导致 V8 改变它的数组内部表示,如这里所述。这也将允许编译器在其他地方使用元素时对形状进行假设。

然而,在我的情况下,我需要一个对象引用数组,并且我能够按顺序初始化它们。所以,继续我们从上面知道的,我想出了这个:

class A {
    constructor() {
        this.particles = new Array(60);
        for (var i = 0; i < particles.length; ++i) {
            this.particles[i] = new Particle();
        }
    }
}
class Particle {
    //...
}
Run Code Online (Sandbox Code Playgroud)

正如第一篇文章所描述的,这是他操作数字类型的最快方法。因此,我假设 V8 假定每个数组的其中一种本机数字类型的大小并为其分配空间。但是,我不确定是哪个。我也不确定对象引用的大小与为每个元素分配的任何本机类型的大小相比如何。

一个较小的问题是,如果默认元素大小分配大于只包含对对象的引用(所有类型都相同,并且都假定在内存中连续)的数组所需的空间,那么每个数组会浪费空间具有此数组初始化策略的元素?有没有办法避免这种情况?如果是这样,是否可以保留访问速度的优化?

优化每个访问的速度,Particle上面的初始化可以改进吗?

jmr*_*mrk 7

V8 开发人员在这里。TL;DR:你所做的很好。

预分配和元素种类跟踪是相互独立的。当您知道需要的大小时,请继续分配具有该容量的数组,无论您要在其中存储何种类型的东西。

也就是说,如果您从一个空数组开始并随着您的进行增长它,那也完全没问题。严格来说,该策略会为增长步骤带来一些额外成本,并且在稍后访问元素时会带来一些好处,因为引擎知道所有元素都存在。但是在绝大多数情况下,整体差异太小了,您应该只做您认为最易读/最方便的事情。

关于您对元素大小的疑惑:在 64 位平台上,所有内容都具有相同的大小(即 64 位 ;-) )。在 32 位平台上,指针是 32 位的,而双精度仍然是 64 位的,但是 V8 不会为更大的元素预先分配空间然后浪费它,所以你不需要担心这个。

关于你的来源:我发现很难理解那篇文章的建议,所以我倾向于忽略它。特别是,呈现的最后一个“技巧”(var a = []; a.length = N;而不是var a = new Array(N);)根本没有任何意义,并且给定的“解释”不适用于这种情况。V8 在引擎盖下有效地执行相同的操作,因此您认为可以测量的任何差异几乎都可以保证是随机噪声。当心微基准测试,因为它们通常具有误导性!

  • Bergi:_dense/packed_ 数组的重点是避免额外的检查。如果 V8 必须做一个“这个索引在密集部分吗?” 检查,这将抵消简单地成为_holey_的大部分可能的好处。此外,_packed_ 和 _holey_ 之间的差异通常很小,因此在该区域投入更多复杂性是不值得的。 (2认同)