Successfully reported this slideshow.
Your SlideShare is downloading. ×

Deep dive into instanceof

More Related Content

Related Books

Free with a 30 day trial from Scribd

See all

Deep dive into instanceof

  1. 1. Deep Dive into instanceof 2018-12-15 JJUG CCC 2018 Fall
  2. 2. In od on Hiroshi Saito @saidie ➔ SmartNews 1.5yrs 広告配信プラットフォーム (Java) ➔ DeNA 3yrs Web サーバサイド (Perl, Ruby on Rails) ➔ Ph.D Student 5yrs 理論神経科学 Java 初心者!!
  3. 3. Sma w 世界中の良質な情報を必要な人に送り届ける / Delivering the world’s quality information to the people who need it
  4. 4. SmartNews Ads ”Ads as Content”
  5. 5. In od on Performance is important ● 大量の広告リクエスト、ビーコン ● ハイパフォーマンスを求められる配信システム
  6. 6. In od on Java 初心者が通る道(?) instanceof を使ってレビューで指摘される OOP 的に良くないだけでなくパフォーマンス的にも 良くない
  7. 7. In od on Why? 一見難しそうに見えるが Set などを使えばいいのでは? E N R S M T W A Z
  8. 8. Research
  9. 9. Res h Java コンパイラによる変換 .java .class JVM javac execute
  10. 10. Res h classの中身 ConstantPool Field Attribute +
  11. 11. Res h JVMはスタックマシン ● 命令の引数はスタックに積んである ● 結果はスタックに入る “Hello, world!” Stack instanceof String 1 Stack コンパイル時に決まっている ConstantPoolから表引き pop push
  12. 12. Res h classの中身 (再掲) 1. ldc: stack に “Hello, world!” を push 2. instanceof を実行 3. jump (空の if 文) 4. return
  13. 13. Res h JVM ● Oracle HotSpot JVM ● Eclipse OpenJ9 今日見ていくのは HotSpot JVM バージョン: jdk-11.0.1+13
  14. 14. Res h ソースコード hg clone https://hg.openjdk.java.net/jdk-updates/jdk11u
  15. 15. Res h interpreter を探せ share/interpreter ● TemplateInterpreter: CPU依存 ○ 普段使われてるもの ● CppInterpreter: CPU非依存 ○ 今日は主にこっちの話
  16. 16. JVM internals
  17. 17. share/oops/ にある C++ の class ● Klass ⇐ Java のクラス/interface ○ ex) Object クラスの Klass, Integer クラスの Klass ● ConstantPool ○ ある Klass から参照される定数のルックアップテーブル ○ Klass*の配列を持っている Div I J 基本的なデータ構造
  18. 18. Div I J バイトコードインタプリタ概要 1. pc の位置にある命令を読む 2. バイトコードに応じて処理を行う ○ 巨大な switch 文 3. pc を更新する 4. 1 に戻る (実際にはスレッデッドコード) 概念図 pc next pc bytecode
  19. 19. Div I J BytecodeInterpreter::runWithCheck:L2266
  20. 20. Div I J instanceof の処理 oop* “Hello,world!” Stack SP pc bytecode 0xc1 index ConstantPool Klass* String Klass*が得られる
  21. 21. Div I J ConstantPoolのindex ConstantPoolからKlass* StackからKlass* subtype check
  22. 22. Div I J Klass::is_subtype_of(Klass*)
  23. 23. Div I J Fast and Decisive Class Checking in the Hotspot VM John Rose & Cliff Click, 2001 以下のような思想の下で考案された ● CPU のオペレーションを少なくしたい ● Memory access を少なくしたい ● VM call をなくしたい Klass.hpp にあるとあるコメント
  24. 24. Div I J subtype check 継承の場合 単純にsuper classを一つ一つチェックする場合 深さに応じた時間がかかる
  25. 25. Div I J 継承ツリー Object Number ArrayList Integer AbstractList AbstractCollection 0 2 1 3 Double AbstractSet HashSet
  26. 26. Div I J primary supers では super depth が重複しない Primary supers: super class のリスト ● Integer: [Object, Number] ● ArrayList: [Object, AbstractCollection, AbstractList] Super depth: 継承の深さ ● Object: 0 ● Number: 1 ● AbstractCollection: 1 ● AbstractList: 2 super depth の位置を調べるだけで良い
  27. 27. Div I J interfaceは二部グラフ Collection Serializable ArrayList Integer AbstractList Iterable AbstractSet HashSet 多対多なので難しい List Set
  28. 28. Div I J Binary matrix Klass に連番をつけて binary matrix を作る クラスの動的load/unloadへの対応が難しい Serializable Collection Iterable List Set Integer o AbstractSet o o o o AbstractList o o o o HashSet o o o o ArrayList o o o o
  29. 29. Div I J 線形探索 + Cache ● 数がそんなに多くなければそれほど遅くない ● 前回のinstanceofで参照されたクラスをキャッシュし ておき、次回は最初にキャッシュをチェックする CollectionSerializable Iterable ListCloneable RandomAccess ArrayList の secondary supers
  30. 30. Div I J ここまでのまとめ ● クラスに対する is_subtype_of ○ 継承ツリーにおける深さで一発で求められる ● interfaceに対する is_subtype_of ○ 前回ヒットしたクラスをチェックする ○ チェックが失敗したら線形探索
  31. 31. Div I J
  32. 32. Div I J primary_supersをKlassに含める primary_supers を固定長にするとKlass自体に埋 め込めて、メモリアクセスが減らせる 現状の実装だと supers の長さは 8 ▷super depth < 8までしか扱えない より深いものは? ▷interfaceと同じ扱いにする == 線形探索対象
  33. 33. Div I J ● super_depthはクラスロード時に確定する ● どちらの条件を使うかも確定する 範囲チェックと T との比較を一括で実現できる load時に決まる
  34. 34. Div I J Klassのメモリレイアウト 0 1 2 3 4 5 6 7 8 9 A B C D E F Layout helper Klass ID Super Check Offset Symbol* name Array<Klass*>* Secondary Supers Klass* Secondary Super Cache Klass* Primary Supers[0] super depth < 88 <= super depth Klass* Primary Supers[1] Klass* Primary Supers[6] Klass* Primary Supers[7] 参照すべきメモリ位置が事前に分かる!
  35. 35. Div I J Klass::is_subtype_of(Klass*) primary supers / キャッシュの位置 primary super / キャッシュ == primary super / キャッシュ super depth < 8 の場合 線形探索
  36. 36. Div I J x86の場合 cpu/x86/macroAssembler_x86.cpp: check_klass_subtype 関数 MOV: メモリ → レジスタ CMP: レジスタ⇔メモリ CMP: レジスタ⇔即値 MOV: Arrayの位置や長さ REPNE: 線形探索
  37. 37. Div I J Experiments C_10 → C_09 → … → C_02 → C_01 → Object fast slow
  38. 38. Div I J Results JIT をオフにして JMH で計測した結果
  39. 39. Summary
  40. 40. Sum y instanceof 実装まとめ ● 継承ツリー深さが8未満の具象クラス ○ 数回のCPUオペレーションとメモリアクセス ● 深さが8以上 or interface ○ 前回のinstanceofと同じであればキャッシュが効いて高速 ○ キャッシュミスしたら線形探索 ■ 効率的なCPU命令がある
  41. 41. Sum y Conclusion 継承が深いクラスやinterfaceを使ったinstanceofを使う場 合はできるだけキャッシュを効かせよう instanceof を使わなくて済むようきちんと設計しよう JVM のコード読むの結構楽しいよ
  42. 42. We want more!! https://smartnews.workable.com/

×