[英]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.