故事
在對性能關鍵代碼的一些測驗中,我觀察到Math.random()的副作用,我不明白。我在尋找
- 一些深刻的技術解釋
- 對我的測驗(或期望)的篡改
- 鏈接到 V8 問題/錯誤票證
問題
看起來呼叫Math.random()分配了一些需要由 Gargabe 收集器 (gc) 清理的記憶體。
測驗:使用 Math.random()
const numberOfWrites = 100;
const obj = {
value: 0
};
let i = 0;
function test() {
for(i = 0; i < numberOfWrites; i ) {
obj.value = Math.random();
}
}
window.addEventListener('DOMContentLoaded', () => {
setInterval(() => {
test();
}, 10);
});
觀察 1:Chrome 組態檔
鉻:95.0.463869,Windows 10,邊緣:95.0.1020.40
在瀏覽器中運行此代碼并記錄性能組態檔將導致經典的記憶體鋸齒形
Math.random() 測驗的記憶體組態檔
觀察 2:Firefox
火狐開發者:95,Windows 10
未檢測到垃圾回收 (CC/GCMinor) - 記憶體相當線性
解決方法
crypto.getRandomValues()
Math.random()使用 self.crypto.getRandomValues`替換為足夠大的預先計算的亂數陣列。
uj5u.com熱心網友回復:
(此處為 V8 開發人員。)
是的,這是意料之中的。這是一個(非常基本的)設計決策,而不是錯誤,并且與Math.random(). V8 將浮點數“裝箱”為堆上的物件。那是因為它在物件中的每個欄位使用 32 位,這對于 64 位雙精度顯然是不夠的,而間接層解決了這個問題。
有許多特殊情況可以避免這種裝箱:
- 在優化的代碼中,對于永遠不會離開當前函式的值。
- 對于其值為足夠小的整數(“Smis”,有符號的 31 位整數范圍)的數字。
- 對于陣列中只將數字視為元素的元素(例如
[1, 2.5, NaN],但不是[1, true, "hello"])。 - 可能是我現在沒有想到的其他情況。此外,所有這些內部細節都可以(并且確實!)隨著時間的推移而改變。
Firefox 使用一種完全不同的技術來存盤內部參考。好處是它避免了將數字裝箱,缺點是它為不是數字的東西使用了更多的記憶體。這兩種方法都沒有嚴格意義上的優于另一種方法,這只是一種不同的權衡。
一般來說,您不必擔心這一點,這只是您的 JavaScript 引擎在做它的事情:-)
問題:在瀏覽器中運行此代碼并記錄性能組態檔將導致經典的記憶體鋸齒形
為什么這是一個問題?這就是垃圾收集記憶體的作業原理。(另外,為了正確看待事情:GC 在您的個人資料中每 8 秒僅花費 0.3 毫秒。)
解決方法:使用 self.crypto.getRandomValues` 將 Math.random() 替換為足夠大的預先計算的亂數陣列。
用一個大而長壽命的陣列替換微小的短壽命 HeapNumbers 聽起來不是節省記憶體的好方法。
如果真的很重要,避免數字裝箱的一種方法是將它們存盤在陣列中而不是作為物件屬性。但是在你的代碼中經歷難以維護的扭曲之前,一定要衡量它對你的應用程式是否真的很重要。在微基準測驗中很容易展示巨大的影響,很少看到它在實際應用中產生太大影響。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/346989.html
標籤:javascript 表现 随机的 垃圾收集 v8
