简体   繁体   English

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

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

In doing a little testing (Chrome on macOS) of the Array.prototype.fill() method, its clearly almost twice as slow (if not slower) than simply creating your own for loop and filling your array.在对Array.prototype.fill()方法进行一些测试(macOS 上的 Chrome)时,它显然比简单地创建自己的for循环并填充数组慢了将近两倍(如果不是更慢的话)。

Clearly doing something like:显然在做类似的事情:

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

vs对比

Array.fill(0);

The Array.fill() method would take ~210-250ms to fill an array of size 10000000, while the for loop would take ~70-90ms. Array.fill()方法需要大约 210-250 毫秒来填充大小为 10000000 的数组,而for循环需要大约 70-90 毫秒。 It just seems that the Array.fill() method could be re-written to simply use a straight forward loop since you'd always know your initial index and target index.似乎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);

The above actually shows an even greater discrepancy compared to when I tested locally.与我在本地测试时相比,以上实际上显示出更大的差异。

Edit: I realize now after further testing that the discrepancies are diminished a lot when switching over to Firefox and its really engine dependent.编辑:经过进一步测试后,我现在意识到,当切换到 Firefox 及其真正依赖于引擎时,差异会减少很多。 I am guessing this is mainly a result of how different JavaScript engines are optimizing loops vs a method.我猜这主要是 JavaScript 引擎在优化循环与方法方面有何不同的结果。 It still seems as though a loop within the Array.prototype.fill() could be optimized to resolve this difference though.似乎仍然可以优化Array.prototype.fill()中的循环来解决这种差异。

The result is consistent with reports that parts of Chrome are written in JavaScript, and rely on run time profiling and optimization to improve performance. 结果与使用JavaScript编写部分Chrome的报告一致,并依赖于运行时分析和优化来提高性能。

I packaged the test code in a function to be called repeatedly from a test page which can be loaded into different browsers (this is not a runnable snippet): 我将测试代码打包在一个要从测试页面重复调用的函数中,该测试页面可以加载到不同的浏览器中(这不是一个可运行的代码片段):

<!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>

The array size can be adjusted to suit the performance of the device used. 可以调整阵列大小以适应所用设备的性能。

The results for Chrome under Windows showed a large performance win for the loop, for the first two test clicks on test . 对于测试中的前两个测试点击, Windows下的Chrome结果显示该循环的性能大幅提升。 On the second click, timing for the loop seemed to improve. 在第二次点击时,循环的时间似乎有所改善。 On the third click both loop and fill methods appeared to be optimized and ran with nearly equal, and improved, speed. 在第三次单击时,循环和填充方法似乎都被优化并以几乎相同且改进的速度运行。 Results were repeatable after reloading the page. 重新加载页面后,结果可重复。

I find this consistent with Chrome script optimization strategies and inconsistent with Chrome's Array.prototype.fill being written in C++ or similar. 我发现这与Chrome脚本优化策略一致,与Chrome的Array.prototype.fill不一致,是用C ++或类似方式编写的。 Although Array.prototype.fill.toString() reports the function body as "native code" is doesn't say what language it is written in. 尽管Array.prototype.fill.toString()将函数体报告为“本机代码”,但并未说明它所使用的语言。


Update 更新

Added timings for a custom fill method, written for speed, and stored as Array.prototype.customFill . 为自定义填充方法添加了时间,为速度编写,并存储为Array.prototype.customFill

Timings for Firefox are consistent with Array.prototype.fill being written in script. Firefox的Array.prototype.fill与使用脚本编写的Array.prototype.fill一致。 The native implementation outperformed the loop and was generally (but not always) faster than the custom fill method. 本机实现优于循环,并且通常(但不总是)比自定义填充方法更快。

Timings for Chrome show are also consistent with Array.prototype.fill being written in some kind of script that becomes optimized. Chrome show的Array.prototype.fill也与Array.prototype.fill一致,它是用某种优化的脚本编写的。 All three fill methods tested shown an increase in speed after one or two test clicks. 测试的所有三种填充方法显示在一次或两次测试点击后速度增加。

However, the custom fill method starts out over ten times faster than Chromes native version. 但是,自定义填充方法的启动速度比Chromes本机版快十倍。 You would need to put nonsense code in the custom method to slow it down enough to approach the native method's initial speed. 您需要在自定义方法中放入无意义的代码,以便将其减慢到足以接近本机方法的初始速度。 Conversely, after optimization, the native method is around twice as fast - the custom method written in JavaScript never gets optimized to the same extent. 相反,在优化之后,本机方法的速度快了两倍 - 用JavaScript编写的自定义方法永远不会在相同的范围内进行优化。

While Chromes Array.prototype.fill method could be written in JavaScript, additional explanation seems to be needed to account for the initial slowness and final performance optimizations noted. 虽然Chromes Array.prototype.fill方法可以用JavaScript编写,但似乎需要额外的解释来解释最初的缓慢和最终性能优化。

JSPerf确认填充比for循环慢。

When array size is 1e5 , fill always wins.当数组大小为1e5时, fill总是获胜。 When array size is 1e7 , for wins当数组大小为1e7时, for wins

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

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