数组慢元素删除

时间:2016-05-30 15:38:42

标签: javascript arrays performance

我有数组,我想从头部删除N个元素。

可以说,数组(带浮点数)有1M个元素,我想先500K出去。我有两种方法,在循环中调用500K次调用或调用splice(0,500000)。

问题是,第一个解决方案是一个可怕的想法(它非常非常慢)。第二个也很慢,因为splice在一个新数组中返回从数组中删除的部分(好吧,它只是分配500K浮点数并将它们抛出窗口)。

在我的应用程序中,我正在使用非常大的矩阵做一些事情,不幸的是,通过拼接删除元素对我来说很慢。有没有更快的方法来实现它?

2 个答案:

答案 0 :(得分:2)

我希望Array#slice与其中任何一个选项一样快 ,并且可能更快。它确实意味着暂时分配重复的内存,但1M数量只有大约64MB的内存(假设JavaScript引擎已经能够使用真正的数组),所以暂时拥有原始的64MB加上32MB的内存你想要的在发布原始64MB之前保持相当便宜:

array = array.slice(500000);

这也有一个优点,就是它不会强制JavaScript引擎使用对象而不是封面下的数组。 (你正在做的其他事情可能会导致这种情况,但......)

你说你用浮点数做这个,你可能会看一下使用Float64Array而不是无类型数组。这限制了您可以执行的操作,但确保您不会使用未经优化的数组。从数组中删除条目时,最终可能会得到未经优化的数组,其访问时间明显慢于优化数组,因为它们最终成为具有命名属性而非偏移访问的对象。 (如果可以的话,一个好的JavaScript引擎会对它们进行优化;使用类型化数组会有助于阻止你进行优化。)

这(断开并且肯定存在缺陷)NodeJS测试表明spliceslice慢了60%到95%,并且V8在保持阵列优化方面做得很好类型化数组的结果实际上与slice情况下无类型数组的结果相同:

"use strict";
let sliceStats = createStats();
let sliceTypedStats = createStats();
let spliceStats = createStats();
for (let c = 0; c < 100; ++c) {
    if (test(buildUntyped, sliceStats, testSlice).length != 500000) throw new Error("1");
    if (test(buildTyped, sliceTypedStats, testSlice).length != 500000) throw new Error("2");
    if (test(buildUntyped, spliceStats, testSplice).length != 500000) throw new Error("3");
    console.log(c);
}
console.log("slice     ", avg(sliceStats.sum, sliceStats.count));
console.log("sliceTyped", avg(sliceTypedStats.sum, sliceTypedStats.count));
console.log("splice    ", avg(spliceStats.sum, spliceStats.count));

function avg(sum, count) {
    return (sum / count).toFixed(3);
}

function createStats() {
    return {
        count: 0,
        sum:   0
    };
}
function buildUntyped() {
    let a = [];
    for (let n = 0; n < 1000000; ++n) {
        a[n] = Math.random();
    }
    return a;
}

function buildTyped() {
    let a = new Float64Array(1000000);
    for (let n = 0; n < 1000000; ++n) {
        a[n] = Math.random();
    }
    return a;
}

function test(build, stats, f) {
    let a;
    let ignore = 0;
    let start = Date.now();
    for (let i = 0; i < 10; ++i) {
        let s = Date.now();
        a = build();
        ignore += Date.now() - s;
        a = f(a);
    }
    stats.sum += Date.now() - start - ignore;
    ++stats.count;
    return a;
}

function testSlice(a) {
    return a.slice(500000);
}
function testSplice(a) {
    a.splice(0, 500000);
    return a;
}

答案 1 :(得分:1)

Immutable.js通过结构共享解决了这个问题。它不会像splice那样复制条目,但会在包含的内容中返回引用 阵列的一部分。您需要将数组移动到Immutable.js数据结构,然后调用不可变操作splice