緩存 Array.length 是老生常談的小優化

問題

緩存 Array.length 是老生常談的小優化。javascript

// 不緩存  for (var i = 0; i < arr.length; i++) { ... } // 緩存 var len = arr.length; for (var i = 0; i < len; i++) { ... } // 或者 for (var i = 0, len = arr.length; i < len; i++) { ... }

但之前寫過 Java 的筆者一直對這種破碎的寫法感到不適,也對這種寫法的實際優化效果產生疑問。html

且推崇這種寫法的朋友彷佛不少也是「前輩這麼說+本身想了一下以爲有道理」。java

因爲 for 循環搭配 Array.length 是極度經常使用的 JavasScript 代碼,因此仍是有必要搞清楚的。數組

結論

通過一番摸索後筆者獲得的結論是:緩存 Array.lengh 對優化的影響沒有想象中的大,甚至可能會有所減慢。緩存

理由

從測試結果上看

stackoverflow 上也有這個討論,For-loop performance: storing array length in a variable 。jsp

accepted 的答案是說緩存會起到加速的結果,給出了 jsPerf 測試。函數

可是有答案反對,也給出了 jsPerf 測試。oop

兩個答案的區別在於「循環不變量代碼移動(Loop-invariant code motion)」,accepted 答案的測試循環裏沒有訪問到數組,是不實際的,後面會講到。測試

從另外一篇文章 Shoud I have to cache my array’s length? 的測試結果也能夠看出緩存差異不大。優化

1

還有這篇 JavaScript's .length Property is a Stored Value

2

從 V8 的中間代碼分析

這篇文章 How the Grinch stole array.length access 從 V8 的 hydrogen 探討 Array.length 在 for 循環中的處理。

正如上面提到的「循環不變量代碼移動」,V8 引擎會聰明的把能肯定不變的代碼移到循環外。

因此像下面這種代碼也不會影響引擎對 Array.length 的優化:

function uncached(arr) { for (var i = 0; i < arr.length; i++) { arr[i] } }

而當在循環中調用函數時,V8 會嘗試將函數進行內聯,從而繼續進行「循環不變量代碼移動」優化。
但當函數不可內聯時,V8 就沒轍了,每次循環都要從新計算一遍 length

function BLACKHOLE(sum, arr) { try { } catch (e) { } } function uncached(arr) { var sum = 0; for (var i = 0; i < arr.length; i++) { sum += arr[i]; if (sum < 0) BLACKHOLE(arr, sum); } return sum; }

但這時即使是在循環外緩存了 length 也是沒有用的,引擎無法預判數組的變化,當須要訪問數組元素時會觸發 bounds check ,從而照樣要計算一遍 length 。因此緩存 length 是沒有用的。

甚至,因爲多了一個變量,底層的寄存器分配器每次循環還要多一次恢復這個變量。固然這個只有在大規模的狀況下才會看出區別。

結尾

固然這篇文章也有侷限性,僅僅討論了 V8 引擎,也沒有討論訪問 length 代價更高的 HTMLCollection 。但這已經足夠說明二者差異不大,通常狀況下咱們不用再侷限於緩存的寫法,能夠放開來按照本身喜歡的方式去寫循環了。

【完】

原文:http://div.io/topic/966

相關文章
相關標籤/搜索