Ahead of Time Compilation (AoT)

1. Introduksjon

I denne artikkelen ser vi på Java Ahead of Time (AOT) kompilatoren, som er beskrevet i JEP-295 og ble lagt til som en eksperimentell funksjon i Java 9.

Først ser vi hva AOT er, og for det andre ser vi på et enkelt eksempel. For det tredje vil vi se noen begrensninger for AOT, og til slutt vil vi diskutere noen mulige brukssaker.

2. Hva ligger foran tidssammensetningen?

AOT-kompilering er en måte å forbedre ytelsen til Java-programmer og spesielt oppstartstiden til JVM. JVM utfører Java bytecode og kompilerer ofte utført kode til naturlig kode. Dette kalles Just-in-Time (JIT) Compilation. JVM bestemmer hvilken kode JIT skal kompilere basert på profilinformasjon samlet inn under utførelse.

Selv om denne teknikken gjør det mulig for JVM å produsere svært optimalisert kode og forbedrer topp ytelse, er oppstartstiden sannsynligvis ikke optimal, siden den utførte koden ennå ikke er JIT-kompilert. AOT har som mål å forbedre denne såkalte oppvarmingsperioden. Kompilatoren som brukes til AOT er Graal.

I denne artikkelen vil vi ikke se på JIT og Graal i detalj. Se våre andre artikler for en oversikt over ytelsesforbedringer i Java 9 og 10, samt et dypdykk i Graal JIT Compiler.

3. Eksempel

For dette eksemplet bruker vi en veldig enkel klasse, kompilerer den og ser hvordan du bruker det resulterende biblioteket.

3.1. AOT-kompilering

La oss ta en rask titt på prøveklassen vår:

public class JaotCompilation {public static void main (String [] argv) {System.out.println (message ()); } offentlig statisk strengmelding () {return "JAOT-kompilatoren sier 'Hei'"; }} 

Før vi kan bruke AOT-kompilatoren, må vi kompilere klassen med Java-kompilatoren:

javac JaotCompilation.java 

Vi passerer deretter det resulterende JaotCompilation.class til AOT-kompilatoren, som ligger i samme katalog som standard Java-kompilator:

jaotc --output jaotCompilation.so JaotCompilation.class 

Dette produserer biblioteket jaotCompilation.so i gjeldende katalog.

3.2. Kjører programmet

Vi kan deretter utføre programmet:

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

Argumentet -XX: AOTBibliotek godtar en relativ eller full sti til biblioteket. Alternativt kan vi kopiere biblioteket til lib mappen i Java-hjemmekatalogen og bare passere navnet på biblioteket.

3.3. Bekrefte at biblioteket kalles og brukes

Vi kan se at biblioteket faktisk ble lastet inn ved å legge til -XX: + PrintAOT som et JVM-argument:

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

Utgangen vil se ut som:

77 1 lastet ./jaotCompilation.so et bibliotek 

Dette forteller oss imidlertid bare at biblioteket var lastet, men ikke at det faktisk ble brukt. Ved å føre argumentet -verbose, kan vi se at metodene i biblioteket faktisk kalles:

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

Utgangen vil inneholde linjene:

11 1 lastet ./jaotCompilation.so aot bibliotek 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 JAOT-kompilatoren sier 'Hei' 

AOT-biblioteket inneholder en klasse fingeravtrykk, som må matche fingeravtrykket til .klasse fil.

La oss endre koden i klassen JaotCompilation.java for å returnere en annen melding:

offentlig statisk strengmelding () {retur "JAOT-kompilatoren sier 'God morgen'"; } 

Hvis vi kjører programmet uten å AOT kompilere den modifiserte klassen:

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

Da vil utdataene bare inneholde:

 11 1 lastet ./jaotCompilation.so et bibliotek JAOT-kompilatoren sier 'God morgen'

Vi kan se at metodene i biblioteket ikke vil bli kalt, ettersom bytekoden til klassen har endret seg. Tanken bak dette er at programmet alltid vil gi det samme resultatet, uansett om et AOT-samlet bibliotek er lastet inn eller ikke.

4. Flere AOT- og JVM-argumenter

4.1. AOT-samling av Java-moduler

Det er også mulig å AOT kompilere en modul:

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

Det resulterende biblioteket javaBase.so er omtrent 320 MB i størrelse og tar litt tid å laste den inn. Størrelsen kan reduseres ved å velge pakkene og klassene som AOT skal kompileres.

Vi vil se på hvordan du gjør det nedenfor, men vi vil ikke dykke dypt inn i alle detaljene.

4.2. Selektiv kompilering med kompileringskommandoer

For å forhindre at AOT-kompilert bibliotek til en Java-modul blir for stort, kan vi legge til kompileringskommandoer for å begrense omfanget av det som blir AOT-kompilert. Disse kommandoene må være i en tekstfil - i vårt eksempel bruker vi filen complileCommands.txt:

kompilerBare java.lang. *

Deretter legger vi det til kompileringskommandoen:

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

Det resulterende biblioteket vil bare inneholde de AOT-kompilerte klassene i pakke java.lang.

For å få reell ytelsesforbedring, må vi finne ut hvilke klasser som påkalles under oppvarmingen av JVM.

Dette kan oppnås ved å legge til flere JVM-argumenter:

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

I denne artikkelen vil vi ikke dykke dypere inn i denne teknikken.

4.3. AOT-samling av en enkelt klasse

Vi kan sette sammen en enkelt klasse med argumentet -klassenavn:

jaotc --output javaBaseString.so - klassenavn java.lang.String 

Det resulterende biblioteket inneholder bare klassen String.

4.4. Kompiler for Tiered

Som standard vil alltid den AOT-koden brukes, og ingen JIT-kompilering vil skje for klassene som er inkludert i biblioteket. Hvis vi vil inkludere profilinformasjonen i biblioteket, kan vi legge til argumentet kompilere-for-tiered:

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

Den forhåndskompilerte koden i biblioteket vil bli brukt til bytekoden blir kvalifisert for JIT-kompilering.

5. Mulige brukstilfeller for AOT-kompilering

Én brukssak for AOT er programmer som kjører kort, som fullfører kjøringen før JIT-kompilering skjer.

En annen brukssak er innebygde miljøer, der JIT ikke er mulig.

På dette punktet må vi også merke oss at det AOT-kompilerte biblioteket bare kan lastes fra en Java-klasse med identisk bytekode, og det kan derfor ikke lastes via JNI.

6. AOT og Amazon Lambda

En mulig brukssak for AOT-kompilert kode er kortvarige lambdafunksjoner der kort oppstartstid er viktig. I denne delen vil vi se på hvordan vi kan kjøre AOT-samlet Java-kode på AWS Lambda.

Bruk av AOT-kompilering med AWS Lambda krever at biblioteket bygges på et operativsystem som er kompatibelt med operativsystemet som brukes på AWS. I skrivende stund er dette Amazon Linux 2.

Videre må Java-versjonen matche. AWS tilbyr Amazon Corretto Java 11 JVM. For å ha et miljø for å kompilere biblioteket vårt, installerer vi det Amazon Linux 2 og Amazon Corretto i Docker.

Vi vil ikke diskutere alle detaljene ved bruk av Docker og AWS Lambda, men bare skissere de viktigste trinnene. For mer informasjon om hvordan du bruker Docker, se den offisielle dokumentasjonen her.

For mer informasjon om hvordan du oppretter en Lambda-funksjon med Java, kan du ta en titt på artikkelen vår AWS Lambda With Java.

6.1. Konfigurasjon av vårt utviklingsmiljø

Først må vi trekke Docker-bildet for Amazon Linux 2 og installer Amazon Corretto:

# last ned Amazon Linux docker trekk amazonlinux # inne i Docker-containeren, installer Amazon Corretto yum install java-11-amazon-corretto # noen ekstra biblioteker som trengs for jaotc yum install binutils.x86_64 

6.2. Sett sammen klassen og biblioteket

Inne i Docker-containeren vår utfører vi følgende kommandoer:

# create folder aot mkdir aot cd aot mkdir jaotc cd jaotc

Navnet på mappen er bare et eksempel og kan selvfølgelig være et hvilket som helst annet navn.

pakke jaotc; public class JaotCompilation {public static int message (int input) {return input * 2; }}

Neste trinn er å kompilere klassen og biblioteket:

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

Her er det viktig å bruke den samme søppeloppsamleren som brukes på AWS. Hvis biblioteket vårt ikke kan lastes på AWS Lambda, kan det være lurt å sjekke hvilken søppeloppsamler som faktisk brukes med følgende kommando:

java -XX: + PrintCommandLineFlags -versjon

Nå kan vi opprette en zip-fil som inneholder biblioteket og klassefilen:

zip -r jaot.zip jaotCompilation.so jaotc /

6.3. Konfigurer AWS Lambda

Det siste trinnet er å logge inn på AWS Lamda-konsollen, laste opp zip-filen og konfigurere Lambda med følgende parametere:

  • Kjøretid: Java 11
  • Behandler: jaotc.JaotCompilation :: melding

Videre må vi lage en miljøvariabel med navnet JAVA_TOOL_OPTIONS og sette verdien til:

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

Denne variabelen lar oss overføre parametere til JVM.

Det siste trinnet er å konfigurere inngangen for Lambda. Standard er en JSON-inngang, som ikke kan overføres til vår funksjon, derfor må vi sette den til en streng som inneholder et heltall, f.eks. "1".

Til slutt kan vi utføre vår Lambda-funksjon og skal se i loggen at vårt AOT-kompilerte bibliotek ble lastet inn:

57 1 lastet ./jaotCompilation.so et bibliotek

7. Konklusjon

I denne artikkelen så vi hvordan AOT kompilerer Java-klasser og moduler. Siden dette fremdeles er en eksperimentell funksjon, er ikke AOT-kompilatoren en del av alle distribusjoner. Ekte eksempler er fremdeles sjeldne å finne, og det vil være opp til Java-samfunnet å finne de beste brukssakene for å bruke AOT.

Alle kodebitene i denne artikkelen finner du i GitHub-depotet vårt.


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