为什么array.includes比javascript中的set.has快一个数量级?

时间:2019-04-11 19:28:24

标签: javascript performance lookup

在C ++中长大后,我一直很清楚哪种算法适合哪种算法。因此,当我注意到该应用程序开始在手机上运行缓慢时,我立即开始查看数据结构及其表示方式。

我注意到Array.includes的效果非常奇怪,比Set.has快了一个数量级。即使Set.has可以为查找进行优化的潜力更大:这是使用集合的全部想法。

我的初始化代码是(此代码不在测试的时间范围内):

function shuffle(a) {
    for (let i = a.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [a[i], a[j]] = [a[j], a[i]];
    }
}

const arr = []
for (let i = 0; i < 1000; i+=1) {
    arr.push(i);
};

shuffle(arr);
const prebuildset=new Set(arr);

测试是:

(new Set(arr)).has(-1); //20.0 kOps/s
arr.includes(-1); //632 kOps/s
(new Set(arr)).has(0); //20.0 kOps/s
arr.includes(0); //720 kOps/s
prebuildset.has(-1); //76.7 kOps/s
prebuildset.has(0); //107 kOps/s

在Ubuntu 18.04上使用https://jsperf.com/set-array-has-test/1在Chrome 73.0.3683.103上进行了测试

我可以期望动态创建集合的版本比直接测试包含数组的版本要慢。 (尽管我想知道为什么chrome不通过JIT来优化数组-我还测试了使用文字数组和文字数组与使用变量在速度上根本无关紧要)。 但是,即使是预构建集,其速度也比数组包含测试慢一个数量级:即使对于最否定的情况(条目不在数组内部)。

这是为什么?什么样的黑魔法正在发生?

编辑:我更新了测试以改写结果,以免歪斜到array.includes()的早期停止时间-虽然不再慢10倍,但仍然慢了许多倍,非常相关且出乎我的意料。

1 个答案:

答案 0 :(得分:1)

首先,我不是JavaScript引擎实现和性能优化方面的专家;但总的来说,您不应该信任此类测试来对性能进行可靠的评估。

底层算法的时间复杂度仅在非常(非常)大的数量上才成为有意义的因素,并且根据经验法则,1000当然不是那么大,尤其是对于简单的整数数组。

在少量的毫秒级操作中,您将在类似的时间范围内在引擎中发生许多其他事情,这将使您的测量工作大为失败。优化,意外的开销等。

例如,我edited your tests通过简单地将数组的大小增加到100,000。我那可怜的旧笔记本电脑上的结果如下:

arr.includes(-1); //3,323 Ops/s
arr.includes(0); //6,132 Ops/s
prebuildset.has(-1); //41,923,084 Ops/s
prebuildset.has(0); //39,613,278 Ops/s

显然,这与您的结果截然不同。我的观点是,不要尝试衡量小型任务的微观性能。使用最适合您的项目的数据结构,保持代码整洁合理,并在需要扩展时进行相应的准备。