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

标签 javascript arrays google-chrome v8

在对 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() 中的循环来解决这种差异。

最佳答案

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

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

<!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 一致。 native 实现优于循环,并且通常(但不总是)比自定义填充方法更快。

Chrome 显示的时间也与 Array.prototype.fill 一致,是用某种经过优化的脚本编写的。测试的所有三种填充方法都显示在一两次测试点击后速度有所提高。

但是,自定义填充方法的启动速度比 Chrome 原生版本快十倍以上。您需要将无意义的代码放入自定义方法中,以使其足够慢以接近 native 方法的初始速度。相反,经过优化后, native 方法的速度大约是原来的两倍——用 JavaScript 编写的自定义方法从未得到同样程度的优化。

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

关于javascript - 为什么 Array.prototype.fill() 与 `for` 循环相比有如此大的性能差异?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48836753/

相关文章:

javascript - 标题未完全隐藏或滚动到顶部时,粘性菜单快速跳转到顶部

javascript - 为什么字段值在提交时被发布两次

javascript - 如何从对象元素中获取没有重复的数组

google-chrome - Chrome --kiosk-printing 有 1-2 分钟的滞后/延迟

file - Chrome : Create file input from blob with Javascript?

javascript - Chrome 背景图片修复了重绘问题

javascript - ASP.NET MVC 控件

javascript - 如何在由框架创建的 &lt;input&gt; 标记中显示不 chop 的文本

java - 从并行数组中搜索和显示信息

PHP 间接修改重载属性 - 使用 ARRAYS