LuaJIT – Just in Time překladač pro programovací jazyk Lua (9 – další vlastnosti trasovacího JITu)

11. 12. 2014
Doba čtení: 19 minut

Sdílet

V další části seriálu o překladači LuaJIT budeme pokračovat v popisu funkce trasovacího překladače. Minule jsme si ukázali, jak trasovací překladač dokáže detekovat často používané programové smyčky, dnes se podíváme na způsob detekce větví ve smyčkách a detekci funkcí volaných s velkou frekvencí.

Obsah

1. LuaJIT – Just in Time překladač pro programovací jazyk Lua (9 – další vlastnosti trasovacího JITu)

2. Trasovací JIT a programové smyčky obsahující rozvětvení

   2.1 Demonstrační příklad test48.lua – programová smyčka s rozvětvením (malý počet iterací)

   2.2 Demonstrační příklad test49.lua – programová smyčka s rozvětvením (velký počet iterací)

3. Programové smyčky se složitějším systémem větvení

   3.1 Demonstrační příklad test50.lua – programová smyčka se složitějším rozvětvením

   3.2 Demonstrační příklad test51.lua – vnořené větve

4. Trasovací JIT a volání funkcí

5. Zjištění, která funkce se má JITovat

6. Demonstrační příklady

   6.1 Demonstrační příklad test52.lua – opakované volání funkce

   6.2 Demonstrační příklad test53.lua – opakované volání funkce z programové smyčky

7. Zdrojové kódy všech šesti dnešních demonstračních příkladů

8. Literatura

9. Odkazy na Internetu

1. LuaJIT – Just in Time překladač pro programovací jazyk Lua (9 – další vlastnosti trasovacího JITu)

V předchozí části seriálu (nejenom) o just in time překladači LuaJIT jsme si řekli základní informace o takzvaných trasovacích JIT překladačích, které se v mnoha ohledech chovají odlišně od známějších JIT překladačů typu hot spot. Trasovací JIT překladače i hot spot JITy se soustředí na detekci často používaných programových smyček a funkcí volaných s velkou frekvencí. Ovšem na rozdíl od klasických hot spot překladačů se trasovací JIT překladače zaměřují na sledování sekvence generovaných a následně prováděných instrukcí a nikoli na (potenciálně složitou) analýzu bajtkódu a už vůbec ne na analýzu zdrojového kódu (právě zde se ukazuje, že vhodně navržený bajtkód popř. mezijazyk může významným způsobem zjednodušit nebo naopak ztížit práci JIT překladačů; problémy tohoto typu jsou patrné i v bajtkódu JVM).

Sledování a následná analýza sekvence generovaných a následně prováděných instrukcí (tato sekvence se nazývá stopa neboli trace) trasovacími JIT překladači vlastně automaticky vede k tomu, že mohou být eliminovány některé podmíněné skoky ve vytvářeném nativním (strojovém) kódu (představme si programovou smyčku s vnitřním rozvětvením). Na druhou stranu se však do nativního kódu musí navíc vkládat strojové instrukce testující, zda se běh programu skutečně ubírá správným směrem (tyto testovací instrukce však v ideálním případě nevedou k provedení podmíněných skoků, což je u moderních mikroprocesorů dosti náročná operace). Nezávisle na tom, zda je použit hot spot JIT či trasovací JIT, je další fáze překladu, tj. optimalizace, prováděna s využitím podobných technologií. Setkáme se zde s eliminací mrtvého kódu, rozbalením smyček, vložením těla kratších funkcí do místa, kde se původně tato funkce volala apod.

2. Trasovací JIT a programové smyčky obsahující rozvětvení

Zatímco detekce programových smyček trasovacími JIT je poměrně jednoduchá úloha, které je navíc v případě LuaJIT ještě ulehčena způsobem překladu smyček do bajtkódu, je detekce rozvětvení ve smyčkách již složitější, a to zejména z toho důvodu, že trasovací JIT překladač vlastně nemá „představu“ o celkové struktuře prováděného algoritmu, pouze dokáže detekovat sekvenci opakujících se instrukcí. Podívejme se proto na několik demonstračních příkladů, v nichž se vyskytuje počítaná programová smyčka a uvnitř této smyčky rozvětvení prováděné na základě nějaké podmínky. U všech následujících příkladů bude uveden jejich zdrojový kód, standardní výstup a taktéž zprávy vypisované tracerem při specifikaci volby -jv. Právě výstup z traceru nám dá poměrně dobrou představu o tom, jaká část programového kódu již byla JITována a jaká nikoli. Výsledky mohou být v některých případech překvapující, o čemž autor LuaJITu suše říká „race compilers are full of surprises like this – have fun! :-)“

2.1 Demonstrační příklad test48.lua – programová smyčka s rozvětvením (malý počet iterací)

Dnešní první demonstrační příklad je velmi jednoduchý, protože obsahuje pouze jedinou počítanou programovou smyčku, v níž se nachází jedno rozvětvení – příkazová struktura if-then-end. Povšimněte si, že počet iterací programové smyčky je nastaven na 100, takže se tato smyčka dostane do skupiny hot loops a provede se její překlad do nativního kódu (počet iterací překročí magickou hodnotu hotloop=56). Naproti tomu se interní rozvětvení provede pouze padesátkrát, což znamená, že tato část programového kódu se nebude automaticky překládat a to jednoduše z toho důvodu, že se sice detekuje stopa (trace), ale ta nebude prováděna tak často, aby si to vyžádalo použití potenciálně náročného JIT překladu. Tato programová smyčka tedy bude přeložena jen z poloviny a bude se přecházet mezi interpretovaným bajtkódem a nativním kódem:

--
-- LuaJIT: demonstrační příklad číslo 48.
--
-- Test JITu - programová smyčka s rozvětvením.
--
 
 
 
-- deklarace a inicializace lokálních proměnných
local i
local x = 0
local y = 0
 
 
 
-- programová smyčka s rozvětvením
for i = 1,100 do
    x = x + 1
    if i > 50 then
        y = y + 1
    end
end
 
print(x, y)
 
 
 
--
-- Finito.
--

Výsledek výpočtu provedeného prvním demonstračním příkladem:

100     50

Z výpisu trasovacích informací získaných díky přepínači -jv je patrné, že se detekovala a JITovala skutečně pouze počítaná programová smyčka a nikoli již rozvětvení uvnitř této smyčky:

[TRACE   1 test48.lua:17 loop]

2.2 Demonstrační příklad test49.lua – programová smyčka s rozvětvením (velký počet iterací)

Donutit LuaJIT k tomu, aby provedl i překlad podmínky umístěné uvnitř počítané programové smyčky, je ve skutečnosti velmi snadné – postačuje totiž zvýšit počet iterací smyčky a současně i počet vstupů do podmíněného bloku. Dnešní druhý demonstrační příklad pojmenovaný test49.lua se od příkladu předchozího liší pouze v těchto dvou detailech (viz limit na počet iterací a porovnání počitadla s hodnotou 500):

--
-- LuaJIT: demonstrační příklad číslo 49.
--
-- Test JITu - programová smyčka s rozvětvením, zvýšení počtu iteraci.
--
 
 
 
-- deklarace a inicializace lokálních proměnných
local i
local x = 0
local y = 0
 
 
 
-- programová smyčka s rozvětvením
for i = 1,1000 do
    x = x + 1
    if i > 500 then
        y = y + 1
    end
end
 
print(x, y)
 
 
 
--
-- Finito.
--

Výsledek výpočtu provedeného prvním demonstračním příkladem:

1000    500

Výpis trasovacích informací, ze kterého je patrné, že se správně detekovala a přeložila jak celá programová smyčka (první řádek), tak i blok příkazů umístěný v podmínce (druhý řádek):

[TRACE   1 test49.lua:17 loop]
[TRACE   2 (1/1) test49.lua:20 -> 1]

3. Programové smyčky se složitějším systémem větvení

Trasovací JIT překladače se samozřejmě musí umět vyrovnat i se složitějším systémem větvení prováděného uvnitř programových smyček. Ve skutečnosti je to dokonce nutnost, protože jen zcela triviální programové smyčky neobsahují větší množství programové logiky a právě takové smyčky typicky bývají jádrem velkého množství algoritmů. Aby bylo možné si otestovat chování LuaJITu i v takovýchto případech, byly vytvořeny další dva demonstrační příklady pojmenované test50.luatest51.lua, v nichž je (opět) použita vnější počítaná programová smyčka s tisíci iteracemi. Takto velký počet iterací si vynutí použití JIT překladu, o čemž se ostatně sami přesvědčíme.

3.1 Demonstrační příklad test50.lua – programová smyčka se složitějším rozvětvením

V demonstračním příkladu test50.lua se ve vnější programové smyčce nachází hned tři větve. Vzhledem k tomu, že je počet iterací nastaven na hodnotu 1000 a podmínky ve větvích umožní vstup do podmíněných bloků v 1000–100=900, 1000–200=800 a 1000–300=700 iteracích, budou detekovány a následně přeloženy celkem čtyři stopy – vlastní programová smyčka a tři stopy obsahující jednotlivé větve:

--
-- LuaJIT: demonstrační příklad číslo 50.
--
-- Test JITu - programová smyčka se složitějším rozvětvením
--
 
 
 
-- deklarace a inicializace lokálních proměnných
local i
local x = 0
local y = 0
local z = 0
 
 
 
-- programová smyčka s rozvětvením
for i = 1,1000 do
    if i > 100 then
        x = x + 1
    end
    if i > 200 then
        y = y + 1
    end
    if i > 300 then
        z = z + 1
    end
end
 
print(x, y, z)
 
 
 
--
-- Finito.
--

Výstupní hodnoty tohoto demonstračního příkladu nám ve skutečnosti říkají, kolikrát se provedly jednotlivé podmíněné bloky:

900     800     700

Nejzajímavější informace o trasování získáme opět s využitím přepínače -jv:

[TRACE   1 test50.lua:18 loop]
[TRACE   2 (1/1) test50.lua:20 -> 1]
[TRACE   3 (2/1) test50.lua:23 -> 1]
[TRACE   4 (3/1) test50.lua:26 -> 1]

Z tohoto výpisu je patrné, že smyčka byla detekována na řádku 18, což je samozřejmě správné. Posléze byla detekována nová stopa začínající na řádku 20, což je taktéž korektní, protože právě zde začíná první podmíněný blok. Tato stopa je navázána na smyčku (viz hodnota 1 v závorce). Další stopa začíná na řádku 23 a poslední stopa pak na řádku 26. Povšimněte si, že detekce stop je provedena ve správném pořadí, protože nejprve je provedeno (minimálně) 56 iterací vnější programové smyčky, poté se detekuje vstup do prvního podmíněného bloku (ve 157 iteraci), následně vstup do druhého bloku (iterace číslo 257) a konečně v iteraci 357 do bloku třetího. Co se však stane, když se prohodí hodnoty 100, 200 a 300 v těchto blocích? Podle očekávání se stopy budou detekovat v opačném pořadí:

[TRACE   1 test50.lua:18 loop]
[TRACE   2 (1/3) test50.lua:26 -> 1]
[TRACE   3 (1/2) test50.lua:23 -> 1]
[TRACE   4 (1/1) test50.lua:20 -> 1]

Pro úplnost si uveďme upravený příklad:

--
-- LuaJIT: demonstrační příklad číslo 50 (úprava!!!).
--
-- Test JITu - programová smyčka se složitějším rozvětvením
--
 
 
 
-- deklarace a inicializace lokálních proměnných
local i
local x = 0
local y = 0
local z = 0
 
 
 
-- programová smyčka s rozvětvením
for i = 1,1000 do
    if i > 300 then
        x = x + 1
    end
    if i > 200 then
        y = y + 1
    end
    if i > 100 then
        z = z + 1
    end
end
 
print(x, y, z)
 
 
 
--
-- Finito.
--

3.2 Demonstrační příklad test51.lua – vnořené větve

Již tak nelehký úkol trasovacího JIT překladače můžeme udělat poněkud složitější tím, že podmínky budou vzájemně zanořeny. Tím se samozřejmě zcela změní struktury jednotlivých stop. Nejprve se však podívejme na zdrojový kód příkladu, v němž se nachází vnější počítaná programová smyčka a trojice vnořených větví:

--
-- LuaJIT: demonstrační příklad číslo 51.
--
-- Test JITu - programová smyčka se složitějším rozvětvením
--
 
 
 
-- deklarace a inicializace lokálních proměnných
local i
local x = 0
local y = 0
local z = 0
 
 
 
-- programová smyčka s rozvětvením
for i = 1,1000 do
    if i > 100 then
        x = x + 1
        if i > 200 then
            y = y + 1
            if i > 300 then
                z = z + 1
            end
        end
    end
end
 
print(x, y, z)
 
 
 
--
-- Finito.
--

Podobně jako tomu bylo v předchozím příkladu, i zde se na výstup vypíše počet vstupů do jednotlivých podmíněných bloků:

900     800     700

Výsledek trasování možná někoho překvapí svou jednoduchostí:

[TRACE   1 test51.lua:18 loop]
[TRACE   2 (1/1) test51.lua:20 -> 1]
[TRACE   3 (2/1) test51.lua:22 -> 1]
[TRACE   4 (3/1) test51.lua:24 -> 1]

Můžeme zde (opět) vidět detekci vnější programové smyčky, což pravděpodobně již nikoho nepřekvapí. Následně jsou detekovány tři další stopy na řádcích 20, 22 a 24. To je možná poněkud zvláštní, když si uvědomíme, že velmi podobný výsledek jsme získali z předchozího příkladu (odlišují se jen čísla řádků, to kvůli zápisu end v předchozím příkladu):

[TRACE   1 test50.lua:18 loop]
[TRACE   2 (1/1) test50.lua:20 -> 1]
[TRACE   3 (2/1) test50.lua:23 -> 1]
[TRACE   4 (3/1) test50.lua:26 -> 1]

Ve skutečnosti se však trasovací JIT zachoval správně, protože implementovaná logika je stejná :-) Toto je jedna ze základních vlastností všech trasovacích JITů – nezávisle na zdrojovém textu sledují, co se děje v runtime.

Zkusme si příklad ještě více upravit a vytvořit pořádné bludiště:

--
-- LuaJIT: demonstrační příklad číslo 51 (upraveno!!!).
--
-- Test JITu - programová smyčka se složitějším rozvětvením
--
 
 
 
-- deklarace a inicializace lokálních proměnných
local i
local x = 0
local y = 0
local z = 0
 
 
 
-- programová smyčka s rozvětvením
for i = 1,1000 do
    if i > 100 then
        x = x + 1
        if i > 200 then
            y = y + 1
            if i > 300 then
                z = z + 1
            end
        end
    end
    if i > 60 then
        x = x * 1.5
        if i > 400 then
            y = y * 1.5
            if i > 600 then
                z = z * 1.5
            end
        end
    end
end
 
print(x, y, z)
 
 
 
--
-- Finito.
--

Trasovací modul se nyní trošku zapotí, protože nalezne a JITuje hned sedm stop:

[TRACE   1 test51.lua:18 loop]
[TRACE   2 (1/2) test51.lua:29 -> 1]
[TRACE   3 (1/1) test51.lua:20 -> 1]
[TRACE   4 (3/1) test51.lua:22 -> 1]
[TRACE   5 (4/1) test51.lua:24 -> 1]
[TRACE   6 (5/2) test51.lua:31 -> 1]
[TRACE   7 (6/1) test51.lua:33 -> 1]

4. Trasovací JIT a volání funkcí

Již v předchozím textu jsme si naznačili, že se LuaJIT zaměřuje na hledání a optimalizaci jak počítaných i nepočítaných programových smyček, tak i často volaných funkcí. Zatímco se programové smyčky s velkým počtem iterací, které se překládají do nativního kódu, označují termínem hot loops, jsou často volané funkce nazývány hot calls. V dalším textu se zaměříme především na detekci hot calls, prozatím však vynecháme drobnou specialitku LuaJITu, kterou je detekce některých typů koncové rekurze (tail call). U volaných funkcí se při překladu obecně mohou provádět podobné typy optimalizací, o jakých jsme se zmiňovali v souvislosti s programovými smyčkami. Jedná se především o detekci mrtvého kódu a taktéž o inlining těl funkcí do volajícího kódu.

5. Zjištění, která funkce se má JITovat

Možná bude zajímavé zjistit, po kolika voláních se nějaká volaná funkce (resp. přesněji řečeno její volání – call) zařadí do skupiny hot calls. Již minule jsme si řekli, že programová smyčka či její část (větvení) je zařazena do skupiny hot loops ve chvíli, kdy je překročen počet padesáti šesti iterací (opakování těla smyčky). Tuto „magickou“ konstantu nalezneme v souboru lj_jit.h, konkrétně v následujícím makru:

/* Optimization parameters and their defaults. Length is a char in octal! */
#define JIT_PARAMDEF(_) \
  _(\010, maxtrace,     1000)   /* Max. # of traces in cache. */ \
  _(\011, maxrecord,    4000)   /* Max. # of recorded IR instructions. */ \
  _(\012, maxirconst,   500)    /* Max. # of IR constants of a trace. */ \
  _(\007, maxside,      100)    /* Max. # of side traces of a root trace. */ \
  _(\007, maxsnap,      500)    /* Max. # of snapshots for a trace. */ \
  \
  _(\007, hotloop,      56)    /* # of iter. to detect a hot loop/call. */ \
  _(\007, hotexit,      10)     /* # of taken exits to start a side trace. */ \
  _(\007, tryside,      4)      /* # of attempts to compile a side trace. */ \
  \
  _(\012, instunroll,   4)      /* Max. unroll for instable loops. */ \
  _(\012, loopunroll,   15)     /* Max. unroll for loop ops in side traces. */ \
  _(\012, callunroll,   3)      /* Max. unroll for recursive calls. */ \
  _(\011, recunroll,    2)      /* Min. unroll for true recursion. */ \
  \
  /* Size of each machine code area (in KBytes). */ \
  _(\011, sizemcode,    JIT_P_sizemcode_DEFAULT) \
  /* Max. total size of all machine code areas (in KBytes). */ \
  _(\010, maxmcode,     512) \
  /* End of list. */

Ve skutečnosti je však hodnota hotloop použita jak pro detekci hot loops, tak i pro detekci hot calls, což však bohužel není přímo ze zdrojových kódů ihned patrné. Vtip spočívá v tom, že pro každou stopu (trace) je nastavena počáteční hodnota počitadla na hodnotu hotloop*2 (hotloop*HOTCOUNT_LOOP). Pokud stopa obsahuje programovou smyčku, je po každé další iteraci snížena hodnota počitadla o dvojku (konkrétně o konstantu HOTCOUNT_LOOP, pokud se naopak volá nějaká funkce, je v této stopě snížena hodnota počitadla o jedničku (konkrétně o konstantu HOTCOUNT_CALL). Ve chvíli, kdy hodnota počitadla dosáhne nuly, byla detekována buď hot loop nebo hot call a je možné provést JIT překlad. Zmíněné konstanty HOTCOUNT_LOOPHOTCOUNT_CALL nalezneme ve zdrojovém souboru lj_dispatch.h:

/* Hotcount decrements. */
#define HOTCOUNT_LOOP           2
#define HOTCOUNT_CALL           1

Inicializaci počitadla hledejte v souboru lj_trace.c:

lj_trace_hot:
 
  /* Reset hotcount. */
  hotcount_set(J2GG(J), pc, J->param[JIT_P_hotloop]*HOTCOUNT_LOOP);

Samotný programový kód, v němž se počitadla snižují a testují na nulu, je již naprogramován v assembleru, podobně jako i další části LuaJITu. Jen pro zajímavost se podívejme, jak vypadají makra provádějící tuto činnost v assemblerech pro procesory s architekturou x86, MIPS a ARM (32bit):

vm_x86.dasc:

|// Decrement hashed hotcount and trigger trace recorder if zero.
|.macro hotloop, reg
|  mov reg, PC
|  shr reg, 1
|  and reg, HOTCOUNT_PCMASK
|  sub word [DISPATCH+reg+GG_DISP2HOT], HOTCOUNT_LOOP
|  jb ->vm_hotloop
|.endmacro
|
|.macro hotcall, reg
|  mov reg, PC
|  shr reg, 1
|  and reg, HOTCOUNT_PCMASK
|  sub word [DISPATCH+reg+GG_DISP2HOT], HOTCOUNT_CALL
|  jb ->vm_hotcall
|.endmacro

vm_mips.dasc:

|.macro hotloop
|  hotcheck HOTCOUNT_LOOP, ->vm_hotloop
|.endmacro
|
|.macro hotcall
|  hotcheck HOTCOUNT_CALL, ->vm_hotcall
|.endmacro

vm_arm.dasc:

|.macro hotloop
|  hotcheck HOTCOUNT_LOOP
|  blo ->vm_hotloop
|.endmacro
|
|.macro hotcall
|  hotcheck HOTCOUNT_CALL
|  blo ->vm_hotcall
|.endmacro

Aby byla celá situace ještě zajímavější, byl pro potřeby LuaJITu vyvinut i vlastní assembler nazvaný Dynamic Assembler. Podrobnější informace o tomto nástroji lze nalézt na adrese http://luajit.org/dynasm.htmlhttp://corsix.github.io/dynasm-doc/tutorial.html.

6. Demonstrační příklady

Podívejme se nyní na dvojici demonstračních příkladů, na nichž si ukážeme základní vlastnosti detekce hot calls. Ani jeden z obou příkladů popsaných v navazujících dvou podkapitolách není příliš složitý, protože se budeme snažit izolovat jen ty části LuaJITu, které se skutečně hot calls týkají.

6.1 Demonstrační příklad test52.lua – opakované volání funkce

Dnešní pátý demonstrační příklad nazvaný test52.lua je sice velmi dlouhý, ovšem při podrobném pohledu značně primitivní :-). Je zde deklarována funkce pojmenovaná adder, v níž se zvyšují hodnoty proměnných x a y (jedná se o lokální proměnné v rámci celého modulu, takže k nim má funkce samozřejmě přístup). Následně je přesně 112× funkce adder volána. Proč zrovna 112×? Jedná se o dvojnásobek hodnoty hotloop, přičemž z předchozí kapitoly víme, že právě hodnota hotloop*2 je použita při detekci často volaných funkcí. Pokud se pokusíte jedno volání funkce adder odstranit, neprovede se její JITování (což je velmi snadné odzkoušet):

--
-- LuaJIT: demonstrační příklad číslo 52.
--
-- Test JITu - volání funkce
--
 
 
 
-- deklarace a inicializace lokálních proměnných
local x = 0
local y = 0
 
 
 
-- funkce, která se bude JITovat
function adder()
    x = x + 1
    y = y + 1
end
 
 
 
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
adder()
 
 
 
print(x,y)
 
 
 
--
-- Finito.
--

Výsledek běhu tohoto příkladu není příliš překvapující, ovšem dovoluje nám otestovat, kolikrát se funkce adder skutečně volala:

112     112

Trasovací modul LuaJITu nakonec při posledním volání funkce adder uznal, že je tato funkce vhodná pro JITování, takže po spuštění výše uvedeného příkladu s přepínačem -jv dostaneme následující zprávu:

[TRACE   1 test52.lua:16 return]

6.2 Demonstrační příklad test53.lua – opakované volání funkce z programové smyčky

Šestý a současně i dnešní poslední demonstrační příklad taktéž deklaruje funkci adder, která se chová podobně jako v předchozím příkladu. Tato funkce je ovšem volána v trojici programových smyček. V první smyčce se provede prvních padesát volání, v další smyčce dalších padesát volání a konečně ve smyčce třetí se funkce zavolá po 101 až 150. Proč je příklad organizován takovým způsobem? Jde nám o to zabránit LuaJITu v detekci smyček s velkým počtem iterací, přičemž hodnota 50 leží pod hraniční hodnotou hotcall=56, takže smyčky samotné JITovány nebudou, ale funkce adder nakonec ano (samozřejmě až v poslední smyčce):

--
-- LuaJIT: demonstrační příklad číslo 53.
--
-- Test JITu - volání funkce
--
 
 
 
-- deklarace a inicializace lokálních proměnných
local i
local x = 0
local y = 0
 
 
 
-- funkce, která se bude JITovat
function adder()
    x = x + 1
    y = y + 1
end
 
 
 
-- programová smyčka, která se neJITuje
for i = 1,50 do
    adder()
end
print("1")
 
-- programová smyčka, která se neJITuje
for i = 1,50 do
    adder()
end
print("2")
 
-- programová smyčka, která se neJITuje
for i = 1,50 do
    adder()
end
print("3")
 
print(x,y)
 
 
 
--
-- Finito.
--

Výsledek běhu příkladu:

1
2
3
150     150

Z následujícího řádku je patrné, že trasovací modul LuaJITu skutečně našel pouze jediné místo, které se bude JITovat, a tím je funkce adder:

[TRACE   1 test53.lua:17 return]

Pokud tento demonstrační příklad nepatrně upravíme takovým způsobem, že se namísto padesáti iterací provede v každé smyčce iterací 150, bude již výsledek mnohem zajímavější, protože se budou překládat jak všechny smyčky, tak i funkce (která se navíc dostane do množiny hot calls již ve smyčce první a nikoli poslední):

[TRACE   1 test53.lua:25 loop]
1
[TRACE   2 test53.lua:17 return]
[TRACE   3 test53.lua:31 loop]
2
[TRACE   4 test53.lua:37 loop]
3
450     450

Upravený zdrojový kód:

bitcoin_skoleni

--
-- LuaJIT: demonstrační příklad číslo 53 (úprava!!!).
--
-- Test JITu - volání funkce
--
 
 
 
-- deklarace a inicializace lokálních proměnných
local i
local x = 0
local y = 0
 
 
 
-- funkce, která se bude JITovat
function adder()
    x = x + 1
    y = y + 1
end
 
 
 
-- programová smyčka, která se JITuje
for i = 1,150 do
    adder()
end
print("1")
 
-- programová smyčka, která se JITuje
for i = 1,150 do
    adder()
end
print("2")
 
-- programová smyčka, která se JITuje
for i = 1,150 do
    adder()
end
print("3")
 
print(x,y)
 
 
 
--
-- Finito.
--

7. Zdrojové kódy všech šesti dnešních demonstračních příkladů

Všech šest dnes použitých demonstračních příkladů bylo, jak je tomu ostatně v tomto seriálu již dlouhodobějším zvykem, uloženo do Git (přesněji řečeno do GitHub) repositáře umístěného na adrese https://github.com/tisnik/luajit-examples.

Následuje tabulka obsahující odkazy na poslední verze dnešních příkladů i upraveného souboru Makefile, který obsahuje mj. i všechny příkazy pro spuštění příkladů a vygenerování všech potřebných výstupních souborů (trasovací informace apod.):

8. Literatura

  1. Bolz, Cuni, Fijalkowski, Rigo:
    „Tracing the Meta-Level: PyPy's Tracing JIT Compiler“
  2. Vasanth Bala, Evelyn Duesterwald, Sanjeev Banerjia:
    „Dynamo: A Transparent Dynamic Optimization System“
  3. Bolz, Cuni, Fijalkowski, Leuschel, Pedroni, Rigo: „Allocation removal by partial evaluation in a tracing JIT“
  4. Bolz:
    „Automatic JIT Compiler Generation with Runtime Partial Evaluation“
  5. Bolz, Kuhn, Lienhard, Matsakis, Nierstrasz, Renggli, Rigo and T. Verwaest:
    „Back to the Future in One Week – Implementing a Smalltalk VM in PyPy“
    pages 123–139. 2008.
  6. Bolz and Rigo:
    „How to not write a virtual machine“
    In Proceedings of the 3rd Workshop on Dynamic Languages and Applications (DYLA), 2007
  7. Bruni, Verwaest:
    „PyGirl: generating Whole-System VMs from High-Level prototypes using PyPy“
    In Tools, accepted for publication, 2009.
  8. Sullivan, Bruening, Baron, Garnett and Amarasinghe:
    „Dynamic native optimization of interpreters“
    In Proceedings of the 2003 Workshop on Interpreters,
    Virtual Machines and Emulators pages 50–57, San Diego, California, 2003. ACM.

9. Odkazy na Internetu

  1. Dynamic Assembler
    http://luajit.org/dynasm.html
  2. The Unofficial DynASM Documentation: Introduction
    http://corsix.github.io/dynasm-doc/index.html
  3. Have tracing JIT compilers won?
    http://lambda-the-ultimate.org/node/3851
  4. Tracing just-in-time compilation
    http://en.wikipedia.org/wi­ki/Tracing_just-in-time_compilation
  5. How does LuaJIT's trace compiler work?
    http://www.freelists.org/pos­t/luajit/How-does-LuaJITs-trace-compiler-work,1
  6. How does LuaJIT's trace compiler work?
    http://stackoverflow.com/qu­estions/20266523/how-does-luajits-trace-compiler-work
  7. TraceMonkey
    https://wiki.mozilla.org/Ja­vaScript:TraceMonkey
  8. TraceMonkey
    http://brendaneich.com/2008/08/tra­cemonkey-javascript-lightspeed/
  9. improving JavaScript performance with JägerMonkey
    http://hacks.mozilla.org/2010/03/im­proving-javascript-performance-with-jagermonkey/
  10. Wikipedia: Mezijazyk
    http://cs.wikipedia.org/wi­ki/Mezijazyk
  11. The LuaJIT Project
    http://luajit.org/index.html
  12. LuaJIT FAQ
    http://luajit.org/faq.html
  13. LuaJIT Performance Comparison
    http://luajit.org/performance.html
  14. LuaJIT 2.0 intellectual property disclosure and research opportunities
    http://article.gmane.org/gma­ne.comp.lang.lua.general/58908
  15. LuaJIT Wiki
    http://wiki.luajit.org/Home
  16. LuaJIT 2.0 Bytecode Instructions
    http://wiki.luajit.org/Bytecode-2.0
  17. Programming in Lua 9.1 – Coroutine Basics,
    http://www.lua.org/pil/9.1.html
  18. Programming in Lua (first edition)
    http://www.lua.org/pil/contents.html
  19. Programming in Lua: 6 – More about Functions
    http://www.lua.org/pil/6.html
  20. Lua Lanes
    http://kotisivu.dnainternet­.net/askok/bin/lanes/
  21. Programming in Lua: 6.1 – Closures
    http://www.lua.org/pil/6.1.html
  22. Programming in Lua: 9.1 – Coroutine Basics
    http://www.lua.org/pil/9.1.html
  23. Programming in Lua: Numeric for
    http://www.lua.org/pil/4.3.4.html
  24. Programming in Lua: break and return
    http://www.lua.org/pil/4.4.html
  25. Programming in Lua: Tables
    http://www.lua.org/pil/2.5.html
  26. Programming in Lua: Table Constructors
    http://www.lua.org/pil/3.6.html
  27. Programovací jazyk Lua
    http://palmknihy.cz/web/kni­ha/programovaci-jazyk-lua-12651.htm
  28. Lua: Tables Tutorial
    http://lua-users.org/wiki/TablesTutorial
  29. Lua: Control Structure Tutorial
    http://lua-users.org/wiki/ControlStruc­tureTutorial
  30. Lua Types Tutorial
    http://lua-users.org/wiki/LuaTypesTutorial
  31. Goto Statement in Lua
    http://lua-users.org/wiki/GotoStatement
  32. Lua 5.2 sources
    http://www.lua.org/source/5.2/
  33. Lua 5.2 sources – lopcodes.h
    http://www.lua.org/source/5­.2/lopcodes.h.html
  34. Lua 5.2 sources – lopcodes.c
    http://www.lua.org/source/5­.2/lopcodes.c.html
  35. Lambda the Ultimate: Coroutines in Lua,
    http://lambda-the-ultimate.org/node/438
  36. Coroutines Tutorial,
    http://lua-users.org/wiki/CoroutinesTutorial
  37. Lua Coroutines Versus Python Generators,
    http://lua-users.org/wiki/LuaCorouti­nesVersusPythonGenerators

Autor článku

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