A JIT fordítás ( angolul Just-in-Time , "pontosan a megfelelő időben"), a dinamikus fordítás ( angol dinamikus fordítás ) egy olyan technológia, amely a bájtkódot használó szoftverrendszerek teljesítményének növelésére szolgál a bájtkód gépi kódba vagy más formátumba való közvetlen fordításával. miközben a program fut. Így nagy végrehajtási sebesség érhető el az értelmezett bájtkódhoz [1] képest (összehasonlítható a lefordított nyelvekkel), a megnövekedett memóriafogyasztás (a fordítási eredmények tárolására) és a fordítási idő miatt. A JIT két korábbi ötletre épít a futási környezettel kapcsolatban:bájtkód-fordítás és dinamikus fordítás .
Mivel a JIT-fordítás valójában a dinamikus fordítás egyik formája, lehetővé teszi olyan technológiák használatát, mint az adaptív optimalizálás és a dinamikus újrafordítás . Emiatt a JIT-fordítás jobb teljesítményt nyújthat, mint a statikus fordítás. Az értelmezés és a JIT-fordítás különösen jól illeszkedik a dinamikus programozási nyelvekhez , míg a futási környezet kezeli a késői típusú kötéseket , és garantálja a futási biztonságot.
Az LLVM , GNU Lightning [2] , libJIT (a DotGNU projekt része ) és RPython (a PyPy projekt része ) projektek használhatók JIT tolmácsok létrehozására bármely szkriptnyelvhez.
A JIT összeállítás mind a teljes programra, mind annak egyes részeire alkalmazható. Egy szövegszerkesztő például menet közben is képes reguláris kifejezéseket fordítani a gyorsabb szövegkeresés érdekében. Az AOT fordítással ez nem lehetséges olyan esetekben, amikor az adatszolgáltatás a program végrehajtása során történik, és nem a fordításkor. A JIT-t a Java (JRE), a JavaScript és a .NET-keretrendszer megvalósításaiban használják, a Python - PyPy egyik megvalósításában . [3] A PHP , Ruby , Perl , Python és hasonlók létező legáltalánosabb értelmezői korlátozott vagy hiányos JIT-ekkel rendelkeznek.
A legtöbb JIT-megvalósítás szekvenciális felépítésű: először az alkalmazást futásidejű virtuálisgép-bytecode-ba (AOT-fordítás), majd a JIT közvetlenül gépi kódba fordítja le. Emiatt az alkalmazás indításakor többletidőt veszítenek, amit utólag a gyorsabb működés kompenzál.
Az olyan nyelveken, mint a Java , PHP , C# , Lua , Perl , GNU CLISP , a forráskódot a bájtkódnak nevezett köztes reprezentációk egyikére fordítják . A bájtkód nem egy adott processzor gépi kódja, és különböző számítógép-architektúrákra portolható, és pontosan ugyanúgy végrehajtható. A bájtkódot a virtuális gép értelmezi (végrehajtja) . A JIT néhány szektorból bájtkódot olvas be (ritkán az összesből egyszerre), és gépi kódba fordítja azokat. Ez a szektor lehet fájl, függvény vagy bármilyen kódrészlet. A lefordított kód gyorsítótárazható, majd újrafordítás nélkül újra felhasználható.
A dinamikusan lefordított környezet olyan környezet, amelyben a fordítót egy alkalmazás futási időben hívhatja. Például a Common Lisp legtöbb implementációja tartalmaz egy függvényt compile, amely futás közben létrehozhat egy függvényt; Pythonban ez egy függvény eval. Ez kényelmes a programozó számára, mivel tudja szabályozni, hogy a kód mely részei legyenek ténylegesen lefordítva. Ezzel a technikával dinamikusan generált kódot is le lehet fordítani, ami bizonyos esetekben még jobb teljesítményt is eredményez, mint a statikusan lefordított kódban történő megvalósítás. Érdemes azonban emlékezni arra, hogy az ilyen funkciók veszélyesek lehetnek, különösen akkor, ha nem megbízható forrásból továbbítják az adatokat. [négy]
A JIT használatának fő célja a statikus fordítás teljesítményének elérése és meghaladása a dinamikus fordítás előnyeinek megőrzése mellett:
A JIT általában hatékonyabb, mint a kódértelmezés. Ezenkívül bizonyos esetekben a JIT jobb teljesítményt tud felmutatni a statikus fordításhoz képest a csak futásidőben lehetséges optimalizálás miatt:
A JIT fordító indításakor előforduló késések tipikus oka a környezet betöltésének és az alkalmazás natív kódra való fordításának költsége. Általánosságban elmondható, hogy minél jobban és minél több optimalizálást hajt végre a JIT, annál hosszabb lesz a késleltetés. Ezért a JIT fejlesztőknek kompromisszumot kell találniuk a generált kód minősége és az indítási idő között. Gyakran azonban kiderül, hogy a fordítási folyamat szűk keresztmetszetét nem maga a fordítási folyamat jelenti, hanem az I / O rendszer késése (például a Java Virtual Machine (JVM) rt.jar mérete 40 MB , és a metaadatok keresése benne elég sok időt vesz igénybe).
Egy másik optimalizálási eszköz az alkalmazásnak csak a leggyakrabban használt részeit fordítja le. Ezt a megközelítést a PyPy és a Sun Microsystems HotSpot Java Virtual Machine programja valósítja meg .
Heurisztikaként az alkalmazás szakasz indítási száma, bájtkód mérete vagy ciklusérzékelő használható.
Néha nehéz megtalálni a megfelelő kompromisszumot. Például a Sun Java virtuális gépének két üzemmódja van: kliens és szerver. Kliens módban minimális a fordítások és optimalizálások száma a gyorsabb indítás érdekében, míg szerver módban a maximális teljesítmény érhető el, de emiatt megnő az indítási idő.
Egy másik technika, az úgynevezett pre-JIT, még a futás előtt lefordítja a kódot. Ennek a technikának az előnye a rövidebb indítási idő, hátránya pedig a lefordított kód rossz minősége a futásidejű JIT-hez képest.
A legelső JIT implementáció a McCarthy által 1960 -ban írt LISP-nek tulajdonítható [5] . A szimbolikus kifejezések rekurzív függvényei és azok gépi számítása című könyvében , I. részben , megemlíti azokat a függvényeket, amelyeket futás közben fordítanak le, így nincs szükség arra, hogy a fordító munkáját lyukkártyákra írják ki .
Egy másik korai utalás a JIT-re Ken Thompsonnak tulajdonítható , aki 1968 -ban úttörő szerepet játszott a reguláris kifejezések használatában a QED szövegszerkesztőben részkarakterláncok keresésére . Az algoritmus felgyorsítása érdekében a Thompson reguláris kifejezések fordítását valósította meg az IBM 7094 gépi kódra .
A lefordított kód megszerzésének módszerét Mitchell javasolta 1970 -ben , amikor megvalósította az LC 2 kísérleti nyelvet . [6] [7]
Smalltalk (1983) a JIT technológia úttörője volt. A natív kódra való fordítást igény szerint végrehajtottuk, és gyorsítótárban tároltuk későbbi használatra. Amikor a memória elfogy, a rendszer eltávolíthatja a gyorsítótárazott kód egy részét a RAM-ból, és visszaállíthatja, amikor újra szükség van rá. Az Self programozási nyelv egy ideig a Smalltalk leggyorsabb megvalósítása volt, és csak kétszer olyan lassú, mint a C , mivel teljesen objektum-orientált.
Self-et a Sun elhagyta, de a kutatás a Java nyelven belül folytatódott. A "Just-in-time compilation" kifejezést a "Just in Time" szakkifejezésből kölcsönözték, és James Gosling népszerűsítette , aki 1993-ban használta ezt a kifejezést. [8] A JIT ma már a Java virtuális gép szinte minden megvalósításában használatos. .
Szintén nagy érdeklődésre tarthat számot Michael Franz 1994-ben az ETH Egyetemen (Svájc, Zürich) megvédett „Dinamikus kódgenerálás – a hordozható szoftver kulcsa” [9] szakdolgozata, valamint az általa a dinamikus kódgenerálásra implementált Juice rendszer [10] . egy hordozható szemantikai fából az Oberon nyelvhez . A Juice rendszert bővítményként kínálták az internetes böngészőkhöz.
Mivel a JIT futtatható kódot állít össze adatokból, felmerül a biztonság és az esetleges sebezhetőség kérdése.
A JIT fordítás magában foglalja a forráskód vagy bájtkód gépi kódba fordítását és végrehajtását. Általános szabály, hogy az eredményt a rendszer a memóriába írja és azonnal végrehajtja anélkül, hogy közbenső lemezre mentené vagy külön programként hívná meg. A modern architektúrákban a biztonság javítása érdekében a memória tetszőleges szakaszai nem hajthatók végre gépi kódként ( NX bit ). A helyes indítás érdekében a memóriarégiókat előzetesen végrehajthatóként kell megjelölni, míg a nagyobb biztonság érdekében a végrehajtási jelzőt csak az írási engedély jelzőjének eltávolítása után lehet beállítani (W^X védelmi séma) [11] .