Weitere ähnliche Inhalte Ähnlich wie Java SE 7 InvokeDynamic in JRuby (20) Mehr von Hiroshi Nakamura (10) Kürzlich hochgeladen (12) Java SE 7 InvokeDynamic in JRuby1. #jt12_s204
Java SE 7 InvokeDynamic
in JRuby
日本JRubyユーザ会 中村浩士
@nahi nahi@ruby-lang.org
https://github.com/nahi
http://slidesha.re/JavaOneJpInvokeDynamic
3. Java SE 7 InvokeDynamicとは
Java SE 7に追加された新機能
変数に型のない動的型付け言語の性能向上支援
● Java仮想マシン(JVM)のバイトコードに
invokedynamic命令を追加
● java.lang.invoke.*に関連APIを追加
9. JIT最適化の例: インライン化
double addAllSqrts(int max) {
double accum = 0;
for (int i = 0; i < max; i++) {
accum = addSqrt(accum, i);
}
return accum;
}
double addSqrt(double a, int b) {
return a + Math.sqrt(b);
}
public static void main(String[] args) {
for (int i = 0; i < 100000; ++i) {
(new Target()).addAllSqrts(10);
}
}
10. JIT最適化の例: インライン化
% java -XX:+PrintCompilation
-XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining Target
66 1 Target::addAllSqrts (27 bytes)
67 2 Target::addSqrt (8 bytes)
@ 3 java.lang.Math::sqrt (5 bytes) (intrinsic)
@ 15 Target::addSqrt (8 bytes) inline (hot)
@ 3 java.lang.Math::sqrt (5 bytes) (intrinsic)
78 1 % Target::main @ 2 (28 bytes)
@ 12 Target::<init> (5 bytes) inline (hot)
@ 1 java.lang.Object::<init> (1 bytes) inline (hot)
@ 17 Target::addAllSqrts (27 bytes) inline (hot)
@ 15 Target::addSqrt (8 bytes) inline (hot)
@ 3 java.lang.Math::sqrt (5 bytes) (intrinsic)
@ 1 java.lang.Object::<init> (1 bytes) inline (hot)
11. JIT最適化の例 double addSqrt(double a, int b) {
return a + Math.sqrt(b);
% java -XX:+PrintCompilation
}
-XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining Target
66 1 Target::addAllSqrts (27 bytes) addSqrtをコンパイル
67 2 Target::addSqrt (8 bytes)
@ 3 java.lang.Math::sqrt (5 bytes) (intrinsic)
@ 15 Target::addSqrt (8 bytes) inline (hot)
@ 3 java.lang.Math::sqrt (5 bytes) (intrinsic)
78 1 % Target::main @ 2 (28 bytes)
@ 12 Target::<init> (5 bytes) inline (hot)
@ 1 java.lang.Object::<init> (1 bytes) inline (hot)
@ 17 Target::addAllSqrts (27 bytes) inline (hot)
@ 15 Math.sqrt呼び出しと加算を
Target::addSqrt (8 bytes) inline (hot)
@ 3 インライン化
java.lang.Math::sqrt (5 bytes) (intrinsic)
@ 1 java.lang.Object::<init> (1 bytes) inline (hot)
12. double addAllSqrts(int max) {
double accum = 0;
for (int i = 0; i < max; i++) {
accum = addSqrt(accum, i); }}
public static void main(String[] args) {
% java -XX:+PrintCompilation
-XX:+UnlockDiagnosticVMOptions 0; i < 100000; ++i) Target
for (int i = -XX:+PrintInlining {
(new Target()).addAllSqrts(10); }}
66 1 Target::addAllSqrts (27 bytes)
67 2 Target::addSqrt (8 bytes)
@mainをコンパイル
3 forの中にあるaddAllSqrtsおよび
java.lang.Math::sqrt (5 bytes) (intrinsic)
@ 15 その中身を全てインライン化
Target::addSqrt (8 bytes) inline (hot)
@ 3 java.lang.Math::sqrt (5 bytes) (intrinsic)
78 1 % Target::main @ 2 (28 bytes)
@ 12 Target::<init> (5 bytes) inline (hot)
@ 1 java.lang.Object::<init> (1 bytes) inline (hot)
@ 17 Target::addAllSqrts (27 bytes) inline (hot)
@ 15 Target::addSqrt (8 bytes) inline (hot)
@ 3 java.lang.Math::sqrt (5 bytes) (intrinsic)
@ 1 java.lang.Object::<init> (1 bytes) inline (hot)
13. double addAllSqrts(int max) {
double accum = 0;
for (int i = 0; i < max; i++) {
accum = addSqrt(accum, i); }}
OSR: On-stack public static void main(String[] args) {
% java -XX:+PrintCompilation
replacement
-XX:+UnlockDiagnosticVMOptions 0; i < 100000; ++i) Target
for (int i = -XX:+PrintInlining {
(new Target()).addAllSqrts(10); }}
66 1 Target::addAllSqrts (27 bytes)
67 2 Target::addSqrt (8 bytes)
@mainをコンパイル
3 forの中にあるaddAllSqrtsおよび
java.lang.Math::sqrt (5 bytes) (intrinsic)
@ 15 その中身を全てインライン化
Target::addSqrt (8 bytes) inline (hot)
@ 3 java.lang.Math::sqrt (5 bytes) (intrinsic)
78 1 % Target::main @ 2 (28 bytes)
@ 12 Target::<init> (5 bytes) inline (hot)
@ 1 java.lang.Object::<init> (1 bytes) inline (hot)
@ 17 Target::addAllSqrts (27 bytes) inline (hot)
@ 15 Target::addSqrt (8 bytes) inline (hot)
@ 3 java.lang.Math::sqrt (5 bytes) (intrinsic)
@ 1 java.lang.Object::<init> (1 bytes) inline (hot)
16. JVMでのメソッド呼び出し
public class Command {
void processOptions(String[] options) {
boolean result;
for (String opt : options) {
result = process(opt.concat("?!"));
}
}
boolean process(String opt) { ... }
void run() {
String[] options = { "yes", "no", "maybe" };
processOptions(options);
}
}
17. JVMでのメソッド呼び出し
public class Command {
void processOptions(String[] options) {
boolean result;
for (String opt : options) {
result = process(opt.concat("?!"));
}
}
boolean process(String opt) { ... }
void run() {
String[] options = { "yes", "no", "maybe" };
processOptions(options);
}
}
18. JVMでのメソッド呼び出し
<Command>
void processOptions(java.lang.String[]);
process(opt.concat("?!"));
20: aload_0 // thisであるCommandをスタックに入れる
21: aload 5
23: ldc #2
25: invokevirtual #3
28: invokevirtual #4
19. JVMでのメソッド呼び出し
"yes"
void processOptions(java.lang.String[]);
<Command>
process(opt.concat("?!"));
20: aload_0 // thisであるCommandをスタックに入れる
21: aload 5 // forループ引数optから"yes"を入れる
23: ldc #2
25: invokevirtual #3
28: invokevirtual #4
20. JVMでのメソッド呼び出し
"?!"
void processOptions(java.lang.String[]);
"yes"
process(opt.concat("?!"));
<Command>
20: aload_0 // thisであるCommandをスタックに入れる
21: aload 5 // forループ引数optから"yes"を入れる
23: ldc #2 // 定数"?!"を入れる
25: invokevirtual #3
28: invokevirtual #4
21. JVMでのメソッド呼び出し
"?!"
void processOptions(java.lang.String[]);
"yes"
process(opt.concat("?!"));
<Command>
20: aload_0 // thisであるCommandをスタックに入れる
21: aload 5 // forループ引数optから"yes"を入れる
23: ldc #2 // 定数"?!"を入れる
25: invokevirtual #3 // String.concat
// スタックからループ引数と"?!"を取り出し
// 引数としてconcatを呼んで...
28: invokevirtual #4
22. JVMでのメソッド呼び出し
"yes?!"
void processOptions(java.lang.String[]);
<Command>
process(opt.concat("?!"));
20: aload_0 // thisであるCommandをスタックに入れる
21: aload 5 // forループ引数optから"yes"を入れる
23: ldc #2 // 定数"?!"を入れる
25: invokevirtual #3 // String.concat
// スタックからループ引数と"?!"を取り出し
// 引数としてconcatを呼んで
// 戻り値をスタックに積む
28: invokevirtual #4
23. JVMでのメソッド呼び出し
"yes?!"
void processOptions(java.lang.String[]);
<Command>
process(opt.concat("?!"));
20: aload_0 // thisであるCommandをスタックに入れる
21: aload 5// forループ引数optから"yes"を入れる
23: ldc #2
// 定数"?!"を入れる
25: invokevirtual #3
// String.concat
// スタックからループ引数と"?!"を取り出し
// 引数としてconcatを呼んで
// 戻り値をスタックに積む
28: invokevirtual #4 // process
// スタックからthisと戻り値文字列を取り出し
// 自身であるCommandのprocessを呼ぶ
25. Call Siteに必要な情報
コンパイル時: 呼び出し先の参照情報
メソッドが属するクラス: String
メソッド名: "concat"
メソッド型(引数と戻り値の型): (String;String)String
[本資料でのメソッド型の表記方法]
Objectとlongの2引数、Objectが戻り値
→ (Object;long)Object
26. Call Siteに必要な情報
コンパイル時: 呼び出し先の参照情報
メソッドが属するクラス: String
メソッド名: "concat"
メソッド型(引数と戻り値の型): (String;String)String
実行時
呼び出し先メソッドの実体: String#concat
レシーバー / 引数オブジェクト: "yes" / "?!"
戻り値オブジェクト / 発生例外: "yes?!"
28. invokevirtualのメソッド呼び出し
コンパイル時: JComponent
メソッドが属するクラス: +paint()
JTextComponent
メソッド名: "getText" JTextComponent
+paint()
メソッド型: ()Void +getText()
実行時:
JTextField JTextArea
呼び出し先メソッドの実体:
+paint() +paint()
JTextField#getText +getText() +getText()
+getColumns() +getRows()
+getColumns()
29. invokevirtualのメソッド呼び出し
コンパイル時: JComponent
メソッドが属するクラス: +paint()
JTextComponent
メソッド名: "getText" JTextComponent
+paint()
メソッド型: ()Void +getText()
実行時:
JTextField JTextArea
呼び出し先メソッドの実体:
+paint() +paint()
JTextField#getText +getText() +getText()
+getColumns() +getRows()
+getColumns()
32. 動的型付け言語のメソッド呼び出し
def process_options(options)
for opt in options
process(opt.concat("?!"))
end
end
mock = Object.new
def mock.concat(arg)
"tested!"
end
options = ["yes", "no", mock]
process_options(options)
34. 例: Java SE 6用のJRuby実装
JRuby独自のCall Site
(呼び出し先の参照情報を格納)
参照先メソッドの検索も独自実装
35. Java SE 6用のJRuby生成バイトコード
process(opt.concat("?!"))
aload_0
invokevirtual main.getCallSite1; "process"CallSite
// "process"呼び出し用のCallSiteをスタックに入れる
aload_0
invokevirtual main.getCallSite2;
aload 9
aload_0
invokevirtual main.getString0;
invokevirtual CallSite.call;
invokevirtual CallSite.call;
36. Java SE 6用のJRuby生成バイトコード
process(opt.concat("?!"))
aload_0
invokevirtual main.getCallSite1; "concat"CallSite
// "process"呼び出し用のCallSiteをスタックに入れる
"process"CallSite
aload_0
invokevirtual main.getCallSite2;
// "concat"呼び出し用のCallSiteをスタックに入れる
aload 9
aload_0
invokevirtual main.getString0;
invokevirtual CallSite.call;
invokevirtual CallSite.call;
37. Java SE 6用のJRuby生成バイトコード
process(opt.concat("?!"))
aload_0
invokevirtual main.getCallSite1; "yes"
// "process"呼び出し用のCallSiteをスタックに入れる
"concat"CallSite
aload_0
invokevirtual main.getCallSite2; "process"CallSite
// "concat"呼び出し用のCallSiteをスタックに入れる
aload 9 // forループ引数optから"yes"を入れる
aload_0
invokevirtual main.getString0;
invokevirtual CallSite.call;
invokevirtual CallSite.call;
38. Java SE 6用のJRuby生成バイトコード
process(opt.concat("?!"))
aload_0
invokevirtual main.getCallSite1; "?!"
// "process"呼び出し用のCallSiteをスタックに入れる
"yes"
aload_0
invokevirtual main.getCallSite2; "concat"CallSite
// "concat"呼び出し用のCallSiteをスタックに入れる "process"CallSite
aload 9 // forループ引数optから"yes"を入れる
aload_0
invokevirtual main.getString0; // 引数の"?!"
invokevirtual CallSite.call;
invokevirtual CallSite.call;
39. Java SE 6用のJRuby生成バイトコード
process(opt.concat("?!"))
aload_0
invokevirtual main.getCallSite1; "?!"
// "process"呼び出し用のCallSiteをスタックに入れる
"yes"
aload_0
invokevirtual main.getCallSite2; "concat"CallSite
// "concat"呼び出し用のCallSiteをスタックに入れる "process"CallSite
aload 9 // forループ引数optから"yes"を入れる
aload_0
invokevirtual main.getString0; // 引数の"?!"
invokevirtual CallSite.call;
// スタックのCallSite情報を元に動的メソッド呼び出し(concat)
invokevirtual CallSite.call;
40. Java SE 6用のJRuby生成バイトコード
process(opt.concat("?!"))
aload_0
invokevirtual main.getCallSite1; "yes?!"
// "process"呼び出し用のCallSiteをスタックに入れる
"process"CallSite
aload_0
invokevirtual main.getCallSite2;
// "concat"呼び出し用のCallSiteをスタックに入れる
aload 9 // forループ引数optから"yes"を入れる
aload_0
invokevirtual main.getString0; // 引数の"?!"
invokevirtual CallSite.call;
// スタックのCallSite情報を元に動的メソッド呼び出し(concat)
invokevirtual CallSite.call;
41. Java SE 6用のJRuby生成バイトコード
process(opt.concat("?!"))
aload_0
invokevirtual main.getCallSite1; "yes?!"
// "process"呼び出し用のCallSiteをスタックに入れる
"process"CallSite
aload_0
invokevirtual main.getCallSite2;
// "concat"呼び出し用のCallSiteをスタックに入れる
aload 9 // forループ引数optから"yes"を入れる
aload_0
invokevirtual main.getString0; // 引数の"?!"
invokevirtual CallSite.call;
// スタックのCallSite情報を元に動的メソッド呼び出し(concat)
invokevirtual CallSite.call;
// 同じく動的メソッド呼び出し(process)
42. Java SE 6でのJRubyメソッド呼び出し
def target(opt)
process(opt.concat("?!"))
end
引数の数、順序の調整
デフォルト引数の補完
RubyString#
CallSite Invoker
... concat
invokevirtual getCallSite2 最終的に
aload 9 ここを呼ぶ
aload_0 呼び出しメソッド検索
invokevirtual getString0 メソッドキャッシュ
invokevirtual CallSite.call キャッシュミス判定
45. Java SE 6でのJRubyメソッド呼び出し
引数の数、順序の調整
デフォルト引数の補完
...
RubyString#
invokevirtual getCallSite2 CallSite Invoker
concat
aload 9
aload_0 最終的に
invokevirtual getString0 ここを呼ぶ
invokevirtual CallSite.call 呼び出しメソッド検索
メソッドキャッシュ
キャッシュミス判定
46. Java SE 7でのJRubyメソッド呼び出し
呼び出しメソッド検索
メソッドキャッシュ 引数の数、順序の調整
キャッシュミス判定 デフォルト引数の補完
MethodHandle
bootstrap
API
RubyString#
CallSite Invoker
concat
... ic
edy nam 最終的に
nvok
aload 9
invokedynamic getString i ここを呼ぶ
invokedynamic concat
47. InvokeDynamic用生成バイトコード
process(opt.concat("?!"))
aload_2 // thisであるCommandをスタックに入れる
aload 9 // forループ引数optから"yes"を入れる
invokedynamic getString [...]
// 引数の"?!"を取り出す
invokedynamic concat (IRubyObject;IRubyObject)IRubyObject [...]
// "concat"メソッドを動的呼び出し
invokedynamic process (IRubyObject;IRubyObject)IRubyObject
[...]
// "process"メソッドを動的呼び出し
52. Java SE 7からのメソッド呼び出し命令
invokestatic staticメソッドを直接リンク
invokespecial private/super/コンストラクタ
invokevirtual インスタンスメソッド検索
(virtual解決)
invokeinterface インターフェースメソッド検索
(interface解決)
invokedynamic 動的MethodHandle検索
(bootstrapによる解決)
53. Java SE 7 InvokeDynamicとは何か
コンセプト 道具
Call Siteと呼出先の動的なリンク invokedynamic, CallSite
リンク先検索ロジックをプログラム可能 bootstrap
メソッド参照 MethodHandle
型の安全かつ自動的な変換 MethodType
実行時の呼び出し先メソッド分岐 MethodHandle合成
動的型付け言語で、Javaの呼び出しと同じ最適化
54. MethodHandle(MH)操作API
Lookup#* クラス名と名前指定でMHを生成
MethodHandle#bindTo 第1引数のレシーバを固定
MethodHandles#* MHを合成して新たなMHを生成
insertArguments 引数を部分適用したMHを生成
guardWithTest test, then, else用の3MHを合成
して実行時に分岐するMH
SwitchPoint#guardWithTest より最適化された
true/false分岐のMH生成
SwitchPoint.invalidateAll sptの無効化
55. def fib(n)
if n < 2
MH操作の例 n
else
fib(n - 2) + fib(n - 1)
end
"n - 1"のリンク先は? end
最初に呼ばれたXInteger#minusにリンク
ただし毎回nの型 == XIntegerのチェックは必要
4つのMHを合成してCallSiteに設定
引数nの型がXIntegerかテストするメソッドのMH
引数1を部分適用したXInteger#minus(1)のMH
XInteger#minus(a)を実装したJavaメソッドのMH
呼出先MHを検索してCallSiteに再設定するメソッドのMH
56. 引数nの型がXIntegerかテストするメソッドのMH
引数1を部分適用したXInteger#minus(1)のMH
XInteger#minus(a)を実装したJavaメソッドのMH
呼出先MHを検索してCallSiteに再設定するメソッドのMH
MethodHandle test, minus, fallback, all;
minus = lookup.findVirtual(XInteger.class, "minus",
MethodType.methodType(XObject.class, long.class));
minus = MethodHandles.insertArguments(minus, 1, 1);
1番目の引数に
1Lを部分適用
57. 引数nの型がXIntegerかテストするメソッドのMH
引数1を部分適用したXInteger#minus(1)のMH
XInteger#minus(a)を実装したJavaメソッドのMH
呼出先MHを検索してCallSiteに再設定するメソッドのMH
MethodHandle test, minus, fallback, all;
minus = lookup.findVirtual(XInteger.class, "minus",
MethodType.methodType(XObject.class, long.class));
minus = MethodHandles.insertArguments(minus, 1, 1);
fallback = lookup.findStatic(Utils.class, "fallback",
MethodType.methodType(CallSite.class));
fallback = fallback.bindTo(site);
58. 引数nの型がXIntegerかテストするメソッドのMH
引数1を部分適用したXInteger#minus(1)のMH
XInteger#minus(a)を実装したJavaメソッドのMH
呼出先MHを検索してCallSiteに再設定するメソッドのMH
MethodHandle test, minus, fallback, all;
minus = lookup.findVirtual(XInteger.class, "minus",
MethodType.methodType(XObject.class, long.class));
minus = MethodHandles.insertArguments(minus, 1, 1);
fallback = lookup.findStatic(Utils.class, "fallback",
MethodType.methodType(CallSite.class));
fallback = fallback.bindTo(site);
test = lookup.findStatic(Utils.class, "testClass",
MethodType.methodType(boolean.class, ..., ...));
test = test.bindTo(self.getClass());
all = MethodHandles.guardWithTest(test, minus, fallback);
site.setTarget(all);
62. 1. 文字列リテラル message = "Hello"
message << name << "!"
適用先: リテラル文字列
● "Hello"はbootstrapに渡すようバイトコード生成
● (ctx)Object を (ctx;str)Object にリンクするため、str引数
を挿入する関数を合成
※ThreadContextはThreadなど実行環境情報を格納したオブジェクト
呼び出しの型: (ThreadContext ctx)Object
&insert(str = "Hello"):引数を1つ挿入
&newString(ctx, str):ターゲット
合成
63. 文字列リテラル参照のインライン化
def target
"Hello"
end
idx = 0
while idx < 50000
target
idx += 1
end
64. 文字列リテラル参照のインライン化
$file::method__0$RUBY$target (7 bytes)
@ 1 j.l.invoke.MH::invokeExact (12 bytes) inline (hot)
@ 5 o.j...IndySupport::newString (10 bytes) inline (hot)
@ 6 o.j.RubyString::newStringShared (22 bytes) inline (hot)
@ 6 o.j.Ruby::getString (5 bytes) inline (hot)
@ 11 o.j.RubyString::<init> (19 bytes) inline (hot)
@ 4 o.j.RubyString::<init> (35 bytes) inline (hot)
@ 3 o.j.RubyObject::<init> (7 bytes) inline (hot)
@ 3 o.j.RubyBasicObject::<init> (42 bytes) inline (hot)
@ 1 java.lang.Object::<init> (1 bytes) inline (hot)
@ 30 o.j...isObjectSpaceEnabled (5 bytes) inline (hot)
@ 38 o.j...addToObjectSpace (30 bytes) never executed
j.l.* == java.lang.*
o.j.* == org.jruby.*
65. 文字列引数の挿入操作
リンクしたターゲット
$file::method__0$RUBY$target (7 bytes)
@ 1 j.l.invoke.MH::invokeExact (12 bytes) inline (hot)
@ 5 o.j...IndySupport::newString (10 bytes) inline (hot)
@ 6 o.j.RubyString::newStringShared (22 bytes) inline (hot)
@ 6 o.j.Ruby::getString (5 bytes) inline (hot)
@ 11 o.j.RubyString::<init> (19 bytes) inline (hot)
@ 4 o.j.RubyString::<init> (35 bytes) inline (hot)
@ 3 この辺はJRubyの内部実装
o.j.RubyObject::<init> (7 bytes) inline (hot)
@ 3 o.j.RubyBasicObject::<init> (42 bytes) inline (hot)
@ 1 java.lang.Object::<init> (1 bytes) inline (hot)
@ 30 o.j...isObjectSpaceEnabled (5 bytes) inline (hot)
@ 38 o.j...addToObjectSpace (30 bytes) never executed
66. 文字列引数の挿入操作
リンクしたターゲット
$file::method__0$RUBY$target (7 bytes)
@ 1 j.l.invoke.MH::invokeExact (12 bytes) inline (hot)
@ 5 o.j...IndySupport::newString (10 bytes) inline (hot)
&insert(str = "Hello"):引数を1つ挿入
&newString(ctx, str):ターゲット
67. times = 10000
2. その他リテラル matcher = /[A-Z][a-z]*/
適用先: 文字列以外の不変リテラル
● 定数値はbootstrapに渡すようバイトコード生成
● 定数を返すMHを生成
● (ctx)Object から ()Object にリンクするため、ctx引数を削
る関数を合成
呼び出しの型: (ThreadContext ctx)Object
&drop(ctx):引数を1つ削る
&constant[10000]:常に10000を返すMH
68. その他リテラル参照のインライン化
引数の削除操作
$file::method__0$RUBY$target (7 bytes)
@ 1 j.l.invoke.MH::invokeExact (9 bytes) inline (hot)
@ 2 sun.invoke...Conversions::identity (2 bytes) inline (hot)
定数を返すMHにリンク
69. invokedynamicの効果
$file::method__0$RUBY$target (7 bytes)
@ 1 j.l.invoke.MH::invokeExact (9 bytes) inline (hot)
@ 2 sun.invoke...Conversions::identity (2 bytes) inline (hot)
Java SE 7でのインライン化結果
$file$method__0$RUBY$target::call (21 bytes) inline (hot)
@ 17 $file::method__0$RUBY$target (9 bytes) inline (hot)
@ 5 o.j...AbstractScript::getFixnum0 (11 bytes) inline (hot)
@ 7 o.j...RuntimeCache::getFixnum (33 bytes) inline (hot)
Java SE 6でのインライン化結果
70. DEFAULT = Container.new.freeze
3. 擬似定数 comtainer = DEFAULT
DEFAULT = nil
適用先: Rubyの定数参照
● Rubyの定数は変更可能なため、「上書きされることの少な
い変数」として扱う
● 上書き検出にSwitchPointを使う
呼び出しの型: (ThreadContext ctx)Object
&SwitchPoint(&,&):任意の定数が定義されたら破棄
&drop(ctx):引数を1つ削る
&constant[obj]:現在の値を定数として返すMH
&fallback(ctx):同じMHを再構築(→値を再取得)
71. 擬似定数参照のインライン化
$file::method__0$RUBY$target (7 bytes)
@ 1 j.l.invoke.MH::invokeExact (25 bytes) inline (hot)
@ 3 j.l.invoke.MH::invokeExact (16 bytes) inline (hot)
@ 2 j.l.invoke.MH::invokeExact (9 bytes) inline (hot)
@ 2 j.l.invoke...CallSite::getTarget (5 bytes) inline (hot)
@ 12 j.l.invoke.MH::invokeExact (5 bytes) inline (hot)
@ 1 sun.inv...Conversions::identity (2 bytes) inline (hot)
@ 10 j.l.invoke.MH::invokeExact (10 bytes) inline (hot)
@ 21 j.l.invoke.MH::invokeExact (6 bytes) inline (hot)
@ 2 sun.inv...Conversions::identity (2 bytes) inline (hot)
72. SwitchPointの内部 SwitchPoint分岐
$file::method__0$RUBY$target (7 bytes)
@ 1 j.l.invoke.MH::invokeExact (25 bytes) inline (hot)
@ 3 j.l.invoke.MH::invokeExact (16 bytes) inline (hot)
@ 2 j.l.invoke.MH::invokeExact (9 bytes) inline (hot)
@ 2 j.l.invoke...CallSite::getTarget (5 bytes) inline (hot)
@ 12 j.l.invoke.MH::invokeExact (5 bytes) inline (hot)
@ 1 sun.inv...Conversions::identity (2 bytes) inline (hot)
@ 10 j.l.invoke.MH::invokeExact (10 bytes) inline (hot)
@ 21 j.l.invoke.MH::invokeExact (6 bytes) inline (hot)
@ 2 sun.inv...Conversions::identity (2 bytes) inline (hot)
SwitchPointの無効化チェック
通常は定数を返す
無効化されていたら再リンクメソッドへ
73. module Cache
4. インスタンス変数 def cache(value)
@cache = value
end
適用先: インスタンス変数アクセス end
● 呼び出し側selfの変数テーブルを参照 class Foo
include Cache
● 変数テーブルはクラスにより異なる end
class Bar
● モジュールが他のクラスに include Other
includeされている場合、クラスにより include Cache
end
"@cache"のテーブル内位置が異なる
● クラスの切り替え判定にguardWithTestを使う
74. 4. インスタンス変数(続き)
呼び出しの型: (ctx;Object self)Object
&guardWithTest(&,&,&):クラスが前回と違えば破棄
&test(self):クラスに変更がないかテスト
&filterRetval(&nullToNil):戻り値変換の合成
&insert(obj = self, index = 2):引数挿入
&getVariable(ctx,obj,index):ターゲット
&fallback:新たなメソッド呼び出しMHをネスト
75. インスタンス変数のインライン化
$file::method__1$RUBY$target (7 bytes)
@ 1 j.l.invoke.MH::invokeExact (25 bytes) inline (hot)
@ 3 j.l.invoke.MH::invokeExact (7 bytes) inline (hot)
@ 3 o.j...Linker::testRealClass (20 bytes) inline (hot)
@ 5 o.j.RubyBasicObj::getMetaClass (5 bytes) inline (hot)
@ 8 o.j.RubyClass::getRealClass (2 bytes) inline (hot)
@ 10 j.l.invoke.MH::invokeExact (10 bytes) inline (hot)
@ 21 j.l.invoke.MH::invokeExact (14 bytes) inline (hot)
@ 3 j.l.invoke.MH::invokeExact (8 bytes) inline (hot)
@ 10 o.j...RuntimeHelpers::nullToNil (10 bytes) inline (hot)
76. guardWithTestによる分岐
$file::method__1$RUBY$target (7 bytes)
guardWithTest分岐
@ 1 j.l.invoke.MH::invokeExact (25 bytes) inline (hot)
@ 3 j.l.invoke.MH::invokeExact (7 bytes) inline (hot)
@ 3 o.j...Linker::testRealClass (20 bytes) inline (hot)
@ 5 o.j.RubyBasicObj::getMetaClass (5 bytes) inline (hot)
@ 8 o.j.RubyClass::getRealClass (2 bytes) inline (hot)
@ 10 j.l.invoke.MH::invokeExact (10 bytes) inline (hot)
@ 21 j.l.invoke.MH::invokeExact (14 bytes) inline (hot)
@ 3 j.l.invoke.MH::invokeExact (8 bytes) inline (hot)
@ 10 o.j...RuntimeHelpers::nullToNil (10 bytes) inline (hot)
bindしておいたクラスとの比較
インスタンス変数テーブルの参照
77. 4. インスタンス変数(続き)
呼び出しの型: (ctx;Object self)Object
&guardWithTest(&,&,&):クラスが前回と違えば破棄
&test(self):クラスに変更がないかテスト
&filterRetval(&nullToNil):戻り値変換の合成
&insert(obj = self, index = 2):引数挿入
&getVariable(ctx,obj,index):ターゲット
&fallback:新たなメソッド呼び出しMHをネスト
79. 4. インスタンス変数(ネストの具体例)
&guardWithTest:クラスが前回と違えば破棄
&test(self):クラスはBarか?
&filterRetval:戻り値がnullならnilに変換
&insert:テーブル序数として3を挿入
&getVariable(index):ターゲット
&guardWithTest:クラスが前回と違えば破棄
&test(self):クラスはFooか?
&filterRetval:戻り値がnullならnilに変換
&insert:テーブル序数として2を挿入
&getVariable(index):ターゲット
&fallback:新たなメソッド呼び出しMHをネスト
80. def process(router)
router.say_hello("Ruby")
5. メソッド呼び出し end
適用先: 任意のメソッド呼び出し
● レシーバーのクラスに応じ呼び出し先が変わる
● レシーバークラスのメソッドが上書きされる可能性がある
● 引数の個数に応じて5つのタイプ
呼び出しの型:
(ctx;self)Object
(ctx;self;arg1)Object
(ctx;self;arg1;arg2)Object
(ctx;self;arg1;arg2;arg3)Object
(ctx;self;arg[])Object
82. メソッド呼び出しのインライン化
$file::method__1$RUBY$target (9 bytes)
@ 3 j.l.invoke.MH::invokeExact (33 bytes) inline (hot)
@ 5 j.l.invoke.MH::invokeExact (20 bytes) inline (hot)
@ 2 j.l.invoke.MH::invokeExact (9 bytes) inline (hot)
@ 2 j.l.invoke...CallSite::getTarget (5 bytes) inline (hot)
@ 16 j.l.invoke.MH::invokeExact (5 bytes) inline (hot)
@ 1 sun.inv...Conversions::identity (2 bytes) inline (hot)
@ 12 j.l.invoke.MH::invokeExact (10 bytes) inline (hot)
@ 29 j.l.invoke.MH::invokeExact (35 bytes) inline (hot)
@ 5 j.l.invoke.MH::invokeExact (7 bytes) inline (hot)
@ 3 o.j...Linker::testMetaclass (17 bytes) inline (hot)
@ 5 o.j...getMetaClass (5 bytes) inline (hot)
@ 14 j.l.invoke.MH::invokeExact (10 bytes) inline (hot)
@ 31 j.l.invoke.MH::invokeExact (10 bytes) inline (hot)
@ 6 $file::method__0$RUBY$stub (7 bytes) inline (hot)
83. メソッド再定義用に
分岐のネスト SwitchPointのチェック
$file::method__1$RUBY$target (9 bytes)
@ 3 j.l.invoke.MH::invokeExact (33 bytes) inline (hot)
@ 5 j.l.invoke.MH::invokeExact (20 bytes) inline (hot)
@ 2 j.l.invoke.MH::invokeExact (9 bytes) inline (hot)
@ 2 j.l.invoke...CallSite::getTarget (5 bytes) inline (hot)
レシーバークラスのチェック
@ 16 j.l.invoke.MH::invokeExact (5 bytes) inline (hot)
@ 1 sun.inv...Conversions::identity (2 bytes) inline (hot)
@ 12 j.l.invoke.MH::invokeExact (10 bytes) inline (hot)
@ 29 j.l.invoke.MH::invokeExact (35 bytes) inline (hot)
@ 5 j.l.invoke.MH::invokeExact (7 bytes) inline (hot)
@ 3 o.j...Linker::testMetaclass (17 bytes) inline (hot)
@ 5 o.j...getMetaClass (5 bytes) inline (hot)
@ 14 j.l.invoke.MH::invokeExact (10 bytes) inline (hot)
@ 31 j.l.invoke.MH::invokeExact (10 bytes) inline (hot)
@ 6 $file::method__0$RUBY$stub (7 bytes) inline (hot)
どちらもOKならリンク済みMHを呼び出し
84. fib(n - 2) + fib(n - 1)
6. 算術演算呼び出し display if (x >= 5)
elapsed = msec * 1000.0
適用先: 右辺が整数、小数の算術演算
● 右辺のunboxをショートカットする
呼び出しの型: (ctx;self)Object
&guardWithTest:左辺がFixnumならショートカット
&drop(ctx):引数を1つ落とす
&test(self):左辺は整数か
&fixnumMinusOne(ctx, self):ターゲット
&invoke(ctx, self, value):直接呼び出し
85. 算術演算呼び出しのインライン化
$file::method__0$RUBY$target (14 bytes)
@ 8 j.l.invoke.MH::invokeExact (33 bytes) inline (hot)
@ 5 j.l.invoke.MH::invokeExact (7 bytes) inline (hot)
@ 3 o.j...MathLinker::fixnumTest (20 bytes) inline (hot)
@ 8 o.j.Ruby::isFixnumReopened (5 bytes) inline (hot)
@ 12 j.l.invoke.MH::invokeExact (10 bytes) inline (hot)
@ 29 j.l.invoke.MH::invokeExact (7 bytes) inline (hot)
@ 29 j.l.invoke.MH::invokeExact (11 bytes) inline (hot)
@ 7 o.j...fixnumOperatorFail (109 bytes) never executed
@ 3 o.j...Linker::fixnum_op_minus_one (9 bytes) inline (hot)
@ 5 o.j.RubyFixnum::op_minus_one (35 bytes) inline (hot)
86. 算術演算呼び出しのインライン化
$file::method__0$RUBY$target (14 bytes) 左辺はFixnumか?
@ 8 j.l.invoke.MH::invokeExact (33 bytes) inline (hot)
@ 5 j.l.invoke.MH::invokeExact (7 bytes) inline (hot)
@ 3 o.j...MathLinker::fixnumTest (20 bytes) inline (hot)
@ 8 o.j.Ruby::isFixnumReopened (5 bytes) inline (hot)
@ 12 j.l.invoke.MH::invokeExact (10 bytes) inline (hot)
@ 29 j.l.invoke.MH::invokeExact (7 bytes) inline (hot)
@ 29 j.l.invoke.MH::invokeExact (11 bytes) inline (hot)
@ 7 o.j...fixnumOperatorFail (109 bytes) never executed
@ 3 o.j...Linker::fixnum_op_minus_one (9 bytes) inline (hot)
@ 5 o.j.RubyFixnum::op_minus_one (35 bytes) inline (hot)
-1専用メソッドを呼び出す
90. #jt12_s204
Java SE 7 InvokeDynamic
動的型付け言語にとっては革命的
複雑だが、言語処理系が自身で最適化するより楽
→ 以後、最適化はJVMに任せる方向へ
InvokeDynamic機能の適用対象候補
● プロファイラ・デバッガ
● 関数合成によるロジック再利用
● etc.