Just-In-Time generátory: pekelně rychlá Java

1. 2. 2000

Sdílet

Většina kritiků programovacího jazyka Java se opírá o širokou veřejností uznávaný fakt, že javovské programy nemo...
Většina kritiků programovacího jazyka Java se opírá o širokou veřejností
uznávaný fakt, že javovské programy nemohou dosáhnout nikdy takové výkonnosti
jako například programy napsané v C++. Tato skutečnost ale neplatí tak docela,
pomocí JIT (Just-In-Time) překladačů lze jejich výkon výrazně zvýšit. A právě o
tom by vás měly přesvědčit následujcící odstavce.
A co že se tedy za tajemným výrazem JIT skrývá? Jedná se vlastně o generátor
kódu, který transformuje javovský bytekód do nativního kódu počítače. Programy
vykonávané právě pomocí JIT překladače pak běží obecně výrazně rychleji než ty,
které by byly vykonávány pouze pomocí standardního interpreteru bytekódu.
Ačkoliv specializované API, které programátorům umožňuje vytvářet generátory
nativního kódu, je implementováno již ve verzi JDK 1.0.2, byly JIT kompilery
dostupné až donedávna zejména od třetích stran a samotná firma Sun se od těchto
nástrojů jakoby distancovala. To se ale také změnilo, a u verze JDK 1.1.6 je
již JIT k dispozici.
Tajemné slovo JIT
Jak vyplývá z názvu JIT překladače, bude se zřejmě jednat o programy, které
něco v přesně definovaném čase překládají. Prakticky to pak znamená, že takový
JIT kompiler konvertuje javovský bytekód do nativního kódu daného hardwaru. K
tomuto překladu dochází při prvním volání funkce nebo objektu a později je již
vykonáván právě tento přeložený kód. Je tedy vidět, že k překladu dochází
opravdu Just-In-Time. Hlavní výhoda takových překladačů se tedy projeví pouze v
případě, že k vykonávání nějaké akce dojde minimálně dvakrát, v druhém (a v
každém dalším) případě se totiž nemusí již volaná funkce znovu překládat a může
se použít rychlý přeložený nativní kód. Samozřejmě na druhou stranu v případě,
kdy je daná funkce volána pouze jednou, dochází k malému zpoždění v důsledku
větší složitosti JIT překladače a nutnosti uložit přeložený kód do paměti. V
praxi ale k takovýmto případů příliš nedochází a obecně lze hovořit o tom, že
takovéto překladače zrychlí běh programů často až několikanásobně, což ostatně
můžete sami porovnat v přiložených tabulkách. Z praxe pak možná znáte příklad z
oblasti interpreterů Java appletů pokud porovnáte rychlost běhu Java appletu v
Microsoft Internet Exploreru (ten má integrován JIT překladač) například se
starším AppletViewerem (standardní překladač) firmy Sun, pak vám musí být
výhody těchto specializovaných překladačů jasné.
Důležité je upozornit, že při použití JIT překladačů samozřejmě nedochází ke
zrušení libovolné javovské výhody přenositelnosti, bezpečnosti nebo velikosti.
Přenositelnost zůstává v každém případě zachována, programy jsou i nadále
distribuovány ve formě bytekódu, pouze jsou při každém spuštění programu
vytvářeny překlady jednotlivých funkcí do nativních instrukcí vykonávajícího
počítače. To samé platí i pro oblast bezpečnosti, kdy předtím, než je programu
umožněn běh a kdy je tedy přeložen, dojde ke stejným bezpečnostním testům jako
v případě standardních interpreterů bytekódu. Zajímavá je také oblast velikosti
přenášených dat, kdy Java má výhodu ve své komplexnosti a používání
standardních knihoven, které jsou již v každém systému obsaženy, a nemusejí se
tedy přenášet. Je jasné, že programy které jsou zapsány pomocí komplexních
instrukcí (což je i případ Javy), zabírají pak méně místa než jejich riscoví
kolegové.
Vlastní funkčnost
Jak tedy takový JIT překladač prakticky funguje? Hlavní odpovědnost v tomto
případě leží na bedrech jediné javovské třídy Java.lang.Compiler, která nahraje
nativní připravené knihovny a nastartuje vlastní inicializaci uvnitř JIT
překladače. Ten je podporován ve formě samostatných platformově závislých
knihoven. V případě, že taková knihovna pro danou platformu existuje, pak je
použito standardní JNI (Java Native Interface) rozhraní, které slouží k volání
funkcí napsaných v jiných programovacích jazycích, nejčastěji pak v jazyce C.
Vlastní funkčnost JIT překladačů pak nejlépe charakterizuje schéma, které
vidíte na předchozí straně. Na první pohled je tedy jasné, že oproti
standardním interpreterům bytekódu nedochází k žádným velkým změnám.
Optimalizace
V současné době ale JIT kompilery již nevystačí jen s pouhým překladem do
nativního kódu, a v honbě za lepšími časy proto začínají přepisovat přímo části
kódu. Velice často se totiž stane, že je program napsán vyloženě neefektivně a
pouhou drobnou změnou jde běh programu výrazně urychlit. Takovým typickým
příkladem může být následující cyklus:
for (int i=0; i < nejakyString(). length(); i++)
// část kódu neměnící délku textu v proměnné nejakyString
}
Na první pohled je vidět, že ačkoliv je celý kód 100% funkční, není pro určité
velikosti textových řetězců příliš optimalizován. Šikovný JIT překladač si ale
dokáže poradit i s takovýmto kódem, který by mohl přetvořit do poněkud
optimalizovanější podoby:
int tmp = nejakyString(). length();
for (int i=0; i < tmp; i++)
// část kódu neměnící délku proměnné nejakyString
}
To samozřejmě není jediná optimalizace, jaké jsou JIT kompilery schopny,
podobné změny dokážou udělat v případě polí a některé překladače dokážou
dokonce z vašich objektových kódů udělat programy podobné jazyku C, kdy
odstraní často zbytečný overhead při volání funkcí, jejichž jedinou povinností
je vrátit interní hodnoty.
Po přečtení předchozích odstavců by se sice mohlo zdát, že Java programy můžete
psát bez velkého přemýšlení, ale situace není ani zdaleka tak jednoduchá. V
předchozím uvedeném příkladě totiž velmi závisí na počtu znaků v daném textovém
řetězci a optimalizace také neproběhne, pokud pracujete s řetězcem ve smyčce, a
překladač pak nemůže s jistotou prohlásit, že nedojde ke změně velikosti daného
textu. Pokud tedy budete psát nějaký kód, určitě myslete na optimalizaci
předem, zcela jistě běhu programu neublížíte.
Překládejte Ahead
Ačkoliv jsou JIT překladače nejrozšířenější, zejména díky své schopnosti
zachovávat 100% přenositelnost javovských bytekódů, objevují se v dnešní době i
jiné typy překladačů, které místo slov Just-In-Time používají výraz
Ahead-Of-Time (předčasný překlad). Tedy takové, které překládají program do
nativního kódu ještě před jeho spuštěním a uchovávají ho pak v této přeložené
podobě. Výhody jsou zřejmé lepší výkon než u JIT překladačů (k překladu
nedochází při každém startu programu) a nezávislost na nutnosti instalovaného
JIT interpreteru na vykonávajícím počítači. Nevýhodou je pak výrazné zvětšení
velikosti programu. V současné době existují dvě následující varianty:
Ahead-Of-Time recompiler jak již název napovídá, nejedná se přímo o překladač,
ale o rekompiler, pracuje tedy podobně jako JIT, ale překládá vlastně celý
program. Vstupem tohoto prográmku jsou přímo Java class soubory, které překládá
do tzv. "fat" class souborů. Tyto soubory pak obsahují jak originální bytekód,
tak nativní kód počítače. Zajímavé je, že tyto "fat" class soubory mohou
obsahovat nativní kódy pro více architektur najednou, výsledný distribuovaný
program se tedy pokusí na spouštějícím počítači vyhledat přímo nativní kód jemu
určený a v případě, že se mu to nepovede, pustí normální interpreter bytekódu.
Výhoda je zřejmá rychlost, možnost více platforem, nevýhodou pak nárůst
velikosti souborů.
Ahead-Of-Time compiler na rozdíl od předchozího případu pracuje tento překladač
přímo se zdrojovými kódy, které překládá do podobných "fat" class souborů.
Platí zde podobné zákony jako v předchozím případě, výhodou je znalost
původního kódu při překladu, nativní kód se tedy může vygenerovat mnohem
optimálněji a možnosti optimalizace jsou daleko větší.
Současná nabídka
V dnešní době lze říci, že JIT překladače pomalu, ale jistě vytlačují
standardní JVM (Java Virtual Machine) do pozadí a díky své rychlosti je nahradí
brzy téměř všude. Právě díky JIT generátorům již nelze Javu odepsat pro náročné
aplikace jako příliš pomalou, rozdíl mezi nejrychlejšími programy v C a Javě se
totiž ukazuje jako minimální, zejména pokud vezmeme v úvahu přenositelnost a
rychlost vývoje Java aplikací. Díky JIT se Java asi začne prosazovat i na
straně serveru ve formě servletů, protože pomalé vykonání bude probíhat pouze v
prvním případě, později bude server odpovídat již téměř stejně rychle jako za
použití CGI programů napsaných v C a často i rychleji.
V dnešní době nabízí JIT generátor opravdu velké množství firem a je často
těžké vybrat ten nejlepší. Takový drobný seznam je na konci tohoto textu, výběr
je opravdu široký, od freeware produktů až po komerční produkty. Zaleží tak jen
na vás, v každém případě se vyplatí uvažovat o zrychlení vašich aplikací.
Samozřejmě někdy to není úplně nutné, jako třeba v případě simulátoru Sinclairu
ZX, který je na předchozí straně. Ale vývoj od té doby poněkud pokročil a Java
má přeci na víc než na emulátory 8bitů.
Odkazy na jit generátory
http://www.microsoft.com/java/
http://www.mozilla.org/projects/ef/
http://www.kaffe.org/
http://www.symantec.com/ibm/can.html/eng/product/jit/jit_readme.html
http://www.ibm.com
http://www.borland.com
0 0494 / als