#jt12_s204Java SE 7 InvokeDynamic        in JRuby      日本JRubyユーザ会 中村浩士     @nahi nahi@ruby-lang.org     https://github.co...
自己紹介ネットワークセキュリティ関連のシステム開発 C/C++ (18年)、Java (13年)、Ruby (13年)余暇のOSS開発 CRuby (8年) とJRuby (2年) のコミッタ soap4r、httpclient他の開発
Java SE 7 InvokeDynamicとはJava SE 7に追加された新機能変数に型のない動的型付け言語の性能向上支援● Java仮想マシン(JVM)のバイトコードに  invokedynamic命令を追加● java.lang.in...
JRubyとは - http://jruby.org/最新リリース版は1.6.7 InvokeDynamic対応は1.7から (来月末のJRubyConfでPreviewリリース)JVM上で動作するRuby(動的型付け言語)Open Sourc...
本日のゴールInvokeDynamic機能について、実例となるソースコード、バイトコードに基づく理解を得るJRubyにおける性能向上とコーディングパターンを学ぶことで、JVMの新たな可能性を見る
Agenda前半: InvokeDynamic機能解説 ● Java言語用メソッド呼び出し ● 動的型付け言語のメソッド呼び出し ● invokedynamic命令と関連API後半: JRubyにおけるInvokeDynamicの活用 ● 利用...
#jt12_s204InvokeDynamic機能解説 Java SE 7 InvokeDynamic in JRuby
JVMとはJava言語のための仮想マシンバイトコード命令を逐次実行必要に応じて最適化(JITコンパイル) インライン化、ループ展開、ロック削除、 デッドコード削除、エスケープ解析
JIT最適化の例: インライン化 double addAllSqrts(int max) {     double accum = 0;     for (int i = 0; i < max; i++) {         accum = a...
JIT最適化の例: インライン化% java -XX:+PrintCompilation        -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining Target66 1   Target:...
JIT最適化の例                   double addSqrt(double a, int b) {                                 return a + Math.sqrt(b);% jav...
double addAllSqrts(int max) {                         double accum = 0;                         for (int i = 0; i < max; i...
double addAllSqrts(int max) {                         double accum = 0;                         for (int i = 0; i < max; i...
JITについてより詳しくhttp://www.slideshare.net/CharlesNutter/redev-2011-jvm-jit-for-dummies-what-the-jvm-does-with-your-bytecode-wh...
#jt12_s204 Java言語用メソッド呼び出しInvokeDynamic機能解説
JVMでのメソッド呼び出しpublic class Command {  void processOptions(String[] options) {    boolean result;    for (String opt : optio...
JVMでのメソッド呼び出しpublic class Command {  void processOptions(String[] options) {    boolean result;    for (String opt : optio...
JVMでのメソッド呼び出し                                           <Command>void processOptions(java.lang.String[]);      process(opt...
JVMでのメソッド呼び出し                                             "yes"void processOptions(java.lang.String[]);                   ...
JVMでのメソッド呼び出し                                             "?!"void processOptions(java.lang.String[]);                    ...
JVMでのメソッド呼び出し                                                 "?!"void processOptions(java.lang.String[]);                ...
JVMでのメソッド呼び出し                                               "yes?!"void processOptions(java.lang.String[]);               ...
JVMでのメソッド呼び出し                                           "yes?!"void processOptions(java.lang.String[]);                   ...
Call Site"process"、"concat"等、呼び出す場所                   this.process                   String#concat
Call Siteに必要な情報コンパイル時: 呼び出し先の参照情報  メソッドが属するクラス:     String  メソッド名:           "concat"  メソッド型(引数と戻り値の型): (String;String)Str...
Call Siteに必要な情報コンパイル時: 呼び出し先の参照情報  メソッドが属するクラス:     String  メソッド名:           "concat"  メソッド型(引数と戻り値の型): (String;String)Str...
Java言語用メソッド呼び出し命令参照先メソッドを決定する4種のバイトコードinvokestatic    staticメソッドを直接リンクinvokespecial   private/super/コンストラクタinvokevirtual  ...
invokevirtualのメソッド呼び出しコンパイル時:                             JComponent  メソッドが属するクラス:                   +paint()         JTex...
invokevirtualのメソッド呼び出しコンパイル時:                             JComponent  メソッドが属するクラス:                   +paint()         JTex...
JVMのメソッド呼び出し命令の特徴型安全 存在しないメソッド、フィールドの参照がないアクセスコントロール private、default、protected、final制御 メソッドの確実なoverrideJVMによる最適化
#jt12_s204動的型付け言語のメソッド呼び出しInvokeDynamic機能解説
動的型付け言語のメソッド呼び出し   def process_options(options)     for opt in options       process(opt.concat("?!"))     end   end   moc...
動的型付け言語のメソッド呼び出しの特徴呼び出し先の解決方法が異なる レシーバ、型、名前、引数の数などに依存 デフォルト引数の補完動的にメソッドが追加される 再定義されることもある呼び出し先が存在しない時に呼ぶメソッド メタプログラミング用途
例: Java SE 6用のJRuby実装JRuby独自のCall Site  (呼び出し先の参照情報を格納)参照先メソッドの検索も独自実装
Java SE 6用のJRuby生成バイトコード     process(opt.concat("?!"))aload_0invokevirtual main.getCallSite1;         "process"CallSite   ...
Java SE 6用のJRuby生成バイトコード     process(opt.concat("?!"))aload_0invokevirtual main.getCallSite1;         "concat"CallSite   /...
Java SE 6用のJRuby生成バイトコード     process(opt.concat("?!"))aload_0invokevirtual main.getCallSite1;              "yes"   // "pro...
Java SE 6用のJRuby生成バイトコード      process(opt.concat("?!"))aload_0invokevirtual main.getCallSite1;                  "?!"   // ...
Java SE 6用のJRuby生成バイトコード      process(opt.concat("?!"))aload_0invokevirtual main.getCallSite1;                  "?!"   // ...
Java SE 6用のJRuby生成バイトコード      process(opt.concat("?!"))aload_0invokevirtual main.getCallSite1;                "yes?!"   //...
Java SE 6用のJRuby生成バイトコード      process(opt.concat("?!"))aload_0invokevirtual main.getCallSite1;                "yes?!"   //...
Java SE 6でのJRubyメソッド呼び出し           def target(opt)             process(opt.concat("?!"))           end                    ...
JRuby独自の最適化素朴な実装のままでは遅いので...● メソッドキャッシュ / 無効化● JITコンパイル  ○ バイトコード動的生成→読み込み  ○ 脱最適化より詳しく:http://bit.ly/JRubyHackingGuide
#jt12_s204invokedynamic命令と動的型付け言語用API(java.lang.invoke.*)   InvokeDynamic機能解説
Java SE 6でのJRubyメソッド呼び出し                                         引数の数、順序の調整                                         デフォルト引...
Java SE 7でのJRubyメソッド呼び出し                              呼び出しメソッド検索                              メソッドキャッシュ             引数の数、順...
InvokeDynamic用生成バイトコード        process(opt.concat("?!"))aload_2 // thisであるCommandをスタックに入れるaload 9 // forループ引数optから"yes"を入れる...
生成されるバイトコードの違い独自のCall Siteはなくinvokedynamic命令で直接呼び出すconcatの呼び出し情報は以下   メソッド名: "concat"   メソッド型: (IRubyObject;IRubyObject)IR...
bootstrapメソッドinvokedynamic命令をよく見ると...invokedynamic concat(IRubyObject;IRubyObject)IRubyObject [...]invokedynamic process(I...
bootstrapメソッドinvokedynamic命令をよく見ると...invokedynamic concat(IRubyObject;IRubyObject)IRubyObject [  invocationBootstrap((Look...
bootstrapメソッド(Lookup;String;MethodType)CallSite初回実行時のみ呼ばれるユーザ定義メソッドLookup:       メソッド検索用オブジェクトString:       メソッド名 ("concat...
Java SE 7からのメソッド呼び出し命令invokestatic    staticメソッドを直接リンクinvokespecial   private/super/コンストラクタinvokevirtual   インスタンスメソッド検索   ...
Java SE 7 InvokeDynamicとは何かコンセプト                  道具Call Siteと呼出先の動的なリンク   invokedynamic, CallSiteリンク先検索ロジックをプログラム可能     b...
MethodHandle(MH)操作APILookup#*              クラス名と名前指定でMHを生成MethodHandle#bindTo   第1引数のレシーバを固定MethodHandles#*       MHを合成して新...
def fib(n)                     if n < 2MH操作の例                 n                     else                       fib(n - 2) ...
引数nの型がXIntegerかテストするメソッドのMH  引数1を部分適用したXInteger#minus(1)のMH    XInteger#minus(a)を実装したJavaメソッドのMH  呼出先MHを検索してCallSiteに再設定する...
引数nの型がXIntegerかテストするメソッドのMH  引数1を部分適用したXInteger#minus(1)のMH    XInteger#minus(a)を実装したJavaメソッドのMH  呼出先MHを検索してCallSiteに再設定する...
引数nの型がXIntegerかテストするメソッドのMH  引数1を部分適用したXInteger#minus(1)のMH    XInteger#minus(a)を実装したJavaメソッドのMH  呼出先MHを検索してCallSiteに再設定する...
InvokeDynamicによる最適化のコツ呼び出し側の型(MethodType)と、呼びたいMethodHandleの型が合うようMH合成Javaコードは極力入れないテスト用コードも極力小さくシンプルにMHの再検索を極力減らす
#jt12_s204 JRubyにおけるInvokeDynamic  利用パターンInvokeDynamic機能の活用
JRuby InvokeDynamic利用パターン1. 文字列リテラル2. その他リテラル3. 擬似定数4. インスタンス変数5. メソッド呼び出し6. 算術演算呼び出し
1. 文字列リテラル                  message = "Hello"                            message << name << "!"適用先: リテラル文字列● "Hello"はboots...
文字列リテラル参照のインライン化     def target       "Hello"     end     idx = 0     while idx < 50000       target       idx += 1     end
文字列リテラル参照のインライン化$file::method__0$RUBY$target (7 bytes) @ 1     j.l.invoke.MH::invokeExact (12 bytes) inline (hot)  @ 5    ...
文字列引数の挿入操作                                            リンクしたターゲット$file::method__0$RUBY$target (7 bytes) @ 1     j.l.invoke....
文字列引数の挿入操作                                          リンクしたターゲット$file::method__0$RUBY$target (7 bytes) @ 1   j.l.invoke.MH::...
times = 100002. その他リテラル            matcher = /[A-Z][a-z]*/適用先: 文字列以外の不変リテラル● 定数値はbootstrapに渡すようバイトコード生成● 定数を返すMHを生成● (ctx)...
その他リテラル参照のインライン化                                              引数の削除操作$file::method__0$RUBY$target (7 bytes) @ 1 j.l.invoke...
invokedynamicの効果$file::method__0$RUBY$target (7 bytes) @ 1 j.l.invoke.MH::invokeExact (9 bytes) inline (hot)  @ 2 sun.invo...
DEFAULT = Container.new.freeze3. 擬似定数             comtainer = DEFAULT                    DEFAULT = nil適用先: Rubyの定数参照● Ruby...
擬似定数参照のインライン化$file::method__0$RUBY$target (7 bytes) @ 1 j.l.invoke.MH::invokeExact (25 bytes) inline (hot)  @ 3 j.l.invoke...
SwitchPointの内部                             SwitchPoint分岐$file::method__0$RUBY$target (7 bytes) @ 1 j.l.invoke.MH::invokeEx...
module Cache4. インスタンス変数                 def cache(value)                              @cache = value                      ...
4. インスタンス変数(続き)呼び出しの型: (ctx;Object self)Object&guardWithTest(&,&,&):クラスが前回と違えば破棄   &test(self):クラスに変更がないかテスト   &filterRetv...
インスタンス変数のインライン化$file::method__1$RUBY$target (7 bytes) @ 1 j.l.invoke.MH::invokeExact (25 bytes) inline (hot)  @ 3 j.l.invo...
guardWithTestによる分岐$file::method__1$RUBY$target (7 bytes)                                           guardWithTest分岐 @ 1 j.l...
4. インスタンス変数(続き)呼び出しの型: (ctx;Object self)Object&guardWithTest(&,&,&):クラスが前回と違えば破棄   &test(self):クラスに変更がないかテスト   &filterRetv...
4. インスタンス変数(ネストの具体例)&guardWithTest:クラスが前回と違えば破棄  &test(self):クラスはFooか?  &filterRetval:戻り値がnullならnilに変換     &insert:テーブル序数と...
4. インスタンス変数(ネストの具体例)&guardWithTest:クラスが前回と違えば破棄  &test(self):クラスはBarか?  &filterRetval:戻り値がnullならnilに変換     &insert:テーブル序数と...
def process(router)                       router.say_hello("Ruby")5. メソッド呼び出し          end適用先: 任意のメソッド呼び出し● レシーバーのクラスに応じ呼び...
5. メソッド呼び出し(続き)呼び出しの型: (ctx;self;arg1)Object&SwitchPoint:そのクラスでメソッドが定義されたら破棄   &guardWithTest:レシーバークラスが違えば破棄      &drop(ct...
メソッド呼び出しのインライン化$file::method__1$RUBY$target (9 bytes) @ 3 j.l.invoke.MH::invokeExact (33 bytes) inline (hot)  @ 5 j.l.invo...
メソッド再定義用に  分岐のネスト                              SwitchPointのチェック$file::method__1$RUBY$target (9 bytes) @ 3 j.l.invoke.MH::i...
fib(n - 2) + fib(n - 1)6. 算術演算呼び出し            display if (x >= 5)                       elapsed = msec * 1000.0適用先: 右辺が整数、...
算術演算呼び出しのインライン化$file::method__0$RUBY$target (14 bytes) @ 8 j.l.invoke.MH::invokeExact (33 bytes) inline (hot)  @ 5 j.l.inv...
算術演算呼び出しのインライン化$file::method__0$RUBY$target (14 bytes)    左辺はFixnumか? @ 8 j.l.invoke.MH::invokeExact (33 bytes) inline (ho...
#jt12_s204JRuby + InvokeDynamic       性能評価    InvokeDynamic機能の活用
マイクロベンチマーク ※高さはJava 7非indyを1としたときの速度比率。高いほうが速い。 ※個々のパターンの処理のみを繰り返しループして測定。     その他リテラル      インスタンス変数         算術演算文字列リテラル  ...
二分木ベンチマーク※高さはJava 7非indyを1としたときの速度比率。高いほうが速い。       AVL木(再帰)               赤黒木(ループ)
#jt12_s204Java SE 7 InvokeDynamic動的型付け言語にとっては革命的複雑だが、言語処理系が自身で最適化するより楽→ 以後、最適化はJVMに任せる方向へInvokeDynamic機能の適用対象候補   ● プロファイラ...
Upcoming SlideShare
Loading in...5
×

Java SE 7 InvokeDynamic in JRuby

4,805

Published on

JavaOne Tokyo 2012での発表スライド

Published in: Technology
0 Comments
17 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
4,805
On Slideshare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
37
Comments
0
Likes
17
Embeds 0
No embeds

No notes for slide

Transcript of "Java SE 7 InvokeDynamic in JRuby"

  1. 1. #jt12_s204Java SE 7 InvokeDynamic in JRuby 日本JRubyユーザ会 中村浩士 @nahi nahi@ruby-lang.org https://github.com/nahihttp://slidesha.re/JavaOneJpInvokeDynamic
  2. 2. 自己紹介ネットワークセキュリティ関連のシステム開発 C/C++ (18年)、Java (13年)、Ruby (13年)余暇のOSS開発 CRuby (8年) とJRuby (2年) のコミッタ soap4r、httpclient他の開発
  3. 3. Java SE 7 InvokeDynamicとはJava SE 7に追加された新機能変数に型のない動的型付け言語の性能向上支援● Java仮想マシン(JVM)のバイトコードに invokedynamic命令を追加● java.lang.invoke.*に関連APIを追加
  4. 4. JRubyとは - http://jruby.org/最新リリース版は1.6.7 InvokeDynamic対応は1.7から (来月末のJRubyConfでPreviewリリース)JVM上で動作するRuby(動的型付け言語)Open Source (CPL, GPL, LGPL)開発開始から10年
  5. 5. 本日のゴールInvokeDynamic機能について、実例となるソースコード、バイトコードに基づく理解を得るJRubyにおける性能向上とコーディングパターンを学ぶことで、JVMの新たな可能性を見る
  6. 6. Agenda前半: InvokeDynamic機能解説 ● Java言語用メソッド呼び出し ● 動的型付け言語のメソッド呼び出し ● invokedynamic命令と関連API後半: JRubyにおけるInvokeDynamicの活用 ● 利用パターン ● 性能評価
  7. 7. #jt12_s204InvokeDynamic機能解説 Java SE 7 InvokeDynamic in JRuby
  8. 8. JVMとはJava言語のための仮想マシンバイトコード命令を逐次実行必要に応じて最適化(JITコンパイル) インライン化、ループ展開、ロック削除、 デッドコード削除、エスケープ解析
  9. 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. 10. JIT最適化の例: インライン化% java -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining Target66 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. 11. JIT最適化の例 double addSqrt(double a, int b) { return a + Math.sqrt(b);% java -XX:+PrintCompilation } -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining Target66 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. 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. 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)
  14. 14. JITについてより詳しくhttp://www.slideshare.net/CharlesNutter/redev-2011-jvm-jit-for-dummies-what-the-jvm-does-with-your-bytecode-when-youre-not-lookingby Charles Oliver Nutter (JRuby co-lead)
  15. 15. #jt12_s204 Java言語用メソッド呼び出しInvokeDynamic機能解説
  16. 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. 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. 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. 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. 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. 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. 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. 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を呼ぶ
  24. 24. Call Site"process"、"concat"等、呼び出す場所 this.process String#concat
  25. 25. Call Siteに必要な情報コンパイル時: 呼び出し先の参照情報 メソッドが属するクラス: String メソッド名: "concat" メソッド型(引数と戻り値の型): (String;String)String [本資料でのメソッド型の表記方法] Objectとlongの2引数、Objectが戻り値 → (Object;long)Object
  26. 26. Call Siteに必要な情報コンパイル時: 呼び出し先の参照情報 メソッドが属するクラス: String メソッド名: "concat" メソッド型(引数と戻り値の型): (String;String)String実行時 呼び出し先メソッドの実体: String#concat レシーバー / 引数オブジェクト: "yes" / "?!" 戻り値オブジェクト / 発生例外: "yes?!"
  27. 27. Java言語用メソッド呼び出し命令参照先メソッドを決定する4種のバイトコードinvokestatic staticメソッドを直接リンクinvokespecial private/super/コンストラクタinvokevirtual インスタンスメソッド検索 (virtual解決)invokeinterface インターフェースメソッド検索 (interface解決)
  28. 28. invokevirtualのメソッド呼び出しコンパイル時: JComponent メソッドが属するクラス: +paint() JTextComponent メソッド名: "getText" JTextComponent +paint() メソッド型: ()Void +getText()実行時: JTextField JTextArea 呼び出し先メソッドの実体: +paint() +paint() JTextField#getText +getText() +getText() +getColumns() +getRows() +getColumns()
  29. 29. invokevirtualのメソッド呼び出しコンパイル時: JComponent メソッドが属するクラス: +paint() JTextComponent メソッド名: "getText" JTextComponent +paint() メソッド型: ()Void +getText()実行時: JTextField JTextArea 呼び出し先メソッドの実体: +paint() +paint() JTextField#getText +getText() +getText() +getColumns() +getRows() +getColumns()
  30. 30. JVMのメソッド呼び出し命令の特徴型安全 存在しないメソッド、フィールドの参照がないアクセスコントロール private、default、protected、final制御 メソッドの確実なoverrideJVMによる最適化
  31. 31. #jt12_s204動的型付け言語のメソッド呼び出しInvokeDynamic機能解説
  32. 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)
  33. 33. 動的型付け言語のメソッド呼び出しの特徴呼び出し先の解決方法が異なる レシーバ、型、名前、引数の数などに依存 デフォルト引数の補完動的にメソッドが追加される 再定義されることもある呼び出し先が存在しない時に呼ぶメソッド メタプログラミング用途
  34. 34. 例: Java SE 6用のJRuby実装JRuby独自のCall Site (呼び出し先の参照情報を格納)参照先メソッドの検索も独自実装
  35. 35. Java SE 6用のJRuby生成バイトコード process(opt.concat("?!"))aload_0invokevirtual main.getCallSite1; "process"CallSite // "process"呼び出し用のCallSiteをスタックに入れるaload_0invokevirtual main.getCallSite2;aload 9aload_0invokevirtual main.getString0;invokevirtual CallSite.call;invokevirtual CallSite.call;
  36. 36. Java SE 6用のJRuby生成バイトコード process(opt.concat("?!"))aload_0invokevirtual main.getCallSite1; "concat"CallSite // "process"呼び出し用のCallSiteをスタックに入れる "process"CallSiteaload_0invokevirtual main.getCallSite2; // "concat"呼び出し用のCallSiteをスタックに入れるaload 9aload_0invokevirtual main.getString0;invokevirtual CallSite.call;invokevirtual CallSite.call;
  37. 37. Java SE 6用のJRuby生成バイトコード process(opt.concat("?!"))aload_0invokevirtual main.getCallSite1; "yes" // "process"呼び出し用のCallSiteをスタックに入れる "concat"CallSiteaload_0invokevirtual main.getCallSite2; "process"CallSite // "concat"呼び出し用のCallSiteをスタックに入れるaload 9 // forループ引数optから"yes"を入れるaload_0invokevirtual main.getString0;invokevirtual CallSite.call;invokevirtual CallSite.call;
  38. 38. Java SE 6用のJRuby生成バイトコード process(opt.concat("?!"))aload_0invokevirtual main.getCallSite1; "?!" // "process"呼び出し用のCallSiteをスタックに入れる "yes"aload_0invokevirtual main.getCallSite2; "concat"CallSite // "concat"呼び出し用のCallSiteをスタックに入れる "process"CallSiteaload 9 // forループ引数optから"yes"を入れるaload_0invokevirtual main.getString0; // 引数の"?!"invokevirtual CallSite.call;invokevirtual CallSite.call;
  39. 39. Java SE 6用のJRuby生成バイトコード process(opt.concat("?!"))aload_0invokevirtual main.getCallSite1; "?!" // "process"呼び出し用のCallSiteをスタックに入れる "yes"aload_0invokevirtual main.getCallSite2; "concat"CallSite // "concat"呼び出し用のCallSiteをスタックに入れる "process"CallSiteaload 9 // forループ引数optから"yes"を入れるaload_0invokevirtual main.getString0; // 引数の"?!"invokevirtual CallSite.call; // スタックのCallSite情報を元に動的メソッド呼び出し(concat)invokevirtual CallSite.call;
  40. 40. Java SE 6用のJRuby生成バイトコード process(opt.concat("?!"))aload_0invokevirtual main.getCallSite1; "yes?!" // "process"呼び出し用のCallSiteをスタックに入れる "process"CallSiteaload_0invokevirtual main.getCallSite2; // "concat"呼び出し用のCallSiteをスタックに入れるaload 9 // forループ引数optから"yes"を入れるaload_0invokevirtual main.getString0; // 引数の"?!"invokevirtual CallSite.call; // スタックのCallSite情報を元に動的メソッド呼び出し(concat)invokevirtual CallSite.call;
  41. 41. Java SE 6用のJRuby生成バイトコード process(opt.concat("?!"))aload_0invokevirtual main.getCallSite1; "yes?!" // "process"呼び出し用のCallSiteをスタックに入れる "process"CallSiteaload_0invokevirtual main.getCallSite2; // "concat"呼び出し用のCallSiteをスタックに入れるaload 9 // forループ引数optから"yes"を入れるaload_0invokevirtual main.getString0; // 引数の"?!"invokevirtual CallSite.call; // スタックのCallSite情報を元に動的メソッド呼び出し(concat)invokevirtual CallSite.call; // 同じく動的メソッド呼び出し(process)
  42. 42. Java SE 6でのJRubyメソッド呼び出し def target(opt) process(opt.concat("?!")) end 引数の数、順序の調整 デフォルト引数の補完 RubyString# CallSite Invoker... concatinvokevirtual getCallSite2 最終的にaload 9 ここを呼ぶaload_0 呼び出しメソッド検索invokevirtual getString0 メソッドキャッシュinvokevirtual CallSite.call キャッシュミス判定
  43. 43. JRuby独自の最適化素朴な実装のままでは遅いので...● メソッドキャッシュ / 無効化● JITコンパイル ○ バイトコード動的生成→読み込み ○ 脱最適化より詳しく:http://bit.ly/JRubyHackingGuide
  44. 44. #jt12_s204invokedynamic命令と動的型付け言語用API(java.lang.invoke.*) InvokeDynamic機能解説
  45. 45. Java SE 6でのJRubyメソッド呼び出し 引数の数、順序の調整 デフォルト引数の補完... RubyString#invokevirtual getCallSite2 CallSite Invoker concataload 9aload_0 最終的にinvokevirtual getString0 ここを呼ぶinvokevirtual CallSite.call 呼び出しメソッド検索 メソッドキャッシュ キャッシュミス判定
  46. 46. Java SE 7でのJRubyメソッド呼び出し 呼び出しメソッド検索 メソッドキャッシュ 引数の数、順序の調整 キャッシュミス判定 デフォルト引数の補完 MethodHandle bootstrap API RubyString# CallSite Invoker concat... ic edy nam 最終的に nvokaload 9invokedynamic getString i ここを呼ぶinvokedynamic concat
  47. 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"メソッドを動的呼び出し
  48. 48. 生成されるバイトコードの違い独自のCall Siteはなくinvokedynamic命令で直接呼び出すconcatの呼び出し情報は以下 メソッド名: "concat" メソッド型: (IRubyObject;IRubyObject)IRubyObject "concat"という名前のメソッドを、2つのRubyオブジェクト (先頭レシーバ、引数1つ)と共に呼び出し戻り値を得る ...呼び出し先はどこ?
  49. 49. bootstrapメソッドinvokedynamic命令をよく見ると...invokedynamic concat(IRubyObject;IRubyObject)IRubyObject [...]invokedynamic process(IRubyObject;IRubyObject)IRubyObject [...]
  50. 50. bootstrapメソッドinvokedynamic命令をよく見ると...invokedynamic concat(IRubyObject;IRubyObject)IRubyObject [ invocationBootstrap((Lookup;String;MethodType)CallSite)]invokedynamic process(IRubyObject;IRubyObject)IRubyObject [ invocationBootstrap((Lookup;String;MethodType)CallSite)]...bootstrapと呼ぶ初期化メソッドが登録されている
  51. 51. bootstrapメソッド(Lookup;String;MethodType)CallSite初回実行時のみ呼ばれるユーザ定義メソッドLookup: メソッド検索用オブジェクトString: メソッド名 ("concat")MethodType: スタック上の引数オブジェクト型CallSite: 検索した呼び出し先メソッドの参照 (MethodHandle)を格納
  52. 52. Java SE 7からのメソッド呼び出し命令invokestatic staticメソッドを直接リンクinvokespecial private/super/コンストラクタinvokevirtual インスタンスメソッド検索 (virtual解決)invokeinterface インターフェースメソッド検索 (interface解決)invokedynamic 動的MethodHandle検索 (bootstrapによる解決)
  53. 53. Java SE 7 InvokeDynamicとは何かコンセプト 道具Call Siteと呼出先の動的なリンク invokedynamic, CallSiteリンク先検索ロジックをプログラム可能 bootstrapメソッド参照 MethodHandle型の安全かつ自動的な変換 MethodType実行時の呼び出し先メソッド分岐 MethodHandle合成動的型付け言語で、Javaの呼び出しと同じ最適化
  54. 54. MethodHandle(MH)操作APILookup#* クラス名と名前指定でMHを生成MethodHandle#bindTo 第1引数のレシーバを固定MethodHandles#* MHを合成して新たなMHを生成 insertArguments 引数を部分適用したMHを生成 guardWithTest test, then, else用の3MHを合成 して実行時に分岐するMHSwitchPoint#guardWithTest より最適化された true/false分岐のMH生成SwitchPoint.invalidateAll sptの無効化
  55. 55. def fib(n) if n < 2MH操作の例 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. 56. 引数nの型がXIntegerかテストするメソッドのMH 引数1を部分適用したXInteger#minus(1)のMH XInteger#minus(a)を実装したJavaメソッドのMH 呼出先MHを検索してCallSiteに再設定するメソッドのMHMethodHandle 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. 57. 引数nの型がXIntegerかテストするメソッドのMH 引数1を部分適用したXInteger#minus(1)のMH XInteger#minus(a)を実装したJavaメソッドのMH 呼出先MHを検索してCallSiteに再設定するメソッドのMHMethodHandle 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. 58. 引数nの型がXIntegerかテストするメソッドのMH 引数1を部分適用したXInteger#minus(1)のMH XInteger#minus(a)を実装したJavaメソッドのMH 呼出先MHを検索してCallSiteに再設定するメソッドのMHMethodHandle 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);
  59. 59. InvokeDynamicによる最適化のコツ呼び出し側の型(MethodType)と、呼びたいMethodHandleの型が合うようMH合成Javaコードは極力入れないテスト用コードも極力小さくシンプルにMHの再検索を極力減らす
  60. 60. #jt12_s204 JRubyにおけるInvokeDynamic 利用パターンInvokeDynamic機能の活用
  61. 61. JRuby InvokeDynamic利用パターン1. 文字列リテラル2. その他リテラル3. 擬似定数4. インスタンス変数5. メソッド呼び出し6. 算術演算呼び出し
  62. 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. 63. 文字列リテラル参照のインライン化 def target "Hello" end idx = 0 while idx < 50000 target idx += 1 end
  64. 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. 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. 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. 67. times = 100002. その他リテラル matcher = /[A-Z][a-z]*/適用先: 文字列以外の不変リテラル● 定数値はbootstrapに渡すようバイトコード生成● 定数を返すMHを生成● (ctx)Object から ()Object にリンクするため、ctx引数を削 る関数を合成 呼び出しの型: (ThreadContext ctx)Object &drop(ctx):引数を1つ削る &constant[10000]:常に10000を返すMH
  68. 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. 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. 70. DEFAULT = Container.new.freeze3. 擬似定数 comtainer = DEFAULT DEFAULT = nil適用先: Rubyの定数参照● Rubyの定数は変更可能なため、「上書きされることの少な い変数」として扱う● 上書き検出にSwitchPointを使う呼び出しの型: (ThreadContext ctx)Object&SwitchPoint(&,&):任意の定数が定義されたら破棄 &drop(ctx):引数を1つ削る &constant[obj]:現在の値を定数として返すMH &fallback(ctx):同じMHを再構築(→値を再取得)
  71. 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. 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. 73. module Cache4. インスタンス変数 def cache(value) @cache = value end適用先: インスタンス変数アクセス end● 呼び出し側selfの変数テーブルを参照 class Foo include Cache● 変数テーブルはクラスにより異なる end class Bar● モジュールが他のクラスに include Other includeされている場合、クラスにより include Cache end "@cache"のテーブル内位置が異なる● クラスの切り替え判定にguardWithTestを使う
  74. 74. 4. インスタンス変数(続き)呼び出しの型: (ctx;Object self)Object&guardWithTest(&,&,&):クラスが前回と違えば破棄 &test(self):クラスに変更がないかテスト &filterRetval(&nullToNil):戻り値変換の合成 &insert(obj = self, index = 2):引数挿入 &getVariable(ctx,obj,index):ターゲット &fallback:新たなメソッド呼び出しMHをネスト
  75. 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. 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. 77. 4. インスタンス変数(続き)呼び出しの型: (ctx;Object self)Object&guardWithTest(&,&,&):クラスが前回と違えば破棄 &test(self):クラスに変更がないかテスト &filterRetval(&nullToNil):戻り値変換の合成 &insert(obj = self, index = 2):引数挿入 &getVariable(ctx,obj,index):ターゲット &fallback:新たなメソッド呼び出しMHをネスト
  78. 78. 4. インスタンス変数(ネストの具体例)&guardWithTest:クラスが前回と違えば破棄 &test(self):クラスはFooか? &filterRetval:戻り値がnullならnilに変換 &insert:テーブル序数として2を挿入 &getVariable(index):ターゲット &fallback:新たなメソッド呼び出しMHをネスト
  79. 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. 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
  81. 81. 5. メソッド呼び出し(続き)呼び出しの型: (ctx;self;arg1)Object&SwitchPoint:そのクラスでメソッドが定義されたら破棄 &guardWithTest:レシーバークラスが違えば破棄 &drop(ctx, self):引数を二つ落とす &test(self):クラスは同じか &target(ctx, self, arg1):ターゲット &fallback:新たなメソッド呼び出しMHをネスト ... &fallback:同じMHを再構築(→ターゲット更新)
  82. 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. 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. 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. 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. 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専用メソッドを呼び出す
  87. 87. #jt12_s204JRuby + InvokeDynamic 性能評価 InvokeDynamic機能の活用
  88. 88. マイクロベンチマーク ※高さはJava 7非indyを1としたときの速度比率。高いほうが速い。 ※個々のパターンの処理のみを繰り返しループして測定。 その他リテラル インスタンス変数 算術演算文字列リテラル 擬似定数 メソッド呼び出し 全て
  89. 89. 二分木ベンチマーク※高さはJava 7非indyを1としたときの速度比率。高いほうが速い。 AVL木(再帰) 赤黒木(ループ)
  90. 90. #jt12_s204Java SE 7 InvokeDynamic動的型付け言語にとっては革命的複雑だが、言語処理系が自身で最適化するより楽→ 以後、最適化はJVMに任せる方向へInvokeDynamic機能の適用対象候補 ● プロファイラ・デバッガ ● 関数合成によるロジック再利用 ● etc.
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×