JIT
什么是JIT
JIT = Just In Time即時編譯,是動態編譯的一種形式,是一種優化虛擬機運行的技術。
程序運行通常有兩種方式,一種是靜態編譯,一種是動態解釋,即時編譯混合了這二者。Java和.Net/mono中都使用了這種技術。
然而IOS中禁止使用(不是針對JIT,而是所有的動態編譯都不支持)!
為什么要使用JIT
解釋執行:
效率低。
代碼暴露。
靜態編譯:
不夠靈活,無法熱更新。
平台兼容性差。
JIT:
效率:高於解釋執行,低於靜態編譯。
安全性:一般都會先轉換成字節碼。
熱更新:無論源碼還是字節碼本質上都是資源文件。
兼容性:虛擬機會處理平台差異,對用戶透明。
JIT是如何實現的
這里講的實際上是JIT的一個變種:自適應動態編譯(adaptive dynamic compilation)。它分為兩種:Method JIT和Trace JIT。
如圖所示,這是jvmjit的流程:
簡單來講:
跟蹤熱點函數或trace,編譯成機器碼執行,並緩存起來供以后使用。
非熱點函數解釋執行。
為什么只編譯熱點函數?
對只執行一次的代碼而言,解釋執行其實總是比JIT編譯執行要快。對這些代碼做JIT編譯再執行,可以說是得不償失。而對只執行少量次數的代碼,JIT編譯帶來的執行速度的提升也未必能抵消掉最初編譯帶來的開銷。只有對頻繁執行的代碼,JIT編譯才能保證有正面的收益。
LuaJIT
vs. Lua
Lua主要由以下三部分組成:
- 語法實現。
- 庫函數。
- 字節碼。
LuaJIT主要由以下四部分組成:
- 語法實現。
- Trace JIT編譯器。
- 庫函數。
- 原生庫++(強化過的原生庫)
- bit
- ffi
- jit
- 字節碼
注:最新luajit對應lua5.1.5。
trace jit編譯器
與jvmjit大致相同。
所謂trace便是一段線性的字節碼序列。熱點trace被編譯成機器碼,非熱點trace解釋執行。
注:並不是所有的代碼都能被JIT。(NYI)
bytecode
bytecode基本上可以認為是虛擬機的指令碼(“基本上”是因為luajit使用了uleb128)。
優點:
- 減少文件大小。
- 生成函數原型更快。
- 增加被破解的難度。
- 對源代碼輕微的優化。
- 庫函數和可執行文件
編譯步驟分三步走:
minilua:實際上是lua原生代碼的一個子集,用來執行lua腳本並生成平台相關的指令。
buildvm:用來生成操作碼/庫函數到匯編/C語言的映射,用來jit編譯。
lib
exec:可以執行lua代碼活轉換字節碼。
編碼
命令行執行
luajit –b <in> <out>。
虛擬機會判斷是否是字節碼,所以無需做額外的操作。
另外,可以混用,即:一部分文件編成字節碼,另一部分保持源代碼。
iSO64位報錯問題
Cannot load incompatible bytecode!
這個錯是因為在luajit里使用gcr用來比較對象指針,在64位環境下只有47位有效值(默認用戶內存不會超過128T)。其余17位中有4位保存對象類型,即一段內存中保存了兩條信息。所以在函數棧操作中有些地方需要一個空值占位。因為字節碼直接反映了函數棧操作,所以64位和32位字節碼不同。