Pred kompiláciou času (AoT)

1. Úvod

V tomto článku sa pozrieme na kompilátor Java Ahead of Time (AOT), ktorý je popísaný v JEP-295 a bol pridaný ako experimentálna funkcia v prostredí Java 9.

Po prvé, uvidíme, čo je AOT, a po druhé, pozrieme sa na jednoduchý príklad. Po tretie, uvidíme určité obmedzenia AOT a nakoniec prediskutujeme niektoré možné prípady použitia.

2. Čo je pred kompiláciou času?

Kompilácia AOT je jedným zo spôsobov zlepšenia výkonu programov Java, a najmä času spustenia JVM. JVM vykonáva bajtový kód Java a kompiluje často vykonávaný kód do natívneho kódu. Táto kompilácia sa nazýva Just-in-Time (JIT). JVM rozhoduje o tom, ktorý kód sa má JIT kompilovať na základe profilových informácií zhromaždených počas vykonávania.

Aj keď táto technika umožňuje JVM produkovať vysoko optimalizovaný kód a zlepšuje špičkový výkon, čas spustenia pravdepodobne nie je optimálny, pretože spustený kód ešte nie je kompilovaný JIT. Cieľom AOT je zlepšiť toto takzvané zahrievacie obdobie. Kompilátor používaný pre AOT je Graal.

V tomto článku sa nebudeme podrobne venovať JIT a Graalovi. V našich ďalších článkoch nájdete prehľad zlepšení výkonu v prostredí Java 9 a 10, ako aj podrobný ponor do kompilátora Graal JIT.

3. Príklad

V tomto príklade použijeme veľmi jednoduchú triedu, zostavíme ju a uvidíme, ako použiť výslednú knižnicu.

3.1. Kompilácia AOT

Poďme sa rýchlo pozrieť na našu triedu vzoriek:

public class JaotCompilation {public static void main (String [] argv) {System.out.println (message ()); } public static String message () {return "Kompilátor JAOT hovorí 'Hello'"; }} 

Predtým, ako budeme môcť použiť kompilátor AOT, musíme kompilovať triedu pomocou kompilátora Java:

javac JaotCompilation.java 

Výsledok potom odovzdáme JaotCompilation.class do kompilátora AOT, ktorý je umiestnený v rovnakom adresári ako štandardný kompilátor Java:

jaotc - výstup jaotCompilation.so JaotCompilation.class 

Takto sa vytvorí knižnica jaotCompilation.so v aktuálnom adresári.

3.2. Spustenie programu

Potom môžeme program spustiť:

java -XX: AOTLibrary =. / jaotCompilation.so JaotCompilation 

Tvrdenie -XX: AOTLibrary akceptuje relatívnu alebo úplnú cestu do knižnice. Prípadne môžeme knižnicu skopírovať do lib priečinok v domovskom adresári Java a odovzdajte iba názov knižnice.

3.3. Overenie, či je knižnica volaná a použitá

Vidíme, že knižnica bola skutočne načítaná pridaním -XX: + PrintAOT ako argument JVM:

java -XX: + PrintAOT -XX: AOTLibrary =. / jaotCompilation.so JaotCompilation 

Výstup bude vyzerať takto:

77 1 načítaná knižnica ./jaotCompilation.so aot 

Toto nám však iba hovorí, že knižnica bola načítaná, ale nie to, že bola skutočne použitá. Predaním argumentu -verbose, môžeme vidieť, že metódy v knižnici sa skutočne volajú:

java -XX: AOTLibrary =. / jaotCompilation.so -verbose -XX: + PrintAOT JaotCompilation 

Výstup bude obsahovať riadky:

11 1 načítaná knižnica ./jaotCompilation.so aot 116 1 aot [1] jaotc.JaotCompilation. () V 116 2 aot [1] jaotc.JaotCompilation.message () Ljava / lang / String; 116 3 aot [1] jaotc.JaotCompilation.main ([Ljava / lang / String;) V Kompilátor JAOT hovorí „Hello“ 

Zostavená knižnica AOT obsahuje a odtlačok triedy, ktoré sa musia zhodovať s odtlačkom prsta .trieda spis.

Zmeňme kód v triede JaotCompilation.java vrátiť inú správu:

public static String message () {return "Kompilátor JAOT hovorí 'Dobré ráno'"; } 

Ak spustíme program bez toho, aby sme AOT zostavili upravenú triedu:

java -XX: AOTLibrary =. / jaotCompilation.so -verbose -XX: + PrintAOT JaotCompilation 

Potom bude výstup obsahovať iba:

 11 1 načítaná knižnica ./jaotCompilation.so aot kompilátor JAOT hovorí „Dobré ráno“

Vidíme, že metódy v knižnici sa nebudú volať, pretože sa zmenil bytecode triedy. Ide o to, že program bude vždy produkovať rovnaký výsledok, bez ohľadu na to, či je načítaná kompilovaná knižnica AOT alebo nie.

4. Viac argumentov AOT a JVM

4.1. AOT kompilácia modulov Java

Je tiež možné AOT zostaviť modul:

jaotc - výstup javaBase.so --modul java.base 

Výsledná knižnica javaBase.so má veľkosť asi 320 MB a načítanie trvá nejaký čas. Veľkosť sa dá zmenšiť výberom balíkov a tried, ktoré sa majú AOT kompilovať.

Ďalej sa pozrieme na to, ako to urobiť, nebudeme sa však hlboko venovať všetkým podrobnostiam.

4.2. Selektívna kompilácia s príkazmi na kompiláciu

Aby sme zabránili tomu, aby sa kompilovaná knižnica AOT modulu Java stala príliš veľkou, môžeme pridať príkazy kompilácie, aby sme obmedzili rozsah toho, čo sa kompiluje AOT. Tieto príkazy musia byť v textovom súbore - v našom príklade tento súbor použijeme complileCommands.txt:

compileOnly iba java.lang. *

Potom ho pridáme do príkazu kompilácie:

jaotc --output javaBaseLang.so --module java.base --compile-commands compileCommands.txt 

Výsledná knižnica bude obsahovať iba AOT zostavené triedy v balík java.lang.

Aby sme dosiahli skutočné zlepšenie výkonu, musíme zistiť, ktoré triedy sú vyvolané počas rozcvičenia JVM.

To sa dá dosiahnuť pridaním niekoľkých argumentov JVM:

java -XX: + UnlockDiagnosticVMOptions -XX: + LogTouchedMethods -XX: + PrintTouchedMethodsAtExit JaotCompilation 

V tomto článku sa nebudeme hlbšie venovať tejto technike.

4.3. AOT kompilácie jednej triedy

S argumentom môžeme zostaviť jednu triedu –Class-name:

jaotc --output javaBaseString.so --class-name java.lang.String 

Výsledná knižnica bude obsahovať iba triedu String.

4.4. Zostavte pre odstupňované

Predvolene sa vždy použije kompilovaný kód AOT a pre triedy zahrnuté v knižnici sa nestane kompilácia JIT. Ak chceme zahrnúť informácie o profilovaní do knižnice, môžeme pridať argument zostaviť pre viacúrovňovú:

jaotc --output jaotCompilation.so --kompilovaná pre viacúrovňovú JaotCompilation.class 

Predkompilovaný kód v knižnici sa bude používať, kým sa bajtkód stane vhodným na kompiláciu JIT.

5. Možné prípady použitia pre kompiláciu AOT

Jedným z prípadov použitia pre AOT sú krátko bežiace programy, ktoré dokončia vykonávanie skôr, ako dôjde k akejkoľvek kompilácii JIT.

Ďalším prípadom použitia sú zabudované prostredia, kde JIT nie je možný.

Na tomto mieste je tiež potrebné poznamenať, že kompilovanú knižnicu AOT je možné načítať iba z triedy Java s rovnakým bajtkódom, a teda ju nemožno načítať cez JNI.

6. AOT a Amazon Lambda

Možným prípadom použitia pre kompilovaný kód AOT sú krátkodobé funkcie lambda, kde je dôležitá krátka doba spustenia. V tejto časti sa pozrieme na to, ako môžeme spustiť AOT zostavený Java kód na AWS Lambda.

Použitie kompilácie AOT s AWS Lambda vyžaduje, aby bola knižnica postavená na operačnom systéme, ktorý je kompatibilný s operačným systémom používaným na AWS. V čase písania článku to tak je Amazon Linux 2.

Ďalej sa musí zhodovať verzia Java. AWS poskytuje Amazon Corretto Java 11 JVM. Nainštalujeme si, aby sme mali prostredie na kompiláciu našej knižnice Amazon Linux 2 a Amazon Corretto v Dockeru.

Nebudeme diskutovať o všetkých podrobnostiach používania Dockeru a AWS Lambda, ale načrtneme iba najdôležitejšie kroky. Ďalšie informácie o tom, ako používať Docker, nájdete v jeho oficiálnej dokumentácii tu.

Viac podrobností o vytváraní funkcie Lambda pomocou Java nájdete v našom článku AWS Lambda With Java.

6.1. Konfigurácia nášho vývojového prostredia

Najprv musíme vytiahnuť obrázok Dockeru pre Amazon Linux 2 a nainštalovať Amazon Corretto:

# stiahnite si Amazon Linux docker stiahnite amazonlinux # do kontajnera Docker, nainštalujte Amazon Corretto yum nainštalujte java-11-amazon-corretto # niektoré ďalšie knižnice potrebné pre jaotc yum nainštalujte binutils.x86_64 

6.2. Zostavte triedu a knižnicu

V našom kontajneri Docker vykonávame nasledujúce príkazy:

# vytvoriť priečinok aot mkdir aot cd aot mkdir jaotc cd jaotc

Názov priečinka je iba príkladom a môže ním samozrejme byť aj akýkoľvek iný názov.

balíček jaotc; public class JaotCompilation {public static int message (int input) {return input * 2; }}

Ďalším krokom je zostavenie triedy a knižnice:

javac JaotCompilation.java cd .. jaotc -J-XX: + UseSerialGC - výstup jaotCompilation.so jaotc / JaotCompilation.class

Tu je dôležité použiť rovnaký systém na odstraňovanie odpadu, aký sa používa na AWS. Ak našu knižnicu nie je možné načítať na AWS Lambda, môžeme pomocou nasledujúceho príkazu skontrolovať, ktorý zberač odpadu sa skutočne používa:

java -XX: + PrintCommandLineFlags -verzia

Teraz môžeme vytvoriť súbor ZIP, ktorý obsahuje našu knižnicu a súbor triedy:

zip -r jaot.zip jaotCompilation.so jaotc /

6.3. Nakonfigurujte AWS Lambda

Posledným krokom je prihlásenie do konzoly AWS Lamda, nahranie súboru zip a konfigurácia Lambdy s nasledujúcimi parametrami:

  • Beh programu: Java 11
  • Psovod: jaotc.JaotCompilation :: správa

Ďalej musíme vytvoriť premennú prostredia s názvom JAVA_TOOL_OPTIONS a nastaviť jej hodnotu na:

-XX: + UnlockExperimentalVMOptions -XX: + PrintAOT -XX: AOTLibrary =. / JaotCompilation.so

Táto premenná nám umožňuje odovzdať parametre JVM.

Posledným krokom je konfigurácia vstupu pre našu Lambdu. Predvolený je vstup JSON, ktorý sa nedá odovzdať našej funkcii, preto ho musíme nastaviť na reťazec, ktorý obsahuje celé číslo, napr. „1“.

Nakoniec môžeme spustiť našu funkciu Lambda a v protokole by sme mali vidieť, že bola načítaná naša kompilovaná knižnica AOT:

57 1 načítaná knižnica ./jaotCompilation.so aot

7. Záver

V tomto článku sme videli, ako AOT zostaviť triedy a moduly Java. Pretože toto je stále experimentálna funkcia, kompilátor AOT nie je súčasťou všetkých distribúcií. Skutočné príklady sa stále vyskytujú len zriedka a bude na komunite Java, aby našla najlepšie prípady použitia AOT.

Všetky útržky kódu v tomto článku nájdete v našom úložisku GitHub.


$config[zx-auto] not found$config[zx-overlay] not found