JIT 為什麼能大幅度提升性能?

求詳解。


靜態編譯優化和動態編譯優化最大的不同是他們在編譯時所得到的信息量的不同。靜態編譯在運行程序之前就把所有的執行代碼編譯完,這時編譯器所接受的編譯信息量是不夠多的。比如說:某個函數是否是大量地被調用了,函數的實參是不是一直是一個常數,等等。

動態編譯之於靜態編譯,缺點是它需要即時編譯代碼,但是有一個優點---編譯器可以獲得靜態編譯期所沒有的信息。比如:通過運行時的profiling可以知道哪些函數是被大量使用的。在哪些execution path上哪些函數的參數一直都沒有變,等等。不要小看這些信息,當即時編譯器了解這些信息之後可以在短時間內編譯出比靜態編譯器更優質的二進位碼。舉例來說,一般程序也遵循90-10原則,即運行時的90%里計算機是在處理其中10%的代碼,尋找到這些執行熱點代碼進行深度優化能得到比靜態編譯更好的性能(因為已知更多信息量)。

然而現實是:即時編譯的開銷非常大,暫時還不能超越靜態編譯的總體性能。不過,一個動態語言(如JAVA,Python)有著靜態語言(如C++)所沒有的各種優勢,必然是將來程序語言發展的方向。伴隨著強大的需求,即時編譯器在將來也會更加強大。


1、對於解釋器語言來說(不管直接解釋 AST 還是位元組碼),指令分發是個開銷很大的過程(即一個很大的 switch case ,根據得到的指令決定解釋器下一步要做什麼),這樣會導致這部分的 CPU 指令緩存命中率大幅下降。

2、對於動態類型語言來說,JIT 時還可以做類型特化。比如一個函數 add(x, y),純解釋的話,每次執行時需要判斷 x 與 y 的類型,根據類型再做具體的操作(整數加法 / 浮點數加法 / 字元串拼接)。假使實際代碼運行過程中,x 和 y 一直都是整型類型,這些操作也不能省略,導致性能下降。但是 JIT 的時候,可以將此函數編譯成 add_int_int(x: int, y: int) 的形式,這樣性能就和編譯型語言完全相等了。

3、函數內聯,JIT 的時候可以根據函數調用情況,將常用的一些函數做內聯編譯。


假定你是個導演,寫了個腳本,讓演員表演。

先讓演員把整個腳本都背下來,吃透,到腦子裡,然後連續的表演一個小時。

或者讓演員表演兩分鐘,再看兩分鐘腳本,思考一下,再表演兩分鐘,再看一會腳本,思考一下。。。恩。。。

單純從效率上來講,前者一定更快吧。

這就是 JIT 的原理。批量的把一件事情先整理成為目標能夠直接接受的方式,然後直接運行。


所謂的性能大幅提升也是建立在數量級之上的,所以如果你寫個hello world,寫個玩具, 沒有誰會在意性能。 但是如果你的程序要7*24不間斷運行,如果你的主循環省了一個指令,那麼乘上數量級省下的時間就可觀了。

例如Java方法JIT的起點是1M次調用,如果你的Java Add方法比Python的Add省了100個指令,那麼1M次調用,你就節省了0.1G個指令。 假設CPU 10GHZ,那這1M次調用你就節省了0.01秒。 好像還是太小,但是JIT之所以能夠大幅提升性能就是因為這1M次調用只是起點, 7*24上要運行多少次這樣的1M循環呢?我覺得這是性能提升的來源


推薦閱讀:

預算4500左右配一台主機(僅主機)玩OW和GTA,求大神推薦一下目前這個價位才能配到最性價比的機器?
為什麼 Microsoft Word 不自動更新文檔目錄?
為維護 iPad 各方面的性能,在使用過程中需要注意什麼?
如何對APP性能進行監測並優化?有沒有工具推薦一下。

TAG:即時編譯JIT | 性能 | 編譯 |