In node v14.3.0, I discovered (while doing some coding work with very large arrays) that sub-classing an array can cause .slice() to slow down by a factor 20x. While, I could imagine that there might be some compiler optimizations around a non-subclassed array, what I do not understand at all is how .slice() can be more than 2x slower than just manually copying elements from one array to another. That does not make sense to me at all. Anyone have any ideas? Is this a bug or is there some aspect to this that would/could explain it?
For the test, I created a 100,000,000 unit array filled with increasing numbers. I made a copy of the array with .slice() and I made a copy manually by then iterating over the array and assigning values to a new array. I then ran those two tests for both an Array and my own empty subclass ArraySub. Here are the numbers:
Running with Array(100,000,000)
sliceTest: 436.766ms
copyTest: 4.821s
Running with ArraySub(100,000,000)
sliceTest: 11.298s
copyTest: 4.845s
The manual copy is about the same both ways. The .slice() copy is 26x slower on the sub-class and more than 2x slower than the manual copy. Why would that be?
And, here's the code:
// empty subclass for testing purposes
class ArraySub extends Array {
}
function test(num, cls) {
let name = cls === Array ? "Array" : "ArraySub";
console.log(`--------------------------------\nRunning with ${name}(${num})`);
// create array filled with unique numbers
let source = new cls(num);
for (let i = 0; i < num; i++) {
source[i] = i;
}
// now make a copy with .slice()
console.time("sliceTest");
let copy = source.slice();
console.timeEnd("sliceTest");
console.time("copyTest");
// these next 4 lines are a lot faster than this.slice()
const manualCopy = new cls(num);
for (let [i, item] of source.entries()) {
manualCopy[i] = item;
}
console.timeEnd("copyTest");
}
[Array, ArraySub].forEach(cls => {
test(100_000_000, cls);
});
FYI, there's a similar result in this jsperf.com test when run in the Chrome browser. Running the jsperf in Firefox shows a similar trend, but not as much of a difference as in Chrome.
slice()is checking for getter/setter methods..slice()would have to do more work than the manual assignment loop?FastSlice(), but is beyond what I can follow.