Pro virtuální stroj programovacího jazyka Java (JVM) v současnosti existuje již poměrně velké množství různých „alternativních“ programovacích jazyků. Kromě samotné Javy jakožto primárního jazyka pro JVM se setkáme především s trojicí oblíbených jazyků Scala, Kotlin a Groovy, za nimiž následuje Clojure, JRuby, minimálně dvě implementace JavaScriptu a v neposlední řadě i Jython, což je varianta Pythonu 2.x přepsaná pro JVM. A právě Jythonem, který doposud stojí poněkud v pozadí, se dnes budeme zabývat.
Obsah
1. Jython aneb poněkud opomíjený programovací jazyk pro virtuální stroj Javy
2. Programovací jazyky pro JVM aneb nejde jen o Javu
6. JRuby, Jython, Rhino a Nashhorn
8. Další programovací jazyky portované na JVM
9. Instalace Jythonu a první spuštění interpretru
11. Základní moduly dodávané společně s Jythonem
12. Interoperabilita mezi Pythonem a Javou
13. Srovnání rychlosti Pythonu 2.x, Pythonu 3.x a Jythonu při výpočtech s FP hodnotami
14. Zdrojový kód jednoduchého benchmarku
15. Skripty pro spuštění benchmarku se zvoleným interpretrem a parametry
16. Výsledky prvního benchmarku
17. Druhý benchmark založený na použití komplexních čísel
18. Výsledky druhého benchmarku
19. Repositář s benchmarky i dalšími příklady
1. Jython aneb poněkud opomíjený programovací jazyk pro virtuální stroj Javy
V dnešním článku si představíme projekt Jython, což je implementace dnes velmi populárního programovacího jazyka Python určená pro běh ve virtuálním stroji jazyka Java (JVM – Java Virtual Machine). A nejenom to – aplikace psané v Jythonu mohou kooperovat s třídami a rozhraními vytvořenými v Javě, což je velmi výhodné, protože s rostoucí složitostí moderních aplikací je většinou zapotřebí mít k dispozici vhodný skriptovací jazyk sloužící jako „lepidlo“ (glue) mezi jednotlivými bloky, z nichž se aplikace skládá. Jython ovšem samozřejmě není dokonalý. Jednou z jeho nevýhod je fakt, že je stále postaven na dnes již obstarožním Pythonu 2, druhou nevýhodou pak ta skutečnost, že se jedná o dosti pomalý jazyk, což si ostatně ukážeme na dvojici benchmarků. Tato pomalost se negativně projeví zejména při výpočtech a někdy i při manipulaci s rozsáhlými datovými strukturami, ovšem u aplikací, v nichž převládají I/O operace se nemusí jednat o kritický nedostatek.
Obrázek 1: Logo programovacího jazyka Jython.
2. Programovací jazyky pro JVM aneb nejde jen o Javu
Virtuální stroj Javy (JVM), specifikace JVM a dokonce ani jeho instrukční soubor vlastně nikde striktně nepředpokládají, že JVM bude spouštět pouze bajtkód získaný překladem javovských programů. Ve specifikaci JVM je dokonce na několika místech explicitně zmíněn předpoklad, že nad virtuálním strojem Javy budou provozovány i další programovací jazyky umožňující přímý či nepřímý překlad do bajtkódu. Kromě toho je samozřejmě možné přímo v Javě vytvořit interpret prakticky libovolného (interpretovaného) programovacího jazyka, takže vlastně nebude velkým překvapením, když zjistíme, že dnes těchto „JVM jazyků“ již existuje relativně velké množství. V následující tabulce jsou vypsány ty nejznámější a pravděpodobně nejpoužívanější jazyky, a to pro úplnost včetně samotné Javy:
Jazyk pro JVM | Stručný popis | Odkaz |
---|---|---|
Java | primární jazyk pro JVM, bajtkód odpovídá právě Javě | https://www.oracle.com/java/index.html |
Clojure | moderní dialekt programovacího jazyka Lisp | https://clojure.org/ |
Groovy | dynamicky typovaný jazyk pro JVM | http://groovy-lang.org/ |
Rhino | jedna z implementací JavaScriptu | https://developer.mozilla.org/en-US/docs/Mozilla/Projects/Rhino |
Nashorn | alternativní implementace JavaScriptu | https://blogs.oracle.com/nashorn/ |
JRuby | portace jazyka Ruby na JVM | http://jruby.org/ |
Jython | portace jazyka Python na JVM | http://www.jython.org/ |
Kotlin | moderní staticky typovaný jazyk | http://kotlinlang.org/ |
Scala | další moderní jazyk pro JVM | https://www.scala-lang.org/ |
3. Scala
Pravděpodobně nejznámějším příkladem programovacího jazyka provozovaného nad JVM je Scala, která nabízí prakticky bezproblémovou spolupráci mezi částmi kódu psanými ve Scale a zbytkem aplikace psaným v Javě (popř. jsou některé projekty psané pouze ve Scale, ovšem provozovány jsou například na ryze javovských serverech – Tomcat, Jetty atd.). Díky tomu, že zdrojové kódy psané ve Scale jsou přímo překládány do bajtkódu, získali tvůrci tohoto programovacího jazyka prakticky zadarmo veškeré vymoženosti, které virtuální stroj Javy (či přesněji řečeno celé JRE) poskytuje – od poměrně pečlivé kontroly bajtkódu při jeho načítání do virtuálního stroje přes použití správců paměti a JIT překladačů (Just in Time Compiler) až po možnost využití rozsáhlých standardních knihoven J2SE a samozřejmě taktéž mnoha dalších knihoven a frameworků, které jsou pro JVM dostupné. Ovšem Scala samozřejmě není jediným programovacím jazykem, který díky překladu do bajtkódu umožňuje využít prakticky veškerého potenciálu JVM/JRE.
Obrázek 2: Logo programovacího jazyka Scala.
4. Clojure
Z dalších překladačů programovacích jazyků, které pro virtuální stroj Javy vznikly, je podle mého názoru nejzajímavějším jazykem a současně i jazykem s velkým potenciálem pro budoucnost programovací jazyk s názvem Clojure, jehož autorem a dodnes nejaktivnějším vývojářem je Rich Hickey. Samotný název tohoto jazyka vznikl vložením písmene „j“ (Java/JVM) do slova closure (toto slovo se používá ve smyslu „lexikální uzávěr“ – důležitá abstrakce používaná nejenom ve funkcionálních programovacích jazycích). Velká část předností a pro mnohé vývojáře taktéž záporů programovacího jazyka Clojure vychází z toho, že se jedná o programovací jazyk, jehož syntaxe a sémantika do značné míry vychází z LISPu a Scheme, tedy jazyků známých především tím, že se v programech v nich psaných používá nadměrné množství kulatých závorek. Podrobnější informace o Clojure byly uvedeny v samostatném seriálu, který již vyšel na serveru www.root.cz.
Obrázek 3: Logo programovacího jazyka Clojure.
5. Groovy
Dalším programovacím jazykem, a nutno říci že jazykem poměrně populárním, je Groovy. Jedná se o jazyk inspirovaný některými dalšími (většinou dynamicky typovanými) programovacími jazyky, jako je Python, Ruby, ale například i Perl. Groovy podporuje objektově orientované programování a oproti Javě umožňuje psát kratší kód, z něhož jsou odstraněny méně podstatné části, které Java jakožto silně staticky typovaný jazyk vyžaduje. Z tohoto důvodu se Groovy velmi často používá všude tam, kde je vyžadovaný skriptovací jazyk běžící nad JVM. Dobrým příkladem je například konzole Jenkinsu, která ve výchozím nastavení používá právě Groovy. Podpora pro tento jazyk je součástí mnoha populárních integrovaných vývojových prostředí, a to včetně IntelliJ IDEA, Netbeans i Eclipse (přes pluginy – doplňkové moduly).
Obrázek 4: Logo programovacího jazyka Groovy.
6. JRuby, Jython, Rhino a Nashhorn
Zatímco Scala, Clojure i Groovy jsou novými jazyky, které původně vznikly přímo a pouze pro potřeby programátorů používajících virtuální stroj Javy (JVM), zmíníme se v této kapitole alespoň ve stručnosti o jazycích „klasických“, které byly na JVM pouze naportovány. V první řadě se jedná o projekty pojmenované JRuby a Jython, což jsou varianty jazyků Ruby a Python. Původní interpretry Ruby a Pythonu jsou naprogramovány v jazyku C (proto se ostatně tato varianta Pythonu nazývá CPython), JRuby a Jython jsou reimplementace pro JVM (navíc byl Python portován i na platformu .NET ve formě projektu IronPython a existuje i varianta Pythonu nazvaná PyPy naprogramovaná v samotném Pythonu, resp. přesněji řečeno v jeho zjednodušené variantě RPython). Na platformu JVM byl portován i programovací jazyk JavaScript, a to dokonce několikrát. Implementaci JavaScriptu zajišťují projekty Rhino a Nashhorn.
Obrázek 5: Logo programovacího jazyka JRuby.
7. Kotlin
Posledním programovacím jazykem určeným pro běh nad virtuálním strojem Javy, o němž se dnes alespoň ve stručnosti zmíníme, je jazyk pojmenovaný Kotlin. Jedná se o moderní staticky typovaný programovací jazyk vyvinutý ve společnosti JetBrains a jeho cíl je vlastně velmi podobný cíli, který si dal již zmíněný a poněkud starší jazyk Scala – efektivní a rychlá tvorba typově bezpečných aplikací určených pro běh nad JVM. Typovost jde ještě o krok dále, než je tomu v Javě, protože například rozlišuje mezi nulovatelnými a nenulovatelnými datovými typy. Na rozdíl od Scaly je však rychlost překladu zdrojových kódů naprogramovaných v Kotlinu rychlejší a blíží se rychlosti překladu kódů napsaných v samotné Javě. Kotlin podporuje různá paradigmata: objektově orientované, procedurální i funkcionální. Tomuto velmi zajímavému programovacímu jazyku se budeme věnovat v samostatném seriálu.
Obrázek 6: Logo programovacího jazyka Kotlin.
8. Další programovací jazyky portované na JVM
Na virtuální stroj Javy bylo portováno i mnoho dalších programovacích jazyků, ať již se jednalo o překladače či o interpretry. V následující tabulce jsou některé z těchto jazyků vypsány. V prvním sloupci je zobrazen název původního jazyka popř. rodiny jazyků, ve sloupci druhém pak jméno jeho konkrétní implementace pro JVM. Povšimněte si, že některé jazyky byly portovány několikrát (to se ostatně týkalo již JavaScriptu zmíněného ve třetí kapitole):
Jazyk | Implementace pro JVM |
---|---|
Ada | JGNAT |
Arden Syntax | Arden2ByteCode |
COBOL | Micro Focus Visual COBOL |
ColdFusion Markup Language (CFML) | Adobe ColdFusion |
ColdFusion Markup Language (CFML) | Railo |
ColdFusion Markup Language (CFML) | Lucee |
ColdFusion Markup Language (CFML) | Open BlueDragon |
Common Lisp | Armed Bear Common Lisp |
Cypher | Neo4j |
Mercury | Mercury (Java grade) |
Pascal | MIDletPascal |
Pascal | Oxygene |
Perl 6 | Rakudo Perl 6 |
PHP | Quercus |
Prolog | JIProlog |
Prolog | TuProlog |
R | Renjin |
Rexx | NetRexx |
Ruby | JRuby |
Ruby | Mirah |
Scheme | Bigloo |
Scheme | Kawa |
Scheme | SISC |
Scheme | JScheme |
Tcl | Jacl |
Visual Basic | Jabaco |
9. Instalace Jythonu a první spuštění interpretru
Instalace Jythonu je velmi jednoduchá, protože pouze postačuje si stáhnout Java archiv z adresy http://search.maven.org/remotecontent?filepath=org/python/jython-standalone/2.7.0/jython-standalone-2.7.0.jar, například takto:
$ <strong>wget -O jython-standalone-2.7.0.jar http://search.maven.org/remotecontent?filepath=org/python/jython-standalone/2.7.0/jython-standalone-2.7.0.jar</strong>
Následně si již můžeme spustit interaktivní interpret, což se provede tímto příkazem:
$ <strong>java -jar jython-standalone-2.7.0.jar</strong>
Po několika sekundách by se měly vypsat dva řádky s informacemi o verzi interpretru Jythonu a o použitém virtuálním stroji Javy. Na třetím řádku se vypíše nápověda k některým příkazů a na řádku následujícím pak výzva (prompt) interpretru:
Jython 2.7.0 (default:9987c746f838, Apr 29 2015, 02:25:11)
[OpenJDK 64-Bit Server VM (Oracle Corporation)] on java1.7.0_79
Type "help", "copyright", "credits" or "license" for more information.
>>>
Povšimněte si, že způsob výpisu je prakticky shodný s nativním CPythonem, který po spuštění taktéž vypíše tři informační řádky následované výzvou:
Python 3.4.3 (default, Nov 28 2017, 16:41:13)
[GCC 4.8.4] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
Vraťme se však ke spuštěnému interpretru Jythonu. Ten se chová prakticky stejně jako klasický interpret Pythonu (CPythonu), a to včetně historie příkazového řádku, základních klávesových zkratek atd. Zkusme si například přímo v interpretru napsat jednoduchou programovou smyčku a otestovat tak, jak se bude měnit výzva (prompt) ve chvíli, kdy se změní kontext (další řádky nejsou přímo interpretovány, ale čeká se na ukončení bloku či bloků prázdným řádkem):
>>> for i in range(10):
... print i
...
0
1
2
3
4
5
6
7
8
9
>>>
Podobným způsobem se kontext přepíná i ve chvíli, kdy deklarujeme funkci (popř. třídu):
>>> def max(x, y):
... if x > 0:
... return x
... else:
... return y
...
>>> max(1,20)
1
Interpret Jythonu můžeme (na Linuxu) kdykoli opustit stiskem klávesové zkratky Ctrl+D.
Obrázek 7: Podpora Jythonu v Eclipse s nainstalovaným modulem PyDev.
10. Interní nápověda
Do interaktivní konzole jazyka Jython je integrována i nápověda, která se vyvolá velmi jednoduše zavoláním funkce help():
>>> help()
Po spuštění této funkce se zobrazí úvodní obrazovka s dalšími volbami, například „keywords“, „modules“ atd. Povšimněte si, že se změnila i výzva (prompt) na help>, což značí změnu kontextu interpretru:
Welcome to Python 2.7! This is the online help utility.
If this is your first time using Python, you should definitely check out
the tutorial on the Internet at http://docs.python.org/2.7/tutorial/.
Enter the name of any module, keyword, or topic to get help on writing
Python programs and using Python modules. To quit this help utility and
return to the interpreter, just type "quit".
To get a list of available modules, keywords, or topics, type "modules",
"keywords", or "topics". Each module also comes with a one-line summary
of what it does; to list the modules whose summaries contain a given word
such as "spam", type "modules spam".
help>
V režimu nápovědy si například můžeme vypsat všechna rozpoznávaná klíčová slova:
help> <strong>keywords</strong>
Here is a list of the Python keywords. Enter any keyword to get more help.
and elif if print
as else import raise
assert except in return
break exec is try
class finally lambda while
continue for not with
def from or yield
del global pass
Nápověda se ukončí prázdným příkazem, tj. stiskem klávesy Enter.
11. Základní moduly dodávané společně s Jythonem
Příkazem modules si můžeme vypsat všechny moduly, které jsou dodávány společně s Jythonem. Ostatně i samotná existence těchto modulů je jedním z důvodů, proč je Java archiv Jythonu tak velký – snahou tvůrců totiž bylo zahrnout javovské varianty většiny modulů, které se v CPythonu velmi často používají pro tvorbu (nejenom) serverových aplikací. Ostatně sami se můžete na seznam dostupných modulů velmi snadno podívat:
BaseHTTPServer cmath javapath sched
CGIHTTPServer cmd javashell select
ConfigParser code jffi sets
Cookie codecs json sgmllib
DocXMLRPCServer codeop jythonlib sha
HTMLParser collections keyword shelve
MimeWriter colorsys linecache shlex
Queue command locale shutil
SimpleHTTPServer commands logging signal
SimpleXMLRPCServer compileall macpath site
SocketServer compiler macurl2path smtpd
StringIO contextlib mailbox smtplib
UserDict cookielib mailcap sndhdr
UserList copy markupbase socket
UserString copy_reg marshal sre
_LWPCookieJar crypt math sre_compile
_MozillaCookieJar csv md5 sre_constants
__builtin__ ctypes mhlib sre_parse
__future__ datetime mime ssl
_abcoll dbexts mimetools stat
_ast decimal mimetypes string
_bytecodetools difflib mimify struct
_codecs dircache modjy subprocess
_collections dis multifile symbol
_csv distutils mutex synchronize
_fsum doctest netrc sys
_functools dom new sysconfig
_google_ipaddr_r234 dumbdbm nntplib tabnanny
_hashlib dummy_thread ntpath tarfile
_imp dummy_threading nturl2path telnetlib
_io email numbers tempfile
_json encodings opcode test
_jyio ensurepip operator tests
_jythonlib errno optparse textwrap
_marshal etree os this
_py_compile exceptions parsers thread
_pyio filecmp pawt threading
_random fileinput pdb time
_rawffi fnmatch pickle timeit
_socket formatter pickletools token
_sre fpformat pipes tokenize
_sslcerts fractions pkgutil trace
_strptime ftplib platform traceback
_systemrestart functools plistlib tty
_threading future_builtins popen2 types
_threading_local gc poplib ucnhash
_weakref genericpath posix unicodedata
_weakrefset getopt posixfile unittest
abc getpass posixpath urllib
aifc gettext pprint urllib2
anydbm glob profile urlparse
argparse grp pstats user
array gzip pty uu
ast hashlib pwd uuid
asynchat heapq py_compile warnings
asyncore hmac pycimport weakref
atexit htmlentitydefs pyclbr webbrowser
base64 htmllib pydoc whichdb
bdb httplib pyexpat wsgiref
binascii ihooks quopri xdrlib
binhex imaplib random xml
bisect imghdr re xmllib
bz2 imp readline xmlrpclib
cPickle importlib repr zipfile
cStringIO inspect rfc822 zipimport
calendar io rlcompleter zlib
cgi isql robotparser
cgitb itertools runpy
chunk jarray sax
12. Interoperabilita mezi Pythonem a Javou
Hlavním důvodem vedoucím k použití Jythonu je samozřejmě jeho velmi dobrá kooperace s Javou, tj. jak s virtuálním strojem Javy, tak i se standardními knihovnami Javy (JSE) a ostatně i s libovolnými dalšími knihovnami. To je velmi důležité, protože ekosystém postavený okolo JVM (a například Mavenu) je dnes velmi rozsáhlý a Jython může být v tomto ekosystému použit ve funkci „lepidla“ (glue), které jednotlivé části dokáže spojit dohromady podle potřeb aplikace.
Interoperabilita mezi Pythonem a Javou je dotažena do poměrně velkých detailů, takže například v následujícím příkladu (spouštěném interaktivně v konzoli) můžeme naimportovat javovskou třídu java.util.Date, vytvořit její instanci a následně ji používat stejně, jako jakýkoli jiný objekt v Pythonu:
>>> import java.util.Date
>>> d = java.util.Date()
>>> d
Sat Mar 31 18:24:14 CEST 2018
Alternativní způsob importu bude samozřejmě taktéž funkční:
>>> from java.util import Date
>>> d = Date()
>>> d
Sat Mar 31 18:24:56 CEST 2018
Přístup k atributům objektu a volání jeho metod:
>>> d.time
1522514182141L
>>> d.getYear()
118
Automatický převod mezi typem „String“ v Javě a „unicode“ v Jythonu:
>>> from java.lang import String
>>> s = String("42")
>>> type(s)
<type 'java.lang.String'>
>>> s.toUpperCase()
u'42'
>>> type(s.toUpperCase())
<type 'unicode'>
Automatická konverze mezi instancí třídy BigInteger v Javě a long v Jythonu:
>>> import java.math.BigInteger
TypeError: java.math.BigInteger(): 1st arg can't be coerced to String, byte[]
>>> b = java.math.BigInteger("2")
>>> type(b)
<type 'java.math.BigInteger'>
>>> type(b.pow(10000))
<type 'long'>
>>> type(b.multiply(b2))
<type 'long'>
Základ práce s kolekcemi:
>>> from java.util import List, ArrayList
>>> l = ArrayList()
>>> l.append("two")
>>> l.append("three")
>>> l.get(1)
'three'
Při volání metod objektů, jejichž třídy byly naprogramovány v Javě, samozřejmě mohou vzniknout výjimky, které „probublají“ do kódu napsaného v Jythonu, kde je můžeme dále zpracovat (jakoby se jednalo o výjimky vyhozené z Pythonovského kódu). Příkladem může být výjimka při přístupu k neexistujícímu prvku seznamu:
>>> l.get(3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
at java.util.ArrayList.rangeCheck(ArrayList.java:635)
at java.util.ArrayList.get(ArrayList.java:411)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
Pro zachycení javovských výjimek musíme nejdříve naimportovat příslušnou třídu:
>>> from java.lang import IndexOutOfBoundsException
A poté již je možné výjimku snadno zachytit a zpracovat:
>>> try:
... l.get(3)
... except IndexOutOfBoundsException as e:
... print e
...
java.lang.IndexOutOfBoundsException: Index: 3, Size: 2
Podívejme se ještě, jak si můžeme snadno v interaktivní smyčce vypsat všechny metody nějakého objektu. Postačuje napsat jméno objektu s tečkou a stlačit klávesu [Tab] (ostatně podobně jako v interaktivních vývojových prostředích):
>>> b.
b.ONE b.TEN b.ZERO b.__class__( b.__copy__(
b.__deepcopy__( b.__delattr__( b.__doc__ b.__ensure_finalizer__( b.__eq__(
b.__format__( b.__ge__( b.__getattribute__( b.__gt__( b.__hash__(
b.__init__( b.__le__( b.__lt__( b.__ne__( b.__new__(
b.__reduce__( b.__reduce_ex__( b.__repr__( b.__setattr__( b.__str__(
b.__subclasshook__( b.__unicode__( b.abs( b.add( b.and(
b.andNot( b.bitCount( b.bitLength( b.byteValue( b.class(
b.clearBit( b.compareTo( b.divide( b.divideAndRemainder( b.doubleValue(
b.equals( b.flipBit( b.floatValue( b.gcd( b.getClass(
b.getLowestSetBit( b.hashCode( b.intValue( b.isProbablePrime( b.longValue(
b.lowestSetBit b.max( b.min( b.mod( b.modInverse(
b.modPow( b.multiply( b.negate( b.nextProbablePrime( b.not(
b.notify( b.notifyAll( b.or( b.pow( b.probablePrime(
b.remainder( b.setBit( b.shiftLeft( b.shiftRight( b.shortValue(
b.signum( b.subtract( b.testBit( b.toByteArray( b.toString(
b.valueOf( b.wait( b.xor(
13. Srovnání rychlosti Pythonu 2.x, Pythonu 3.x a Jythonu při výpočtech s FP hodnotami
Jedním z důvodů, proč není Jython tak populární, jako některé další programovací jazyky, které vznikly pro virtuální stroj Javy, je jeho relativně malý výpočetní výkon. Už klasický CPython ostatně velkou rychlostí neoplývá a podle některých benchmarků je Jython dokonce ještě pomalejší než CPython. Tento problém se většinou týká programů, které jsou zaměřeny na výpočty; u typických serverových aplikací s databází, messagingem atd. se jedná o méně závažné omezení (zde více záleží na tom, jak se nám podaří odstranit potřebu synchronizace vláken). Zkusme si nyní vytvořit dva jednoduché benchmarky určené pro porovnání rychlosti Jythonu, CPythonu 2.x a CPythonu 3.x se zaměřením na výpočty (další benchmarky se zpracováním datových struktur budou popsány příště). Všechny benchmarky byly spuštěny na Fedoře 27 a použity byly následující verze Pythonu:
- Jython 2.7.0
- Python 2.7.14
- Python 3.6.3
14. Zdrojový kód jednoduchého benchmarku
Vzhledem k tomu, že se v dnešním benchmarku budeme do značné míry snažit vyhnout měření rychlosti knihovních funkcí, bude celý benchmark skutečně provádět prakticky jen výpočty s výpisem výsledku výpočtů na standardní výstup. Ten bude přesměrován do souboru, protože výsledkem výpočtů budou bitmapy ve formátu Portable Pixel Map (viz [1]). Samozřejmě, že i výpis hodnot na standardní výstup znamená nutnost volání knihovních funkcí, ovšem oproti počtu numerických operací se bude jednat o minimální čas, což je možné zjistit například profilerem (to je ovšem téma, kterým se budeme zabývat v navazujícím článku). Celý benchmark spočívá ve výpočtu barev pixelů Mandelbrotovy množiny, přičemž rozlišení výsledného rastrového obrázku i maximální počet iterací bude možné zvolit z příkazového řádku. Následuje výpis zdrojového kódu benchmarku (kód je napsán tak, aby byl kompatibilní s Pythonem 2.x, Pythonem 3.x i Jythonem):
import palette_mandmap
from sys import argv, exit
def calc_mandelbrot(width, height, maxiter, palette):
print("P3")
print("{w} {h}".format(w=width, h=height))
print("255")
cy = -1.5
for y in range(0, height):
cx = -2.0
for x in range(0, width):
zx = 0.0
zy = 0.0
i = 0
while i < maxiter:
zx2 = zx * zx
zy2 = zy * zy
if zx2 + zy2 > 4.0:
break
zy = 2.0 * zx * zy + cy
zx = zx2 - zy2 + cx
i += 1
r = palette[i][0]
g = palette[i][1]
b = palette[i][2]
print("{r} {g} {b}".format(r=r, g=g, b=b))
cx += 3.0/width
cy += 3.0/height
if __name__ == "__main__":
if len(argv) < 4:
print("usage: python mandelbrot width height maxiter")
exit(1)
width = int(argv[1])
height = int(argv[2])
maxiter = int(argv[3])
calc_mandelbrot(width, height, maxiter, palette_mandmap.palette)
V benchmarku se používá i další modul nazvaný palette_mandmap.py, který obsahuje barvovou paletu (palette, color map). Paleta byla získána ze známého (a dnes již vlastně historického) programu Fractint a obsahuje 256 trojic hodnot R, G, B. Samotná paleta nemá prakticky žádný vliv na naměřené hodnoty, ale výsledné obrázky jsou díky ní hezčí:
# taken from Fractint
palette = (
(255, 255, 255), (224, 224, 224), (216, 216, 216), (208, 208, 208),
(200, 200, 200), (192, 192, 192), (184, 184, 184), (176, 176, 176),
(168, 168, 168), (160, 160, 160), (152, 152, 152), (144, 144, 144),
(136, 136, 136), (128, 128, 128), (120, 120, 120), (112, 112, 112),
(104, 104, 104), (96, 96, 96), (88, 88, 88), (80, 80, 80),
(72, 72, 72), (64, 64, 64), (56, 56, 56), (48, 48, 56),
(40, 40, 56), (32, 32, 56), (24, 24, 56), (16, 16, 56),
(8, 8, 56), (000, 000, 60), (000, 000, 64), (000, 000, 72),
(000, 000, 80), (000, 000, 88), (000, 000, 96), (000, 000, 104),
(000, 000, 108), (000, 000, 116), (000, 000, 124), (000, 000, 132),
(000, 000, 140), (000, 000, 148), (000, 000, 156), (000, 000, 160),
(000, 000, 168), (000, 000, 176), (000, 000, 184), (000, 000, 192),
(000, 000, 200), (000, 000, 204), (000, 000, 212), (000, 000, 220),
(000, 000, 228), (000, 000, 236), (000, 000, 244), (000, 000, 252),
(000, 4, 252), (4, 12, 252), (8, 20, 252), (12, 28, 252),
(16, 36, 252), (20, 44, 252), (20, 52, 252), (24, 60, 252),
(28, 68, 252), (32, 76, 252), (36, 84, 252), (40, 92, 252),
(40, 100, 252), (44, 108, 252), (48, 116, 252), (52, 120, 252),
(56, 128, 252), (60, 136, 252), (60, 144, 252), (64, 152, 252),
(68, 160, 252), (72, 168, 252), (76, 176, 252), (80, 184, 252),
(80, 192, 252), (84, 200, 252), (88, 208, 252), (92, 216, 252),
(96, 224, 252), (100, 232, 252), (100, 228, 248), (96, 224, 244),
(92, 216, 240), (88, 212, 236), (88, 204, 232), (84, 200, 228),
(80, 192, 220), (76, 188, 216), (76, 180, 212), (72, 176, 208),
(68, 168, 204), (64, 164, 200), (64, 156, 196), (60, 152, 188),
(56, 144, 184), (52, 140, 180), (52, 132, 176), (48, 128, 172),
(44, 120, 168), (40, 116, 160), (40, 108, 156), (36, 104, 152),
(32, 96, 148), (28, 92, 144), (28, 84, 140), (24, 80, 136),
(20, 72, 128), (16, 68, 124), (16, 60, 120), (12, 56, 116),
(8, 48, 112), (4, 44, 108), (000, 36, 100), (4, 36, 104),
(12, 40, 108), (16, 44, 116), (24, 48, 120), (28, 52, 128),
(36, 56, 132), (40, 60, 140), (48, 64, 144), (52, 64, 148),
(60, 68, 156), (64, 72, 160), (72, 76, 168), (76, 80, 172),
(84, 84, 180), (88, 88, 184), (96, 92, 192), (104, 100, 192),
(112, 112, 196), (124, 120, 200), (132, 132, 204), (144, 140, 208),
(152, 152, 212), (164, 160, 216), (172, 172, 220), (180, 180, 224),
(192, 192, 228), (200, 200, 232), (212, 212, 236), (220, 220, 240),
(232, 232, 244), (240, 240, 248), (252, 252, 252), (252, 240, 244),
(252, 224, 232), (252, 208, 224), (252, 192, 212), (252, 176, 204),
(252, 160, 192), (252, 144, 184), (252, 128, 172), (252, 112, 164),
(252, 96, 152), (252, 80, 144), (252, 64, 132), (252, 48, 124),
(252, 32, 112), (252, 16, 104), (252, 000, 92), (236, 000, 88),
(228, 000, 88), (216, 4, 84), (204, 4, 80), (192, 8, 76),
(180, 8, 76), (168, 12, 72), (156, 16, 68), (144, 16, 64),
(132, 20, 60), (124, 20, 60), (112, 24, 56), (100, 24, 52),
(88, 28, 48), (76, 32, 44), (64, 32, 44), (52, 36, 40),
(40, 36, 36), (28, 40, 32), (16, 44, 28), (20, 52, 32),
(24, 60, 36), (28, 68, 44), (32, 76, 48), (36, 88, 56),
(40, 96, 60), (44, 104, 64), (48, 112, 72), (52, 120, 76),
(56, 132, 84), (48, 136, 84), (40, 144, 80), (52, 148, 88),
(68, 156, 100), (80, 164, 112), (96, 168, 124), (108, 176, 136),
(124, 184, 144), (136, 192, 156), (152, 196, 168), (164, 204, 180),
(180, 212, 192), (192, 220, 200), (208, 224, 212), (220, 232, 224),
(236, 240, 236), (252, 248, 248), (252, 252, 252), (252, 252, 240),
(252, 252, 228), (252, 252, 216), (248, 248, 204), (248, 248, 192),
(248, 248, 180), (248, 248, 164), (244, 244, 152), (244, 244, 140),
(244, 244, 128), (244, 244, 116), (240, 240, 104), (240, 240, 92),
(240, 240, 76), (240, 240, 64), (236, 236, 52), (236, 236, 40),
(236, 236, 28), (236, 236, 16), (232, 232, 0), (232, 232, 12),
(232, 232, 28), (232, 232, 40), (236, 236, 56), (236, 236, 68),
(236, 236, 84), (236, 236, 96), (240, 240, 112), (240, 240, 124),
(240, 240, 140), (244, 244, 152), (244, 244, 168), (244, 244, 180),
(244, 244, 196), (248, 248, 208), (248, 248, 224), (248, 248, 236),
(252, 252, 252), (248, 248, 248), (240, 240, 240), (232, 232, 232))
15. Skripty pro spuštění benchmarku se zvoleným interpretrem a parametry
Pro spuštění benchmarku použijeme trojici prakticky shodných skriptů, které budou postupně zvětšovat rozlišení výsledného obrázku. Pro malý počet iterací se tedy bude spíše měřit rychlost nastartování interpretru Pythonu popř. virtuálního stroje Javy, ovšem u vyšších rozlišení (přibližně od 128×128 pixelů) již začne převládat samotná doba výpočtu a vliv startu interpretru/JVM tak bude jen marginální.
Skript pro Python 2.x
sizes="16 24 32 48 64 96 128 192 256 384 512 768 1024 1536 2048 3072 4096"
OUTFILE="python2.times"
PREFIX="python2"
rm $OUTFILE
for size in $sizes
do
echo $size
echo -n "$size " >> $OUTFILE
/usr/bin/time --output $OUTFILE --append --format "%e %M" python2 -B mandelbrot.py $size $size 255 > "${PREFIX}_${size}_${size}.ppm"
done
Skript pro Python 3.x
sizes="16 24 32 48 64 96 128 192 256 384 512 768 1024 1536 2048 3072 4096"
OUTFILE="python3.times"
PREFIX="python3"
rm $OUTFILE
for size in $sizes
do
echo $size
echo -n "$size " >> $OUTFILE
/usr/bin/time --output $OUTFILE --append --format "%e %M" python3 -B mandelbrot.py $size $size 255 > "${PREFIX}_${size}_${size}.ppm"
done
Skript pro Jython
Tento skript vyžaduje, aby se v aktuálním adresáři nacházel Java archiv s Jythonem popř. jen symbolický link na tento archiv:
sizes="16 24 32 48 64 96 128 192 256 384 512 768 1024 1536 2048 3072 4096"
OUTFILE="jython.times"
PREFIX="jython"
rm $OUTFILE
for size in $sizes
do
echo $size
echo -n "$size " >> $OUTFILE
/usr/bin/time --output $OUTFILE --append --format "%e %M" java -jar jython-standalone-2.7.0.jar mandelbrot.py $size $size 255 > "${PREFIX}_${size}_${size}.ppm"
done
16. Výsledky prvního benchmarku
Výsledky získané po spuštění prvního benchmarku jsou vypsány v následující tabulce:
Podívejme se nyní na slíbené výsledky:
# | x-res | y-res | Jython (s) | Python 2 (s) | Python 3 (s) |
---|---|---|---|---|---|
1 | 16 | 16 | 1.79 | 0.01 | 0.02 |
2 | 24 | 24 | 1.79 | 0.01 | 0.02 |
3 | 32 | 32 | 1.84 | 0.02 | 0.02 |
4 | 48 | 48 | 2.11 | 0.03 | 0.03 |
5 | 64 | 64 | 2.01 | 0.04 | 0.05 |
6 | 96 | 96 | 2.16 | 0.08 | 0.09 |
7 | 128 | 128 | 2.24 | 0.15 | 0.15 |
8 | 192 | 192 | 2.43 | 0.32 | 0.33 |
9 | 256 | 256 | 2.81 | 0.57 | 0.58 |
10 | 384 | 384 | 3.87 | 1.25 | 1.29 |
11 | 512 | 512 | 5.05 | 2.27 | 2.28 |
12 | 768 | 768 | 8.61 | 5.07 | 5.21 |
13 | 1024 | 1024 | 13.22 | 9.00 | 9.10 |
14 | 1536 | 1536 | 28.15 | 20.73 | 21.24 |
15 | 2048 | 2048 | 50.03 | 36.11 | 38.24 |
16 | 3072 | 3072 | 100.78 | 81.93 | 84.02 |
17 | 4096 | 4096 | 179.21 | 144.45 | 148.44 |
Obrázek 9: Výsledky prvního benchmarku vynesené do grafu.
17. Druhý benchmark založený na použití komplexních čísel
Vzhledem k tomu, že Python podporuje i práci s komplexními čísly, si můžeme benchmark ještě více upravit, a to takovým způsobem, aby se v něm všechny výpočty prováděly právě nad typem complex. Zajímavé bude změření a porovnání rychlosti výpočtů, protože samotný virtuální stroj Javy primitivní typ „komplexní číslo“ nezná a tím pádem ani nepodporuje. Výsledkem bude následující zdrojový kód:
import palette_mandmap
from sys import argv, exit
def calc_mandelbrot(width, height, maxiter, palette):
print("P3")
print("{w} {h}".format(w=width, h=height))
print("255")
c = 0.0 - 1.5J
for y in range(0, height):
c = complex(-2.0, c.imag)
for x in range(0, width):
z = 0.0 + 0.0J
i = 0
while i < maxiter:
if abs(z) > 4.0:
break
z = z**2 + c
i += 1
r = palette[i][0]
g = palette[i][1]
b = palette[i][2]
print("{r} {g} {b}".format(r=r, g=g, b=b))
c += 3.0/width
c += 3.0J/height
if __name__ == "__main__":
if len(argv) < 4:
print("usage: python mandelbrot width height maxiter")
exit(1)
width = int(argv[1])
height = int(argv[2])
maxiter = int(argv[3])
calc_mandelbrot(width, height, maxiter, palette_mandmap.palette)
18. Výsledky druhého benchmarku
Z výsledků druhého benchmarku je patrné, že je Jython při práci s komplexními čísly výrazně pomalejší, než nativní CPython 2.x i CPython 3.x:
# | x-res | y-res | Jython (s) | Python 2 (s) | Python 3 (s) |
---|---|---|---|---|---|
1 | 16 | 16 | 1.77 | 0.01 | 0.02 |
2 | 24 | 24 | 1.99 | 0.01 | 0.02 |
3 | 32 | 32 | 1.80 | 0.02 | 0.03 |
4 | 48 | 48 | 1.90 | 0.03 | 0.04 |
5 | 64 | 64 | 1.99 | 0.04 | 0.06 |
6 | 96 | 96 | 2.20 | 0.08 | 0.11 |
7 | 128 | 128 | 2.70 | 0.14 | 0.18 |
8 | 192 | 192 | 3.15 | 0.32 | 0.43 |
9 | 256 | 256 | 4.13 | 0.56 | 0.77 |
10 | 384 | 384 | 6.61 | 1.25 | 1.60 |
11 | 512 | 512 | 10.10 | 2.22 | 2.71 |
12 | 768 | 768 | 20.59 | 5.12 | 6.37 |
13 | 1024 | 1024 | 34.45 | 9.09 | 10.78 |
14 | 1536 | 1536 | 77.73 | 20.33 | 24.25 |
15 | 2048 | 2048 | 134.13 | 35.95 | 43.61 |
16 | 3072 | 3072 | 294.04 | 81.64 | 99.77 |
17 | 4096 | 4096 | 523.57 | 148.13 | 176.97 |
Obrázek 10: Výsledky druhého benchmarku vynesené do grafu.
19. Repositář s benchmarky i dalšími příklady
Oba dva jednoduché demonstrační příklady a taktéž oba benchmarky byly uloženy do Git repositáře, který naleznete na adrese https://github.com/tisnik/jython-examples. Následují odkazy na jednotlivé příklady (pro jejich spuštění je nutné mít v aktuálním adresáři symlink na Java archiv jython-standalone-2.7.0.jar):
Úvodní příklady
Zdrojový kód/skript | Adresa |
---|---|
hello_world | https://github.com/tisnik/jython-examples/blob/master/benchmark2/mandelbrot_complex.py |
java_interop | https://github.com/tisnik/jython-examples/blob/master/java_interop |
První benchmark
Zdrojový kód/skript | Adresa |
---|---|
mandelbrot.py | https://github.com/tisnik/jython-examples/blob/master/benchmark1/mandelbrot.py |
palette_mandmap.py | https://github.com/tisnik/jython-examples/blob/master/benchmark1/palette_mandmap.py |
test_jython.sh | https://github.com/tisnik/jython-examples/blob/master/benchmark1/test_jython.sh |
test_python2.sh | https://github.com/tisnik/jython-examples/blob/master/benchmark1/test_python2.sh |
test_python3.sh | https://github.com/tisnik/jython-examples/blob/master/benchmark1/test_python3.sh |
clean.sh | https://github.com/tisnik/jython-examples/blob/master/benchmark1/clean.sh |
Druhý benchmark
Zdrojový kód/skript | Adresa |
---|---|
mandelbrot_complex.py | https://github.com/tisnik/jython-examples/blob/master/benchmark2/mandelbrot_complex.py |
palette_mandmap.py | https://github.com/tisnik/jython-examples/blob/master/benchmark2/palette_mandmap.py |
test_jython.sh | https://github.com/tisnik/jython-examples/blob/master/benchmark2/test_jython.sh |
test_python2.sh | https://github.com/tisnik/jython-examples/blob/master/benchmark2/test_python2.sh |
test_python3.sh | https://github.com/tisnik/jython-examples/blob/master/benchmark2/test_python3.sh |
clean.sh | https://github.com/tisnik/jython-examples/blob/master/benchmark2/clean.sh |
20. Odkazy na Internetu
- Stránka projektu Jython
http://www.jython.org/ - Jython (Wikipedia)
https://en.wikipedia.org/wiki/Jython - Scripting for the Java Platform (Wikipedia)
https://en.wikipedia.org/wiki/Scripting_for_the_Java_Platform - JSR 223: Scripting for the JavaTM Platform
https://jcp.org/en/jsr/detail?id=223 - List of JVM languages
https://en.wikipedia.org/wiki/List_of_JVM_languages - Stránka programovacího jazyka Java
https://www.oracle.com/java/index.html - Stránka programovacího jazyka Clojure
http://clojure.org - Stránka programovacího jazyka Groovy
http://groovy-lang.org/ - Stránka programovacího jazyka JRuby
http://jruby.org/ - Stránka programovacího jazyka Kotlin
http://kotlinlang.org/ - Stránka programovacího jazyka Scala
https://www.scala-lang.org/ - Projekt Rhino
https://developer.mozilla.org/en-US/docs/Mozilla/Projects/Rhino - Clojure (Wikipedia)
https://en.wikipedia.org/wiki/Clojure - Groovy (Wikipedia)
https://en.wikipedia.org/wiki/Groovy_%28programming_language%29 - JRuby (Wikipedia)
https://en.wikipedia.org/wiki/JRuby - Kotlin (Wikipedia)
https://en.wikipedia.org/wiki/Kotlin_%28programming_language%29 - Scala (Wikipedia)
https://en.wikipedia.org/wiki/Scala_%28programming_language%29 - Python Interpreters Benchmarks
https://pybenchmarks.org/u64q/jython.php - Apache Kafka Producer Benchmarks - Java vs. Jython vs. Python
http://mrafayaleem.com/2016/03/31/apache-kafka-producer-benchmarks/ - What is Jython and is it useful at all? (Stack Overflow)
https://stackoverflow.com/questions/1859865/what-is-jython-and-is-it-useful-at-all
19. 4. 2018 at 10:27
Do toho tveho rozcestniku nahore, neco na rozsireni:
https://blogs.oracle.com/developers/announcing-graalvm
19. 4. 2018 at 18:00
Aha oni to už vydali? Super, zkouknu. dík!
19. 4. 2018 at 18:17
prej to umi llvm… cili prakrticky cokoliv.. asi trochu kecaji, a je to imho silne nestabilnmi.. ale zacatek dobrej.
Jo a vzniko to pred 10 lety behem internshipu 22(!) letyho doktoranda v sun labs. Ted tomu sefuje… No a jak sme zvykly, ma to zase jakysi closed parts :(. Napis o tom clanke z praxe O:)
23. 4. 2018 at 08:31
jj já jsem tento projekt kdysi sledoval, ale to se nějak moc nevyvíjelo. Takže je zajímavé, že to nakonec dotáhli do použitelného stavu, stojí za to to odzkoušet.
23. 4. 2018 at 11:10
Pockej 😀 pockej :DDD slovopouzitlene tu nepadlo :)))) Ale ozkouset to stoji za to. Tak pomlaej star.. ze jo nez to z JITuje samo sebe …. a tak no 🙂
14. 5. 2019 at 12:23
Help. I am looking for guide