Ispred sastavljanja vremena (AoT)

1. Uvod

U ovom ćemo članku pogledati kompajler Java Ahead of Time (AOT), koji je opisan u JEP-295 i dodan je kao eksperimentalna značajka u Javi 9.

Prvo ćemo vidjeti što je AOT, a drugo, pogledat ćemo jednostavan primjer. Treće, vidjet ćemo neka ograničenja AOT-a i na kraju ćemo razgovarati o nekim mogućim slučajevima korištenja.

2. Što je pred vremenskom kompilacijom?

AOT kompilacija jedan je od načina za poboljšanje performansi Java programa, a posebno vremena pokretanja JVM-a. JVM izvršava Java bajt kod i kompajlira često izvršavani kôd u izvorni kôd. To se naziva Just-in-Time (JIT) kompilacija. JVM odlučuje koji će kôd sastaviti na temelju podataka o profiliranju prikupljenih tijekom izvršavanja.

Iako ova tehnika omogućuje JVM-u da proizvodi visoko optimizirani kôd i poboljšava vrhunske performanse, vrijeme pokretanja vjerojatno nije optimalno, jer izvršeni kod još nije kompiliran JIT-om. Cilj AOT-a je poboljšati ovo takozvano razdoblje zagrijavanja. Prevoditelj koji se koristi za AOT je Graal.

U ovom članku nećemo detaljno razmatrati JIT i Graal. Pogledajte naše ostale članke za pregled poboljšanja performansi u Javi 9 i 10, kao i dubinsko zarobljavanje Graal JIT Compilera.

3. Primjer

Za ovaj primjer koristit ćemo vrlo jednostavnu klasu, sastaviti je i vidjeti kako koristiti rezultirajuću knjižnicu.

3.1. AOT Kompilacija

Kratko ćemo pogledati naš uzorak klase:

Javna klasa JaotCompilation {public static void main (String [] argv) {System.out.println (message ()); } public static String message () {return "JAOT-ov kompajler kaže 'Zdravo'"; }} 

Prije nego što možemo koristiti AOT kompajler, moramo kompajlirati klasu s Java kompajlerom:

javac JaotCompilation.java 

Zatim prosljeđujemo rezultirajuće JaotCompilation.class AOT kompajleru, koji se nalazi u istom direktoriju kao i standardni Java kompajler:

jaotc --izlaz jaotCompilation.so JaotCompilation.class 

Ovo stvara knjižnicu jaotCompilation.tako u trenutnom direktoriju.

3.2. Pokretanje programa

Tada možemo izvršiti program:

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

Argument -XX: AOTLibrary prihvaća relativni ili puni put do knjižnice. Alternativno, možemo kopirati knjižnicu u lib mapu u početnom direktoriju Java i prosljeđuje samo ime knjižnice.

3.3. Provjera poziva i koristi li se knjižnica

Vidimo da je knjižnica doista učitana dodavanjem -XX: + IspisAOT kao JVM argument:

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

Izlaz će izgledati ovako:

77 1 učitana ./jaotCompilation.so aot knjižnica 

Međutim, ovo nam samo govori da je knjižnica učitana, ali ne i da je zapravo korištena. Donošenjem argumenta -verbozna, možemo vidjeti da se metode u knjižnici doista nazivaju:

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

Izlaz će sadržavati retke:

11 1 učitana ./jaotCompilation.so aot biblioteka 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 Prevodnik JAOT kaže 'Pozdrav' 

AOT kompajlirana knjižnica sadrži razredni otisak prsta, koji mora odgovarati otisku prsta .razred datoteka.

Promijenimo kod u razredu JaotCompilation.java da biste vratili drugu poruku:

public static String message () {return "JAOT kompajler kaže 'Dobro jutro'"; } 

Ako program izvršimo bez AOT-a koji kompilira modificiranu klasu:

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

Tada će izlaz sadržavati samo:

 11 1 učitana ./jaotCompilation.so aot biblioteka JAOT kompajler kaže 'Dobro jutro'

Vidimo da se metode u knjižnici neće pozivati ​​jer se promijenio bajtkod klase. Ideja koja stoji iza toga je da će program uvijek dati isti rezultat, bez obzira je li učitana AOT kompajlirana knjižnica ili nije.

4. Više argumenata AOT i JVM

4.1. AOT Kompilacija Java modula

Također je moguće AOT sastaviti modul:

jaotc --izlaz javaBase.so --modul java.base 

Rezultirajuća knjižnica javaBase.tako velik je oko 320 MB i potrebno mu je neko vrijeme za učitavanje. Veličina se može smanjiti odabirom paketa i klasa koje će se kompilirati AOT.

Kako ćemo to učiniti, pogledati ćemo u nastavku, međutim, nećemo duboko zaranjati u sve detalje.

4.2. Selektivna kompilacija s naredbama za sastavljanje

Da spriječimo da AOT kompajlirana biblioteka Java modula ne postane prevelika, možemo dodati naredbe za kompajliranje kako bismo ograničili opseg onoga što dobiva AOT kompilirano. Te naredbe moraju biti u tekstualnoj datoteci - u našem ćemo primjeru koristiti datoteku complileCommands.txt:

compileOnly java.lang. *

Zatim ga dodajemo naredbi compile:

jaotc --output javaBaseLang.so --modul java.base --compile-naredbe compileCommands.txt 

Rezultirajuća knjižnica sadržavat će samo AOT prevedene razrede u paket java.lang.

Da bismo postigli stvarno poboljšanje izvedbe, moramo otkriti koje se klase pozivaju tijekom zagrijavanja JVM-a.

To se može postići dodavanjem nekoliko JVM argumenata:

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

U ovom članku nećemo dublje zaranjati u ovu tehniku.

4.3. AOT Kompilacija jedne klase

S argumentom možemo sastaviti jednu klasu –Razred -razreda:

jaotc --izlaz javaBaseString.so - naziv klase java.lang.String 

Dobivena knjižnica sadržavat će samo razred Niz.

4.4. Sastaviti za Tiered

Prema zadanim postavkama uvijek će se koristiti AOT kompilirani kôd i neće se dogoditi JIT kompilacija za klase uključene u knjižnicu. Ako podatke o profiliranju želimo uključiti u knjižnicu, možemo dodati argument sastaviti za višerazinski:

jaotc --output jaotCompilation.so --compile-for-tiered JaotCompilation.class 

Prethodno kompilirani kôd u knjižnici koristit će se dok bajt kod ne postane prihvatljiv za JIT kompilaciju.

5. Mogući slučajevi upotrebe za AOT kompilaciju

Jedan slučaj upotrebe AOT-a su kratkotrajni programi, koji završavaju izvršenje prije nego što se dogodi bilo koja JIT kompilacija.

Drugi slučaj upotrebe su ugrađena okruženja, gdje JIT nije moguć.

U ovom trenutku također moramo primijetiti da se AOT kompajlirana knjižnica može učitati samo iz Java klase s identičnim bajt kodom, pa se ne može učitati putem JNI.

6. AOT i Amazon Lambda

Mogući slučaj upotrebe AOT-kompiliranog koda su kratkotrajne lambda funkcije kod kojih je važno kratko vrijeme pokretanja. U ovom ćemo odjeljku pogledati kako možemo pokrenuti AOT kompilirani Java kôd na AWS Lambda.

Korištenje AOT kompilacije s AWS-om Lambda zahtijeva da se knjižnica izgradi na operativnom sustavu koji je kompatibilan s operativnim sustavom koji se koristi na AWS-u. U vrijeme pisanja ovog članka, ovo je Amazon Linux 2.

Nadalje, inačica Java mora se podudarati. AWS pruža Amazon Corretto Java 11 JVM. Instalirat ćemo kako bismo imali okruženje za kompajliranje naše knjižnice Amazon Linux 2 i Amazon Corretto u Dockeru.

Nećemo raspravljati o svim pojedinostima korištenja Dockera i AWS Lambde, već ćemo samo dati najvažnije korake. Za više informacija o korištenju Dockera, pogledajte njegovu službenu dokumentaciju ovdje.

Za više detalja o stvaranju Lambda funkcije s Javom možete pogledati naš članak AWS Lambda s Javom.

6.1. Konfiguracija našeg razvojnog okruženja

Prvo, moramo povući Dockerovu sliku za Amazon Linux 2 i instalirajte Amazon Corretto:

# preuzmite Amazon Linux docker povucite amazonlinux # unutar spremnika Docker instalirajte Amazon Corretto yum install java-11-amazon-corretto # neke dodatne biblioteke potrebne za jaotc yum install binutils.x86_64 

6.2. Sastavite razred i knjižnicu

Unutar našeg Docker spremnika izvršavamo sljedeće naredbe:

# stvori mapu aot mkdir aot cd aot mkdir jaotc cd jaotc

Naziv mape samo je primjer i može, naravno, biti bilo koje drugo ime.

paket jaotc; javna klasa JaotCompilation {javna statička int poruka (int ulaz) {return input * 2; }}

Sljedeći je korak kompajliranje klase i knjižnice:

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

Ovdje je važno koristiti isti sakupljač smeća koji se koristi na AWS-u. Ako se naša knjižnica ne može učitati na AWS Lambda, možda bismo htjeli provjeriti koji se sakupljač smeća zapravo koristi pomoću sljedeće naredbe:

java -XX: + PrintCommandLineFlags -verzija

Sada možemo stvoriti zip datoteku koja sadrži našu knjižnicu i datoteku klase:

zip -r jaot.zip jaotCompilation.so jaotc /

6.3. Konfigurirajte AWS Lambda

Posljednji korak je prijava na AWS Lamda konzolu, prijenos zip datoteke i konfiguriranje Lambde sa sljedećim parametrima:

  • Vrijeme izvođenja: Java 11
  • Rukovatelj: jaotc.JaotCompilation :: poruka

Nadalje, moramo stvoriti varijablu okruženja s imenom JAVA_TOOL_OPTIONS i postaviti njezinu vrijednost na:

-XX: + OtključajEksperimentalneVMOpcije -XX: + IspišiAOT -XX: AOTLibrary =. / JaotCompilation.so

Ova varijabla omogućuje nam prosljeđivanje parametara JVM-u.

Posljednji korak je konfiguriranje ulaza za našu Lambdu. Zadani je JSON ulaz, koji se ne može proslijediti našoj funkciji, stoga ga moramo postaviti na String koji sadrži cijeli broj, na pr. "1".

Napokon, možemo izvršiti našu Lambda funkciju i u zapisniku bismo trebali vidjeti da je učitana naša AOT kompajlirana biblioteka:

57 1 učitana ./jaotCompilation.so aot biblioteka

7. Zaključak

U ovom smo članku vidjeli kako AOT sastaviti Java klase i module. Budući da je ovo još uvijek eksperimentalna značajka, AOT kompajler nije dio svih distribucija. Prave primjere još uvijek je rijetko pronaći, a na Java zajednici bit će da pronađu najbolje primjere za primjenu AOT-a.

Svi isječci koda u ovom članku mogu se naći u našem spremištu GitHub.