簡體   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