繁体   English   中英

为什么 Array.prototype.fill() 与 `for` 循环相比有如此大的性能差异?

[英]Why does Array.prototype.fill() have such a large performance difference compared to a `for` loop?

在对Array.prototype.fill()方法进行一些测试(macOS 上的 Chrome)时,它显然比简单地创建自己的for循环并填充数组慢了将近两倍(如果不是更慢的话)。

显然在做类似的事情:

for( var i = 0; i < Array.length; i++) {
   A[i] = 0;
}

对比

Array.fill(0);

Array.fill()方法需要大约 210-250 毫秒来填充大小为 10000000 的数组,而for循环需要大约 70-90 毫秒。 似乎Array.fill()方法可以重写为简单地使用直接循环,因为您总是知道初始索引和目标索引。

 let arrayTest = new Array(10000000), startTime, endTime; startTime = performance.now(); arrayTest.fill(0); endTime = performance.now(); console.log("%sms", endTime - startTime); arrayTest = new Array(10000000); startTime = performance.now(); for (let i = 0; i < arrayTest.length; i++){ arrayTest[i] = 0; } endTime = performance.now(); console.log("%sms", endTime - startTime);

与我在本地测试时相比,以上实际上显示出更大的差异。

编辑:经过进一步测试后,我现在意识到,当切换到 Firefox 及其真正依赖于引擎时,差异会减少很多。 我猜这主要是 JavaScript 引擎在优化循环与方法方面有何不同的结果。 似乎仍然可以优化Array.prototype.fill()中的循环来解决这种差异。

结果与使用JavaScript编写部分Chrome的报告一致,并依赖于运行时分析和优化来提高性能。

我将测试代码打包在一个要从测试页面重复调用的函数中,该测试页面可以加载到不同的浏览器中(这不是一个可运行的代码片段):

<!DOCTYPE html>
<html><head><meta charset="utf-8">
<title>Array.prototype.fill</title>
<script>

Array.prototype.customFill = function( value, start = 0, end = this.length) {
    var count = end-start;
    if( count > 0 && count === Math.floor(count)){
        while( count--)
            this[start++]=value;
    }
    return this;
}

function test() {  
    let arrayTest,
        startTime,
        endTime,
        arraySize = 1000000;

    arrayTest = new Array(arraySize);
    startTime = performance.now();
    for (let i = 0; i < arrayTest.length; i++){
      arrayTest[i] = 0;
    }
    endTime = performance.now();
    console.log("%sms (loop)", endTime - startTime);

    arrayTest = new Array(arraySize);
    startTime = performance.now();
    arrayTest.fill(0);
    endTime = performance.now();
    console.log("%sms (fill)", endTime - startTime);

    arrayTest = new Array(arraySize);
    startTime = performance.now();
    arrayTest.customFill(0);
    endTime = performance.now();
    console.log("%sms (custom fill)", endTime - startTime);   
}
</script>
</head>
<body>
    open the console and click <button type="button" onclick="test()">test</button>
</body>
</html>

可以调整阵列大小以适应所用设备的性能。

对于测试中的前两个测试点击, Windows下的Chrome结果显示该循环的性能大幅提升。 在第二次点击时,循环的时间似乎有所改善。 在第三次单击时,循环和填充方法似乎都被优化并以几乎相同且改进的速度运行。 重新加载页面后,结果可重复。

我发现这与Chrome脚本优化策略一致,与Chrome的Array.prototype.fill不一致,是用C ++或类似方式编写的。 尽管Array.prototype.fill.toString()将函数体报告为“本机代码”,但并未说明它所使用的语言。


更新

为自定义填充方法添加了时间,为速度编写,并存储为Array.prototype.customFill

Firefox的Array.prototype.fill与使用脚本编写的Array.prototype.fill一致。 本机实现优于循环,并且通常(但不总是)比自定义填充方法更快。

Chrome show的Array.prototype.fill也与Array.prototype.fill一致,它是用某种优化的脚本编写的。 测试的所有三种填充方法显示在一次或两次测试点击后速度增加。

但是,自定义填充方法的启动速度比Chromes本机版快十倍。 您需要在自定义方法中放入无意义的代码,以便将其减慢到足以接近本机方法的初始速度。 相反,在优化之后,本机方法的速度快了两倍 - 用JavaScript编写的自定义方法永远不会在相同的范围内进行优化。

虽然Chromes Array.prototype.fill方法可以用JavaScript编写,但似乎需要额外的解释来解释最初的缓慢和最终性能优化。

JSPerf确认填充比for循环慢。

当数组大小为1e5时, fill总是获胜。 当数组大小为1e7时, for wins

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM