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/ib­m/can.html/eng/product/jit/jit_re­adme.html

http://www.ibm.com

http://www.borland.com

0 0494 / als