Panamaを先取り!? JVMCIでJITと遊ぶ

523 views

Published on

Java Day Tokyo 2017講演資料(D1-C1)
http://www.oracle.co.jp/events/javaday/2017/
https://github.com/YaSuenag/jdt-2017-examples

Published in: Software
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
523
On SlideShare
0
From Embeds
0
Number of Embeds
29
Actions
Shares
0
Downloads
2
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Panamaを先取り!? JVMCIでJITと遊ぶ

  1. 1. 末永 恭正 @YaSuenag
  2. 2. https://github.com/YaSuenag/jdt-2017-examples
  3. 3. OracleとJavaは、Oracle Corporation及びその子会社、関連会社の米国及びその他の国における登録商標です。 文中の社名、商品名等は各社の商標または登録商標である場合があります。 • • • • • • •
  4. 4. • • • • • http://icedtea.classpath.org/wiki/HeapStats/jp
  5. 5. • • https://bugs.openjdk.java.net/
  6. 6. • • http://openjdk.java.net/jeps/243 • • • • •
  7. 7. JITコンパイラ クラスライブラリ アプリケーション Graal HotSpot JVMCI
  8. 8. 1. 2. • 3. 4. • 5.
  9. 9. 1. ランタイム取得 2. バックエンド取得 3. プロバイダ取得 4. メタデータ取得
  10. 10. JDK内部モジュールです
  11. 11. • •
  12. 12. • •
  13. 13. バイトコード インタープリタ JIT Tier 0 Tier 1~4 Deopt プロファイリング プロファイリング
  14. 14. JITコンパイルに かかったか? コンパイルレベル (Tier) メソッドメタデータ取得 (HotSpotResolvedJavaMethod)
  15. 15. hasCompiledCode = true Compile level = 3
  16. 16.
  17. 17. プロファイリング情報の 取得
  18. 18. 十分 プロファイルできた? 実行回数 例外発生の有無 分岐の割合 0.0(分岐しない)~1.0(全部分岐)
  19. 19. HotSpotProfilingInfo<exceptionSeen@0: FALSE; exceptionSeen@1: FALSE; exceptionSeen@2: FALSE; exceptionSeen@3: FALSE; exceptionSeen@4: FALSE; exceptionSeen@5: FALSE; executionCount@6: 501760; branchProbability@6: 0.000002; exceptionSeen@6: FALSE; exceptionSeen@7: FALSE; exceptionSeen@8: FALSE; exceptionSeen@9: FALSE; exceptionSeen@10: FALSE; exceptionSeen@11: FALSE; exceptionSeen@12: FALSE; exceptionSeen@13: FALSE; exceptionSeen@14: FALSE; executionCount@15: 501759; branchProbability@15: 1.000000; exceptionSeen@15: FALSE; exceptionSeen@16: FALSE; exceptionSeen@17: FALSE; exceptionSeen@18: FALSE; exceptionSeen@19: FALSE> Compile level = 3 isMature = true BCI 6: execution count = 501760, branch = 1.992984693877551E-6 BCI 15: execution count = 501759, branch = 1.0
  20. 20. public static int runLoop(int); descriptor: (I)I flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=3, args_size=1 0: iconst_0 1: istore_1 2: iconst_1 3: istore_2 4: iload_2 5: iload_0 6: if_icmpgt 18 9: iinc 1, 1 12: iinc 2, 1 15: goto 4 18: iload_1 19: ireturn ほとんど分岐しない (0.000002) 上にジャンプ (1.0)
  21. 21. HotSpotProfilingInfo<exceptionSeen@0: FALSE; exceptionSeen@1: FALSE; exceptionSeen@2: FALSE; exceptionSeen@3: FALSE; exceptionSeen@4: FALSE; exceptionSeen@5: FALSE; executionCount@6: 501760; branchProbability@6: 0.000002; exceptionSeen@6: FALSE; exceptionSeen@7: FALSE; exceptionSeen@8: FALSE; exceptionSeen@9: FALSE; exceptionSeen@10: FALSE; exceptionSeen@11: FALSE; exceptionSeen@12: FALSE; exceptionSeen@13: FALSE; exceptionSeen@14: FALSE; executionCount@15: 501759; branchProbability@15: 1.000000; exceptionSeen@15: FALSE; exceptionSeen@16: FALSE; exceptionSeen@17: FALSE; exceptionSeen@18: FALSE; exceptionSeen@19: FALSE> Compile level = 3 isMature = true BCI 6: execution count = 501760, branch = 1.992984693877551E-6 BCI 15: execution count = 501759, branch = 1.0 100万回動かしてるはずなんだけど…
  22. 22. MDO
  23. 23. • • • • 実際の呼び出し回数と一致しない!
  24. 24. • •
  25. 25. • •
  26. 26. • • •
  27. 27. 262 167 % 3 TestBase::runLoop @ 4 (20 bytes) 263 168 3 TestBase::runLoop (20 bytes) 263 169 % 4 TestBase::runLoop @ 4 (20 bytes) 264 167 % 3 TestBase::runLoop @ 4 (20 bytes) made not entrant 265 169 % 4 TestBase::runLoop @ 4 (20 bytes) made not entrant : runLoop() finished. 1136 168 3 TestBase::runLoop (20 bytes) made not entrant reprofile. 3466 219 % 4 TestBase::runLoop @ 4 (20 bytes) 3468 220 4 TestBase::runLoop (20 bytes) 3468 219 % 4 TestBase::runLoop @ 4 (20 bytes) made not entrant runLoop() finished. リプロファイルで 破棄された 最初の生成 リプロファイル後の 生成コード
  28. 28. • • • • 暖気後 • 妥当な
  29. 29. http://cr.openjdk.java.net/~vlivanov/talks/2016_JVMLS_MachineCodeSnippets.pdf
  30. 30. Machine Code Snippetsっぽいことができる!
  31. 31. • • • • • • •
  32. 32. • • • • O S R •
  33. 33. • • • •
  34. 34. s1 s2 s3 s4 s5 s6 s7 s8src dest d1 d2 d3 d4 d5 d6 d7 d8 + + + + + + + + s1+d1 s2+d2 s3+d3 s4+d4 s5+d5 s6+d6 s7+d7 s8+d8 = = = = = = = =
  35. 35. • • •
  36. 36. https://wiki.openjdk.java.net/display/HotSpot/CallingSequences
  37. 37. 呼び出し規約の 種類 使えるレジスタたちの取得 呼び出し規約と 型を指定 引数の順番を指定
  38. 38. ハンドアセンブル!
  39. 39. 1. 2. 3.
  40. 40. • vmovdqu <配列>, %ymm1 vpaddd <配列>, %ymm1, %ymm0 vmovdqu %ymm0, <配列> ※GAS形式
  41. 41. • • https://software.intel.com/en-us/articles/intel-sdm
  42. 42. • • •
  43. 43. • • • • •
  44. 44. Javaの世界の配列を どうアセンブラの世界で扱うか?
  45. 45. ヘッダ 長さ 中身 引数で渡ってくる配列オブジェクトの先頭アドレス Unsafe.ARRAY_INT_BASE_OFFSET • • O O P • •
  46. 46. インストール コード確定 プロローグ エピローグ 配列の中身への オフセット
  47. 47. 実行 PAddInt#padd() に設定
  48. 48. 9 9 9 9 9 9 9 9 $ jcmd 11217 Compiler.codelist | grep padd 582 4 PAddTest$PAddInt.padd([I[I)V [0x00007f9f8970a090, 0x00007f9f8970a220 - 0x00007f9f8970a250] コンソール出力 コードキャッシュ
  49. 49. (gdb) disas 0x00007f9f8970a220, 0x00007f9f8970a250 Dump of assembler code from 0x7f9f8970a220 to 0x7f9f8970a250: 0x00007f9f8970a220: nopl 0x0(%rax,%rax,1) 0x00007f9f8970a225: push %rbp 0x00007f9f8970a226: mov %rsp,%rbp 0x00007f9f8970a229: sub $0x10,%rsp 0x00007f9f8970a230: vmovdqu 0x10(%rdx),%ymm1 0x00007f9f8970a235: vpaddd 0x10(%rsi),%ymm1,%ymm0 0x00007f9f8970a23a: vmovdqu %ymm0,0x10(%rsi) 0x00007f9f8970a23f: mov %rbp,%rsp 0x00007f9f8970a242: pop %rbp 0x00007f9f8970a243: retq src側配列をAVXレジスタにロード 足し算(SIMD) 計算結果をdest側配列にストア
  50. 50. 好きなアセンブラを動かせる =何でもできるはず!
  51. 51. • • •
  52. 52. • •
  53. 53. • • • •
  54. 54. • •
  55. 55. • • https://linuxjm.osdn.jp/html/LDP_man-pages/man2/syscall.2.html
  56. 56. mov $39, %rax # getpid = 39 syscall ※GAS形式
  57. 57. Little Endian
  58. 58. 戻り値を Javaの世界に戻す
  59. 59. JIT生成コードオブジェクト 開始アドレス システムコール番号 (39)
  60. 60. Installed address: 0x7fda896fba10 1656 $ jcmd 1656 Compiler.codelist | grep 7fda896fba10 569 4 SyscallTest$GetPID.getPid()I [0x00007fda896fba10, 0x00007fda896fbba0 - 0x00007fda896fbbc8] コンソール出力 コードキャッシュ
  61. 61. (gdb) disas 0x00007fda896fbba0, 0x00007fda896fbbc8 Dump of assembler code from 0x7fda896fbba0 to 0x7fda896fbbc8: 0x00007fda896fbba0: nopl 0x0(%rax,%rax,1) 0x00007fda896fbba5: push %rbp 0x00007fda896fbba6: mov %rsp,%rbp 0x00007fda896fbba9: sub $0x10,%rsp 0x00007fda896fbbb0: mov $0x27,%rax 0x00007fda896fbbb7: syscall 0x00007fda896fbbb9: mov %rbp,%rsp 0x00007fda896fbbbc: pop %rbp 0x00007fda896fbbbd: retq システムコール番号の設定 システムコール実行
  62. 62. • • •
  63. 63. • 関数アドレスの特定 • • CALL命令の発行 •
  64. 64. • • 1. どうやってdlsym(3)を呼ぶ? 2. 3. 4.
  65. 65. • • https://github.com/graalvm • •
  66. 66.
  67. 67. • • 1. 2. どうやってchar *を生成する? 3. 4.
  68. 68. • • • • String#getBytes() →足せばいい →Unsafe.ARRAY_BYTE_BASE_OFFSET
  69. 69. String→char * char *→String
  70. 70. • • 1. 2. 3. どうやって引数を渡せばいい? 4.
  71. 71. • • http://refspecs.linuxfoundation.org/ •
  72. 72. • • 1. 2. 3. 4. どうやって戻り値を設定すればいい?
  73. 73. Javaクラスの型から 戻り値に使うレジスタを取得
  74. 74. 第1引数 (ライブラリハンドル) 第2引数 (シンボル名へのポインタ)CALL命令発行 戻り値 (ポインタ:long)
  75. 75. RTLD_DEFAULTで探索 (/usr/include/dlfcn.h) 関数アドレスを CALL命令にかけるコードを インストール
  76. 76. 1000 $ id uid=1000(yasuenag) gid=1000(yasuenag) groups=1000(yasuenag) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 コンソール出力 実行ユーザーは…
  77. 77. • •
  78. 78. • ライブラリのロード • • • •
  79. 79.
  80. 80. ヘッダ 長さ 中身 引数で渡ってくる配列オブジェクトの先頭アドレス ココ
  81. 81. 第2引数 (エラー文字列へのポインタ) 第3引数 (エラー文字列の長さ)
  82. 82. ライブラリロード失敗時は エラーメッセージを表示 ネイティブ関数 呼び出し 関数アドレスを CALL命令にかけるコードを インストール
  83. 83. from native ./native/libnativ.so: cannot open shared object file: No such file or directory もし存在しないライブラリをロードさせたら…
  84. 84. • • • • • •
  85. 85. • • •
  86. 86. 使い方次第で無限の可能性!!

×