什麼是method JIT? 什麼是DFG JIT? JSC的DFG JIT與V8 Crankshaft相比有何優劣?
JIT:一個方法式的JIT(a method jit)
*關於method JIT,可以參考另一份檔案。
關於解譯器(interpreter)和堆棧(stack,也就是寄存器檔案,register file)都是應用了方法式JIT(method JIT). 所謂簡單方法JIT(確實沒什麼特別的名字)做得事和位元組碼解譯器完全一樣,只不過它的結果是機器碼,而不是虛擬機令集。
也沒什麼好說的,JIT的結果可以減少調度的開銷,特別是允許對特定上下文進行編譯時間(原文:jitting the code has the result you would expect, reducing dispatching overhead, while at the same time allowing some context-specific compilation), 就比如當給一個變數賦個整數常量。這個JIT只能算是湊合(quick-and dirty), 所以傳統的Method JIT,像HotSopt
C1及C2, 並沒有明顯的好處。相對而言,寄存器式的VM位元組碼卻保證可以為JSC帶來顯著的提升,不過到目前為止,JSC在這個方向上並沒做什麼。
再思考一下,我認為對於JavaScript而言,CSE只有在你知道類型時才可能有作用,畢竟JS的valueOf()並不那麼靠譜!
DFG:JSC的Crankshaft? (dfg: a new crankshaft for jsc)?
由Gavin Barraclough和Filip Pizlo開發的資料流圖JIT(data flow graph,DFG JIT)是一種推測性最佳化技術。比如,當有如下JS代碼:
a[i++] = 0.7*x;
那麼a很可能是一個浮點型數組,而i則很可能是一個整數。你應該可以想到,如果使用原始的數組和整數操作,就能達到很好的效能,於是嘗試著將這份代碼按照這個假設編譯出了一個新的版本。如果這個方法最終失敗了(有不符先前假設的情況發生了),就把這份代碼中全身而退(bail out),再回到原先的method JIT。
事實上,解譯器(interpreter)和simple method JIT有一個清晰的位元組碼語義模型來確保在必要時從DFG JIT中輕易地退出來。只需要重建虛擬寄存器和Register Windows(奇怪的名字,和register file一樣. ^_^)的狀態,然後跳回原先的代碼即可。 (V8 稱之為"逆最佳化(deoptimization)"; DFG則稱之為"投機不成(speculation failure)".)
還有另一種從simple JIT跳轉到DFG JIT的方法,叫棧上替換(on-stack replacement, OSR). DFG JIT就是這樣做的。我就聽說用了OSR就可以在Kraken(一個JS效能測試器)測試中勝出,這個測試器會使用大量緊湊的迴圈,所以你必須能夠在不依賴函數的反覆進入的情況下最佳化你的代碼(Method
JIT就是這麼乾的!)。
當DFG JIT啟用後,解譯器(如果有的話)和簡單方法(simple method) JIT會對效能資料進行判斷,它會記錄代碼各部分的流轉類型(flow types)。如果一個迴圈被執行相當次數(目前是大於1000次),或者一個函數會調用了很多次(目前是70次), DFG JIT就會行動了。它將相應的位元組碼解析為SSA(Static Single Assignment)形式, 然後沿著執行路徑收集類型資訊。這個過程很像我在另一篇文章中所說明的情形。
JSC和Crankshaft的區別在於Crankshaft從Inline Caches中直接獲得類型資訊,而不是從代碼中檢測。我認為Crankshaft的實現更為優雅一些,但當GC(記憶體回收,garbage collection)釋放了緩衝(cache)後,它就會得很弱了。
我以前提到過inlining, DFG JIT就會使用它,並且像HotSpot一樣在解析時使用。類型分析(type profiling) (也稱為value profiling)組合運用一些簡易的靜態分析技術,使得DFG可以使int32和雙精確度資料類型區分開來。
有一件事DFG JIT目前還做不到,就是代碼移動中一些情況(code motion,又是一個編譯領域的名詞)。它雖然可以做去除無作用程式碼和公用運算式,但它的前提條件是必須能做類型分析(value profile)。而針對迴圈不變數的code motion,這是做不到的(因為值不變嘛)。
另外,DFG的寄存器分配器(register allocator)不及Crankshaft的好。這是由於JSC彙編造成的的阻礙。在構造良好的、健壯的程式碼片段的同時,JSC的組譯工具使用雙地址的介面(interface)而不是三址的。這意味著,不需要像add(dest,op1,op2)的方法,而是add(op1,op2),其結果被預設存在第一個運算元中。它是針對x86指令集的,但對於有更多寄存器的系統(如x86 - 64),這並不算什麼。
對於計算結果,需要更多代碼的基於計數的最佳化觸發方式並不是絕對必要, 但這種策略確實有個很好的特性:DFG的效能是可預見的且可衡量的。而另一方面的Crankshaft則使用統計方式觸發,其效能是一種統計式的分布。
對於效能,因為DFG僅在Mac OS 64位版本可用,所以可以使用AWFY on the mac進行測試。你一定要使用正確的工具進行效能評測。
看看結果就知道,JSC對V8基準(benchmark)的表現很不錯。有趣的是,對於SunSpider測試,JSC打敗了V8。不過V8一旦熱身後,仍然是表現最好的. JSC做得很不錯,而且正在逐漸改進中。
未來(future)
這就是JavaScriptCore. 當前團隊的三個人(是的),正專註在DFG JIT上。其中最關鍵的是什麼時候可以將DFG JIT運行到其它系統上。
JSC的另一個工作是新一代的記憶體回收行程(new generational garbage collector)。它確實在做,但很慢。雖然有了Card-Marking write barriers(這是一記憶體回收領域的專業名詞)的預留函數(stubs),但目前為止並沒有相應的實現。不過,至少JSC提供了Handle API,
蓋過了SpiderMonkey。
上一篇: JavaScriptCore, WebKit的JS實現(一)
轉載請註明出處: http://blog.csdn.net/horkychen