1. A
Just
In
Time
Compiler
for
CRuby
CRuby言語処理系向けJITコンパイラ
Masahiro
Ide
Yokohama
Na=onal
University
RubyKaigi
2014
2014年9月19日(金)
1
2. Outline
• Ruby
• Just-‐In-‐Time(JIT)
compiler
• RuJIT:
a
JIT
compiler
for
Ruby
※あまり実装よりの内容ではありません
また,CRubyの実装に詳しくない人でも分かる
内容(にしたつもり)です.
2
3. Ruby
Language
and
Implementa=on
• Ruby
is
a
dynamically
typed
scrip=ng
language
– Support
excep=on,
garbage
collec=on,
con=nua=on
– CRuby
is
Ruby
interpreter
in
C
(de
facto
standard
implementa=on
of
Ruby)
– Many
Implementa=ons
JRuby(Java),
IRonRuby(.NET),
Rubinius(Ruby),
MRuby(C),
Topaz(PyPy)
– Numeric
benchmarks
10-‐100
=mes
slower
than
C
• Need
to
improve!!!
3
4. CRuby
Internal
YARV
bytecode
#toplevel
09
putobject
100
11
send
:=mes,
block
17
leave
#block
02
putself
03
getdynamic
i
06
send
:puts,
nil
14
leave
Ruby
code
100.=mes
do
|i|
puts
i
end
Parser
AST
Bytecode
Compiler
Interpreter
Memory
Manager
C
library
CPU
4
5. HOW
TO
SPEEDUP
RUBY
1. Translate/Compile
to
low-‐level
language
– プログラム実行中にコンパイル→
Just-‐In-‐Time(JIT)
– プログラム実行前にコンパイル→Ahead-‐Of-‐Time(AOT)
– Target
• Java
bytecode
(JRuby),
.NET
CLR
(IronRuby),
LLVM
IR(Rubinius)
2. Speedup
Interpreter
– ディスパッチ方法の工夫
– メソッドキャッシュ
– …
5
6. Outline
• Ruby
• Just-‐In-‐Time(JIT)
compiler
• RuJIT:
a
JIT
compiler
for
Ruby
– Design
– Usage
– Current
status
6
7. RuJIT
• A
Trace
based
JIT
compiler
for
CRuby
– Like
Firefox’s
javascript
VM
approach
• Based
on
current
version
of
CRuby
• Started
in
April
2014
• More
Speed,
Speed,
Speed
– …
but
maintain
compa=bility
with
current
Ruby
7
8. Implementa=on
Strategies
• Objec=ve
– Improve
Ruby’s
performance
• Development
policy
– Low
development
cost
• 1
person,
2~3
month
– High
extensibility
• Easy
to
develop
new
op=miza=on
techniques
– 実用的なソフトウェア
• いつかRubyのメインラインに
→この方針に基づいて設計を行っている
8
9. RuJIT
overview
RuJIT
YARV
bytecode
#toplevel
09
putobject
100
11
send
:=mes,
block
17
leave
#block
02
putself
03
getdynamic
i
06
send
:puts,
nil
14
leave
Ruby
code
100.=mes
do
|i|
puts
i
end
Parser
AST
Bytecode
Compiler
Interpreter
Memory
Manager
C
library
CPU
Na=ve
code
9
10. RuJIT
Design
• Trace
based
JIT
compiler
– Detect
hot
path(e.g.
loop)
• Intermediate
Representa=on
(IR)
for
op=miza=on
– Low
level
– Contain
run=me
informa=on
• Na=ve
Code
translator
– Use
C
compiler
(GCC,
clang)
as
backend
10
YARV
Trace
Selec=on
Engine
Trace
Cache
RuJIT
Run=me
CRuby
Run=me
IR
Generator
Op=mizer
invoke
Code
Generator
C
Compiler
Na=ve
code
Trace
compile
11. トレース方式コンパイラの実行の流れ
11
def square(x)!
return x * x!
end!
!
i = 0; y = 0!
while y < 100000!
y += square(i)!
i = i + 1!
end
12. Construct
trace
def square(x)!
return x * x!
end!
!
i = 0; y = 0!
while y < 100000!
y += square(i)!
i = i + 1!
end
12
BB0:
guard_method_redefine(Fixnum.*)
guard_method_redefine(Fixnum.+)
guard_method_redefine(Fixnum.<)
BB1:
guard_is_fixnum(i)
push_frame
#
invoke
square
guard_method_redefine(Fixnum.*)
tmp
=
fixnum_mul(i,
i)
pop_frame
#
leave
square
guard_method_redefine(Fixnum.+)
y
=
fixnum_plus(y,
tmp)
guard_method_redefine(Fixnum.+)
i
=
fixnum_plus(i,
1)
guard_method_redefine(Fixnum.<)
tmp2
=
fixnum_lt(i,
100000)
guard_nil(tmp2)
jump
BB1
トレース
13. Op=mize
trace
def square(x)!
return x * x!
end!
!
i = 0; y = 0!
while y < 100000!
y += square(i)!
i = i + 1!
end
13
トレース
BB0:
guard_method_redefine(Fixnum.*)
guard_method_redefine(Fixnum.+)
guard_method_redefine(Fixnum.<)
BB1:
guard_is_fixnum(i)
push_frame
#
invoke
square
guard_method_redefine(Fixnum.*)
tmp
=
fixnum_mul(i,
i)
pop_frame
#
leave
square
guard_method_redefine(Fixnum.+)
y
=
fixnum_plus(y,
tmp)
guard_method_redefine(Fixnum.+)
i
=
fixnum_plus(i,
1)
guard_method_redefine(Fixnum.<)
tmp2
=
fixnum_lt(i,
100000)
guard_nil(tmp2)
jump
BB1
RuJIT
can
hoist
guard_method_redefine
14. Op=mize
trace
def square(x)!
return x * x!
end!
!
i = 0; y = 0!
while y < 100000!
y += square(i)!
i = i + 1!
end
14
トレース
BB0:
guard_method_redefine(Fixnum.*)
guard_method_redefine(Fixnum.+)
guard_method_redefine(Fixnum.<)
BB1:
guard_is_fixnum(i)
push_frame
guard_method_redefine(Fixnum.*)
tmp
=
fixnum_mul(i,
i)
pop_frame
guard_method_redefine(Fixnum.+)
y
=
fixnum_plus(y,
tmp)
guard_method_redefine(Fixnum.+)
i
=
fixnum_plus(i,
1)
guard_method_redefine(Fixnum.<)
tmp2
=
fixnum_lt(i,
100000)
guard_nil(tmp2)
jump
BB1
RuJIT
can
remove
method
frame
15. Op=mize
trace
def square(x)!
return x * x!
end!
!
i = 0; y = 0!
while y < 100000!
y += square(i)!
i = i + 1!
end
15
トレース
BB0:
guard_method_redefine(Fixnum.*)
guard_method_redefine(Fixnum.+)
guard_method_redefine(Fixnum.<)
guard_is_fixnum(i)
BB1:
guard_is_fixnum(i)
push_frame
guard_method_redefine(Fixnum.*)
tmp
=
fixnum_mul(i,
i)
pop_frame
guard_method_redefine(Fixnum.+)
y
=
fixnum_plus(y,
tmp)
guard_method_redefine(Fixnum.+)
i
=
fixnum_plus(i,
1)
guard_method_redefine(Fixnum.<)
tmp2
=
fixnum_lt(i,
100000)
guard_nil(tmp2)
jump
BB1
RuJIT
can
hoist
guard_is_fixnum
Because
variable
‘i’
is
always
fixnum
16. Op=mize
trace
def square(x)!
return x * x!
end!
!
i = 0; y = 0!
while y < 100000!
y += square(i)!
i = i + 1!
end
16
Na=ve
code
BB0:
guard_method_redefine(Fixnum.*)
guard_method_redefine(Fixnum.+)
guard_method_redefine(Fixnum.<)
guard_is_fixnum(i)
BB1:
tmp
=
fixnum_mul(i,
i)
y
=
fixnum_plus(y,
tmp)
i
=
fixnum_plus(i,
1)
tmp2
=
fixnum_lt(i,
100000)
guard_nil(tmp2)
jump
BB1
トレース
17. トレースからインタプリタへの移行
YARV
Bytecode
17
Fixnum.*
is
redefined!
Invalidate
compiled
code
and
Fall
back
to
yarv
interpreter
#toplevel
09
putobject
100
11
send
:=mes,
block
17
leave
#block
02
putself
03
getdynamic
i
06
send
:puts,
nil
14
leave
Parser
AST
Bytecode
Compiler
Interpreter
CRuby
BB0:
guard_method_redefine(Fixnum.*)
guard_method_redefine(Fixnum.+)
guard_method_redefine(Fixnum.<)
guard_is_fixnum(i)
BB1:
tmp
=
fixnum_mul(i,
i)
y
=
fixnum_plus(y,
tmp)
i
=
fixnum_plus(i,
1)
tmp2
=
fixnum_lt(i,
100000)
guard_nil(tmp2)
jump
BB1
26. Conclusion
• RuJIT
:
a
trace
based
JIT
compiler
for
CRuby
– トレース方式JITコンパイラによりRubyスクリプトのうち
頻繁に実行される箇所を機械語へ変換
– How
to
use
RuJIT
1. git
clone
git@github.com:imasahiro/rujit.git
2. cd
/path/to/rujit
3. ./configure
&&
make
&&
make
install
4. ruby
hello_world.rb
• 今後の課題
– 互換性(Trace
API,
Excep=on,
Thread対応)
– Need
more
test
26