JRubyにおけるInvokeDynamicの
利用パターンと性能評価
日本JRubyユーザ会 中村浩士(なひ)
@nahi nahi@ruby-lang.org
資料: http://bit.ly/jjug-indy-jruby
サンプル: http://bit.ly/jjug-indy-jruby-code
自己紹介
ネットワークセキュリティ関連のシステム開発
C/C++ (18年)、Java (13年)、Ruby (13年)
余暇のOSS開発: CRuby (8年)、JRuby (2年)、
soap4r、httpclient他
JRubyとは - http://jruby.org/
最新リリース版は1.6.6(もうすぐ1.6.7)
JVM上で動作するRuby(動的型付け言語)
Open Source (CPL, GPL, LGPL)
開発開始から10年
フルタイム開発者登場から5年
本日のゴール
JRubyを題材に、InvokeDynamicの実例を見る
伝えたいこと
InvokeDynamic機能のおさらい
呼び出し(invokedynamic)とメソッド(MH)を実行時にリンク
動的型付け言語で、Javaの呼び出しと同じ最適化
InvokeDynamic
利用パターン
MH juggling in JRuby
InvokeDynamicによる最適化のコツ
呼び出し型の凡例:
Objectとlongの2引数、Objectが戻り値
→(Object self, long value)Object
JRuby InvokeDynamic利用パターン
1. 文字列リテラル
2. その他リテラル
3. 擬似定数
4. インスタンス変数
5. メソッド呼び出し
6. 算術演算呼び出し
1. 文字列リテラル
適用先: リテラル文字列
message = "Hello"�message << name << "!"
呼び出しの型: (ThreadContext ctx)Object
&jrNewString(ctx, str):ターゲット
&insert(str = "Hello"):引数を1つ挿入
合成
ThreadContextは、Threadなど実行環境情報を格納したオブジェクト。どの呼び出しにも必須。
2. その他リテラル
適用先: 文字列以外の不変リテラル
times = 10000�matcher = /[A-Z][a-z]*/
呼び出しの型: (ThreadContext ctx)Object
&constant[10000]:常に10000を返すMH
&drop(ctx):引数を1つ削る
3. 擬似定数
適用先: Rubyの定数参照
&thisMethod(ctx):同じMHを再設定(→値を再取得)
&constant[obj]:現在の値を定数として返すMH
&drop(ctx):引数を1つ削る
&SwitchPoint(&,&):任意の定数が定義されたら破棄
DEFAULT = Container.new.freeze�comtainer = DEFAULT�DEFAULT = nil
呼び出しの型: (ThreadContext ctx)Object
4. インスタンス変数
適用先: インスタンス変数アクセス
module Cache� def cache(value)� @cache = value� end�end�class Foo� include Cache�end�class Bar� include Cache�end
4. インスタンス変数(続き)
&jrGetVariable(obj, index):ターゲット
&thisMethod(self):同じMHを再設定(→selfの更新)
呼び出しの型: (Object self)Object
&insert(obj = self, index = 2):引数挿入
&filterRetval(&nullToNil):戻り値変換の合成
&jrTestClass(self):クラスが前回と同じかテスト
&GuardWithTest(&,&,&):クラスが前回と違えば破棄
5. メソッド呼び出し
適用先: 任意のメソッド呼び出し
$name = [:JRuby, "Ruby", 42]�def process(router)� router.say_hello($names.sample)�end
呼び出しの型:
(ctx, self)Object
(ctx, self, arg1)Object
(ctx, self, arg1, arg2)Object
(ctx, self, arg1, arg2, arg3)Object
(ctx, self, arg[])Object
5. メソッド呼び出し(続き)
&jrTestClass(self):クラスは同じか
&drop(ctx, self):引数を一つ落とす
&GuardWithTest:クラスが前回と違えば破棄
&SwitchPoint:そのクラスでメソッドが定義されたら破棄
呼び出しの型: (ctx, self, arg1)Object
&jrTarget(ctx, self, arg1):ターゲット
&thisMethod(ctx, self, arg1):再設定へ
&thisMethod(ctx, self, arg1):再設定へ
6. 算術演算呼び出し
適用先: 右辺が整数、小数の算術演算
fib(n - 2) + fib(n - 1)�display if (x >= 5)�elapsed = msec * 1000.0
呼び出しの型: (ctx, self)Object
&jrInvoke(ctx, self, value):直接呼び出し
&jrTestClass(self):左辺は整数か
&jrFixnumMinusTwo(ctx, self):ターゲット
&drop(ctx):引数を1つ落とす
&GuardWithTest:左辺がFixnumならショートカット
JRuby + InvokeDynamic
性能評価
Hooray JVM!
マイクロベンチマーク
文字列リテラル
その他リテラル
擬似定数
インスタンス変数
メソッド呼び出し
算術演算
全て
※高さはJava 7非indyを1としたときの速度比率。高いほうが速い。
※個々のパターンの処理のみを繰り返しループして測定。
二分木ベンチマーク
AVL木(再帰)
赤黒木(ループ)
※高さはJava 7非indyを1としたときの速度比率。高いほうが速い。
InvokeDynamicまとめ
速い(ちゃんと使えば)
複雑(まだ使いこなせてない)
言語処理系が自身で最適化するよりは楽
→ 最適化はJVMに任せる
適用範囲の終わりはまだ見えない; 例えば