改進了程式碼快取
V8使用從Chrome 66開始,我們通過在頂層執行後生成快取來快取更多程式碼。這會導致初始載入時分析和編譯時間縮短20-40%。
背景
V8使用兩種程式碼快取來快取生成的程式碼,以便以後重用。首先是每個V8例項中都有的記憶體快取。初始編譯後生成的程式碼儲存在此快取中,並鍵入源字串。這可以在V8的相同例項中重複使用。另一種程式碼快取序列化生成的程式碼並將其儲存在磁碟上供將來使用。該快取並不特定於V8的特定例項,並可用於V8的不同例項。這篇博文主要關注Chrome中使用的第二種程式碼快取。(其他嵌入程式也使用這種程式碼快取;它不僅限於Chrome,但本博文僅關注Chrome中的使用情況。)
Chrome將序列化的生成程式碼儲存在磁碟快取中,並使用指令碼資源的URL將其鍵入。載入指令碼時,Chrome會檢查磁碟快取。如果指令碼已被快取,則Chrome會將序列化資料作為編譯請求的一部分傳遞給V8。然後V8反序列化這些資料,而不是解析和編譯指令碼。還有額外的檢查來確保程式碼仍然可用(例如:版本不匹配導致快取的資料無法使用)。
真實世界的資料顯示,程式碼快取命中率(對於可以快取的指令碼)很高(〜86%)。雖然這些指令碼的快取命中率很高,但是我們每個指令碼快取的程式碼量並不是很高。我們的分析表明,增加快取的程式碼量可以減少解析和編譯JavaScript程式碼大約40%的時間。
增加快取的程式碼量
在以前的方法中,程式碼快取與編譯指令碼的請求相結合。
嵌入者可以請求V8序列化它在頂級編譯新的JavaScript原始檔時生成的程式碼。編譯指令碼後,V8返回了序列化程式碼。當Chrome再次請求相同的指令碼時,V8會從快取中獲取序列化的程式碼並對其進行反序列化。
V8完全避免了重新編譯已經在快取中的函式。
下圖顯示了這些場景:
V8僅編譯在頂層編譯期間預期立即執行的函式(IIFE),並標記用於延遲編譯的其他函式。
這有助於通過避免編譯不需要的函式來提高頁面載入時間,但這意味著序列化資料僅包含急切編譯的函式的程式碼。
在Chrome 59之前,我們必須在任何執行開始之前生成程式碼快取。較早的V8基本編譯器(Full-codegen)為執行上下文生成專用程式碼。Full-codegen將程式碼修補用於特定執行上下文的快速路徑操作。通過刪除要在其他執行上下文中使用的特定於上下文的資料,不能輕易地序列化此類程式碼。
隨著在Chrome 59中,這一限制不再是必要的。點火使用來執行當前執行環境中的快速路徑操作。上下文相關資料儲存在反饋向量中並與生成的程式碼分開。
這已經開啟了即使在執行指令碼之後也能生成程式碼快取的可能性。在我們執行指令碼時,會編譯更多的函式(標記為惰性編譯的函式),從而允許我們快取更多的程式碼。
V8公開了一個新API,ScriptCompiler::CreateCodeCache以請求獨立於編譯請求的程式碼快取。請求程式碼快取以及編譯請求已被棄用,並且不適用於V8 v6.6及更高版本。從版本66開始Chrome使用此API在頂層執行後請求程式碼快取。下圖顯示了請求程式碼快取的新場景。程式碼快取在頂層執行之後被請求,並因此包含在指令碼執行期間稍後被編譯的函式的程式碼。在後面的執行中(在下圖中顯示為熱執行),它避免了在頂層執行期間編譯函式。
結果
使用我們內部的測量此功能的效能。下圖顯示了早期快取記憶體方案中分析和編譯時間的縮短。在大多數頁面上,解析和編譯時間都會減少20-40%左右。
來自wild的資料顯示了類似的結果,在桌面和移動裝置上編譯JavaScript程式碼的時間減少了20-40%。在Android上,這種優化還可以轉化為頂級頁面載入指標減少1-2%,例如網頁成為互動時所需的時間。我們還監測了Chrome的記憶體和磁碟使用情況,但沒有看到任何明顯的迴歸。
釋出者Mythri Alle,首席程式碼Cacher
原文地址:https://v8project.blogspot.com/2018/04/improved-code-caching.html