LLVM相比於JVM,有哪些技術優勢?

JVM是執行byte code,LLVM是執行IR,如果都是處理中間碼,LLVM有什麼優勢?

感謝Tim Shen的回答,再更新下描述。

假定有這樣的場景,我們對一段表達式做代碼生成,然後再執行生成的代碼。

比較下面兩種方案:

1. 生成java的byte code,然後邊解釋邊JIT。

2. 生成LLVM的某種code,然後編譯成bin code,再執行。

第一種方案的優點:解釋執行一開始還是挺快的,那麼響應就比較好,JIT還可以做運行時的優化

第二種方案:編譯可能要慢,但是編譯完以後生成的二進位執行效率不錯,因為java的執行模型是堆棧式的,而LLVM可以基於寄存器。現在的LLVM可能還有一個不足,就是運行時優化還不如JVM?

這樣理解是否正確?


這問題是個好坑。肯定很多人都對LLVM與JVM的關係有各種誤解,包括從業人士也會有片面理解。本來看到這個問題就想馬上回答的,被小葉子攪和的根本脫不開身…ToT

還好現在已有的回答都在靠譜的方向上,感覺題主應該已經能充分感受到正解的方向性了。我這裡就寫點已有答案沒寫的或者表述不充分的吧。

簡單說,LLVM與JVM不是同一範疇的東西,就像蘋果和梨,本來不能直接對比。

不過在出於某種特定目的去考察的時候,兩者就有一定可比性,例如說同樣是要實現一門編程語言,在選擇代碼生成器(code generator)的解決方案時,是選擇LLVM、GCC等,還是JVM、.NET等,還是自己手寫,這個角度就可以對它們進行特定的比較。好比想要保健身體而吃水果,是吃蘋果更有益還是吃梨更有益,兩者可以做特定的比較。

而換個角度,LLVM與JVM並不是互斥的兩個技術——兩者可以有機的結合在一起。我現在所在的Azul Systems公司就在做基於LLVM的JVM JIT編譯器,微軟也有一個組在做基於LLVM的.NET JIT/AOT編譯器LLILC。好比蘋果樹和梨樹可以嫁接,在梨樹上嫁接蘋果樹得到梨蘋果(嗯不是蘋果梨)。

正好我們對JVM和LLVM都很熟悉,回答這個問題有更多第一手信息吧。詳細請跳傳送門:如何看待微軟LLILC,一個新的基於LLVM的CoreCLR JIT/CoreRT AOT編譯器? - RednaxelaFX 的回答

================================================

一些雜談

JVM是一套規範,有許多不同的實現,各自取捨差異很大。不指定到非常細緻的供應商、目標平台、版本信息的話,根本無法確定說的是怎樣的實現。

不要說多個JVM了,就算一個JVM里也可能有多個編譯器,要說編譯器實現細節的話可得指定清楚具體是哪個了。

LLVM則是單一的實現,雖然可以拆開來有很多花樣可玩。只要指定版本和目標平台,還有指定是公版還是帶有定製,大家都知道說的是什麼。

拿JVM的Java bytecode來跟LLVM IR比的話,這還可以比,因為這都還是在表面跟編譯器前端打交道的「接觸面」上,定義標準而抽象。但下到下面的實現就必須指定實現來比較了。

目前最流行的JVM實現毫無疑問是OpenJDK / Oracle JDK里的HotSpot VM,正好我也對它最熟悉,所以下面會使用它和若干其它JVM實現來跟LLVM做一下對比。

大家提到「JVM」的時候,大都是把它當作一個黑盒子來用,而不會把它拆開來單獨使用其中的一些組件。這個黑盒子包含一籃子運行時服務,典型情況不但有負責執行代碼的解釋器或JIT編譯器,還有GC、線程支持、元數據管理(例如類載入)等功能,外加配套的Java標準庫。這些全部打包在一起構成Java運行時環境(Java Runtime Environment),作為一個整體提供給用戶使用。

生成Java bytecode就是指望讓JVM來運行它,而且通常不會指望JVM把位元組碼編譯出來的機器碼吐回給用戶做後續的操作——也就是說很少有人會想拿JVM當作靜態編譯器的後端來用(呃雖說其實這種可能性和相關實驗也是存在的…畢竟奇葩)。

而LLVM是一個編譯器套件,用法就豐富的多。

最常規的,用它來做靜態編譯器後端,沒問題;

做動態編譯器後端,也能行。

單獨拿出LLVM的一個或多個pass來做IR到IR的轉換也不在話下。

基於LLVM來做調試器,也沒問題。

拿LLVM IR當跨平台彙編用也很有趣。

還有許許多多基於LLVM做代碼分析的。

================================================

IR層面的對比

之前回答的一個問題正好是預備知識:IR和ByteCode有什麼區別? - RednaxelaFX 的回答

JVM的Java bytecode跟LLVM IR都可以看作編譯器IR。但它們的設計目的是完全不同的,因而不適合直接比較優劣。

Java bytecode與LLVM IR都用於表述計算的模型,但兩者所處的抽象層次不同。

Java bytecode更高層(更抽象),是一種非常高層的IR。其位元組碼的語義與Java語言的語法結構有非常直接的對應關係,包含大量(類Java的)面向對象語言的高層操作,例如虛方法調用、介面方法調用等。

它最大的「缺點」——沒有暴露任何顯式的指針操作,因而用於實現一些精密操作時顯得拘謹。而且它遵循Java的類型系統,(到Java 9為止)不允許在對象內嵌套對象,在需要精確指定內存布局的場景上無能為力。這些「缺點」都不是一個VM的必然,跟Java位元組碼相似的.NET的MSIL就有這些自由度。看這個把CoreCLR的JIT編譯器單獨拆出來插到CPython上的例子,體會一下MSIL位元組碼的表達能力:Pyjion的代碼質量一例 [20160221] - 編程語言與高級語言虛擬機雜談(仮) - 知乎專欄

LLVM IR更低層(更接近機器),但尚未完全暴露出具體平台相關的特徵,所以可以看作一種中層IR。簡單的說,C語言能表述的,在LLVM IR里也可以直接表述(沒有例外),而C語言所不能表述的,在LLVM IR里多半也不能直接表述(有例外)。

(待續…什麼基於棧基於虛擬寄存器之類的表皮差異也可以討論,還有很多別的更有趣的也可以討論,例如說用作編程語言實現的code generator時的差異。估計題主關心的也就是這個,但這個展開說可以寫太多,一時來不及碼字。)

舉個小例子來說明Java bytecode與LLVM IR都無法(直接)表述的語義。

假如我們要寫一個位元組碼解釋器,想儘可能利用平台相關的資源,特別是想要把解釋器棧跟native棧混在一起的話,就會有直接在自己的代碼里顯式操作棧指針寄存器的需求。在這一點上,Java bytecode的抽象層次太高自然是無法(直接)表述,而LLVM IR也不可以——這就是C語言不能表述的,LLVM IR也無法表述的一種例子。

我們有同事嘗試過用LLVM IR來實現跨平台的高性能位元組碼解釋器(希望實現的性能特徵跟HotSpot VM的template interpreter類似),但苦於不能方便的在LLVM IR里表述「棧指針寄存器」這個概念,而我們的JVM的解釋器調用約定(calling convention)又有點特別,這實驗就暫時放下了。中間其實做出過幾個實驗版是能在x86-64上跑的,但在表述RSP的時候都用了hack(例如說利用intrinsic…),離完美還很遙遠。

再舉一個小例子來說明LLVM IR能表述,而C語言無法直接表述的語義。

LLVM IR允許對指針指定「地址空間」(address space)。默認的address space 0代表普通內存,而非0的地址空間可以由LLVM的使用者自行指定其語義,例如說顯存,又例如說GC管理的內存,等等。最重要的一點是:不同地址空間之間的指針不能混用——這也就隱含了LLVM不會混合優化不同地址空間之間的指針,這是保證LLVM不亂動指針的很重要的手段。

================================================

JVM的JIT / AOT編譯器的複雜度

一個JVM的JIT編譯器可以容忍多大的複雜度呢?舉個例子,前面提到,Azul Systems正在給JVM開發基於LLVM的JIT編譯器。這個編譯器目前是用類似Clang -O3的pass來把Java位元組碼編譯到機器碼的——是的,我們能容忍這樣的複雜度,並不是稍微複雜一點的演算法就不敢用的。並且它依託於我們的JVM原本就有的profiling和code cache management等基礎設施,自帶PGO,最終的代碼管理也不依賴MCJIT。

即便說回到現有的JIT編譯器,HotSpot VM與Zing VM的Server Compiler(C2),其中也使用了許多很重的演算法。

它目前最慢的組件是寄存器分配器,使用改進版的Chaitin-Briggs圖著色(graph coloring)演算法;

它的指令選擇用的是一種BURS(bottom-up rewrite system),這個階段的IR形式跟LLVM的SelectionDAG其實頗相似但比後者更完整一些,是對整個編譯單元的;

它有專門做全局調度(global scheduling),當把代碼確定調度到某個基本塊後,會在基本塊內做拓撲排序(以及其它的基於權重的排序)來實現局部調度(local scheduling)。大的全局調度只會做一次,但其實有若干小的全局調度是混在循環優化里做的。要想多加一些調度、排序代碼那也是不眨眼的,只要生成的代碼質量好;

它會創建許多現代編譯器里常見的輔助數據結構,例如dominator tree、loop nesting tree之類,並且會在某些範圍內維護這些數據結構的時效性。

而另外一個例子,IBM J9的Testarossa編譯器(J9 TR),雖然它最主要的應用場景是在IBM J9 JVM里充當JIT編譯器用,但同一個編譯器也有用於靜態編譯COBOL、C/C++等(Static Testarossa,sTR)。在以最高優化層次編譯Java位元組碼時,它能容忍的演算法複雜度同樣非常高。

這些JVM之所以能容忍JIT編譯器使用高複雜度的演算法,主要是通過多層編譯(tiered compilation)來達到啟動開銷與頂峰性能之間的平衡。最初代碼要麼用解釋器執行,要麼用較低優化程度的編譯器或者編譯器的較低優化級別來編譯,然後逐步把對性能影響大的代碼(「熱」的代碼)用更高優化級別來編譯。這樣就可以在初期用較低開銷達到較高的性能基準,同時並行的慢慢把性能推向峰值。

當然,即便JVM的JIT編譯器可以容忍高複雜度的演算法,但當其要支持的目標場景的常見情況可以用更低複雜度的演算法就達到一樣或相似的優化效果時,JVM實現們還是會傾向使用後者的。只是,當大家都把性能推向極限時,堆高複雜度的實現也不是啥稀奇事。

況且,JVM並不是只能有JIT編譯器。做AOT編譯器也是很自然的事。這些編譯器自然可以像一般C/C++編譯器那樣容忍更高的複雜度。這裡就算不展開說。先放傳送門把過去回答匯總下:Java中有類似於NGen的工具(AOT編譯器)嗎? - RednaxelaFX 的回答


這兩個怎麼比較的……

你看llvm自己都不稱自己為low levelvirtual machine了

當然,llvm依然可以jit執行 依然有人或者項目致力於使用llvm的jit

但是今天llvm的主要應用在靜態編譯上

順便附張昨天拍的照片

至於樓主的問題實際上是要比較jit和aot?那麼不要拿llvm出來比較嘛。

ps: 話說現在llvm各種拿bitcode做object code替代品 用來在後端前再造一層

然後會上有些人各種吐槽llvm層次太多


雖然我不了解JVM byte code的spec,但是我相信和LLVM IR確實屬於同一類東西,即中間表示。

中間表示往下就不一樣了。

LLVM 主打靜態編譯到機器碼,而不是像JIT那樣一邊解釋順帶編譯一下,所以幾乎不存在犧牲binary質量換取編譯速度的代碼邏輯,而編譯速度基本是「&比GCC快就好&不要太慢就好」。

LLVM 工具鏈中編譯器llc才是用得最多的。解釋器lli在我的經驗里基本是用來賣萌的。另外Clang應該是直接調用PassManager生成的binary,不過本質上和pipe進opt然後llc沒差。

LLVM JIT我並不了解,好像並沒有印象深刻的應用。但是由於復用的LLVM的代碼,所以「犧牲執行效率換取編譯速度」的邏輯可能並不充足,把它弄去實現解釋器不一定效果好。

而JVM以解釋器起家,後來JIT優化附體,基本上得保證編譯很快才行,很多優化演算法複雜度稍高就未必敢直接對所有代碼做,可能要等確認是hot path後再做。

所以LLVM簡直成了cutting edge 演算法們的遊樂場啊,什麼圖演算法都是加加加,圖結構跑跑跑,數據結構改改改,analysis做做做,完全不用擔心拖慢runtime。我想實現個優化隨手加個MachineFunctionPass把所有函數掃一遍,為每個函數的每個BasicBlock做一次拓撲排序(ScheduleDAG)幾乎不用動腦子。只有像SelectionDAG這種玩脫了才需要剎車(FastISel)和整改(Global ISel)。

JIT的收集運行時統計數據的優勢也希望被PGO趕上,但是PGO我同事還在做,不知道有沒有到能打的階段。


大牛們都說得很全了

去就補充一個聽來LLVM JIT的應用:

那個Matlab就是先生成自己的IR 再轉成LLVM IR

然後讓LLVM JIT執行

和mathwork的人閑聊時打聽到的 不保證完全正確


一個虛擬機,一個編譯器框架,兩個有毛關係?你拿llvm跟soot比或許更好些


看懂這篇文章先:

The Architecture of Open Source Applications: LLVM

其實就一圍繞 IR 建立起來的一堆工具庫而已。

至於 JVM, ... (⊙o⊙)…, 私以為和 LLVM 根本不是一回事,這是怎麼扯到一塊兒的... 對那些把他們扯到一塊長篇大論,聯想如此豐富還得高票的的答案,只能表示聳聳肩了。因為這其中最大的關聯可能就是 IR 和 bytecode 之間有什麼類似的東西了,別的,╮(╯▽╰)╭

單獨說下 LLVM 吧,圍繞 IR 建立工具庫的話,最致命的問題是: IR 的形式足夠好嗎? SSA 夠用嗎?

想讓 gcc 變戰五渣,╭(╯^╰)╮,哪那麼容易。


這是一個好問題,咋一看標題,還以為有人閑的沒事兒找噴呢。但一看內容描述,貌似還懂點兒,便陷入了深思……


二者的關係不大,但是,

聽說當年Chris Lattern開發LLVM 的時候參考了編譯原理的虎書,而虎書中的其中一本就是用Java 實現的,所以,


llvm和jvm沒有可比性

如果要說llvm和jvm具體的編譯技術 jvm各個實現都不同 你想問的是哪個呢


JVM 和 LLVM 為什麼沒有硬體實現? - Java 虛擬機(JVM)

JVM和LLVM差距還是蠻大的吧


有人可憐到去按舉報按鈕了?

JVM和LLVM的關係僅僅在於字面上有VM兩個字母。其他內涵完全沒有可比性。


我2012年在港科大研究LLVM的時候,還很清晰記得首頁第一句話就是,說LLVM是個虛擬機完全是個誤會。

它只是個模塊化更清晰,做研究更方便的編譯框架。


推薦閱讀:

過程間和過程內的數據流分析演算法在類似LLVM的IR或HotSpot C1的HIR中,是如何實現的?

TAG:Java虛擬機JVM | 編譯器 | LLVM |