Search code examples
javascriptinheritanceoptimizationv8

Extending JavaScript arrays in V8 and maintaining PACKED kind


I've read about packed arrays in the V8 engine and would like use them in some performance-critical parts of my game (for example as a container for particles).

I would like to make a custom subclass of Array that will always stay PACKED but it seems it's not possible to extend self with another array.

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);
    }
}

Is there a way to extend sub-classed array instance and maintain the PACKED kind in V8?

I know I can push values one by one or even call this.push(...values), but it's slower than .concat() and fails for large arrays.


Solution

  • V8 developer here. TL;DR: the best thing you can do with "packed elements" is not to worry about them. The difference is almost never measurable.

    In certain microbenchmarks, where there are only a handful of machine instructions in the optimized code for the hot core loop, every single instruction matters, and if the "hole"-check for the current element is one of them (actually two: cmp + je on x86), then tracking which arrays don't need it can affect the benchmark score. But in real-world applications where you do non-trivial operations on the array element, the impact of two machine instructions is not measurable. Any contortions that you go through with your custom wrapper class are most likely more expensive than the minuscule bit of overhead you might be able to save.


    The specific question you ask can be solved by preferring "has-a" over "is-a" composition:

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

    Which would also address the issue that with a subclass, code could still use my_packed_array[10000] = "now you have holes" to side-step the .extend() method. However, keep in mind what I wrote above: the impact of a hole-check is tiny, and any of these extra wrappings probably cost way more than they save.

    EDIT: what @MathiasBynens writes is also a very good point: Don't optimize for V8, let V8 optimize for you! :-)