Just-in-time kompilace
JIT (akronym pro just-in-time) je v informatice označení pro speciální metodu překladu využívající různé techniky pro urychlení běhu programů přeložených do mezikódu (např. CIL). Používá se například pro programovací jazyk Java. Program, který je spuštěn a prováděn, je v době provádění přeložen přímo do nativního strojového kódu počítače, na kterém je prováděn, čímž dochází k urychlení jeho běhu. Negativem této techniky je prodleva, kterou JIT kompilátor (nikoli interpret) stráví překladem do nativního kódu, a proto se do nativního kódu často překládají jen mnohokrát (řádově 10 000×) volané úseky programu. Hlavním problémem JIT je, že má málo času na provedení své práce. Tyto nevýhody lze eliminovat použitím trvalé cache. Naopak výhodou je, že je možné lépe optimalizovat pro daný procesor a využít jeho rozšířených instrukcí.
Druhy JIT kompilátorů
editovat- Překlad v době instalace – k přeložení dochází v době instalace aplikace či komponenty. Výhodou je odstranění zpoždění, které způsobuje samotný překlad. V tomto případě se už nejedná o Just In Time, ale Ahead Of Time kompilaci.
- Opravdový JIT překladač – před spuštěním aplikace dojde k překladu a optimalizaci. Výsledek je srovnatelný s konvenčním překladačem. Nevýhodou je poměrně velké zdržení před spuštěním aplikace.
- Ekonomický JIT překladač – provádí částečný překlad programu. Jsou překládány jen ty části, k jejichž vykonání má dojít, a optimalizační algoritmy jsou vypnuty. Uskutečněné překlady jsou archivovány, aby se nemusely překládat znovu. Hlavní výhodou je menší paměťová náročnost.
Používané techniky
editovat- odstranění mrtvého kódu
- vkládání těl metod
- odstranění shodných podvýrazů
- rozbalování smyček, ...
Podrobnosti
editovatZdrojový kód je přeložen do mezikódu známého jako bajtkód (anglicky bytecode). Bajtkód není strojový kód pro konkrétní počítač a je přenositelný mezi různými počítačovými platformami. Bajtkód je následně interpretován a spuštěn pomocí virtuálního stroje (tzv. aplikační virtualizace). Just In Time kompilátor může být použit jako způsob urychlení spouštění bajtkódu. V době, kdy má být vykonán blok kódu, přeloží JIT kompilátor některé či všechny jeho části do strojového kódu pro lepší výkon. Toto lze provést na soubor, na funkci nebo dokonce na libovolný fragment kódu. Překlad tedy může být proveden až za běhu, odtud název Just In Time – Právě v čas. Překlad může být uložen do souboru (disková cache) a pokud má být znovu použit později, není nutná opětovná kompilace.
Tradiční interpretové virtuální stroje budou interpretovat bajtkód obvykle s mnohem nižším výkonem. Některé interprety dokonce interpretují zdrojový kód bez mezikroku překladu do bajtkódu, což vede k ještě horšímu výkonu. Staticky zkompilovaný kód nebo nativní kód je přeložen ještě před jeho spuštěním. Dynamické prostředí pro překlad pak umožňuje využití kompilátoru i za běhu programu. To poskytuje mnoho výhod JIT, ale programátor má raději kontrolu nad částmi kódu, které jsou přeloženy. Většina JIT systémů však umožňuje přeložit dynamicky generovaný kód, který poskytuje v mnoha situacích podstatné výkonnostní výhody oproti staticky kompilovanému kódu.
Všeobecný cílem užívání JIT technik je dosáhnout nebo překonat výkonnost statické kompilace při zachování výhod bajtkódové interpretace. V době překladu do bajtkódu je provedeno velmi náročné parsování původního zdrojového kódu a provedení základní optimalizace. Překlad z bajtkódu do strojového kódu je pak mnohem rychlejší než překlad ze zdrojového kódu. Překladače z bajtkódu do strojového kódu jsou jednodušší na napsání, protože překladač generující přenositelný bajtkód již udělal mnoho práce. Navíc má běhové prostředí kontrolu nad kompilací stejně jako interpretovaný kód, díky čemuž může běžet v chráněném režimu.
Kompilace může být optimalizována pro cílový procesor a operační systém, v němž aplikace běží. JIT může vybrat takové instrukce, o nichž zjistí, že jsou daným procesorem podporovány. K zajištění tohoto stupně optimalizační specifikace se statickým kompilátorem se musí buď přeložit binárka pro každou zamýšlenou platformu či architekturu, nebo seskupit mnoho verzí částí kódu v rámci jedné binárky.
Systém JIT je schopen shromažďovat statistické údaje o tom, jak program aktuálně běží v prostředí, v němž je spuštěn, a může tak kód přeuspořádat a přeložit ho pro optimální výkon. Nicméně některé statické kompilátory mohou rovněž získat informace o cílovém běhovém prostředí.
Systém může provádět globální optimalizaci kódu bez ztráty výhod dynamického linkování a bez nutné režie spojené s funkcí statických kompilátorů a linkerů. Speciálně když se provádí globální substituce, může proces statické kompilace potřebovat běhové ověření a zajistit, aby virtuální volání nastalo pouze v případě, že aktuální třída objektu implementuje danou metodu, a dále ověřit splnění hraničních podmínek při přístupu k polím, které je nutné zpracovat v průběhu cyklu. S JIT kompilací může být v mnoha případech toto zpracování přesunuto mimo cyklus, což často přináší obrovský nárůst rychlosti.
Zpoždění při startu a optimalizace
editovatJIT typicky způsobuje mírné zpoždění při spouštění aplikace kvůli času potřebnému k načtení bajtkódu do paměti a jeho překladu. V angličtině se toto zpoždění nazývá "startup time delay". Obecně platí, že čím větší optimalizace JIT provádí, tím lepší kód vygeneruje, ale tím se také zvětšuje počáteční zpoždění. Proto JIT kompilátor musí dělat kompromis mezi dobou potřebnou pro optimalizaci překladu a kvalitou kódu, který má vygenerovat.
Velká část zpoždění při spuštění je však často způsobena V/V operacemi než JIT kompilací. Například třídní datový soubor rt.jar pro Java Virtual Machine (dále jen JVM) má okolo 40 MB a JVM musí prohledávat velké množství dat v tomto obrovském souboru. Nicméně občas je při spouštění mnoho času zabráno spíše kvůli I/O operacím než kvůli JIT kompilaci.
Jednou z možných optimalizací, které používá virtuální stroj Java HotSpot společnosti Sun, je kombinace interpretace a JIT kompilace. Zpočátku je kód aplikace interpretován, čímž se snižuje počáteční zpoždění; JVM však monitoruje, které sekvence bajtkódu se provádějí nejčastěji, a překládá je do strojového kódu pro přímé spuštění na hardwaru k maximalizaci rychlosti běhu. Díky tomu, že program tráví většinu času prováděním malé části kódu, je redukce doby překladu značná.
Správný kompromis se může měnit v závislosti na okolnostech. Například JVM má dva hlavní režimy – klient a server. V klientském režimu je provedena minimální kompilace a optimalizace k redukci doby spouštění. V režimu server jsou provedeny rozsáhlé optimalizace a kompilace k maximalizaci výkonu za obětování mnohem delšího času. Jiné Java JIT překladače využívají statistiky. Zjišťují kolikrát je metoda vyvolána a v kombinaci s velikostí bajtkódu heuristicky rozhodnou kdy co zkompilovat. Další překladače zase zjišťují počet spuštění kombinované s detekcí cyklů. Obecně je velmi těžké přesně předpovědět, jaké metody optimalizovat v krátce běžících a jaké v dlouhodobě běžících aplikacích.
Native Image Generator (NGEN), česky generátor nativního obrazu od společnosti Microsoft má jiný přístup ke snížení počátečního zpoždění. NGEN zkompiluje bajtkód do obrazu s nativním kódem počítače. Výsledkem je, že pro druhé a další spuštění není nutná běhová kompilace. Od .NET Frameworku verze 2.0 dodávané s Visual Studiem 2005 běží NGEN hned po instalaci nad všemi knihovnami DLL od Microsoftu. JIT pre-kompilace poskytuje způsob, jak zlepšit (zrychlit) dobu spuštění. Nicméně kvalita generovaného kódu nemusí být tak dobrá z podobných důvodů, jako u staticky zkompilovaného kódu bez možnosti optimalizací proveditelných pouze za běhu. Existují také implementace Javy, které kombinují ahead-of-time kompilátor, což je kompilace mezijazyka jakým je java bajtkód nebo .NET Intermedite Language do nativní (systémově závislé) binárky, s Just in Time kompilací.
Stále více moderních běhových prostředí, jako například Microsoft .NET Framework a většina implementací Javy jsou založené na Just In Time kompilaci.
Historie
editovatZa první zveřejněný JIT kompilátor bývá považován McCartymo kompilátor LISPu z roku 1960.[1] V jeho seminární práci Rekurzivní funkce symbolických výrazů a jejich strojové vyhodnocování, Část I, zmiňuje funkce, které jsou překládány za běhu programu, ušetří se tedy nutnost ukládat výstup kompilátoru na děrné štítky.[2] V roce 1968 Thompson představil metodu pro automatickou kompilaci regulárních výrazů do strojového kódu, který je poté spouštěn, aby mohl provést srovnávání na vstupním textu.[1][3] Významná technika pro odvozování zkompilovaného kódu pro interpretaci byla propagována Mitchellem v roce 1970, kterou implementoval pro experimentální jazyk LC.[1][4]
Průkopníkem nových trendů při JIT kompilaci byl Smalltalk. Například překlad do strojového kódu se prováděl na vyžádání a výsledek se ukládal do vyrovnávací paměti pro další použití. V okamžiku, kdy začala docházet volná paměť, systém začal části tohoto kódu mazat a znovu je vytvořil až když byly potřeba.[1][5] Tyto techniky významně vylepšil jazyk Self firmy Sun, který byl jednu dobu nejrychlejším Smalltalk systémem na světě; dosahoval až poloviční rychlosti optimalizovaného kódu jazyka C,[6] a to pro plně objektově orientovaný jazyk.
Self byl Sunem zavržen, výzkum se přesunul do jazyka Java, momentálně je využíván ve velké většině implementací Java Virtual Machine, například HotSpot, který je na těchto principech založen a bohatě využívá dostupnou výzkumnou základnu.
HP projekt Dynamo byl experimentální JIT kompilátor, jehož bajtkódový formát a formát strojového kódu byl stejný; systém překládal strojový kód HPA-8000 na strojový kód HPA-8000. To nečekaně vedlo ke zrychlení, v mnoha případech až o 30 %, protože bylo možné provádět optimalizace na úrovni strojového kódu, například vkládání kódu pro lepší využití cache, optimalizaci volání dynamických knihoven a mnoho jiných optimalizací při běhu programu, kterých konvenční kompilátory nebyly schopny.[7]
Reference
editovatV tomto článku byl použit překlad textu z článku Just-in-time compilation na anglické Wikipedii.
- ↑ a b c d AYCOCK, J. A brief history of just-in-time. ACM Computing Surveys. 2003, s. 97–113. Dostupné online [cit. 2010-05-24]. DOI 10.1145/857076.857077.
- ↑ MCCARTHY, J. Recursive functions of symbolic expressions and their computation by machine, Part I. Communications of the ACM. 1960, s. 184–195. Dostupné online [cit. 24 May 2010]. DOI 10.1145/367177.367199.
- ↑ THOMPSON, K. Programming Techniques: Regular expression search algorithm. Communications of the ACM. 1968, s. 419–422. Dostupné online [cit. 2010-05-24]. DOI 10.1145/363347.363387.
- ↑ MITCHELL, J.G. The design and construction of flexible and efficient interactive programming systems. [s.l.]: [s.n.], 1970.
- ↑ DEUTSCH, L.P.; SCHIFFMAN, A.M. Efficient implementation of the Smalltalk-80 system. POPL '84: Proceedings of the 11th ACM SIGACT-SIGPLAN Symposium on Principles of Programming Languages. 1984, s. 297–302. DOI 10.1145/800017.800542.
- ↑ http://research.sun.com/jtech/pubs/97-pep.ps (webarchive)
- ↑ Ars Technica on HP's Dynamo