JamVM aneb alternativa k HotSpotu (nejenom) pro embedded zařízení, tablety a chytré telefony

9. 8. 2011
Doba čtení: 21 minut

Sdílet

V sedmnácté části poněkud nepravidelně vycházejícího seriálu o programovacím jazyce Java a o vlastnostech JVM si řekneme základní informace o projektu JamVM. Pod tímto názvem se skrývá virtuální stroj Javy představující alternativu ke známému HotSpotu pocházejícímu z dílny firmy Sun a později Oracle.

Obsah

1. JamVM aneb alternativa k HotSpotu (nejenom) pro embedded zařízení, tablety a chytré telefony

2. Základní vlastnosti JamVM

3. Jak přeložit IcedTea s podporou JamVM?

4. Výsledné obrazy JDK a JRE

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

9. Odkazy na Internetu

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 JREHotSpotem a JREJamVM. 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=NO­NE), 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:

ict ve školství 24

#!/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

  1. JamVM home page
    http://jamvm.sou­rceforge.net/
  2. GNU Classpath
    www.gnu.org/s/clas­spath/
  3. JamVM na stránkách SourceForge
    http://source­forge.net/pro­jects/jamvm/
  4. Java VMs Compared
    http://bugblog­ger.com/java-vms-compared-160/
  5. JamVM (Wikipedia)
    http://en.wiki­pedia.org/wiki/Jam­VM
  6. Statistika projektu JamVM na službě Ohloh
    https://www.oh­loh.net/p/jam­vm
  7. IcedTea Build Requirements
    http://icedte­a.classpath.or­g/wiki/BuildRe­quirements
  8. IcedTea Fedora Build Instructions
    http://icedte­a.classpath.or­g/wiki/Fedora­BuildInstructi­ons
  9. IcedTea Debian Building Instructions
    http://icedte­a.classpath.or­g/wiki/Debian­BuildingInstruc­tions
  10. IcedTea Gentoo Build Instructions
    http://icedte­a.classpath.or­g/wiki/Gentoo­BuildInstructi­ons
  11. JSRs: Java Specification Requests – JSR 223: Scripting for the Java Platform
    http://www.jcp­.org/en/jsr/de­tail?id=223
  12. Scripting for the Java Platform
    http://java.sun­.com/developer/techni­calArticles/J2SE/Des­ktop/scriptin­g/
  13. Scripting for the Java Platform (Wikipedia)
    http://en.wiki­pedia.org/wiki/Scrip­ting_for_the_Ja­va_Platform
  14. Java Community Process
    http://en.wiki­pedia.org/wiki/Ja­va_Specificati­on_Request
  15. Package javax.script (JavaDoc)
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/package-summary.html
  16. javax.script.Bin­dings
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/Bindings.html
  17. javax.script.Com­pilable
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/Compilable.html
  18. javax.script.In­vocable
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/Invocable.html
  19. javax.script.Scrip­tContext
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/ScriptContex­t.html
  20. javax.script.Scrip­tEngine
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/ScriptEngine­.html
  21. javax.script.Scrip­tEngineFactory
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/ScriptEngine­Factory.html
  22. javax.script.Ab­stractScriptEn­gine
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/AbstractScrip­tEngine.html
  23. javax.script.Com­piledScript
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/CompiledScrip­t.html
  24. javax.script.Scrip­tEngineManager
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/ScriptEngine­Manager.html
  25. javax.script.Sim­pleBindings
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/SimpleBindin­gs.html
  26. javax.script.Sim­pleScriptContext
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/SimpleScrip­tContext.html
  27. javax.script.Scrip­tException
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/ScriptExcep­tion.html
  28. The Java Compatibility Test Tools: JavaTest Harness
    http://java.sun­.com/developer/techni­calArticles/JCPto­ols2/
  29. JavaScript engine (Wikipedia)
    http://en.wiki­pedia.org/wiki/Ja­vaScript_engi­ne
  30. Rhino (JavaScript engine)
    http://en.wiki­pedia.org/wiki/Rhi­no_(JavaScrip­t_engine)
  31. Rhino: JavaScript for Java
    http://www.mo­zilla.org/rhi­no/
  32. Java HotSpot VM Options
    http://www.ora­cle.com/technet­work/java/java­se/tech/vmopti­ons-jsp-140102.html
  33. HugePages
    http://linux-mm.org/HugePages
  34. Tuning big java heap and linux huge pages
    http://www.ti­kalk.com/alm/fo­rums/tuning-big-java-heap-and-linux-huge-pages
  35. How do I set up hugepages in Red Hat Enterprise Linux 4
    http://magazi­ne.redhat.com/2007/05­/29/how-do-i-set-up-hugepages-in-red-hat-enterprise-linux-4/
  36. Java SE Tuning Tip: Large Pages on Windows and Linux
    http://blogs.sun­.com/dagastine/en­try/java_se_tu­ning_tip_large
  37. Translation lookaside buffer
    http://en.wiki­pedia.org/wiki/Tran­slation_looka­side_buffer
  38. Physical Address Extension
    http://en.wiki­pedia.org/wiki/Phy­sical_Address_Ex­tension
  39. Java HotSpot VM Options
    http://www.ora­cle.com/technet­work/java/java­se/tech/vmopti­ons-jsp-140102.html
  40. Amdahl's law
    http://en.wiki­pedia.org/wiki/Am­dahl_law
  41. Garbage collection (computer science)
    http://en.wiki­pedia.org/wiki/Gar­bage_collecti­on_(computer_sci­ence)
  42. Dr. Dobb's | G1: Java's Garbage First Garbage Collector
    http://www.drdob­bs.com/article/prin­tableArticle.jhtml?ar­ticleId=219401061­&dept_url=/ja­va/
  43. Java's garbage-collected heap
    http://www.ja­vaworld.com/ja­vaworld/jw-08–1996/jw-08-gc.html
  44. Compressed oops in the Hotspot JVM
    http://wikis.sun­.com/display/Hot­SpotInternals/Com­pressedOops
  45. 32-bit or 64-bit JVM? How about a Hybrid?
    http://blog.ju­ma.me.uk/2008/10/­14/32-bit-or-64-bit-jvm-how-about-a-hybrid/
  46. Compressed object pointers in Hotspot VM
    http://blogs.sun­.com/nike/entry/com­pressed_objec­t_pointers_in_hot­spot
  47. Java HotSpot™ Virtual Machine Performance Enhancements
    http://downlo­ad.oracle.com/ja­vase/7/docs/techno­tes/guides/vm/per­formance-enhancements-7.html
  48. Using jconsole
    http://downlo­ad.oracle.com/ja­vase/1.5.0/doc­s/guide/manage­ment/jconsole­.html
  49. jconsole – Java Monitoring and Management Console
    http://downlo­ad.oracle.com/ja­vase/1.5.0/doc­s/tooldocs/sha­re/jconsole.html
  50. Great Computer Language Shootout
    http://c2.com/cgi/wi­ki?GreatCompu­terLanguageSho­otout
  51. x86–64
    http://en.wiki­pedia.org/wiki/X86–64
  52. Physical Address Extension
    http://en.wiki­pedia.org/wiki/Phy­sical_Address_Ex­tension
  53. Java performance
    http://en.wiki­pedia.org/wiki/Ja­va_performance
  54. 1.6.0_14 (6u14)
    http://www.ora­cle.com/technet­work/java/java­se/6u14–137039.html?ssSou­rceSiteId=otncn
  55. Update Release Notes
    http://www.ora­cle.com/technet­work/java/java­se/releasenotes-136954.html
  56. Java virtual machine: 4.10 Limitations of the Java Virtual Machine
    http://java.sun­.com/docs/book­s/jvms/second_e­dition/html/Clas­sFile.doc.html#88659
  57. Java™ Platform, Standard Edition 7 Binary Snapshot Releases
    http://dlc.sun­.com.edgesuite­.net/jdk7/bina­ries/index.html
  58. Trying the prototype
    http://mail.o­penjdk.java.net/pi­permail/lambda-dev/2010-August/002179.html
  59. Better closures (for Java)
    http://blogs.sun­.com/jrose/en­try/better_clo­sures
  60. Lambdas in Java: An In-Depth Analysis
    http://www.in­foq.com/articles/lam­bdas-java-analysis
  61. Class ReflectiveOpe­rationExcepti­on
    http://downlo­ad.java.net/jdk7/doc­s/api/java/lan­g/ReflectiveO­perationExcep­tion.html
  62. Proposal: Indexing access syntax for Lists and Maps
    http://mail.o­penjdk.java.net/pi­permail/coin-dev/2009-March/001108.html
  63. Proposal: Elvis and Other Null-Safe Operators
    http://mail.o­penjdk.java.net/pi­permail/coin-dev/2009-March/000047.html
  64. Java 7 : Oracle pushes a first version of closures
    http://www.bap­tiste-wicht.com/2010/05­/oracle-pushes-a-first-version-of-closures/
  65. Groovy: An agile dynamic language for the Java Platform
    http://groovy­.codehaus.org/O­perators
  66. Better Strategies for Null Handling in Java
    http://www.sli­deshare.net/Step­han.Schmidt/bet­ter-strategies-for-null-handling-in-java
  67. Control Flow in the Java Virtual Machine
    http://www.ar­tima.com/under­thehood/flowP­.html
  68. Java Virtual Machine
    http://en.wiki­pedia.org/wiki/Ja­va_virtual_machi­ne
  69. ==, .equals(), compareTo(), and compare()
    http://leepoin­t.net/notes-java/data/expres­sions/22compa­reobjects.html
  70. New JDK7 features
    http://openjdk­.java.net/pro­jects/jdk7/fe­atures/
  71. Project Coin: Bringing it to a Close(able)
    http://blogs.sun­.com/darcy/en­try/project_co­in_bring_close
  72. ClosableFinder source code
    http://blogs.sun­.com/darcy/re­source/Projec­tCoin/Closeable­Finder.java
  73. Joe Darcy blog about JDK
    http://blogs.sun­.com/darcy
  74. Java 7 – more dynamics
    http://www.bap­tiste-wicht.com/2010/04­/java-7-more-dynamics/
  75. ArrayList (JDK 1.4)
    http://downlo­ad.oracle.com/ja­vase/1.4.2/doc­s/api/java/util/A­rrayList.html

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.