Dobrá otázka. Rychlost startu je totiž přesně to, v čem native-image
z GraalVM vyniká!
Takže jsem nelenil a přepsal ten svůj příklad tak, aby počítal jen jedno prvočíslo. Nu a zde je výsledek:
sieve/go$ go build; time ./go First prime is 2 One prime number in 0 ms real 0m0.001s user 0m0.000s sys 0m0.000s java/algorithm$ JAVA_HOME=/graalvm mvn clean install -Psvm -DskipTests sieve/java/algorithm$ time ./target/sieve First prime is 2 and took 0 ms real 0m0.001s user 0m0.000s sys 0m0.000s
Tak asi tak. Nula nula nic. Prostě zkuste uvěřit, že kód vygenerovaný pomocí native-image
není ta stará a pomalá Java. Je to dost dobrá náhrada v místech, kde se do teď hodilo používat Go.
PS: Omlouvám se, že jsem tento příklad neuvedl v článku. Hodil by se tam.
Ještě mne napadlo přidat to samé měření na normální JVM:
java/algorithm$ time java -jar target/sieve-algorithm-1.0-SNAPSHOT.jar First prime is 2 and took 1 ms real 0m0.091s user 0m0.092s sys 0m0.008s
Startuje to asi tak stokrát pomaleji. Jak jsem psal: nenažraný interpretovaný bumbrlíček.
Ano, je to tak. Pokud přežijete fázi interpretování a necháte JIT kompilátor vygenerovat pořádný kód, může to být velmi rychle (minule jsem napsal rychlejší než Céčko a dost jsem to schytal, ale asi tak nějak):
sieve$ mvn -f java/algorithm/ package exec:java Hundred thousand primes computed in 90 ms sieve$ JAVA_HOME=/graalvm mvn -f java/algorithm/ exec:java Hundred thousand primes computed in 84 ms sieve$ ./c/sieve Hundred thousand prime numbers in 98 ms sieve$ git log | head commit 64aed1ac16a71b193449898b5c79d97b89401f3e
Problém standardní Javy není v tom, že by její JITovaný kód nemohl být rychlý, ale v tom, že k tomu potřebuje strašně moc metadat. S native-image
je kód o něco pomalejší, ale běžící proces je výrazně úspornější hlavně z pohledu spotřeby paměti.
Predpokladam, ze to Ccko je kompilovane s patricnymi optimalizacemi, vcetne -fwhole-program nebo , aby to bylo fer?
Vlastni program v C je jina otazka, asi by to clovek takhle normalne nenapsal - predpokladam ze boxovani celych cisel ma nejaky hlubsi smysl, stejne jako pouzivani DL seznamu misto pole? To vyplivl nejaky automat, nebo to je podle nejakych Javovskych best practices? Jako kompilator si s s tim poradi, ale opravdu se nekomu cte tohle lepe nez normani Ccko?
O drobnostech jako testovat ve smycce nextPrime zda je promenna inicializovana namisto testu venku/jeji inicializace predem.
Tohle je jiné porovnání, než si asi představujete. Tohle je snaha porovnávat rychlost jazyka v typických úlohách (ne v typických úlohách pro daný jazyk, ale v úlohách, které jsou asi nejběžnější napříč všemi aplikacemi). Takže to není kód, který by byl optimální v daném jazyce a už vůbec ne optimalizace překladu. Ono by se to takhle samozřejmě nenapsalo ani v Javě. Proto je tam to boxování celých čísel a spojový seznam, aby tam byla nějaká práce s dynamickou pamětí, což je typická úloha.