如何通俗易懂地介紹「即時編譯」(JIT),它的優點和缺點是什麼?

PyPy 用 JIT (Just-in-time compilation) 來大幅提高性能。既然這麼好,為什麼 CPython,Ruby 不用 JIT ?是不是 Java 這樣的靜態語言容易實現 JIT 而動態語言不容易實現 JIT ?


Josh Haberman: Hello, JIT World: The Joy of Simple JITs


從語言的特性來說,JIT 恰恰更適合動態語言。這是 LuaJIT 的作者對 static optimization 和 JIT 優化的分析:
Re: [ANN] llvm-lua 1.0


....首先題主要明白一個事情 即時編譯跟語言是否是靜態無關 那是虛擬機語言的優勢
現在基於JVM的腳本語言也不少 比如Scala、Groovy或者Jython 他們與Java文件一樣 最終都會被編譯成class文件 而class被虛擬機裝載後 會以解釋模式運行一段時間 當發覺某些代碼運行比較頻繁(一種描述 關於代碼被優化的原因有很多 調用次數只是其中一個) 則會對這些代碼進行本地化 也就是JIT (你也可以去搜索HotspotJVM為什麼叫Hotspot)

然後題主就又會問了 為什麼不一開始就本地編譯呢?
1.編譯耗時 有的需求是需要考慮程序的啟動時間的
2.在運行期間收集一些數據可以更好的優化原本的代碼(激進優化) 這也是JIT的優勢之一
3.Java本身就支持一開始就本地編譯...所以看你的選擇


通俗的說,PyPy的JIT就是在運行過程中檢測到loop,然後把loop的代碼編譯成機器代碼,而省去了絕大部分的解釋開銷。
Cpython不用JIT是因為JIT不是那麼好做的。
「可能很多人在學過編譯原理後都能自己寫個簡單的解釋器,但是JIT卻沒有那麼簡單就可以實現」(這句話來自PyPy官網某篇文章,大意)。


我就來通俗一點,拋磚引玉。

JIT ,簡單的來說,就是代碼在目標平台上運行的時候,實時的把代碼編譯為目標機器上的機器碼。

實現 JIT 需要大量的人力物力,Java 擁有一個優秀的 JIT 是因為曾經有不少的業界大佬燒了不少的錢在這上面。

其他語言一般難有優秀的 JIT ,只是因為沒有足夠多的人力財力物力投入,僅此而已。

順便說一下,C# 的 JIT 就不錯,原因自然是因為微軟有錢有人。。。

JIT 的優點就不說了,自然是性能好。說說缺點,缺點其實就是,由於 JIT 本質上是一個編譯器,所以它必然不是跨平台的,對於每個平台必然需要單獨編寫代碼。當然這個因素一定程度上也導致了 JIT 開發成本高。


JIT有什麼優點?讓我們通過如下幾個具體的例子來觀察。

例1 你發現有一個第三方的庫函數大量被你的程序調用,而且這個庫函數很短很小。你試圖優化它,希望把它內聯,但是這個庫不是開源的,製作方只提供DLL。
試問:Java當然可以把這個庫函數內聯了。但是如果用的是C/C++這樣的靜態編譯模型語言,有沒有辦法優化它?

例2 還是這個庫。你發現調用這個庫的另一個不開源的方法時,某幾個實參的值總是一樣的。
試問:Java可以通過Global Value Propagation來優化這個不開源的方法,C/C++編譯器有沒有辦法做到?

例3 你有一段10年前某位掃地僧寫多平台代碼,可讀性為零但是無比重要。有一天你們打算把編譯器從GCC4.1升級到GCC4.4,然後突然間某平台上生成的代碼crash了,怎麼調試都無法通過。(出自我個人經歷)
試問:還要升級編譯器嗎?還是花重金請牛人把代碼推倒重寫一遍?

例4 你寫了一段代碼,裡面用了很多virtual function。
試問:靜態編譯模型的語言(比如C/C++)有沒有辦法優化這些virtual calls,比如把它們內聯了?

例5 你想寫一段代碼然後發布到不同的平台上.....

.....還有很多例子


以Java/C#,NET為代表的Coffee Based 語言,是建立在虛擬機之上的。
這種虛擬機和一般其他語言的運行庫有一個很大不同,就是它好像一個有獨立體系結構的計算機。
Java/C#,NET的程序要運行在這些虛擬機之上,就必須要編譯成虛擬機獨有的中間語言。Java叫位元組碼,C#,NET有很多種叫法,姑且簡稱CIL。這些中間語言身上有很深很深的彙編指令集樣式語法痕迹,這也從 另一個側面說明虛擬機的體系結構多麼像一個馮諾依曼計算機。
虛擬機傳統的解釋器,就是要在中間語言,和真正的平台體系結構之間的指令做映射。比如把Java的load指令換成native code 的load指令。
JIT的出現,是為了補強虛擬機邊運行邊解釋的低性能。它會智能地對熱點代碼進行優化且重複利用。從策略的角度來講,就是通過查表或者緩存而不是重複解決子問題而大大縮短解決問題的時間。
它的優點就是,智能縮短映射的過程。
它的缺點就是,過於複雜。首先代碼優化的種種策略都是基於各種各樣的假定,假定不一定會成真,即使費盡心機做的代碼優化,也有可能在現實中無法提高性能。其次,不同平台和體系結構的技術特點千差萬別,這個映射過程只能單獨定製。比如至今Java平台還不能在IOS上做基本映射(即解釋器都不行),最近高司令突然心血來潮搞了一個新的專案想要搞起來。如果真的要搞,又不知道要燒多少錢死多少人的腦細胞。
以前JIT剛出現的時候,只是一個掛載的外掛一樣的東西。現在開始喧賓奪主取代了解釋器。這種轉變其實是對中間語言解釋器作為一個「薄的膠合層」的一個設計上的自我否定,極大地增加了設計的複雜性。目前對於這種強行加速做得比較好的,就是Java係為代表的各種虛擬機,微軟的虛擬機,或者谷歌搞出來的V8引擎(這不知道是不是目前為止設計進生產實踐中惟一被廣泛應用的動態強行加速)。這幾件東西其實無一不是大型商業公司的強大支持的產物。現在主流的動態語言似乎都是社區在弄,沒那麼多資源來搞這個,是他們沒有好的JIT的一個關鍵因素。本來動態語言的應用場景就不是為了追求高性能,更何況現在各種應用場景下的瓶頸大部分都在語言之外,語言的性能考量又是一個比較次要的需求了。

題外話:
當初安德斯剛到微軟的時候,曾經試過用JIT超越C/C++的性能。這可以說明JIT確實有獨到之處,所以大型公司死命要搞JIT。


這是stackoverflow回答:(已經非常通俗易懂)

A JIT compiler runs after the program has started and compiles the code (usually bytecode or some kind of VM instructions) on the fly (or just-in-time, as it"s called) into a form that"s usually faster, typically the host CPU"s native instruction set. A JIT has access to dynamic runtime information whereas a standard compiler doesn"t and can make better optimizations like inlining functions that are used frequently.

This is in contrast to a traditional compiler that compiles all the code to machine language before the program is first run.

To paraphrase, conventional compilers build the whole program as an EXE file BEFORE the first time you run it. For newer style programs, an assembly is generated with pseudocode (p-code). Only AFTER you execute the program on the OS (e.g., by double-clicking on its icon) will the (JIT) compiler kick in and generate machine code (m-code) that the Intel-based processor or whatever will understand.

再上個圖,參考。


以Android系統為例,在2.2時代,Dalvik 虛擬機增加了 JIT,運行速度相比之前版本顯著提升,但是由於 Android OS 的開放性和基於Dalvik 虛擬機運行的特點,用戶體驗流暢度和 IOS 相比還是有差距的。(為什麼 Android 比 Windows Phone 和 iOS 卡頓情況嚴重?)

Google 從4.0之後一直在改進 Android OS 的性能問題,終於在4.4上推出了 ART (Android RunTime Android runtime_百度百科) ,用來取代 Dalvik 虛擬機,和 Dalvik 虛擬機相比,一個最大的變化是在 ART 環境下,代碼是預編譯成機器碼的,即 與 JIT (Just In Time) 相對的AOT(Ahead Of Time) 模式。

在 ART 環境下,預編譯是在應用安裝過程中的,因此Android 應用的安裝過程會比之前稍慢,安裝後的也會比 Dalvik 下佔用更多空間,但是運行時的速度會有很大提升。

在下一代 Android 系統上,ART 模式將會默認啟用,原生的4.4系統可以在開發者選項下選擇切換。


「首先代碼優化的種種策略都是基於各種各樣的假定,假定不一定會成真,即使費盡心機做的代碼優化,也有可能在現實中無法提高性能。」
不同意,這明明是jit的優點好嗎,gcc/g++之類的靜態編譯器才需要各種假定,jit可以通過之前解釋器的 profile獲得精確的運行時信息,它的假定比gcc之類準確的多

-----
原來是想評論 @梁川 的答案,不小心回答了,求摺疊


推薦閱讀:

做軟體開發但是現在公司學不到技術性東西想跳,自己又技不如人怎麼辦?
各種語言寫網路爬蟲有什麼優點缺點?
大文件傳輸主要技術瓶頸都有哪些?如何處理的?
HTTPS除了使用CA機構頒發的數字證書外,有沒有使用數字簽名技術驗證內容的完整性?

TAG:編程語言 | Ruby | Python | Java | 即時編譯JIT |