在V8中扩展JavaScript数组并维护压缩类型

在V8中扩展JavaScript数组并维护压缩类型,javascript,inheritance,optimization,v8,Javascript,Inheritance,Optimization,V8,我已经读过,并希望在我游戏的一些性能关键部分使用它们(例如,作为粒子容器) 我想创建一个数组的自定义子类,它将始终保持打包状态,但似乎不可能用另一个数组扩展self class PackedArray extends Array { constructor() { super(); } extend(values) { // `values` is another array. // This obviously won'

我已经读过,并希望在我游戏的一些性能关键部分使用它们(例如,作为粒子容器)

我想创建一个数组的自定义子类,它将始终保持打包状态,但似乎不可能用另一个数组扩展self

class PackedArray extends Array {
    constructor() {
        super();
    }

    extend(values) {
        // `values` is another array.
        // This obviously won't work, is there another way?
        this = this.concat(values);
    }
}
是否有一种方法可以扩展子类数组实例并在V8中维护压缩类


我知道我可以一个接一个地推送值,甚至可以调用
this.push(…values)
,但它比
.concat()
慢,并且对于大型数组失败。

您可以使用
this.splice()
修改当前数组

extend(values) {
    this.splice(this.length, 0, ...values);
}

您可以使用
this.splice()
修改当前数组

extend(values) {
    this.splice(this.length, 0, ...values);
}

这里是V8开发者。TL;DR:对于“包装元素”,你能做的最好的事情就是不要担心它们。这种差异几乎是无法衡量的

在某些微基准中,热内核循环的优化代码中只有少数机器指令,每一条指令都很重要,如果“洞”——检查当前元素是其中之一(实际上是两条:
cmp
+
je
,在x86上),然后跟踪哪些数组不需要它会影响基准分数。但在对数组元素执行非平凡操作的实际应用程序中,两条机器指令的影响是不可测量的。定制包装器类中的任何扭曲都很可能比您可能节省的极小开销更昂贵


你提出的具体问题可以通过选择“has-a”而不是“is-a”组合来解决:

class PackedArray {
  extend(values) {
    this.#data = this.#data.concat(values);
  }
  get(i) { return this.#data[i]; }

  #data = [];
}
这也解决了一个问题,即对于子类,代码仍然可以使用
my\u packeted\u array[10000]=“now you have holes”
来绕过
.extend()
方法。然而,请记住我在上面写的:孔检查的影响很小,任何这些额外包装的成本都可能比它们节省的成本高


编辑:@MathiasBynens写的也是一个很好的观点:不要为V8优化,让V8为你优化吧!:-)

V8开发者在这里。TL;DR:对于“包装元素”,你能做的最好的事情就是不要担心它们。这种差异几乎是无法衡量的

在某些微基准中,热内核循环的优化代码中只有少数机器指令,每一条指令都很重要,如果“洞”——检查当前元素是其中之一(实际上是两条:
cmp
+
je
,在x86上),然后跟踪哪些数组不需要它会影响基准分数。但在对数组元素执行非平凡操作的实际应用程序中,两条机器指令的影响是不可测量的。定制包装器类中的任何扭曲都很可能比您可能节省的极小开销更昂贵


你提出的具体问题可以通过选择“has-a”而不是“is-a”组合来解决:

class PackedArray {
  extend(values) {
    this.#data = this.#data.concat(values);
  }
  get(i) { return this.#data[i]; }

  #data = [];
}
这也解决了一个问题,即对于子类,代码仍然可以使用
my\u packeted\u array[10000]=“now you have holes”
来绕过
.extend()
方法。然而,请记住我在上面写的:孔检查的影响很小,任何这些额外包装的成本都可能比它们节省的成本高


编辑:@MathiasBynens写的也是一个很好的观点:不要为V8优化,让V8为你优化吧!:-)

wat是您希望推进的潜在阵列的大小?我删除了我的答案,因为我没有读你的最后一句话。你能不能给我们一个测试场景,让我们做点什么?@Icepickle有几十到几千个元素。@Icepickle有很多场景,一些例子:自定义信号实现的事件接收器列表,游戏中实体列表,可渲染精灵列表,等等。在游戏中,每个数组都应该尽可能快,因为它们每秒被访问和迭代数千次。@Icepickle,所以我需要一个通用容器,它可以分块分配内存(扩展自身),并可以在尽可能多的场景中使用。我是您链接到的V8文章的作者。创建一个
PackedArray
子类将V8实现细节与代码混合在一起,这在IMHO中应该避免。一般来说,重点是编写可读、惯用的代码。如果希望避免数组中出现漏洞,则无需为其创建子类——只需使用前面提到的技术即可。请注意,即使是
HOLEY
元素类型也可以优化,因此根据您的用例,性能差异可能不是什么大问题。您是否测量了
HOLEY
packeted
对您的代码的影响?您希望推进的潜在阵列的大小是多少?我删除了我的答案,因为我没有读你的最后一句话。你能不能给我们一个测试场景,让我们做点什么?@Icepickle有几十到几千个元素。@Icepickle有很多场景,一些例子:自定义信号实现的事件接收器列表,游戏中实体列表,可渲染精灵列表,等等。在游戏中,每个数组都应该尽可能快,因为它们每秒被访问和迭代数千次。@Icepickle,所以我需要一个通用容器,它可以分块分配内存(扩展自身),并可以在尽可能多的场景中使用。我是您链接到的V8文章的作者。创建一个
PackedArray
子类将V8实现细节与代码混合在一起,这在IMHO中应该避免。一般来说,重点是编写可读、惯用的代码。如果希望避免数组中出现漏洞,则无需为其创建子类——只需使用前面提到的技术即可。请注意,即使是
HOLEY
元素类型也可以优化,因此根据您的用例,perf-dif