JIT Compiling - Leibniz Universität Hannover
Transcription
JIT Compiling - Leibniz Universität Hannover
JIT Compiling hallo Implementierung moderner virtueller Maschinen am Beispiel von Java Sommersemester 2009 Nicole Ullmann Leibniz Universität Hannover 1 Gliederung ● Motivation ● JustInTime Compiling ● Java HotSpot Virtual Machine ● HotSpot Konzept ● Java Hotspot Client Compiler ● Kompilierungsphasen ● Demo und Fazit 2 Motivation ● Vorteil: Java bzw. Bytecode plattformunabhägig ● Nachteil: Bytecode muss interpretiert werden – – ● Dispatch Loop ● Befehl laden ● Interpretieren ● Ausführen Alles zur Laufzeit Fazit: Performanceverlust 3 JustinTime Compiling ● ● Übersetzung von Bytecode in Maschinencode zur Laufzeit („just in time“) Gegenteil: AheadofTime (AOT) Compiling – ● Kompilierung vor Ausführung des Programms Wichtig: Kompilierunggeschwindigkeit – Kompilierung in „User Time“ – JIT Compiler müssen schnell arbeiten 4 Java HotSpot Virtual Machine ● „Highperformance“ VM (laut Sun) ● Verbesserungen im Bereich: – Runtime: verbesserte Fehlermeldungen – Garbage collection: größere Heaps – Thread Synchronisation („ultra fast“) – Memory Model („handleless objects“) – JITCompiling 5 Java Hotspot Compilers ● 2 alternative JITCompiler verfügbar ● ● Unterschiedliche Design Entscheidungen Nutzen dieselbe Virtual Machine Quelle: http://java.sun.com/products/hotspot/docs/whitepaper/Java_HotSpot_WP_Final_4_30_01.html 6 Client Compiler ● Optimiert für interaktive Anwendungen, z.B. GUIs ● Ziel: Hohe Kompilierungsgeschwindigkeit ● Einfache Analysen und Optimierungen ● Schnelle StartupPhase ● Kleiner Memory Footprint 7 Server Compiler ● Optimiert für typische ServerAnwedungen ● Ziel: Hohe MaschinencodeQualität ● Hohe Ausführungsgeschwindigkeit ● Vollständige Optimierung ● Langsame StartupPhase ● Größerer Memory Footprint 8 Das Hotspot Konzept ● Beobachtung: – ● Programme verbringen ca. 80 90% der Rechenzeit damit, nur 10 – 20% ihres Codes auszuführen Schlußfolgerung: – Nur performancekritische Methoden („hot spots“) justintime kompilieren – Hotspots: ● Häufig aufgerufene Methoden ● Methoden mit lang laufenden Schleifen 9 Hotspot Detection (1) ● ● Bytecode zunächst nur interpretiert und analysiert Erkennung der Hotspots anhand zweier Schwellwerte: – Aufrufhäufigkeit einer Methode (method_entry_counter) – Anzahl von Schleifendurchläufen in einer Methode (backedge_counter); 10 Hotspot Detection (2) ● Methode wird kompiliert, falls method_entry_counter > CompileThreshold ● ● Grundeinstellung CompileThreshold: – 1500 im ClientCompiler – 10000 im ServerCompiler – VMOption: -XX:CompileThreshold=<WERT> Beim nächsten Methodenaufruf wird die kompilierte Methode verwendet 11 Hotspot Detection (3) ● Methode wird kompiliert, falls backedge_counter > CompileThreshold ● Wechsel zur kompilierten Methode während der MethodenInterpretation ● OnStackReplacement (OSR) ● Kompilierte Methode enthält OSR entry point 12 OnStackReplacement (OSR) ● Beispiel* Stack nach OSR Stack vor OSR m1 compiled frame m1 interpreted frame void m1() { ... while(i < n1){ mo frame //hier OSR ... dead m1 frame mo frame } ... } Beispiel aus: Client Compiler for the Java HotSpotTM Virtual Machine, Tom Rodriguez et al. 13 Java Hotspot Client Compiler ● Kompilierung einer Methode in 3 Phasen aufgeteilt: 1.Phase: HighLevel Intermediate Representation 2.Phase: LowLevel Intermediate Representation 3.Phase: Maschinencode Generierung (HIR) Generierung und gleichzeitige bzw. (LIR) Generierung und anschließende Register anschließende Optimierungen Allocation Struktur des Java Hotspot Client Compilers* *Quelle: Design of the Java Hotspot Client Compiler, Kotzmann et al. 2008 14 HighLevel Intermediate Representation (HIR) ● Plattformunabhängiger, graphbasierter Zwischencode ● Repräsentiert Methoden als Kontrollflußgraph ● Knoten sind Basis Blöcke ● Implizite Operanden ● In Static Single Assignment (SSA) Form – Beispiel y := 1 y := 2 x := y y1 := 1 y2 := 2 x1 := y2 15 HIR Generierung ● 1 Phase: – ● Basis Blöcke bestimmen 2 Phase: – BytecodeInstruktionen in HIRInstruktionen umwandeln – Basis Blöcke verlinken (Kontrollfluß bilden) 16 HIRGenerierung Beispiel* 0 iconst_1 1 istore_1 2 iconst_1 3 istore_2 4 iload_2 5 iload_0 6 if_icmpgt 19 9 iload_1 10 iload_2 11 imul 12 istore_1 13 iinc 2 1 16 goto 4 19 iload_1 20 ireturn *Beispiel aus: SSA Form for the Java HotSpotTM Client Compiler, Christian Wimmer 2009 17 HIR Generierung Beispiel (Forts.) Phase 1: Basis Blöcke bestimmen 0 iconst_1 1 istore_1 2 iconst_1 3 istore_2 4 iload_2 5 iload_0 6 if_icmpgt 19 9 iload_1 10 iload_2 11 imul 12 istore_1 13 iinc 2 1 16 goto 4 19 iload_1 20 ireturn 0 1 2 3 iconst_1 istore_1 iconst_1 istore_2 4 iload_2 5 iload_0 6 if_icmpgt 19 9 iload_1 10 iload_2 11 imul 12 istore_1 13 iinc 2 1 16 goto 4 19 iload_1 20 ireturn 18 HIR Generierung Beispiel (Forts.) Phase 2: HIRInstruktionen erzeugen & Kontrollfluß bilden 0 1 2 3 iconst_1 istore_1 iconst_1 istore_2 4 iload_2 5 iload_0 6 if_icmpgt 19 9 iload_1 10 iload_2 11 imul 12 istore_1 13 iinc 2 1 16 goto 4 19 iload_1 20 ireturn i0 parameter n i1 1 2 goto B1 B0 i3 phi[i0,i3] B1 i4 phi[i1,i7] i5 phi[i1,i9] 6 i5 > i3 ? B3 : B2 i7 i4 * i5 i8 1 i9 i5 + i8 10 goto B1 11 return i4 B2 B3 19 Lokale Optimierungen auf HIR ● Lokale Optimierungen während der HIR Generierung ● Beziehen sich auf einzelne Basis Blöcke ● Konstantenfaltung („constant folding“) – Beispiel: double pi = 3.14159 double u = 2 * pi * r } double u = 6.2830 * r 20 Lokale Optimierungen auf HIR (2) ● Entfernen gemeinsamer Teilausdrücke mittels „local value numbering“ – Beispiel: a = b + c; d = b; b = a; e = d + c; a b → #1 #3 c → #2 b + c → #1 + #2 → #3 a → #3 d → #1 d + c → #1 + #2 → #3 e → #3 21 Lokale Optimierungen auf HIR (3) ● MethodInlining – ● ● Methoden kleiner als 35 Byte werden „geinlined“ Problem: Java ist objektorientiert und dynamisch – MethodenAufrufe „virtuell“ – „virtuell“ := potenziell polymorph Folge: Inlining „falscher“ Methoden 22 MethodInlining: ProblemBeispiel* class VirtualCall { public void foo() { A a = create(); a.bar(); } } public A create() { if(...){ return new A();} else {return new B();} } class A { void bar() { ... } } class B extends A { void bar() { ... } } ● Klasse B wird nachgeladen ● Aktive (kompilierte) Methode foo() wird ungültig ● Deoptimierung *Beispiel aus: Design of the Java Hotspot Client Compiler, Kotzmann et al. 2008 23 Deoptimierung ● Ausführung des Maschinencodes stoppen ● Compileroptimierungen rückgängig machen ● Methodenausführung im Interpreter fortsetzen Stack comp. foo() frame [inlined bar()] bar() interpreted mo frame mo frame foo() interpreted Beispiel aus: Client Compiler for the Java HotSpotTM Virtual Machine, Tom Rodriguez et al. 24 Globale Optimierungen auf HIR ● ● Globale Optimierungen nach der HIR Generierung Beziehen sich auf gesamten HIRGraphen – Entfernen von Laufzeitüberprüfungen („null check elimination“) – Entfernen gemeinsamer Teilausdrücke mittels „global value numbering“ – Entfernen unerreichbarer Zweige („conditional expression elimination“) 25 LowLevel Intermediate Representation (LIR) ● ● ● ● Aus HIRGraph generiert Plattformabhängiger, maschinennaher Zwischencode Einige highlevel Instruktionen (z.B. Typ Überprüfung) Explizite Operanden in virtuellen Registern – Optimal für anschließende RegisterAllokation 26 Linear Scan Register Allocation ● ● Aufgabe: virtuelle Register durch physische Register der ZielArchitektur ersetzen Beispiel* mit 2 physischen Registern r1 und r2 v1 v2 v3 v4 v5 (1) v1 = 10 (2) v2 = 20 (3) v3 = v1 + v2 (4) v4 = v2 + v3 (5) v1 = v3 + v4 (6) v5 = v4 + v1 (7) return v1 + v5 *Beispiel aus: Linear Scan Register Allocation for the Java HotSpot Client Compiler, Christian Wimmer 2004 27 Linear Scan Register Allocation (2) ● Liste aus Live Intervallen konstruieren – [v1, v2, v3, v4, v5] v1 v2 mem1 r1 r2 v3 r1 v4 r2 (1) v5 r1 Registerzuordnung: (2) v1 → mem1 v2 → r2 v3 → r1 v4 → r2 v5 → r1 (3) (4) (5) (6) (7) v1 v2 v2 v4 v3 active list: v5 28 Linear Scan Register Allocation (2) ● Code nach der RegisterAllokation Registerzuordnung: v1 → mem1 v2 → r2 v3 → r1 v4 → r2 v5 → r1 vorher: nachher: (1) v1 = 10 mem1 = 10 (2) v2 = 20 r2 = 20 (3) v3 = v1 + v2 r1 = mem1 + r2 (4) v4 = v2 + v3 r2 = r2 + r1 (5) v1 = v3 + v4 mem1 = r1 + r2 (6) v5 = v4 + v1 r1 = r2 + mem1 (7) return v1 + v5 return mem1 + r1 29 Maschinencode Erzeugung ● Aus LIRGraph generiert ● Vorgefertigte MaschinencodeSchablonen – ● 12 Assemblerbefehle pro LIRInstruktion – ● aus ArchitectureDescription Files highlevel Instruktionen benötigen mehrere Native Method Object – Maschinencode – Meta Daten für Deoptimierung und GC 30 Meta Daten ● ● Debug Information – Mapping zwischen kompilierten Code und InterpreterStatus – Speichert Status aller lokalen Variablen und OperandenStack Oop Maps (Ordinary Object Pointers): – Informationen über Pointer zu HeapObjekten 31 Demo ● CompileThreshold ändern – ● JITCompiler ausschalten: – ● Xint JITCompiler Zeit: – ● XX:CompileThreshold=<WERT> XX:+CITime JITCompiler Ausgabe – XX:+PrintCompilation 32 Fazit ● BytecodeInterpretation ist langsam ● JIT schafft Abhilfe, muss aber schnell sein ● Hotspot Client Compiler vs. Server Compiler ● Nur Hotspots kompilieren ● Struktur des Client Compiler ● Kompilierung in 3 Phasen ● Demo 33