RyuJIT為什麼比JIT64編譯速度快?
CoreCLR總算開源,得以一睹RyuJIT的真面目:coreclr/src/jit at master · dotnet/coreclr · GitHub
粗略看了一下代碼,本以為全新寫的JIT編譯器代碼會比較乾淨,但卻意外發現它的代碼似乎有不少地方都跟老的JIT編譯器混在一起,要挑清楚來讀略頭大。想問問對CLR實際實現熟悉的大大們,RyuJIT相對JIT64在編譯器速度上到底快在哪裡?
MSDN上的RyuJIT系列文章:JIT, NGen, and other Managed Code Generation Stuff其中這篇 RyuJIT: The next-generation JIT compiler for .NET 提到:RyuWHAT? The .NET Code Generation team has been working on a new, next-generation x64 compiler, codenamed RyuJIT. This new JIT is twice as fast, meaning apps compiled with RyuJIT start up to 30% faster (Time spent in the JIT compiler is only one component of startup time, so the app doesn』t start twice as fast just because the JIT is twice as fast.) Moreover, the new JIT still produces great code that runs efficiently throughout the long run of a server process.
從代碼看RyuJIT的寄存器分配器用的是線性掃描(LSRA)。以前的JIT64的寄存器分配器用的是什麼演算法,是圖著色(graph coloring)么?是不是在這塊帶來了性能差距的大頭?
P.S. dotnet/coreclr下面只有src和tests目錄;為啥沒有doc目錄!doc/BookOfTheRuntime在哪裡啊在哪裡? ToT喔,@Thomson大大提醒GitHub上已經有人提issue了:[Missing?] documentation · Issue #26 · dotnet/coreclr · GitHub 坐等文檔更新:RyuJIT的文檔在CoreCLR上有了:coreclr/ryujit-overview.md at master · dotnet/coreclr · GitHub2015-02-08更新:dotnet/coreclr下果然開始有文檔更新了。第一個是:coreclr/intro-to-clr.md at master · dotnet/coreclr · GitHub贊到爆啊。Vance Morrison大大不知道現在還在不在CLR組呢?
被R大 @RednaxelaFX 邀了!!!看來必須得答了。首先聲明一下自己只是在jit team待了幾個月,那時候主要是和Carol阿姨搞SIMD優化,很多地方只能是有大體概念,很多細節也不是非常清楚,現在本人主要搞得是CLR 的debuging 和 profiling api已經不參與jit的開發了,jit team現在也不署於CLR管轄了。如果下文有什麼不對的地方歡迎友好討論,也希望現在開源後有更多的高手來研究解讀。對了如果關注debugger實現的朋友,非常歡迎交流:),鄙人現在的專長是debugging debugger;)
回歸正題,今天就這個問題特意和jit的兩個大神討論了一下。首先必須要清楚的是jit64 和 jit32的區別,jit32的設計就是為了jit,所以大部分演算法是linear的。而jit64的是以static compiler為基礎的, 具體來說就是UTC為原型設計的,如果熟悉.net native的人知道.net native的編譯後端就是用的UTC,而微軟C++的後端也是UTC,UTC的缺點是很多時候演算法都是平方級或者更高,所有jit速度會很慢,但所生成的代碼質量會很高。大概3年前jit team的幾個大牛Kevin Frei,David Detlefs, Carol Eidt 和 Matt Grice提出了要設計一個新的jit,這個就是現在的reyujit--一個編譯速度快但代碼生成質量相對不錯的64位jit換句話說讓jit32編譯64位代碼。Ryujit很多演算法是linear的,後端基本上和jit32是一樣,這裡先科普一下ryujit的前端後端定義,首先Ryujit大體上由importer,morph/flowgraph, ssa, cse, loop cloning, rationalizer, lowering, LSRA, codegen, emit組成。jit team內部從importer到loop clone稱為前端,把rationalizer到emti叫後端。 所以細心的同學一能發現其實rationalizer主要目的就是要把新寫的前端「嫁接」到老的jit32的後端。雖然ryujit後端是老的jit32但編譯的速度卻比jit64提升了很多。並且Carol大神在jit32的基礎上把register allocation的優化了,這點R大已經看出來了,ryujit用的是LSRA而老的jit64是graph coloring,所以速度提升你懂的。 而前端的話編譯速度提升主要是靠importer, 實際上jit的編譯的時候大部分時間一個花在了請求type system,而還有一部分就是花在了importer,老的jit64用的是把msil轉換成linear representation,而新的ryujit是用的樹形結構表示。對於msil這種stack machine 的語言,顯然轉換成樹狀結構會快很多,而把msil轉換成linear representation並且不丟失type信息,這個代價是相當大。 並且前端大部分是優化或者重新寫過的。綜上所述jit32的後端LSRA和importer還有大部分基於linear的演算法是ryujit編譯速度比jit64快的原因。對了之前有人 @Thomson 提到book of runtime, 那本書側重的是VM,即便jit的部分也是側重的是jit和vm交互GC,EH這些方面,但jit裡面設計思想實現細節沒有講。David之前對內部寫過一個ryujit的設計文檔,那個是專門講ryujit的設計思路的,希望以後能公開這個文件。
最後感嘆一下,今天jit64主要維護人可能也是現在唯一的維護人matt走了,所以jit64真的已經死了。jit team的老大kevin frei和我的好朋友max在去年去了facebook,David在13年去了google,Matt今天也去了facebook,老的jit team剩下的人已經不多了。。。答這個題目的時候又想起了和他們一起工作的時候,那些天天看彙編jit dump的日子,能和這群人工作是我的光榮,希望他們今後一切順利:)快在哪裡倒是不知道,JIT64是從VC++ back-end移植過來的,那就很可能用的是graph coloring來分配寄存器,這樣看的話是會快不少。BookOfTheRuntime在github上已經有人open issue了,據說是正在把格式轉成markdown的,很快會放出來。但是應該2.0後就沒人更新這個了。
EDIT: 看了下文檔,和現在的代碼還有一定契合度,我有點懷疑這個JIT是不是完全重寫的了。文檔寫的還是挺細的,大家一起等吧。推薦閱讀:
※程序集什麼玩意?我知道其表現形式為dll和exe,但是exe不是直接執行的文件嗎?而dll只是類庫,供exe調用代碼?