Obsah
1. JamVM aneb alternativa k HotSpotu (nejenom) pro embedded zařízení, tablety a chytré telefony
3. Jak přeložit IcedTea s podporou JamVM?
5. První porovnání JamVM a HotSpotu – velikost JRE
6. Druhé porovnání JamVM a HotSpotu – paměťová náročnost JRE při spuštění virtuálního stroje
7. Třetí porovnání JamVM a HotSpotu – výpočetní výkon při běhu benchmarku ConcatTest
8. Čtvrté porovnání JamVM a HotSpotu – výpočetní výkon dosahovaný při složitějších výpočtech
1. JamVM aneb alternativa k HotSpotu (nejenom) pro embedded zařízení a chytré telefony
V předchozích šestnácti částech seriálu o programovacím jazyce Java i o vlastnostech virtuálního stroje Javy jsme se zabývali převážně dvěma variantami JDK – OpenJDK6 a OpenJDK7. Tyto verze JDK a JRE jsou vybaveny virtuálním strojem Javy nazvaným HotSpot. Jedná se o virtuální stroj využívající poměrně velké množství sofistikovaných a po dlouhý čas pečlivě ověřovaných technologií, ať již se jedná o různé typy správců paměti (garbage collectors), tak i o just in time (JIT) překladač bajtkódu do nativního kódu platformy, na níž je virtuální stroj spuštěn. HotSpot dnes skutečně patří mezi technologickou špičku na poli virtuálních strojů, ovšem má i několik nevýhod, které mohou v některých případech omezovat možnosti jeho použití. Jednou z nevýhod je to, že HotSpot je k dispozici pro relativně omezené množství procesorových architektur: konkrétně ho lze použít na procesorových architekturách x86, x86_64 (AMD64), Itanium (končící podpora pro JDK7) a samozřejmě též na architekturách Sparc a UltraSparc.
Obrázek 1: Jeden z typů mikroprocesorů SuperSPARC, jenž byl vyráběný společností Sun Microsystems.
Originální zdrojové kódy HotSpotu obsahují přibližně deset tisíc řádků nízkoúrovňového a platformně závislého kódu napsaného v C++ a zhruba 1000 řádků kódu v assembleru. I z tohoto důvodu by bylo velmi složité přidat podporu pro další procesorovou architekturu. Aby bylo možné OpenJDK spouštět i na dalších platformách, vzniklo několik projektů, jejichž cílem bylo doplnit či dokonce zcela nahradit původní HotSpot jiným řešením. Jedním z těchto projektů je Zero-assembler port, což je (zjednodušeně řečeno) interpret bajtkódu psaný v programovacím jazyku C++, který je podporován relativně úzkou mezivrstvou mezi interpretrem bajtkódu a voláním nativního kódu (to zajišťuje knihovna libffi). Předností tohoto řešení je zajištěná přenositelnost (včetně platformy IBM S/390), nevýhodou pak pomalý běh programů způsobený interpretací bajtkódu. Druhou možností (či spíše doplňkem) je projekt Shark, který umožňuje využití just in time (JIT) překladače z LLVM. Tímto projektem se pravděpodobně budeme zabývat v některém z dalších dílů tohoto seriálu. Další alternativu k HotSpotu představuje projekt CACAO VM (ke kterému se opět ještě vrátíme) a taktéž JamVM popsaný v následujících kapitolách.
Obrázek 2: Architektura mikroprocesorů UltraSPARC T1.
2. Základní vlastnosti JamVM
JamVM je v mnoha ohledech poměrně zajímavý projekt, protože se jedná o samostatný a současně i plnohodnotný virtuální stroj Javy, který je z velké části vyvíjený jedním autorem, jímž je Robert Lougher. Předností JamVM v porovnání s HotSpotem je menší velikost samotného virtuálního stroje a taktéž prakticky neomezená přenositelnost na různé platformy (ovšem s určitým množstvím práce při přidávání další nové platformy), což se ukazuje být velmi důležitá vlastnost především v současnosti, kdy je mnoho vestavných (embedded) zařízení i smartphonů a tabletů (dokonce i několik typů netbooků) postaveno na mikroprocesorech ARM nebo MIPS, takže poptávka po výkonných ale paměťově nenáročných virtuálních strojích Javy je dosti velká (ostatně ani Dalvik není s JamVM – politicky korektně řečeno :-) – úplně nepodobný). JamVM je naprogramován převážně v čistém céčku, ovšem několik časově kritických částí je napsáno v assembleru, což znamená, že pro každou procesorovou platformu je nutné relativně malou část kódu naprogramovat znovu. Pro ilustraci: jedná se průměrně o zhruba tři až osm kilobajtů zdrojového kódu, což není mnoho.
Obrázek 3: Podíl jednotlivých programovacích jazyků ve zdrojových kódech JamVM.
Zdroj: analýza provedena službou Ohloh.
V současnosti je JamVM podporován na následujících architekturách:
- PowerPC
- i486 a vyšší (myšlena je zde 32bitová architektura x86)
- AMD64
- ARM (samozřejmě včetně populárního Beagle Boardu)
- MIPS
I když JamVM neobsahuje plnohodnotný just in time překladač, je jeho interpret bajtkódu v porovnání s konkurencí velmi rychlý, takže se jeho výkonnost zdola přibližuje k JIT překladačům. To je velmi důležité především na mikroprocesorech ARM a MIPS, pro něž není k dispozici originální HotSpot a Zero-assembler port je dosti pomalý. Součástí každého virtuálního stroje Javy však není pouze interpret bajtkódu či jeho JIT překladač, ale také správce či správci paměti. V případě JamVM je k dispozici modifikovaný správce paměti typu Mark and Sweep, který může běžet synchronně či asynchronně ve svém vlastním vláknu. Obrazy Javovských objektů na haldě se odlišují od obrazů používaných v HotSpotu, například tím, že se používají přímé ukazatele na objekty a hlavička obrazů objektů je pojata minimalisticky (obsahuje pouze dvě slova, z nichž jedno je ukazatel na třídu, které je objekt instancí).
Obrázek 4: Graf postupného růstu počtu řádků (včetně prázdných řádků a komentářů) ve zdrojových kódech virtuálního stroje JamVM.
Zdroj: analýza provedena službou Ohloh.
3. Jak přeložit IcedTea s podporou JamVM?
Virtuální stroj JamVM lze přeložit a využívat buď společně s projektem GNU Classpath (jako překladač se v tomto případě většinou používá gcj nebo ecj), popř. je možné využít jednodušší variantu – přeložit IcedTea (tj. OpenJDK s přidanými knihovnami, několika desítkami patchů a podporou překladu pouze pomocí GNU nástrojů), ovšem s náhradou původního HotSpotu právě za JamVM. Tento postup sice na první pohled může vypadat složitě, ovšem ve skutečnosti je již potřebná funkcionalita zabudována přímo do IcedTea. Nejprve si ukažme, jakým způsobem se překládá IcedTea6 založené na OpenJDK6, což je postup využívaný pro přípravu balíčků s OS Javou (SDK+JRE) na většině Linuxových distribucí. Celý postup je velmi jednoduchý, samozřejmě za předpokladu, že jsou nainstalovány všechny potřebné nástroje a knihovny:
hg clone http://icedtea.classpath.org/hg/icedtea6 ./autogen.sh ./configure --disable-bootstrap make
Při překladu IcedTea6 s podporou JamVM namísto původního HotSpotu přibude pouze jedna nová volba –enable-jamvm v příkazu configure:
hg clone http://icedtea.classpath.org/hg/icedtea6 ./autogen.sh ./configure --disable-bootstrap --enable-jamvm make
Poznámka: v případě, že se překlad provádí na počítači vybaveném větším množstvím mikroprocesorů, popř. vícejádrovými procesory, lze překlad urychlit pomocí volby –with-parallel-jobs=n, kde se za n dosadí počet procesů spuštěných během překladu:
hg clone http://icedtea.classpath.org/hg/icedtea6 ./autogen.sh ./configure --disable-bootstrap --enable-jamvm --with-parallel-jobs=4 make
4. Výsledné obrazy JDK a JRE
V případě, že překlad celého projektu IcedTea proběhne bez chyby, je obraz plnohodnotného JDK (používaného pro vývoj v Javě) uložen v podadresáři openjdk.build/j2sdk-image a obraz samotného JRE (pouze běhové prostředí určené například pro instalaci na aplikační servery) je v podadresáři openjdk.build/j2re-image:
ls -l openjdk.build total 124 drwxr-xr-x 2 tisnik tisnik 4096 Aug 5 22:18 bin drwxr-xr-x 5 tisnik tisnik 4096 Aug 5 22:15 btclasses drwxr-xr-x 2 tisnik tisnik 4096 Aug 5 22:11 btjars drwxr-xr-x 2 tisnik tisnik 4096 Aug 5 22:05 bundles drwxr-xr-x 9 tisnik tisnik 4096 Aug 5 22:16 classes drwxr-xr-x 9 tisnik tisnik 4096 Aug 5 22:07 corba drwxr-xr-x 9 tisnik tisnik 4096 Aug 5 22:17 demo drwxr-xr-x 3 tisnik tisnik 4096 Aug 5 22:17 democlasses drwxr-xr-x 6 tisnik tisnik 4096 Aug 5 22:20 docs drwxr-xr-x 3 tisnik tisnik 4096 Aug 5 22:16 gennativesrc drwxr-xr-x 6 tisnik tisnik 4096 Aug 5 22:16 gensrc drwxr-xr-x 4 tisnik tisnik 4096 Aug 5 22:05 hotspot drwxr-xr-x 7 tisnik tisnik 4096 Aug 5 22:11 impsrc drwxr-xr-x 3 tisnik tisnik 4096 Aug 5 22:16 include drwxr-xr-x 5 tisnik tisnik 4096 Aug 5 22:20 j2re-image <-------- drwxr-xr-x 9 tisnik tisnik 4096 Aug 5 22:21 j2sdk-image <-------- drwxr-xr-x 2 tisnik tisnik 4096 Aug 5 22:05 java.net drwxr-xr-x 5 tisnik tisnik 4096 Aug 5 22:07 jaxp drwxr-xr-x 5 tisnik tisnik 4096 Aug 5 22:07 jaxws drwxr-xr-x 4 tisnik tisnik 4096 Aug 5 22:05 langtools drwxr-xr-x 10 tisnik tisnik 4096 Aug 5 22:21 lib drwxr-xr-x 6 tisnik tisnik 4096 Aug 5 22:17 sample -rw-r--r-- 1 tisnik tisnik 12469 Aug 5 22:11 sanityCheckMessages.txt -rw-r--r-- 1 tisnik tisnik 4338 Aug 5 22:11 sanityCheckWarnings.txt drwxr-xr-x 2 tisnik tisnik 4096 Aug 5 22:05 source-bundles drwxr-xr-x 3 tisnik tisnik 4096 Aug 5 22:20 symbols drwxr-xr-x 24 tisnik tisnik 4096 Aug 5 22:21 tmp
Jak JDK, tak i JRE lze zkopírovat nebo nalinkovat do adresáře /usr/lib/jvm a nakonfigurovat cesty ke všem zde přítomným souborům pomocí alternatives, popř. podobného nástroje (nebo lze provést stejnou operaci ručně :-).
5. První porovnání JamVM a HotSpotu – velikost JRE
Po překladu obou variant IcedTea, tj. první varianty založené na klasickém HotSpotu a druhé varianty, která se spoléhá na alternativní JamVM v roli virtuálního stroje Javy, se můžeme podívat na to, v čem se ve skutečnosti odlišuje JRE s HotSpotem a JRE s JamVM. První rozdíl mezi oběma virtuálními stroji je patrný již při zcela zběžném pohledu na celkovou velikost adresářů s oběma variantami JRE. Následující hodnoty byly získány na 64bitové architektuře x86_64 (AMD64), což mj. znamená, že na 32bitových systémech budou zjištěné hodnoty o několik procent menší (poměr mezi jejich velikostmi však zůstane přibližně stejný).
HotSpot JRE:
du -s -b -h openjdk.build/j2re-image 95M j2re-image
JamVM JRE:
du -s -b -h openjdk.build/j2re-image 86M j2re-image
Při podrobnějším průzkumu obou adresářů obsahujících JRE lze zjistit, že naprostá většina souborů má shodnou délku i obsah a dalších několik souborů (většinou nativních knihoven) se ve velikosti liší pouze o několik desítek bajtů. Ovšem nejmarkantnější rozdíl je ve velikosti souborů libjvm.so, což je nativní knihovna obsahující vlastní virtuální stroj Javy. Tato knihovna má v případě HotSpotu přeloženého na platformě x86_64 velikost:
ls -lhR j2re-image | grep libjvm.so -rwxr-xr-x 1 tisnik tisnik 11M 2011-08-05 23:06 libjvm.so
tj. 11 megabajtů, zatímco pro JamVM dostaneme zcela odlišnou hodnotu:
ls -lhR j2re-image | grep libjvm.so -rwxr-xr-x 1 tisnik tisnik 371K 2011-08-05 22:49 libjvm.so
tj. velikost mnohem nižší. Aby bylo porovnání férové, byly porovnávány velikosti stripovaných souborů, v opačném případě by totiž byl rozdíl ve velikosti v rozsahu dvou řádů (!), samozřejmě v neprospěch HotSpotu, který obsahuje mnohem víc funkcí, metod a proměnných s přidruženými ladicími informacemi.
Poznámka: vyhledávání nativní knihovny libjvm.so ve výpisu pomocí příkazu grep jsem zvolil z toho důvodu, že se umístění této knihovny v adresářích JRE liší podle toho, zda je překlad proveden na 32bitovém systému (zde jsou v případě HotSpotu dostupné varianty klient i server) či na systému 64bitovém (zde je k dispozici pouze varianta server) a taktéž v závislosti na zvolené platformě. V našem konkrétním případě, kdy byl překlad IcedTea proveden na platformě x86_64, byla nalezena knihovna libjvm.so ležící v adresáři:
j2re-image/lib/amd64/server
6. Druhé porovnání JamVM a HotSpotu – paměťová náročnost JRE při spuštění virtuálního stroje
V předchozí kapitole jsme si ukázali, že velikosti nativních knihoven s virtuálním strojem Javy se sice vzájemně dosti významně odlišují, to ovšem ještě nemusí znamenat, že se budou ve stejném poměru lišit i obrazy virtuálních strojů v operační paměti počítače po jejich spuštění. Ovšem i pro tento případ je možné použít sice poněkud nepřesné, ale stále velmi jednoduché měření. Jedná se o výpis spotřeby paměti JVM v případě, že je v tomto stroji spuštěn nějaký velmi jednoduchý Javovský program, který jen vypíše číslo svého procesu (pro možnost podrobnějšího průzkumu) a posléze již pouze čeká na stisk klávesy Enter. Následuje výpis zdrojového kódu tohoto velmi jednoduchého testu:
import java.io.*; public class SimpleMemTest { static { // Nejprve vypiseme PID - cislo procesu. // Nasledujici trik je odzkousen pouze na Linuxu: try { System.out.println("PID=" + new File("/proc/self").getCanonicalFile().getName()); } catch (IOException e) { System.out.println("windows???"); } // potom jiz jen cekame na stisk Enteru System.console().readLine("Press Enter to exit this test"); // hotovo System.exit(0); } }
Ihned poté, co program vypíše číslo svého procesu, můžeme se z druhého terminálu (konzole) podívat, jakým způsobem je paměť pro daný virtuální stroj Javy alokována.
HotSpot JRE:
cat /proc/***PID***/status Name: java State: S (sleeping) SleepAVG: 68% Tgid: 10488 Pid: 10488 PPid: 10098 TracerPid: 0 Uid: 14282 14282 14282 14282 Gid: 14282 14282 14282 14282 FDSize: 256 Groups: 5283 14282 VmPeak: 2393280 kB VmSize: 2393280 kB VmLck: 0 kB VmHWM: 13444 kB VmRSS: 13444 kB VmData: 2293224 kB VmStk: 88 kB VmExe: 36 kB VmLib: 13528 kB VmPTE: 228 kB StaBrk: 01ed9000 kB Brk: 01fae000 kB StaStk: 7fff6e18dee0 kB Threads: 14 SigQ: 0/71680 SigPnd: 0000000000000000 ShdPnd: 0000000000000000 SigBlk: 0000000000000000 SigIgn: 0000000000000000 SigCgt: 0000000181005ccf CapInh: 0000000000000000 CapPrm: 0000000000000000 CapEff: 0000000000000000 Cpus_allowed: 00000000,00000000,00000000,00000000,00000000,00000000,00000000,0000000f Mems_allowed: 00000000,00000001
JamVM JRE:
cat /proc/***PID***/status Name: java State: S (sleeping) SleepAVG: 68% Tgid: 10455 Pid: 10455 PPid: 10098 TracerPid: 0 Uid: 14282 14282 14282 14282 Gid: 14282 14282 14282 14282 FDSize: 256 Groups: 5283 14282 VmPeak: 1351768 kB VmSize: 1319960 kB VmLck: 0 kB VmHWM: 9340 kB VmRSS: 9340 kB VmData: 1173200 kB VmStk: 88 kB VmExe: 36 kB VmLib: 2872 kB VmPTE: 200 kB StaBrk: 197d1000 kB Brk: 1991e000 kB StaStk: 7fff57c7b690 kB Threads: 6 SigQ: 0/71680 SigPnd: 0000000000000000 ShdPnd: 0000000000000000 SigBlk: 0000000000000000 SigIgn: 0000000000000000 SigCgt: 0000000180004207 CapInh: 0000000000000000 CapPrm: 0000000000000000 CapEff: 0000000000000000 Cpus_allowed: 00000000,00000000,00000000,00000000,00000000,00000000,00000000,0000000f Mems_allowed: 00000000,00000001
Ilustrativnější však bude zobrazení rozdílů mezi oběma virtuálními stroji:
diff --side-by-side status-hotspot status-jamvm Hotspot VM JamVM VmPeak: 2393280 kB | VmPeak: 1351768 kB VmSize: 2393280 kB | VmSize: 1319960 kB VmLck: 0 kB VmLck: 0 kB VmHWM: 13444 kB | VmHWM: 9340 kB VmRSS: 13444 kB | VmRSS: 9340 kB VmData: 2293224 kB | VmData: 1173200 kB VmStk: 88 kB VmStk: 88 kB VmExe: 36 kB VmExe: 36 kB VmLib: 13528 kB | VmLib: 2872 kB VmPTE: 228 kB | VmPTE: 200 kB
(nerelevantní řádky byly odstraněny). Předchozí měření je samozřejmě pouze přibližné, protože při spouštění virtuálního stroje Javy lze pomocí mnoha parametrů velmi přesně nastavit velikost jednotlivých paměťových regionů (zásobníku, haldy…), ovšem i samotná velikost HotSpotu či JamVM v tomto případě hraje svoji roli.
7. Třetí porovnání JamVM a HotSpotu – výpočetní výkon při běhu benchmarku ConcatTest
V tomto seriálu jsme se již několikrát setkali s jednoduchým benchmarkem nazvaným ConcatTest. V tomto benchmarku je v programové smyčce vytvářen řetězec skládaný postupně z menších řetězců. Vzhledem k tomu, že pro spojování řetězců je použit operátor +, prověřuje tento test jak funkci správců paměti (v jedné iteraci totiž dochází ke vzniku několika objektů s krátkou dobou života), tak i implementaci subrutin používaných pro kopii znaků mezi řetězci. Zdrojový kód benchmarku je následující:
public class ConcatTest { private static final int LOOP_COUNT = 10000; public static String createString(int loopCount) { String str = ""; for (int i = 0; i < loopCount; i++) { str += i + " "; } return str; } public static void main(String[] args) { long t1 = System.currentTimeMillis(); // vypocitat pocet iteraci int loopCount = args.length == 0 ? LOOP_COUNT : (1 << Integer.parseInt(args[0])); String str = ""; for (int i = 0; i < 10; i++) { str = createString(loopCount); } long t2 = System.currentTimeMillis(); System.out.format("Loop count: %8d time: %6d string length: %8d\n", loopCount, (t2-t1), str.length()); } }
Obrázek 5: Zdrojový kód prvního benchmarku se zvýrazněním syntaxe.
Tento benchmark je spouštěn ve skriptu, v němž se mění počet iterací programové smyčky použité pro konstrukci výsledného řetězce:
#!/bin/bash HOTSPOT_JRE=~/icedtea6-hotspot/openjdk.build/j2sdk-image/bin/ JAMVM_JRE=~/icedtea6-jamvm/openjdk.build/j2sdk-image/bin/ ${HOTSPOT_JRE}java -version 2> hotpspot.txt for i in `seq 5 16`; do ${HOTSPOT_JRE}java ConcatTest $i >> hotpspot.txt done ${JAMVM_JRE}java -version 2> hotpspot.txt for i in `seq 5 16`; do ${JAMVM_JRE}java ConcatTest $i >> jamvm.txt done
Obrázek 6: Zdrojový kód spouštěcího skriptu prvního benchmarku se zvýrazněním syntaxe.
Při spuštění benchmarku se kupodivu ukazuje, že HotSpot s povoleným JITem a JamVM s pouhým interpretrem bajtkódu jsou prakticky stejně rychlá řešení, a to převážně z toho důvodu, že kopie polí znaků a spouštění správců paměti jsou na obou virtuálních strojích realizovány shodným či velmi podobným způsobem:
java version "1.6.0_23" OpenJDK Runtime Environment (IcedTea6 1.11pre+r013f83c2fa16) (linux-gnu build 1. OpenJDK 64-Bit Server VM (build 20.0-b11, mixed mode) Loop count: 32 time: 1 string length: 86 Loop count: 64 time: 3 string length: 182 Loop count: 128 time: 6 string length: 402 Loop count: 256 time: 17 string length: 914 Loop count: 512 time: 52 string length: 1938 Loop count: 1024 time: 133 string length: 4010 Loop count: 2048 time: 443 string length: 9130 Loop count: 4096 time: 1399 string length: 19370 Loop count: 8192 time: 5471 string length: 39850 Loop count: 16384 time: 29295 string length: 87194 Loop count: 32768 time: 129135 string length: 185498
java version "1.6.0_23" IcedTea6 Runtime Environment (1.11pre+r013f83c2fa16) (linux-gnu build 1.6.0_23-b JamVM (build 1.6.0-devel, inline-threaded interpreter) Loop count: 32 time: 1 string length: 86 Loop count: 64 time: 3 string length: 182 Loop count: 128 time: 5 string length: 402 Loop count: 256 time: 13 string length: 914 Loop count: 512 time: 42 string length: 1938 Loop count: 1024 time: 153 string length: 4010 Loop count: 2048 time: 457 string length: 9130 Loop count: 4096 time: 1732 string length: 19370 Loop count: 8192 time: 6899 string length: 39850 Loop count: 16384 time: 28577 string length: 87194 Loop count: 32768 time: 122037 string length: 185498
8. Čtvrté porovnání JamVM a HotSpotu – výpočetní výkon při složitějších výpočtech
Benchmark uvedený v předchozí kapitole neukazoval prakticky žádné významnější rozdíly mezi HotSpotem a JamVM, a to především z toho důvodu, že se v něm nemohl výrazněji projevit JIT překladač, který je v HotSpotu implementován. Z tohoto důvodu si ještě ukážeme, jak se výpočetní výkon (přesněji řečeno rychlost provádění Javovských programů) změní ve chvíli, kdy se bude spouštět benchmark s mnoha do sebe vloženými programovými smyčkami, v nichž se provádí složitější výpočty, při nichž se nemusí volat žádné knihovní funkce. Právě zde by se měla projevit kvalita JIT překladače oproti pouhému interpretru bajtkódu. Jako druhý benchmark jsem zvolil program pro výpočet Mandelbrotovy množiny, jehož zdrojový kód je následující::
public class Mandelbrot { public static final int MAXITER = 1000; public static void mandelbrot(int resolution, int maxiter) { double cy = -2.0; for (int y = 0; y < resolution; y++) { double cx = -2.0; for (int x = 0; x < resolution; x++) { double zx = 0.0, zy = 0.0, zx2, zy2; int iter = 0; while (++iter < maxiter) { zx2 = zx * zx; zy2 = zy * zy; if (zx2 + zy2 > 4.0) break; zy = 2.0 * zx * zy + cy; zx = zx2 - zy2 + cx; } //System.out.print(iter==maxiter ? ' ' : '.'); cx += 4.0/resolution; } //System.out.println(); cy += 4.0/resolution; } } public static void main(String[] args) { long t1 = System.currentTimeMillis(); mandelbrot(1 << Integer.parseInt(args[0]), MAXITER); long t2 = System.currentTimeMillis(); System.out.format("time: %6d\n", t2-t1); } }
Obrázek 7: Zdrojový kód druhého benchmarku se zvýrazněním syntaxe.
Na rozdíl od předchozího benchmarku nyní budeme testovat výkonnost tří virtuálních strojů. Prvním virtuálním strojem je HotSpot s interpretrem bajtkódu (používá se volba -Djava.compiler=NONE), druhým virtuálním strojem je taktéž HotSpot, tentokrát však využívající svůj JIT překladač a konečně třetí virtuální stroj je představován JamVM. O spouštění všech tří benchmarků se stará následující skript:
#!/bin/bash HOTSPOT_JRE=~/icedtea6-hotspot/openjdk.build/j2sdk-image/bin/ JAMVM_JRE=~/icedtea6-jamvm/openjdk.build/j2sdk-image/bin/ for i in `seq 4 12`; do ${HOTSPOT_JRE}java -Djava.compiler=NONE Mandelbrot $i >> hotpspot_int.txt done for i in `seq 4 12`; do ${HOTSPOT_JRE}java Mandelbrot $i >> hotpspot_jit.txt done for i in `seq 4 12`; do ${JAMVM_JRE}java Mandelbrot $i >> jamvm.txt done
Obrázek 8: Zdrojový kód spouštěcího skriptu druhého benchmarku se zvýrazněním syntaxe.
V tomto případě se již naplno projevuje kvalita JIT překladače, který je zhruba desetkrát rychlejší, než interpret bajtkódu zabudovaný v HotSpotu. Zajímavé však je, že interpret z JamVM je rychlejší než interpret z HotSpotu, což jen svědčí o kvalitě návrhu JamVM, který i přesto, že se jedná v podstatě o one man show, dokáže konkurovat interpretru vyvíjenému již několik let větší vývojovou skupinou:
Rozlišení | HotSpot interpret | HotSpot JIT | JamVM |
---|---|---|---|
2×2 | 2 | 2 | 1 |
4×4 | 7 | 9 | 6 |
8×8 | 26 | 15 | 21 |
16×16 | 100 | 22 | 83 |
32×32 | 399 | 53 | 328 |
64×64 | 1595 | 191 | 1310 |
128×128 | 6362 | 698 | 5230 |
256×256 | 25456 | 2773 | 20928 |
512×512 | 101863 | 11015 | 83703 |
9. Odkazy na Internetu
- JamVM home page
http://jamvm.sourceforge.net/ - GNU Classpath
www.gnu.org/s/classpath/ - JamVM na stránkách SourceForge
http://sourceforge.net/projects/jamvm/ - Java VMs Compared
http://bugblogger.com/java-vms-compared-160/ - JamVM (Wikipedia)
http://en.wikipedia.org/wiki/JamVM - Statistika projektu JamVM na službě Ohloh
https://www.ohloh.net/p/jamvm - IcedTea Build Requirements
http://icedtea.classpath.org/wiki/BuildRequirements - IcedTea Fedora Build Instructions
http://icedtea.classpath.org/wiki/FedoraBuildInstructions - IcedTea Debian Building Instructions
http://icedtea.classpath.org/wiki/DebianBuildingInstructions - IcedTea Gentoo Build Instructions
http://icedtea.classpath.org/wiki/GentooBuildInstructions - JSRs: Java Specification Requests – JSR 223: Scripting for the Java Platform
http://www.jcp.org/en/jsr/detail?id=223 - Scripting for the Java Platform
http://java.sun.com/developer/technicalArticles/J2SE/Desktop/scripting/ - Scripting for the Java Platform (Wikipedia)
http://en.wikipedia.org/wiki/Scripting_for_the_Java_Platform - Java Community Process
http://en.wikipedia.org/wiki/Java_Specification_Request - Package javax.script (JavaDoc)
http://download.oracle.com/javase/6/docs/api/javax/script/package-summary.html - javax.script.Bindings
http://download.oracle.com/javase/6/docs/api/javax/script/Bindings.html - javax.script.Compilable
http://download.oracle.com/javase/6/docs/api/javax/script/Compilable.html - javax.script.Invocable
http://download.oracle.com/javase/6/docs/api/javax/script/Invocable.html - javax.script.ScriptContext
http://download.oracle.com/javase/6/docs/api/javax/script/ScriptContext.html - javax.script.ScriptEngine
http://download.oracle.com/javase/6/docs/api/javax/script/ScriptEngine.html - javax.script.ScriptEngineFactory
http://download.oracle.com/javase/6/docs/api/javax/script/ScriptEngineFactory.html - javax.script.AbstractScriptEngine
http://download.oracle.com/javase/6/docs/api/javax/script/AbstractScriptEngine.html - javax.script.CompiledScript
http://download.oracle.com/javase/6/docs/api/javax/script/CompiledScript.html - javax.script.ScriptEngineManager
http://download.oracle.com/javase/6/docs/api/javax/script/ScriptEngineManager.html - javax.script.SimpleBindings
http://download.oracle.com/javase/6/docs/api/javax/script/SimpleBindings.html - javax.script.SimpleScriptContext
http://download.oracle.com/javase/6/docs/api/javax/script/SimpleScriptContext.html - javax.script.ScriptException
http://download.oracle.com/javase/6/docs/api/javax/script/ScriptException.html - The Java Compatibility Test Tools: JavaTest Harness
http://java.sun.com/developer/technicalArticles/JCPtools2/ - JavaScript engine (Wikipedia)
http://en.wikipedia.org/wiki/JavaScript_engine - Rhino (JavaScript engine)
http://en.wikipedia.org/wiki/Rhino_(JavaScript_engine) - Rhino: JavaScript for Java
http://www.mozilla.org/rhino/ - Java HotSpot VM Options
http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html - HugePages
http://linux-mm.org/HugePages - Tuning big java heap and linux huge pages
http://www.tikalk.com/alm/forums/tuning-big-java-heap-and-linux-huge-pages - How do I set up hugepages in Red Hat Enterprise Linux 4
http://magazine.redhat.com/2007/05/29/how-do-i-set-up-hugepages-in-red-hat-enterprise-linux-4/ - Java SE Tuning Tip: Large Pages on Windows and Linux
http://blogs.sun.com/dagastine/entry/java_se_tuning_tip_large - Translation lookaside buffer
http://en.wikipedia.org/wiki/Translation_lookaside_buffer - Physical Address Extension
http://en.wikipedia.org/wiki/Physical_Address_Extension - Java HotSpot VM Options
http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html - Amdahl's law
http://en.wikipedia.org/wiki/Amdahl_law - Garbage collection (computer science)
http://en.wikipedia.org/wiki/Garbage_collection_(computer_science) - Dr. Dobb's | G1: Java's Garbage First Garbage Collector
http://www.drdobbs.com/article/printableArticle.jhtml?articleId=219401061&dept_url=/java/ - Java's garbage-collected heap
http://www.javaworld.com/javaworld/jw-08–1996/jw-08-gc.html - Compressed oops in the Hotspot JVM
http://wikis.sun.com/display/HotSpotInternals/CompressedOops - 32-bit or 64-bit JVM? How about a Hybrid?
http://blog.juma.me.uk/2008/10/14/32-bit-or-64-bit-jvm-how-about-a-hybrid/ - Compressed object pointers in Hotspot VM
http://blogs.sun.com/nike/entry/compressed_object_pointers_in_hotspot - Java HotSpot™ Virtual Machine Performance Enhancements
http://download.oracle.com/javase/7/docs/technotes/guides/vm/performance-enhancements-7.html - Using jconsole
http://download.oracle.com/javase/1.5.0/docs/guide/management/jconsole.html - jconsole – Java Monitoring and Management Console
http://download.oracle.com/javase/1.5.0/docs/tooldocs/share/jconsole.html - Great Computer Language Shootout
http://c2.com/cgi/wiki?GreatComputerLanguageShootout - x86–64
http://en.wikipedia.org/wiki/X86–64 - Physical Address Extension
http://en.wikipedia.org/wiki/Physical_Address_Extension - Java performance
http://en.wikipedia.org/wiki/Java_performance - 1.6.0_14 (6u14)
http://www.oracle.com/technetwork/java/javase/6u14–137039.html?ssSourceSiteId=otncn - Update Release Notes
http://www.oracle.com/technetwork/java/javase/releasenotes-136954.html - Java virtual machine: 4.10 Limitations of the Java Virtual Machine
http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html#88659 - Java™ Platform, Standard Edition 7 Binary Snapshot Releases
http://dlc.sun.com.edgesuite.net/jdk7/binaries/index.html - Trying the prototype
http://mail.openjdk.java.net/pipermail/lambda-dev/2010-August/002179.html - Better closures (for Java)
http://blogs.sun.com/jrose/entry/better_closures - Lambdas in Java: An In-Depth Analysis
http://www.infoq.com/articles/lambdas-java-analysis - Class ReflectiveOperationException
http://download.java.net/jdk7/docs/api/java/lang/ReflectiveOperationException.html - Proposal: Indexing access syntax for Lists and Maps
http://mail.openjdk.java.net/pipermail/coin-dev/2009-March/001108.html - Proposal: Elvis and Other Null-Safe Operators
http://mail.openjdk.java.net/pipermail/coin-dev/2009-March/000047.html - Java 7 : Oracle pushes a first version of closures
http://www.baptiste-wicht.com/2010/05/oracle-pushes-a-first-version-of-closures/ - Groovy: An agile dynamic language for the Java Platform
http://groovy.codehaus.org/Operators - Better Strategies for Null Handling in Java
http://www.slideshare.net/Stephan.Schmidt/better-strategies-for-null-handling-in-java - Control Flow in the Java Virtual Machine
http://www.artima.com/underthehood/flowP.html - Java Virtual Machine
http://en.wikipedia.org/wiki/Java_virtual_machine - ==, .equals(), compareTo(), and compare()
http://leepoint.net/notes-java/data/expressions/22compareobjects.html - New JDK7 features
http://openjdk.java.net/projects/jdk7/features/ - Project Coin: Bringing it to a Close(able)
http://blogs.sun.com/darcy/entry/project_coin_bring_close - ClosableFinder source code
http://blogs.sun.com/darcy/resource/ProjectCoin/CloseableFinder.java - Joe Darcy blog about JDK
http://blogs.sun.com/darcy - Java 7 – more dynamics
http://www.baptiste-wicht.com/2010/04/java-7-more-dynamics/ - ArrayList (JDK 1.4)
http://download.oracle.com/javase/1.4.2/docs/api/java/util/ArrayList.html