プログラミング初学者がLLVMとJITコンパイルについてまとめてみた
juliaでプログラミングを学び始めた頃、よく出てくるLLVMとかJITコンパイルの概念や違いがどうも腑に落ちなかった経験があった。そこで、かなり表層的な知識になってしまいますが、ここにメモとしてまとめておく。
間違っていたら、ご指摘いただけると幸いです。
目次
コンパイラ、JITコンパイル、LLVMの順で書く。
コンパイラの仕事
-
機械語への翻訳
-
最適な機械語を出す
-
有限のリソースの管理
JITコンパイル
JITコンパイルは、コンパイルが行われるタイミングのことである。JITとは、Just In Timeコンパイルの略で、関数が実行されるそのタイミングでコンパイルが行われるように設計されている。
例えば、以下のjuliaコードの場合を例にする。
function a(x::Int)
2*x
end
function b(x::Int)
x + 2
end
function c(x::Int, y::Int)
a(x) - b(y) #関数a, bがcの中で呼びだれた時に初めてコンパイルが行われる
end
関数の単位で実行されるタイミングでコンパイルが行われる。これをJITコンパイルという。上の例でいうと、関数a, bがcの中で呼びだされた時に初めてコンパイルが行われる
また、bという関数を少しだけ修正した場合、再びcを実行するときは変更がないaはそのままキャッシュから呼び出し、bの部分だけコンパイルが行われることになる。
なお、C言語は,test.cに含まれる関数a, b, cが含まれるmain文を全部実行前にコンパイルしてtest.oを作る。その際に、オプションが合って,許される限りの時間でコードの最適化が行われるようである。そのメリットはプログラム全体のソースコードを最適化できることにある。
一方、Pythonは,上から1行ずつ機械語に翻訳していくインタプリンティングが行われる。コンパイルが不要な分、実行速度が遅くなる。
LLVM
JITコンパイルとは、コンパイルがいつ行われるのかのタイミングの話であった。
それではコンパイルを誰が行うのであろうか。例えばjuliaでは、LLVMが基本的にコンパイラに相当する。
LLVMは、言語やハードウェアに非依存な共通の言語に移し替える。従来、コンパイラは複数あるが故にコンパイラごとにビルドしたものを複数用意しなければならなかったが、LLVMを使うことでビルドが一回で済むようになったことは画期的であった。
Juliaの関数が実際にどのように変換されているかは、llvm_codeを実行すると表示されるみたいです。
# 実行
code_llvm(c, (Int, Int))
結果
define i64 @julia_c_247(i64 signext %0, i64 signext %1) #0 {
top:
; @ REPL[19]:2 within `c`
; ┌ @ REPL[16]:2 within `a`
; │┌ @ int.jl:88 within `*`
%2 = shl i64 %0, 1
; └└
; ┌ @ REPL[17]:2 within `b`
; │┌ @ int.jl:87 within `+`
%.neg1 = sub i64 -2, %1
; └└
; ┌ @ int.jl:86 within `-`
%3 = add i64 %.neg1, %2
; └
ret i64 %3
}
参考