Hlavní navigace

Programovací jazyk Clojure 6: futures nejsou jen finanční deriváty

17. 7. 2012
Doba čtení: 19 minut

Sdílet

V dnešní části seriálu o programovacím jazyce Java i o virtuálním stroji Javy se již pošesté budeme zabývat popisem vlastností programovacího jazyka Clojure. Již minule jsme se stručně zmínili o jedné variantě „paralelizace“ programů a dnes budeme v tomto dosti důležitém tématu pokračovat.

Obsah

1. Programovací jazyk Clojure a paralelní programy

2. Funkce pcalls – alternativa k funkci pmap

3. Využití návratové hodnoty funkce pcalls

4. Malá odbočka – zkrácený zápis anonymních funkcí

5. Funkce pcalls a lazy sekvence

6. Způsob použití makra pvalues

7. Spuštění výpočtů v samostatných vláknech pomocí future

8. Způsoby řízení „future“ výpočtů

9. Odkazy na Internetu

1. Programovací jazyk Clojure a paralelní programy

V předchozí části tohoto seriálu jsme zabývali převážně problematikou zpracování sekvencí a především pak lazy sekvencí, které tvoří velmi důležitou část programovacího jazyka Clojure (skalní lispaři se na lazy sekvence mohou poněkud zjednodušeně dívat jako na zobecnění seznamů). Mj. jsme si řekli, že přímo v základní knihovně tohoto programovacího jazyka se nachází několik funkcí a maker umožňujících velmi jednoduše zajistit, aby se nějaká sekvence či množina funkcí zpracovala paralelně (programátor si ovšem musí dát pozor na to, že se budou volat funkce bez vedlejších efektů, popř. takové funkce, jejichž vedlejší efekty nebudou vzájemně kolidovat). Základem je v tomto případě funkce pmap, která svým chováním odpovídá funkci map, ovšem celá sekvence na vstupu této funkce je zpracovávána paralelně. To může (ale taktéž nemusí) přinést zvýšení výkonu celé aplikace, ovšem je ponecháno pouze na úsudku programátora, kterou z těchto funkcí ve své aplikaci použije – v případě jednodušších výpočtů totiž režie nutná pro vytvoření nových vláken převáží nad výhodami souběžného běhu těchto vláken.

Měření doby běhu nějaké funkce (resp. doby vyhodnocení formy) lze zajistit funkcí time, jejíž způsob použití je ukázán na následujícím příkladu (převzatém z předchozí části tohoto seriálu):

(defn fibonacci
    [n]
    (if (< n 2)
        n
        (+ (fibonacci (- n 2)) (fibonacci (- n 1)))))
 
(time (dorun (map fibonacci (range 100 150))))
 
(time (dorun (pmap fibonacci (range 100 150))))

V závislosti na počtu jader mikroprocesoru, nastavení parametrů virtuálního stroje Javy (JRE) a dalších parametrech počítače, na němž je předchozí příklad spuštěn, se budou vypisovat rozdílné časy doby běhu, které by ale měly být na moderních strojích s minimálně dvoujádrovým CPU menší při použití funkce pmap namísto funkce map.

Příklad výsledku předchozího jednoduchého benchmarku na stroji s nízkým počtem procesorových jader:

user=> (time (dorun (map fibonacci (range 20 30))))
"Elapsed time: 3105.400932 msecs"
nil
 
user=> (time (dorun (pmap fibonacci (range 20 30))))
"Elapsed time: 2558.662981 msecs"
nil

2. Funkce pcalls – alternativa k funkci pmap

Kromě funkce pmap, která je pro použití v reálných aplikacích velmi jednoduchá i praktická, lze v programovacím jazyku Clojure použít i makro nazvané pvalues sloužící pro paralelní vyhodnocení několika forem (opět záleží jen na programátorovi, aby zajistil, že nedojde ke kolizím v případě vedlejších efektů – kdo programuje čistě funkcionálně, nemá problém :-). Taktéž lze použít funkci pcalls, jež umožňuje zavolat a současně (paralelně) spustit několik funkcí předaných ve formě parametrů této funkce. U funkce pcalls spočívá jediný – ovšem v mnoha případech dosti závažný – problém v tom, že volané funkce jsou bezparametrické. V následujícím dosti umělém demonstračním příkladu se paralelně volá trojice bezparametrických funkcí, z nichž každá má vedlejší efekt – výpis textu na standardní výstup (jedná se o jeden z méně zákeřných vedlejších efektů). Nejprve jsou všechny tři funkce nadeklarovány a posléze je dvakrát po sobě zavolána funkce pcalls, aby bylo patrné, že pořadí výpisu na standardní výstup je z hlediska programátora „náhodné“, protože ten nemůže explicitně ovlivnit pořadí spuštění, popř. způsob přepínání vláken:

; jednoduché bezparametrické funkce,
; které pouze na standardní výstup
; vypíšou nějaký text
 
(defn fce1 [] (println "Funkce 1"))
 
(defn fce2 [] (println "Funkce 2"))
 
(defn fce3 [] (println "Funkce 3"))
; nyní všechny tři funkce spustíme tak,
; že každá funkce poběží ve svém vláknu
 
(pcalls fce1 fce2 fce3)
Funkce 1
Funkce 2
(Funkce 3
nil nil nil)

V předchozím výpisu došlo ke smíchání výpisu všech tří funkcí s hodnotou, jejíž význam si popíšeme ve třetí kapitole.

; opakované spuštění, aby bylo zřejmé,
; že pořadí výpisu je "náhodné"
 
(pcalls fce1 fce2 fce3)
Funkce 1
(Funkce 3
Funkce 2
nil nil nil)

3. Využití návratové hodnoty funkce pcalls

Zajímavá je taktéž trojice hodnot nil nil nil vypsaná předchozím demonstračním příkladem na posledním řádku (ve skutečnosti k těmto hodnotám patří i otevírací a uzavírací kulaté závorky, které se však ve výpisu mohou jednoduše přehlédnout: tyto závorky nám říkají, že výsledkem je buď seznam nebo nějaká obecná sekvence). Samozřejmě se v případě (nil nil nil), jak by měl text správně vypadat, jedná o výsledek samotné funkce pcalls, protože smyčka REPL automaticky vypisuje návratové hodnoty všech vyhodnocených forem. Ve skutečnosti totiž funkce pcalls vrací lazy sekvenci obsahující výsledky všech paralelně spuštěných funkcí, čehož lze samozřejmě náležitě využít. Podívejme se na poněkud složitější demonstrační příklad, v němž se bude paralelně počítat několik členů Fibonacciho řady/posloupnosti (http://cs.wikipedia.org/wi­ki/Fibonacciho_posloupnos­t).

Prezentovaný výpočet je samozřejmě z hlediska optimalizací algoritmu nesmyslný, protože by bylo mnohem jednodušší prostě ukládat jednotlivé mezivýsledky do seznamu nebo vektoru. V našem příkladu ovšem výpočet jednoho členu Fibonacciho řady použijeme proto, že se jedná o jednoduchou a současně i snadno pochopitelnou funkci, jejíž vyhodnocení může trvat delší dobu (na mém obstarožním počítači je již výpočet třicátého členu tak pomalý, že je prodleva ve vyhodnocení jasně viditelná):

; nejprve je uvedena deklarace funkce pro výpočet jednoho
; členu Fibonacciho řady:
 
(defn fibonacci
    [n]
    (if (< n 2)
        n
        (+ (fibonacci (- n 2)) (fibonacci (- n 1)))))
; následně nadeklarujeme pomocné jednoúčelové funkce,
; které nahradí volání funkce fibonacci s parametrem
; (nejedná se o nejšťastnější způsob tvorby programů :-)
 
(defn fib20 [] (fibonacci 20))
(defn fib25 [] (fibonacci 25))
(defn fib30 [] (fibonacci 30))
; test, zda jsou funkce zapsány korektně
 
user=> (fib20)
6765
 
user=> (fib25)
75025
 
user=> (fib30)
832040
; dobře, můžeme zkusit spustit výpočty paralelně
 
user=> (pcalls fib20 fib25 fib30)
(6765 75025 832040)

4. Malá odbočka – zkrácený zápis anonymních funkcí

Použití pomocných funkcí fib20, fib25 a fib30 nadeklarovaných v demonstračním příkladu uvedeném na konci předchozí kapitoly bylo vynuceno již zmíněným omezením funkce pcalls na volání funkcí, které nemají žádné parametry. Jedná se o již na první pohled velmi nepěkný způsob programování, kdy se na globální úrovni (přesněji řečeno v implicitním jmenném prostoru) vytváří jednoúčelové funkce. Je zajímavé, že v objektově orientovaných jazycích je tomu mnohdy naopak – zde se totiž doporučuje používat jednoduché jednoúčelové metody, ty jsou ovšem omezeny na jednu třídu, popř. na jednu větev hierarchie tříd. Zmíněné omezení funkce pcalls lze však ve skutečnosti obejít i jinak – pomocí anonymních funkcí, což je ve funkcionálním jazyku obecně považováno za mnohem elegantnější přístup. Jedno z možných řešení může vypadat tak, že se přímo ve volání funkce pcalls vytváří potřebné anonymní funkce (ty nesmí mít v tomto případě žádné parametry, protože jejich hodnoty by nebylo možné nijak předat):

(pcalls
    (fn [] (fibonacci 20))
    (fn [] (fibonacci 25))
    (fn [] (fibonacci 30))
)
 
(6765 75025 832040)

Tento způsob zápisu však není o moc čitelnější, než tomu bylo v příkladu předchozím, protože se pro vytvoření anonymní funkce použila speciální forma fn, která je dosti upovídaná. Kratší řešení ovšem existuje – vzhledem k tomu, že se anonymní funkce používají při tvorbě aplikací v programovacím jazyku Clojure velmi často, je možné pro jejich zápis alternativně použít i zkrácenou formu začínající znakem #, za nímž je v závorce přímo napsáno tělo anonymní funkce (tato forma se překládá do volání fn ještě v preprocesoru smyčky REPL, tj. samotná smyčka REPL již „vidí“ pouze zápis speciální formy fn):

#(tělo anonymní funkce)

To, že chybí jméno funkce je pochopitelné – kdyby funkce měla jméno, už by nebyla funkcí anonymní :-) ovšem absence jmen parametrů možná může být poněkud matoucí, protože anonymní funkce bez parametrů by vlastně odpovídala vrácení konstanty (popř. nefunkcionálním „šílenostem“ typu random). Ve skutečnosti však i anonymní funkce vytvořená pomocí znaku # parametry mít může. Ty jsou pojmenovány v závislosti na své pozici takto: %1, %2 atd. Předpokládá se totiž, že anonymní funkce budou velmi krátké a s malým množstvím parametrů – pokud by tomu tak nebylo, nic programátorům nebrání vrátit se ke speciální formě fn.

Ukažme si nějaké příklady použití vytvoření anonymních funkcí pomocí znaku #.

Aplikace anonymní funkce vytvořené s využitím speciální formy fn:

((fn [x y] (* x y)) 6 7)

Zkrácený (ekvivalentní) způsob zápisu:

(#(* %1 %2) 6 7)

Anonymní funkce se velmi často používají jako parametry funkcí map, pmap, filter apod.:

(map #(/ 2 %1) [1 2 3 4 5 6])
(2 1 2/3 1/2 2/5 1/3)

Popř.:

(map #(/ 2 %1) (range 1 7))
(2 1 2/3 1/2 2/5 1/3)

Vraťme se nyní k původnímu (trošku umělému) příkladu. Namísto:

(pcalls
    (fn [] (fibonacci 20))
    (fn [] (fibonacci 25))
    (fn [] (fibonacci 30))
)
 
(6765 75025 832040)

můžeme jednoduše napsat:

(pcalls
    #(fibonacci 20)
    #(fibonacci 25)
    #(fibonacci 30)
)
 
(6765 75025 832040)

Zde máme situaci ještě jednodušší, protože vlastně žádný parametr anonymní funkce nepotřebujeme zpracovat a ve skutečnosti ani nemůžeme, protože – jak jsme si již řekli – funkce pcalls nedovoluje volání funkcí s parametry.

5. Funkce pcalls a lazy sekvence

O tom, že je výsledkem volání funkce pcalls skutečně lazy sekvence, se můžeme přesvědčit jednoduše. Uložíme výsledek do nějaké proměnné a potom si vypíšeme jak hodnotu této proměnné, tak i výsledky některých predikátů:

user=> (def results (pcalls fib20 fib25 fib30))
#'user/results
 
user=> (list? results)
false
 
user=> (vector? results)
false
 
user=> (seq? results)
true
 
user=> results
(6765 75025 832040)

Je přitom samozřejmé, že na výslednou lazy sekvenci lze aplikovat všechny funkce, které se sekvencemi mohou pracovat:

user=> (map #(/ 1 %1) results)
(1/6765 1/75025 1/832040)
user=> (map #(/ 1 (* %1 %1)) results)
(1/45765225 1/5628750625 1/692290561600)

Programovací jazyk Clojure je při práci s lazy sekvencemi skutečně „líný“, protože se výzva smyčky REPL může objevit ještě předtím, než je výpočet dokončen – to nám samozřejmě vůbec nevadí, protože ani funkce fibonacci, ani její „obalující“ funkce fib20, fib25 a fib30 nemají vedlejší efekty. Co se však stane v případě, kdy vedlejší efekt naschvál přidáme, například takovým způsobem, že každá z pomocných funkcí vytiskne text na začátku a na konci výpočtu? Můžeme si to snadno vyzkoušet:

(defn fib20
    []
    (println "pocitam fibonacci 20")
    (println "dokonceno fibonacci 20 ="
        (fibonacci 20)))
 
(defn fib25
    []
    (println "pocitam fibonacci 25")
    (println "dokonceno fibonacci 25 ="
        (fibonacci 25)))
 
(defn fib30
    []
    (println "pocitam fibonacci30")
    (println "dokonceno fibonacci 30 ="
        (fibonacci 30)))

Sice jsme kvůli výpisu zprávy ztratili výsledek výpočtu, to nám však pro ilustraci funkce makra pcalls nemusí vadit. Ještě si vytvoříme pomocnou funkci compute_all, která spustí výpočet a ihned poté vypíše na standardní výstup zprávu „konec vypoctu???“:

(defn compute_all
    []
    (println "zacatek vypoctu")
    (def result (pcalls fib20 fib25 fib30 fib30))
    (println "konec vypoctu???")
    result)

Zajímavá věc se stane po spuštění této funkce:

user=> (compute_all)
zacatek vypoctu
pocitam fibonacci 20
pocitam fibonacci 25
konec vypoctu???
(pocitam fibonacci30
pocitam fibonacci30
dokonceno fibonacci 20 = 6765
dokonceno fibonacci 25 = 75025
nil dokonceno fibonacci 30 = 832040
dokonceno fibonacci 30 = 832040
nil nil nil)

V předchozích řádcích jsou navzájem promíchány tři typy zpráv. Jsou to zprávy tištěné pomocnými funkcemi fib20, fib25 a fib30, které jsou spuštěny paralelně, což je ostatně z výpisu patrné. Další typ zpráv „zacatek vypoctu“ a „konec vypoctu???“ tiskne funkce compute_all a povšimněte si, že tato funkce vypíše „konec vypoctu???“ ještě předtím, než je výpočet skutečně dokončen. To nám však nevadí, protože do proměnné result je uložena lazy sekvence, která se vyhodnotí ve chvíli, kdy je to potřeba, popř. se nevyhodnotí nikdy. Vyhodnocení jsme si vynutili posledním výrazem ve funkci compute_all, kterým říkáme, že se má vrátit lazy sekvence. K vyhodnocení dojde z toho důvodu, že je funkce compute_all volána ze smyčky REPL a ta samozřejmě vypíše každou vyhodnocenou formu – v tomto případě jsou to tři hodnoty nil (bylo by jistě možné funkce fib* upravit tak, aby tyto funkce vracely správné hodnoty, to by však již vyžadovalo použití formy let, s níž jsme se prozatím v tomto seriálu neseznámili).

6. Způsob použití makra pvalues

Z trojice funkcí a maker pmap, pcalls a pvalues nám již zbývá popsat pouze makro pvalues. To je ve skutečnosti na použití velmi jednoduché, protože umožňuje paralelně vyhodnotit libovolné množství forem (výrazů), přičemž výsledkem volání makra pvalues je lazy sekvence obsahující hodnoty všech vyhodnocených forem. Může se jednat o zcela libovolné formy, tj. o literály, volání funkcí s různými parametry atd:

user=> (pvalues true nil "retezec" (/ 1 2) (* 6 7) (fibonacci 35))
(true nil "retezec" 1/2 42 9227465)

Pokud zůstaneme u našeho dosti umělého příkladu s výpočtem prvků Fibonacciho řady, můžeme namísto volání pcalls (kde bylo nutné použít buď anonymní funkce nebo bezparametrické „obalové“ funkce) použít makro pvalues následujícím způsobem:

; zavolání funkce Fibonacci
; s následným vyhodnocením lazy sekvence:
user=> (pvalues (fibonacci 20) (fibonacci 25) (fibonacci 30))
(6765 75025 832040)
; výsledek je možné skutečně použít jako jakoukoli jinou lazy sekvenci
user=> (filter odd? (pvalues (fibonacci 20) (fibonacci 25) (fibonacci 30)))
(6765 75025)
; samozřejmě i velmi užitečnou funkcí reduce
user=> (reduce + (pvalues (fibonacci 20) (fibonacci 25) (fibonacci 30)))
913830

Vzhledem k tomu, že i makro pcalls vrací lazy sekvenci, je zajištěno i líné vyhodnocení výsledků až ve chvíli, kdy jsou tyto výsledky skutečně zapotřebí:

user=> (def results (pvalues (fibonacci 20) (fibonacci 25) (fibonacci 35)))
#'user/results
 
user=> results
(6765 75025 9227465)

Poznámka: makro pvalues je ve skutečnosti vytvořeno takovým způsobem, že interně používá funkci pcalls. O makrech jsme si prozatím neříkali žádné podrobnosti, ovšem ze zdrojového kódu makra pvalues je zřejmé, že se funkce pcalls skutečně využívá (co znamenají znaky ` @ atd. si řekneme pravděpodobně až v osmé části tohoto podseriálu):

(defmacro pvalues
  [& exprs]
  `(pcalls ~@(map #(list `fn [] %) exprs)))

Poznámka2: zajímavé taktéž je, že samotná funkce pcalls interně volá funkci pmap, což vlastně znamená, že právě pmap je jedinou „paralelní“ funkcí, která musí být podporována v jádru jazyka Clojure a zbylá dvojice pcalls+pvalues je již řešitelná v samotném Clojure:

(defn pcalls
  [& fns] (pmap #(%) fns))

(znak % v anonymní funkci zde odpovídá %1, význam znaku & si vysvětlíme příště; zde jen bez dalších podrobností uvedu, že se všechny skutečné argumenty při volání funkce pcalls vloží do fns, takže je lze zpracovat jako běžnou sekvenci).

7. Spuštění výpočtů v samostatných vláknech pomocí future

Použití trojice funkcí/maker pmap+pcalls+pvalues není v žádném případě jedinou možností, jak v programovacím jazyce Clojure zapisovat algoritmy zpracovávané paralelně. Velmi elegantní a přitom jednoduchý způsob spuštění paralelního výpočtu představuje použití takzvaných futures. Mimochodem, jedná se o technologii, která existuje i v knihovnách programovacího jazyka Java (dokonce již od verze 5.0), i když v Javě se pravděpodobně jedná o méně známé API – osobně si však myslím, že využití futures v Javě je mnohdy výhodnější, než explicitní tvorba vláken, synchronizovaných metod/bloků a dalších nízkoúrovňových prostředků.

Co se však vlastně pod pojmem futures skrývá? Kromě názvu jednoho typu finančního derivátu :-) představuje future(s) výpočet, který běží asynchronně k hlavnímu vláknu aplikace. Uživatel pouze výpočet spustí a teprve ve chvíli, kdy potřebuje výsledek tohoto výpočtu, začne systém řešit, jakým způsobem má asynchronní výpočet ukončit, tj. jak má provést synchronizaci obou vláken. V ideálním případě je výpočet již dokončen, takže se přímo použije jeho výsledek, v případě opačném se až při čtení výsledku počká na dokončení výpočtu. Je samozřejmé, že jakýkoli přístup ke sdíleným prostředkům musí být omezen, což však v programovacím jazyce, jehož hodnoty jsou neměnitelné, není až tak velký problém (Clojure navíc používá poněkud jinou sémantiku změny stavu, než je tomu v běžných jazycích s proměnnými – více viz následující část tohoto seriálu).

8. Způsoby řízení „future“ výpočtů

Podívejme se nyní, jak by bylo možné paralelně spustit dva výpočty 35. členu Fibonacci řady (pokud máte rychlý počítač, můžete si index vypočteného členu patřičně zvýšit):

; opět klasický paralelní zápis funkce fibonacci
 
(defn fibonacci
    [n]
    (if (> n 2)
        n
        (+ (fibonacci (- n 2)) (fibonacci (- n 1)))))
; namísto přímého volání funkce fibonacci však spustíme
; dva asynchronní výpočty s využitím (future funkce)
 
user=> (def future_fibonacci1 (future (fibonacci 35)))
#'user/future_fibonacci1
 
user=> (def future_fibonacci2 (future (fibonacci 35)))
#'user/future_fibonacci2

Oba výpočty (fibonacci 35) byly spuštěny v samostatných vláknech, což mj. znamená, že smyčka REPL nemusí čekat na výsledek výpočtu a nabídne výzvu (prompt) prakticky ihned po zadání obou předchozích příkazů. Jak se však dostaneme k výsledku? Postačuje kdekoli v programu či přímo v REPL zavolat deref, jemuž se předá název future objektu:

user=> (deref future_fibonacci1)
9227465
 
user=> (deref future_fibonacci2)
9227465

Co se vlastně stalo? Vzhledem k tomu, že smyčka REPL běží v jiném vlákně, než samotný výpočet a nyní potřebuje získat návratovou hodnotu tohoto asynchronního výpočtu, dojde k čekání na dokončení výpočtového vlákna spjatého se future objektem. Jakmile je výpočet dokončen, je získán jeho výsledek a výpočtové vlákno je zrušeno, resp. přesněji řečeno je vráceno do poolu, aby mohlo být kdykoli později znovu využito (to je důležité v případě, že se asynchronní výpočty == futures používají v aplikaci častěji).

Vzhledem k tomu, že deref se v multivláknových programech používá poměrně často, je možné použít zkrácený způsob zápisu, kdy se namísto (deref foo) použije pouze @foo, tj. neuvádí se ani kulaté závorky. Ekvivalentem předchozího příkladu by tedy byl následující výpočet:

CS24_early

user=> (def future_fibonacci1 (future (fibonacci 35)))
#'user/future_fibonacci1
 
user=> (def future_fibonacci2 (future (fibonacci 35)))
#'user/future_fibonacci2
user=> @future_fibonacci1
9227465
 
user=> @future_fibonacci2
9227465

Poznámka: hodnota 35 byla zvolena tak, aby výpočet trval přibližně 10 sekund na mém postarším (no…velmi postarším :-) počítači. Počítače vážených čtenářů budou s velkou pravděpodobně mnohem rychlejší, takže si prosím zvolte příslušně větší konstantu, popř. je možné namísto výpočtu použít pouhé čekání – ovšem v tomto případě neuvidíte žádné zatížení v programu top či htop.

Čekání, resp. pozastavení aktuálního vlákna lze provést jednoduše zavoláním příslušné javovské funkce:

(Thread/sleep doba_cekani_v_milisekundach)

9. Odkazy na Internetu

  1. Clojure home page
    http://clojure.org/downloads
  2. Clojure – Functional Programming for the JVM
    http://java.ociweb.com/mar­k/clojure/article.html
  3. Clojure quick reference
    http://faustus.webatu.com/clj-quick-ref.html
  4. 4Clojure
    http://www.4clojure.com/
  5. ClojureDoc
    http://clojuredocs.org/
  6. Clojure (Wikipedia EN)
    http://en.wikipedia.org/wiki/Clojure
  7. Clojure (Wikipedia CS)
    http://cs.wikipedia.org/wiki/Clojure
  8. Riastradh's Lisp Style Rules
    http://mumble.net/~campbe­ll/scheme/style.txt
  9. Dynamic Languages Strike Back
    http://steve-yegge.blogspot.cz/2008/05/dynamic-languages-strike-back.html
  10. Scripting: Higher Level Programming for the 21st Century
    http://www.tcl.tk/doc/scripting.html
  11. Java Virtual Machine Support for Non-Java Languages
    http://docs.oracle.com/ja­vase/7/docs/technotes/gui­des/vm/multiple-language-support.html
  12. New JDK 7 Feature: Support for Dynamically Typed Languages in the Java Virtual Machine
    http://java.sun.com/develo­per/technicalArticles/Dyn­TypeLang/
  13. JSR 223: Scripting for the JavaTM Platform
    http://jcp.org/en/jsr/detail?id=223
  14. JSR 292: Supporting Dynamically Typed Languages on the JavaTM Platform
    http://jcp.org/en/jsr/detail?id=292
  15. Java 7: A complete invokedynamic example
    http://niklasschlimm.blog­spot.com/2012/02/java-7-complete-invokedynamic-example.html
  16. InvokeDynamic: Actually Useful?
    http://blog.headius.com/2007/01/in­vokedynamic-actually-useful.html
  17. A First Taste of InvokeDynamic
    http://blog.headius.com/2008/09/first-taste-of-invokedynamic.html
  18. Java 6 try/finally compilation without jsr/ret
    http://cliffhacks.blogspot­.com/2008/02/java-6-tryfinally-compilation-without.html
  19. An empirical study of Java bytecode programs
    http://www.mendeley.com/research/an-empirical-study-of-java-bytecode-programs/
  20. Java quick guide: JVM Instruction Set (tabulka všech instrukcí JVM)
    http://www.mobilefish.com/tu­torials/java/java_quickgu­ide_jvm_instruction_set.html
  21. The JVM Instruction Set
    http://mpdeboer.home.xs4a­ll.nl/scriptie/node14.html
  22. Control Flow in the Java Virtual Machine
    http://www.artima.com/under­thehood/flowP.html
  23. Root.cz: Využití komprimovaných ukazatelů na objekty v JVM
    http://www.root.cz/clanky/vyuziti-komprimovanych-ukazatelu-na-objekty-v-nbsp-jvm/
  24. Root.cz: JamVM aneb alternativa k HotSpotu nejenom pro embedded zařízení a chytré telefony
    http://www.root.cz/clanky/jamvm-aneb-alternativa-k-hotspotu-nejenom-pro-embedded-zarizeni-tablety-a-chytre-telefony/
  25. The JavaTM Virtual Machine Specification, Second Edition
    http://java.sun.com/docs/bo­oks/jvms/second_edition/html/VMSpec­TOC.doc.html
  26. The class File Format
    http://java.sun.com/docs/bo­oks/jvms/second_edition/html/Clas­sFile.doc.html
  27. javap – The Java Class File Disassembler
    http://docs.oracle.com/ja­vase/1.4.2/docs/tooldocs/win­dows/javap.html
  28. javap-java-1.6.0-openjdk(1) – Linux man page
    http://linux.die.net/man/1/javap-java-1.6.0-openjdk
  29. Using javap
    http://www.idevelopment.in­fo/data/Programming/java/mis­cellaneous_java/Using_javap­.html
  30. Examine class files with the javap command
    http://www.techrepublic.com/ar­ticle/examine-class-files-with-the-javap-command/5815354
  31. BCEL Home page
    http://commons.apache.org/bcel/
  32. BCEL Manual
    http://commons.apache.org/bcel/ma­nual.html
  33. Byte Code Engineering Library (Wikipedia)
    http://en.wikipedia.org/wiki/BCEL
  34. Java programming dynamics, Part 7: Bytecode engineering with BCEL
    http://www.ibm.com/develo­perworks/java/library/j-dyn0414/
  35. Bytecode Engineering
    http://book.chinaunix.net/spe­cial/ebook/Core_Java2_Volu­me2AF/0131118269/ch13lev1sec6­.html
  36. BCEL Tutorial
    http://www.smfsupport.com/sup­port/java/bcel-tutorial!/
  37. ASM Home page
    http://asm.ow2.org/
  38. Seznam nástrojů využívajících projekt ASM
    http://asm.ow2.org/users.html
  39. ObjectWeb ASM (Wikipedia)
    http://en.wikipedia.org/wi­ki/ObjectWeb_ASM
  40. Java Bytecode BCEL vs ASM
    http://james.onegoodcooki­e.com/2005/10/26/java-bytecode-bcel-vs-asm/
  41. Bytecode Outline plugin for Eclipse (screenshoty + info)
    http://asm.ow2.org/eclipse/index.html
  42. aspectj (Eclipse)
    http://www.eclipse.org/aspectj/
  43. Aspect-oriented programming (Wikipedia)
    http://en.wikipedia.org/wi­ki/Aspect_oriented_program­ming
  44. AspectJ (Wikipedia)
    http://en.wikipedia.org/wiki/AspectJ
  45. EMMA: a free Java code coverage tool
    http://emma.sourceforge.net/
  46. Cobertura
    http://cobertura.sourceforge.net/
  47. FindBugs
    http://findbugs.sourceforge.net/
  48. GNU Classpath
    www.gnu.org/s/classpath/
  49. Java VMs Compared
    http://bugblogger.com/java-vms-compared-160/
  50. JSRs: Java Specification Requests – JSR 223: Scripting for the Java Platform
    http://www.jcp.org/en/jsr/de­tail?id=223
  51. Scripting for the Java Platform
    http://java.sun.com/develo­per/technicalArticles/J2SE/Des­ktop/scripting/
  52. Scripting for the Java Platform (Wikipedia)
    http://en.wikipedia.org/wi­ki/Scripting_for_the_Java_Plat­form
  53. Java Community Process
    http://en.wikipedia.org/wi­ki/Java_Specification_Requ­est
  54. Java HotSpot VM Options
    http://www.oracle.com/technet­work/java/javase/tech/vmop­tions-jsp-140102.html
  55. Great Computer Language Shootout
    http://c2.com/cgi/wiki?Gre­atComputerLanguageShootout
  56. Java performance
    http://en.wikipedia.org/wi­ki/Java_performance
  57. Trying the prototype
    http://mail.openjdk.java.net/pi­permail/lambda-dev/2010-August/002179.html
  58. Better closures (for Java)
    http://blogs.sun.com/jrose/en­try/better_closures
  59. Lambdas in Java: An In-Depth Analysis
    http://www.infoq.com/articles/lambdas-java-analysis
  60. Class ReflectiveOperationException
    http://download.java.net/jdk7/doc­s/api/java/lang/Reflective­OperationException.html
  61. Scala Programming Language
    http://www.scala-lang.org/
  62. Run Scala in Apache Tomcat in 10 minutes
    http://www.softwaresecret­weapons.com/jspwiki/run-scala-in-apache-tomcat-in-10-minutes
  63. Fast Web Development With Scala
    http://chasethedevil.blog­spot.cz/2007/09/fast-web-development-with-scala.html
  64. Top five scripting languages on the JVM
    http://www.infoworld.com/d/developer-world/top-five-scripting-languages-the-jvm-855
  65. Proposal: Indexing access syntax for Lists and Maps
    http://mail.openjdk.java.net/pi­permail/coin-dev/2009-March/001108.html
  66. Proposal: Elvis and Other Null-Safe Operators
    http://mail.openjdk.java.net/pi­permail/coin-dev/2009-March/000047.html
  67. Java 7 : Oracle pushes a first version of closures
    http://www.baptiste-wicht.com/2010/05/oracle-pushes-a-first-version-of-closures/
  68. Groovy: An agile dynamic language for the Java Platform
    http://groovy.codehaus.org/Operators
  69. Better Strategies for Null Handling in Java
    http://www.slideshare.net/Step­han.Schmidt/better-strategies-for-null-handling-in-java
  70. Control Flow in the Java Virtual Machine
    http://www.artima.com/under­thehood/flowP.html
  71. Java Virtual Machine
    http://en.wikipedia.org/wi­ki/Java_virtual_machine
  72. ==, .equals(), compareTo(), and compare()
    http://leepoint.net/notes-java/data/expressions/22com­pareobjects.html
  73. New JDK7 features
    http://openjdk.java.net/pro­jects/jdk7/features/
  74. Project Coin: Bringing it to a Close(able)
    http://blogs.sun.com/darcy/en­try/project_coin_bring_clo­se
  75. CloseableFinder source code
    http://blogs.sun.com/darcy/re­source/ProjectCoin/Closea­bleFinder.java
  76. Joe Darcy blog about JDK
    http://blogs.sun.com/darcy
  77. Java 7 – more dynamics
    http://www.baptiste-wicht.com/2010/04/java-7-more-dynamics/
  78. New JDK 7 Feature: Support for Dynamically Typed Languages in the Java Virtual Machine
    http://java.sun.com/develo­per/technicalArticles/Dyn­TypeLang/index.html

Byl pro vás článek přínosný?

Autor článku

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