AOT (tietotekniikka)

**AOT** (engl. *ahead-of-time compilation*) on ohjelmakoodin kääntämisen strategia, jossa lähdekoodi tai välikoodi käännetään kohdeympäristön konekielelle ennen ohjelman suoritusta, tyypillisesti käännös- tai asennusvaiheessa.
AOT-kääntäminen vähentää ajonaikaista käännöstyötä ja voi parantaa esimerkiksi ohjelmien käynnistysnopeutta sekä pienentää riippuvuutta ajonaikaisista kääntäjäkomponenteista.

AOT-kääntämistä käytetään sekä perinteisesti staattisesti käännettävissä ohjelmointikielissä kuten C ja C++ että hallituissa ajoympäristöissä, joissa välikoodi kuten CIL tai Java-ohjelmointikielen tavukoodi käännetään natiiviksi konekieleksi ennen ohjelman suorittamista. Menetelmä nähdään usein vastakohtana ajonaikaiselle kääntämiselle (engl. *just-in-time*, JIT).

## Periaate
AOT-kääntämisessä ohjelmakoodi kulkee tavanomaisen käännösputken läpi ennen suoritusta. Yleisiä vaiheita ovat lähdekoodin jäsentäminen ja sisäisen esityksen muodostaminen, kieli- ja alustariippumattomat optimoinnit, kohdearkkitehtuurille suunnattujen optimointien tekeminen sekä kohdeprosessorin konekielen tuottaminen ja linkittäminen suoritettavaksi ohjelmaksi tai kirjastoksi.

Perinteisissä C- ja C++-natiivikääntäjissä koko sovellus käännetään ennen suoritusta. Käännöksestä syntyvä binääri voidaan ajaa suoraan käyttöjärjestelmässä ilman erillistä ajonaikaista käännösvaihetta. Hallituissa ajoympäristöissä AOT-kääntäminen voi tarkoittaa välikoodin kääntämistä natiiviksi konekieleksi asennuksen tai julkaisun yhteydessä, jolloin ajonaikaisen JIT-kääntäjän tehtävä kevenee tai poistuu kokonaan.

Laajemmassa merkityksessä AOT-kääntämisellä voidaan viitata myös tilanteisiin, joissa koodi käännetään etukäteen ajoympäristön omaan tavukoodiin tai esimerkiksi optimoituun JavaScript-moottorin käyttämään välikoodiin, kunhan käännös tapahtuu ennen varsinaista suoritusta.

Vaikka staattinen kääntäminen on luonteeltaan ahead-of-time-kääntämistä, termiä käytetään erityisesti ympäristöissä, joissa vaihtoehtona on ajonaikainen optimointi.

## Suhde ajonaikaiseen kääntämiseen ja tulkkaukseen
Ajonaikainen kääntäminen (JIT) kääntää ohjelmakoodia konekielelle suorituksen aikana. Se voi hyödyntää ajonaikaisista suoritustiedoista koottuja mittareita ja profiilitietoa, kuten sitä mitkä koodiosat suoritetaan useimmin, miten haarautumiset yleensä käyttäytyvät ja minkälaisia olioita ja tietotyyppejä ohjelma käytännössä käsittelee. Tulkki puolestaan suorittaa koodia tulkitsemalla ilman erillistä konekieliseen muotoon kääntämistä.

AOT-kääntäminen sijoittuu näiden strategioiden rinnalle. AOT poistaa tulkin jatkuvan rivikohtaisen tulkinnan tarpeen ja vähentää tulkkauksen aiheuttamaa riippuvuutta ajoympäristöstä. AOT siirtää käännöstyön kokonaan tai lähes kokonaan ajosta käännös- tai asennusvaiheeseen, jolloin ohjelman käynnistyminen nopeutuu ja ajonaikainen muistinkäyttö voi pienentyä, koska JIT-kääntäjää ei tarvitse ladata tai ylläpitää.

Monissa järjestelmissä käytetään yhdistelmää, jossa osa koodista käännetään AOT-muotoon ja osa JIT-kääntäjällä. Näin pyritään hyödyntämään sekä etukäteisoptimointeja että ajonaikaisia optimointeja.

JIT-kääntäjät voivat tehdä dynaamisia optimointeja, kuten kuumien koodipolkujen uudelleenkääntämistä ja ajonaikaiseen profiiliin perustuvaa optimointia. AOT-kääntäjät eivät voi samalla tavalla hyödyntää ajonaikaista tietoa, mutta niillä on usein enemmän aikaa ja mahdollisuus tehdä raskaita optimointeja, jotka voisivat olla liian hitaita suorituksen aikana.

## Käyttökohteita

### Staattisesti käännettävät kielet
Monet perinteiset, staattisesti käännettävät kielet, kuten C ja C++, ovat esimerkkejä ympäristöistä, joissa AOT-kääntäminen on oletusarvoinen malli. Koodi käännetään suoritettaviksi binääreiksi ennen ajamista. Tässä merkityksessä kaikki staattinen kääntäminen on ahead-of-time-kääntämistä, vaikka termiä käytetään useimmiten korostamaan sellaisia tapauksia, joissa AOT tarjoaa selkeitä etuja verrattuna JIT-kääntämiseen tai tulkkaukseen.

### Hallitut ajoympäristöt ja virtuaalikoneet
Hallituissa ajoympäristöissä, kuten Common Language Infrastructure (CLI) ja Java Virtual Machine (JVM), ohjelmat käännetään usein ensin välikoodimuotoon, kuten esimerkiksi CIL tai Java-tavukoodi. Välikoodi käännetään JIT-kääntäjällä natiiviksi konekieleksi suorituksen aikana, mutta useat toteutukset tukevat myös AOT-kääntämistä.

CLI-ympäristöissä on toteutuksia, joissa CIL-koodi voidaan kääntää etukäteen natiiviksi konekieleksi. Esimerkki tästä on .NET-ympäristön Native AOT -tekniikka, jossa sovellus käännetään julkaisuvaiheessa itsenäiseksi natiivibinääriksi.

Java-alustalla AOT-kääntämistä on tuotu saataville muun muassa JEP 295 -ehdotuksessa ja sitä hyödyntävissä työkaluissa, kuten Java 9:n *jaotc*-kääntäjässä ja GraalVM:n Native Image -tekniikassa.

Joissakin kielissä, kuten Clojurea käyttäviä JVM-toteutuksia hyödyntävissä järjestelmissä, AOT-kääntäminen muuntaa lähdekoodin tai välikoodin etukäteen virtuaalikoneen omaksi tavukoodiksi, mikä voi nopeuttaa käynnistymistä ja pienentää ajonaikaisia kustannuksia.

Myös Julia-kielessä on järjestelmäimageja ja pakettikuvia varten käytetty AOT-kääntämisjärjestelmä, joka tuottaa valmiiksi käännettyjä binäärejä nopeuttamaan ohjelmien käynnistymistä.

### Mobiili- ja sulautetut ympäristöt
Mobiili- ja sulautetuissa järjestelmissä AOT-kääntämistä käytetään usein vähentämään laitteen resurssikuormaa.

Android Runtime (ART) hyödyntää AOT-kääntämistä korvatessaan Dalvik-virtuaalikoneen JIT-painotteisen mallin. Sovellukset käännetään asennusvaiheessa natiivikoodiksi, mikä nopeuttaa käynnistystä ja vähentää ajonaikaista kääntämistä, vaikka asennus voi kestää kauemmin ja viedä enemmän tallennustilaa.

Flutter-kehys hyödyntää Dart-kieltä, jossa käytetään kehitysvaiheessa JIT-kääntämistä ja tuotantoversioissa AOT-kääntämistä natiivikoodiksi. Tavoitteena on, että julkaistut sovellukset käynnistyvät nopeasti ja toimivat suorituskykyisesti mobiililaitteilla.

Sulautetuissa järjestelmissä AOT-kääntämistä käytetään tyypillisesti silloin, kun ohjelmisto halutaan toimittaa valmiina binäärinä, joka käynnistyy nopeasti, toimii rajallisilla muisti- ja suorituskykyresursseilla ja jossa ajonaikaisia kääntäjäkomponentteja ei haluta sisällyttää laiteohjelmistoon.

### Web-ympäristöt ja JavaScript
JavaScript-ympäristöissä AOT-kääntämistä on käytetty muun muassa optimoidun välikoodin tai natiivikoodin muodostamiseen etukäteen, jotta selaimen ei tarvitse tehdä kaikkea optimointia ajonaikaisesti. Asm.js- ja WebAssembly-teknologioiden yhteydessä koodi voidaan kääntää valmiiksi tehokkaaksi binäärimuodoksi, joka ladataan ja suoritetaan nopeasti selaimessa.

## Etuja
AOT-kääntämiseen liitetään tyypillisesti seuraavia etuja:

* Nopeampi käynnistysaika Käännöstyötä ei tarvitse tehdä ohjelman käynnistyessä, joten sovellus voi alkaa suorittaa natiivikoodia välittömästi.

* Pienempi ajonaikainen muistinkäyttö. JIT-kääntäjää ja sen tarvitsemia datastruktuureja ei välttämättä tarvita, mikä voi vähentää muistinkulutusta erityisesti laitteissa, joissa resurssit ovat rajallisia.

* Yksinkertaisempi ajonaikainen ympäristö. Osa ajonaikaisesta ympäristöstä voidaan jättää pois, kun käännös on tehty etukäteen. Tämä voi helpottaa sovelluksen jakelua ja turvallisuusarviointia esimerkiksi sulautetuissa järjestelmissä.

* Mahdollisuus raskaisiin optimointeihin. Kääntäjällä on enemmän aikaa ja resursseja tehdä laajoja optimointeja, jotka voisivat olla liian hitaita ajonaikaisesti suoritettaviksi.

## Haittoja ja rajoitteita
AOT-kääntämiseen liittyy myös rajoitteita ja kompromisseja:

* Heikompi dynaaminen optimointi. Ilman ajonaikaista profiilia AOT-kääntäjä ei voi optimoida niin kutsutun kuuman koodipolun (*hot code path*) funktioita ja silmukoita, joiden läpi ohjelman suoritus kulkee toistuvasti. Tämä voi joissakin tapauksissa johtaa hieman alhaisempaan suorituskykyyn.

* Alustasidonnaisuus. AOT-käännetyt binäärit ovat tyypillisesti sidottuja tiettyyn kohdealustaan, eli prosessoriarkkitehtuurin ja käyttöjärjestelmän yhdistelmään. Sama binääri ei yleensä toimi eri prosessoriarkkitehtuurilla tai toisessa käyttöjärjestelmässä ilman uutta käännöstä, koska sekä prosessorin käskykanta että käyttöjärjestelmän määrittelemä binäärirajapinta ja järjestelmäkutsut poikkeavat toisistaan.

* Suuremmat binäärikoot. Valmiiksi käännetty koodi ja aggressiivinen optimointi voivat kasvattaa binäärien kokoa verrattuna pelkkään välikoodiin tai tulkattavaan lähdekoodiin.

* Kääntämisvaiheen kesto. AOT-käännös voi olla aikaa vievä prosessi, minkä vuoksi sitä käytetään usein vasta julkaisuvaiheessa tai erillisessä *build*-vaiheessa, ei jokaisella kehityssyklin iteraatiolla.

Monet modernit järjestelmät tasapainottavat näitä rajoitteita hybridimalleilla, joissa osa koodista käännetään AOT-muotoon ja osa jätetään JIT-kääntimen tai tulkin hoidettavaksi.

## Katso myös

* Ajonaikainen kääntäminen
* Tavukoodi
* Common Language Infrastructure
* CIL (tietotekniikka)
* Common Language Runtime
* WebAssembly

## Lähteet