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

时间:2018-02-17 00:32:37

标签: javascript arrays google-chrome v8

在对Array.prototype.fill()方法进行一些测试(Chrome on macOS)时,它显然几乎是创建自己的for循环并填充数组的两倍慢(如果不慢)。 / p>

显然做了类似的事情:

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

VS

Array.fill(0);

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

&#13;
&#13;
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);
&#13;
&#13;
&#13;

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

编辑:我在进一步测试后意识到,当切换到Firefox及其真正依赖引擎时,差异会大大减少。我猜这主要是因为不同的JavaScript引擎优化循环与方法的结果。看起来好像Array.prototype.fill()中的循环可以优化以解决这个差异。

2 个答案:

答案 0 :(得分:1)

结果与使用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脚本优化策略一致,与使用C ++或类似内容编写的Chrome Array.prototype.fill不一致。虽然Array.prototype.fill.toString()将函数体报告为“本机代码”,但并未说明它所使用的语言。

<小时/> 更新

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

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

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

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

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

答案 1 :(得分:1)

JSPerf确认填充比for循环慢。