JS特性性能缺陷及JIT的解決方案


        拜讀了David的《Know Your Engines: How to Make Your JavaScript Fast》,David是Mozilla的JS引擎工程師,文章主要介紹了JIT與GC原理,以及如何根據某些基本原理,優化js代碼的執行效率,雖然是老文了,但對我來說仍受益匪淺。這里,我根據上文整理了本文,同時,大家也可以從側面了解下JIT。

 

        近5年來,在主流瀏覽器上,Javascript的運行速度有10-100倍的提升,這要歸功於Javascript新引擎JIT。但在深入了解JIT前,我們先看看Javascript的一個最重要的特性:untyped(無類型)。

 

一. 無類型:

        Javascript是個無類型的語言,這導致了 x = y +z這種表達式,可以有很多含義。比如:

        (1)y,z是數字,則+表示加法。

        (2)y,z是字符串,則+表示字符串連接。

          ……

        而JS引擎內部則使用“細粒度”的類型,比如:32-bit* integer, 64-bit* floating-point,如圖:

        2

        這就要求js類型-js引擎類型,需要做“boxed/unboxed(裝箱/解箱)”,在處理一次x = y + z這種計算,需要經過的步驟如下:

        (1)從內存,讀取 x = y + z的操作符。

        (2)從內存,讀取 y,z。

        (3)檢查y,z類型,確定操作的行為。

        (4)unbox y,z。

        (5)執行 操作符 的行為(唯一有效的步驟……)。

        (6)box x。

        (7)把x寫入內存。

        只有(5)是真正有效的操作,其他都是為(5)做准備/收尾的,效率之低可見。javascript的untyped特性很好用,但也為此付出了很大的性能代價。

 

二. 對象屬性

function f(obj) {
        return obj.a + 1;
}

        在Js里,對象屬性的訪問是比較慢的。至於原因,要從Javascript對象存儲說起,這里借用其他文章的一個圖:

        4

        如上圖,訪問對象屬性,需要先從本地變量表找到對象,然后遍歷屬性,如果在本對象的屬性列表里沒找到,再得從prototype里面一層層的找。不能直接索引,只能遍歷,這就慢的原因。

 

二. 2006版-Javascript引擎

        這版引擎在執行x = y + z時,就是執行了以上流程。它模塊圖如下:

        2013-02-06_170725

 

三. 2011新版-Javascript引擎

        模塊圖如下:

        3

        可以看到,除了老版的解析器外,新引擎增加了JIT,以及Type-specializing JIT。

 

1. JIT

        先看看JIT對untyped的優化,在JIT下,執行x = y + z流程:

        (1)從內存,讀取 x = y + z的操作符。

        (2)從內存,讀取 y,z。

        (3)檢查y,z類型,確定操作的行為。

        (4)unbox y,z。

        (5)執行 操作符 的行為(唯一有效的步驟……)。

        (6)box x。

        (7)把x寫入內存。

        其中,(1),(2) CPU幫我們搞定;(7)JIT把結果保存在寄存器里。

        但可惜不是所有情況都能使用JIT,上面看到,Front-end有3條分支,“一般的情況”可以走JIT分支,比如:number + number;string + string …,但特殊情況,比如:number + undefined就不行了,只能走舊解析器。

 

        除了針對untyped的優化,新引擎還對“對象屬性”訪問做了優化,解決方案叫:inline caching,俗稱:IC。簡單的說,就是做cache。優化流程直接看圖:

        5

        這個相當於遍歷cache list了,如果當list很大時,這種方案反而影響效率。下圖是評測:

        7

 

2. Type-specializing JIT

        從名稱上可以猜到,這個引擎是處理typed類型(聲明類型)變量的。厄……但Javascript都是untype類型的……

        Type-specializing JIT的解決方案是:

        (1)先通過掃描,監測類型。

        (2)通過編譯優化(當然,他的優化對象不僅僅只是“類型”,還包括對JS代碼的優化,但類型優化是核心的。),生成類型變量。

        (3)再做后續計算。

 

         來看看Type-specializing JIT的執行x = y + z流程吧:

        (1)從內存,讀取 x = y + z的操作符。

        (2)從內存,讀取 y,z。

        (3)檢查y,z類型,確定操作的行為。

        (4)unbox y,z。

        (5)執行 操作符 的行為。

        (6)box x。

        (7)把x寫入內存。

        高效的優化啊……當然,這也是有代價的,代價就是:前置的掃描類型,編譯優化。所以Type-specializing JIT的應用是有選擇性,選擇使用這個引擎的場景包括:

        (1)熱點代碼。

        (2)通過啟發式算法估算出來的有價值的代碼……

   

        另外,有2點也需要注意:

        (1)當 變量類型 發生變化時,引擎有2種處理方式:

                【1】少量變更,重編譯,再執行。

                【2】大量變更……還是交給JIT執行吧。

        (2)數組,object properties,閉包變量不在優化范疇之列。

 

        本文主要整理了JIT針對Javascript某些語言特性的優化方案,至於GC,以及更多Js代碼優化建議,可查閱原文。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM