# JIT vs. Interpreter: konkreter Unterschied?



## Deprecated (2. Aug 2008)

Hallo,

mag eine ziemlich bekloppte Frage sein, aber irgendwie konnte mir bisher niemand eine konkrete Antwort geben:

Worin besteht eigentlich der genaue Unterschied zwischen der Arbeitsweise eine JIT-Compilers und eines Interpreters?

Ich meine, beide haben doch die Aufgabe, zur Laufzeit eine Bytecodeanweisung "irgendwie" auf Maschinencode abzubilden und diese Maschinencodeanweisung(en) dann zur Ausführung zu bringen. Soweit korrekt?

Einen Interpreter stelle ich mir wie folgt vor: Beim Starten eines Programms, lese die erste Bytecode-Anweisung, bilde diese "irgendwie" auf Maschinensprache ab, lasse den Rechner die entsprechende(n) Maschineninstruktionen ausführen, gehe dann zur nächsten Bytecodeanweisung und mache alles von vorne.

Und irgendwie checke ich jetzt nicht, was der JIT konkret anders macht. Auch der muss ja während der Laufzeit irgendwie den Bytecode auf Maschinencode abbilden. 

Besteht der Unterschied etwa darin, wie die Abbildung "Bytecode -> Maschinencode" genau gemacht wird (aufwändiges Umrechnen vs. Nachschlagen in einer Art Hashmap, o.ä.) ?
Oder ist der Unterschied, dass der JIT zum Teil den einmal abgebildeten Maschinencode speichert, und den Bytecode dann nicht mehr neu abzubilden braucht, sondern einfach die gespeicherten Maschineninstruktionen ausführt falls die Stelle erneut durchlaufen wird? Das würde ja Sinn machen, wenn das Abbilden (d.h. das Berechnen und Schreiben des passenden Maschinencodes) aufwändig ist und viel Rechenzeit benötigt...

Teilweise wird ja auch behauptet, dass ein JIT beim Programmstart immer den gesamten Bytecode nach Maschinencode kompiliert und erst dann das Ausführen dieses Maschinencodes veranlasst, so dass das Laden sehr lange dauert, das Ausführen aber schnell geht weil die ganze Zeit Maschinencode läuft. Das glaube ich aber nicht so richtig, denn das wäre für mich kein JIT-Verhalten mehr.

Ein weiterer Unterschied, der angegeben wird, ist dass ein Interpreter immer bloß eine einzige Bytecodeanweisung abbildet und diese dann ausführt, während der JIT zum Teil auch mehrere Anweisungen hintereinander abbildet und erst dann ausführt. Das mag zwar stimmen, ist doch aber kein Argument warum ein JIT schneller ist, denn beide Verfahren würden doch insgesamt die gleiche Zeit beanspruchen...


Die Frage wäre jetzt: was stimmt davon, was nicht? Oder habe ich den konkreten Unterschied etwa gar nicht erfasst?
Über ein paar Expertenmeinungen wäre ich sehr dankbar...


----------



## Gelöschtes Mitglied 6946 (2. Aug 2008)

Ich bin kein Experte, aber soweit ich weiß, übersetzt ein Interpreter das Stückchen Code immer wieder, während ein JIT den einmal compilierten Code speichert und deshalb letzten Endes schneller arbeitet. Oder so irgendwie...


----------



## Guest (2. Aug 2008)

http://de.wikipedia.org/wiki/Hotspot-Optimierung


----------



## Guest (2. Aug 2008)

Danke für die Antworten, ich habe noch folgendes gefunden:



> The performance improvement over interpreters originates from caching the results of translating blocks of code, and not simply reevaluating each line or operand each time it is met."



Derzeit habe ich also den Eindruck, als ob Interpreter und JIT-Compiler eigentlich grundsätzlich gleich arbeiten.
Beide analysieren den Bytecode und berechnen den zu einer Bytecodeanweisung äquivalenten Maschinencode, welcher dann ausgeführt wird.
Einziger grundlegender Unterschied scheint tatsächlich das Cachen durch den JIT zu sein: der JIT speichert den nach der Analyse berechneten äquivalenten Maschinencode (oder ersetzt sogar den Bytecode durch den Maschinencode), so dass die zeitaufwendige Bytecodeanalyse und Maschinencode-Berechnung nicht erneut durchgeführt werden muss, wenn die Stelle wieder aufgerufen wird. Ein Interpreter würde also nicht cachen, sondern bei jeder Ausführung einer Bytecodeanweisung diese neu analysieren und jedes Mal den gleichen äquivalenten Maschinencode berechnen.

Aber sollte das tatsächlich der einzige grundlegende Unterschied sein?
Ich werde das Gefühl nicht los, dass es noch einen weiteren Unterschied gibt, der irgendwie etwas damit zu tun hat, wie das Übersetzen/Berechnen/Abbilden des Bytecodes in den Maschinencode läuft.

Weitere Anmerkungen sind also willkommen...


----------



## didjitalist (2. Aug 2008)

Der Interpreter erzeugt keinen Maschinencode, der Interpreter führt den Bytecode aus. Du schreibst in deinem Programm also beispielsweise eine for-Schleife. Der Java compiler erzeugt daraus entsprechenden Bytecode. Der Interpreter ruft seine Methoden auf, die für die Abwicklung einer Schleife notwendig sind. Der Interpreter ist also ein in Maschinencode geschriebenes Programm, das den Bytecode ausführen kann.

Ein JIT compiler ist genau das, was der Name sagt: ein compiler. Er erzeugt also aus dem Bytecode nativen Maschinencode, der direkt auf dem Zielsystem ausgeführt werden kann. Anstatt den gesamten Bytecode zu interpretieren, kompiliert so ein JIT compiler bestimmte Anteile des codes, die fortan nativ auf dem Zielsystem ausgeführt werden können. Da dieses Kompilieren erst während der Laufzeit geschieht, nennt man sie just in time compiler.
Was genau kompiliert wird und was nicht, ist allerdings eine Wissenschaft für sich.


----------



## Deprecated (2. Aug 2008)

didjitalist hat gesagt.:
			
		

> Der Interpreter erzeugt keinen Maschinencode, der Interpreter führt den Bytecode aus.


Ok, der Interpreter erzeugt den auszuführenden nativen Code nicht, sondern hat den auszuführenden Code bereits integriert. D.h. anstatt den Code zu generieren, wird in einem Interpreter zur passenden enthaltenen Maschinenanweisung gesprungen und diese dann ausgeführt.

Was ich jetzt nicht raffe: ist die Maschinenanweisung, die ein Interpreter letztendlich aufsucht und ausführt, nicht die selbe Maschinenanweisung, die ein JIT-Compiler generieren und ausführen würde?
Anders gesagt: warum ist der Code, den der JIT erzeugt, besser (d.h. performativer) als der im Interpreter benutzte Code? Dauert das generieren (d.h. berechnen und schreiben) nicht sogar länger, als einfach nur zu einer bestimmten Adresse zu springen?

Oder ist der JIT nur schneller, weil er mehrere Bytecodeanweisungen zusammen betrachten, und dann irgendwelche Optimierungen durchführen kann? Ich denke nicht, denn das würde ja bedeuten, dass es a) in zusammenhängenden Bytecodesequenzen so etwas wie Redundanzen gibt, die man wegoptimieren kann, und b) man genauso gut einen Interpreter erstellen könnte, der mehrere Anweisungen zusammen betrachtet und dann halt zu einer intergrierten, entsprechend optimierten Maschinenanweisung springt und diese aufruft...

Das Argument der Optimierungsmöglichkeit scheint also noch nicht ausreichend zu sein.

Ein anderer Vorteil der mir einfällt ist, dass der JIT ja eine zusammenhängende Anweisungssequenz erzeugt, die die Maschine ohne Sprünge zurück zum Interpreter ausführen kann. Der Interpreter muss nämlich nach jeder ausgeführten Maschinenanweisung zurück gehen, springt also ständig zwischen bestimmten Adressen im Speicher hin und her. Das wäre beim JIT nicht nötig und schont Zeit, aber ist das wirklich der Grund?

Was macht den vom JIT erstellten Code also schneller als den Code aus dem Interpreter??


----------



## Wildcard (3. Aug 2008)

Was ist schneller? Ein Programm das eine Sequenz einließt und abarbeitet um zu Ergebnis X zu kommen, oder ein Programm das Ergebnis X berechnet?
Sehr vereinfacht:
Stell dir vor du lässt den Benutzer eine einfach mathematische Formel eingeben a+b
Du erkennst die beiden Summanden und die Operation. Du rüfst deine add(a,b) Methode auf und gibst das Ergebnis aus.
Die andere Variante, dein Programm besteht aus der Anweisung System.out.println(a+b);
Der JIT Compiler erstellt sozusagen kleine Unterprogramme die direkt ausgeführt werden, anstatt als Automat auf einer Befehlssequenz zu arbeiten.


----------



## Deprecated (3. Aug 2008)

Sorry, ich verstehe leider weder den Unterschied den du aufzeigen möchtest, noch dein Beispiel.

Ich fasse nochmal zusammen was ich nicht kapiere:

Der Interpreter kommt zu einer bestimmten Methode, die ausgeführt werden soll. Er analysiert Anweisung für Anweisung, springt jeweils zu bestimmten Maschineninstruktionen, und veranlasst das Ausführen dieser Instruktionen durch den Prozessor, so dass am Ende das gewünschte Resultat der Methode vorliegt.

Der JIT liefert genau das gleiche Resultat. Auch er analysiert den Bytecode der Methode (allerdings nicht unbedingt Zeile für Zeile unabhängig, ist das der Unterschied?). Er springt allerdings nicht zu bereits vorhandenen Maschineninstruktionen, sondern generiert diese zur Laufzeit, bevor er das Ausführen durch den Prozessor veranlasst.

Wie gesagt liefert er am Ende das gleiche Resultat wie der Interpreter. Daraus schließe ich, dass er im Grunde auch die gleichen Maschineninstruktionen generiert und ausgeführt hat, zu denen der Interpreter zwecks Ausführung hingesprungen ist. Stimmt das??

Wenn ich jetzt annehme dass das stimmt, dann stellt sich die Frage: wenn das Resultat der Methode und deshalb auch die hierfür letztendlich ausgeführten Maschineninstruktionen bei Interpreter und JIT *gleich* sind, wieso ist der JIT dann schneller?  [/b]


----------



## Guest (3. Aug 2008)

Deprecated hat gesagt.:
			
		

> Wie gesagt liefert er am Ende das gleiche Resultat wie der Interpreter. Daraus schließe ich, dass er im Grunde auch die gleichen Maschineninstruktionen generiert und ausgeführt hat, zu denen der Interpreter zwecks Ausführung hingesprungen ist. Stimmt das??



Kurze Antwort: nein.

Versuche dir vielleicht nicht alles selbst erklären zu wollen, sondern lies dich einfach mal in die Materie ein.


----------



## Saxony (3. Aug 2008)

Hmm,

ich verstehe nicht, was du da nicht verstehst!

Bei Wikipedia wird doch alles haarklein beschrieben:

JIT
Interpreter
HotSpot Optimierung

Das lesen und mehr wissen. 
Noch mehr dazu steht auch in der englischen Wikipedia!

bye Saxony


----------



## Deprecated (3. Aug 2008)

Saxony hat gesagt.:
			
		

> Bei Wikipedia wird doch alles haarklein beschrieben:


Allzu haarklein finde ich das ehrlich gesagt nicht, habe das auch schon gelesen bevor ich hier gepostet habe. 

Um es nochmal deutlich zu machen: ich verstehe den Vorteil eines AOT Compilers gegenüber einem Interpreter, nämlich die Nichtnotwendigkeit, den Bytecode zur Laufzeit zu analysieren.
Was ich nicht raffe ist der Vorteil des JIT Compilers gegenüber dem Interpreter, denn der JIT muss ja genauso zur Laufzeit den Bytecode analysieren, um daraus den Maschinencode generieren zu können.

Was der Interpreter letztendlich ausführt, ist ja Maschinencode. Das Resultat der JIT Kompilierung ist ebenfalls Maschinencode.  
Jetzt bin ich ja schon ein bisschen schlauer, denn oben wurde ja gesagt, dass der vom Interpreter ausgeführte Maschinencode nicht der gleiche ist, wie der vom JIT generierte Maschinencode, sondern aus irgendeinem Grund schneller läuft. 




			
				Saxony hat gesagt.:
			
		

> ich verstehe nicht, was du da nicht verstehst!


Wenn es so einfach ist, dann frage ich mich, warum mir bisher niemand den einfachen Grund dafür nennen konnte, warum der vom JIT erzeugte Code besser (d.h. schneller) ist, als der vom Interpreter ausgeführte Code.

Den Bytecode zur Laufzeit analysieren müssen beide. Wikipedia geht dann imo etwas lax mit dem Begriff des Interpretierens um, da wird gesagt dass der Interpreter den Bytecode "einliest, analysiert und ausführt". Ist mir aber klar: was er am Ende ausführt ist äquivalenter Maschinencode, welcher das gewünschte Resultat bringt. Das gleiche Resultat wird aber auch durch den vom JIT generierten Maschinencode erreicht, dieser Code ist nur schneller.

Was also ist am Maschinencode des JIT anders als am Maschinencode des Interpreters?

Sorry dass ich da so hartnäckig bin...


----------



## didjitalist (3. Aug 2008)

Stell es dir ganz einfach vor. Es gibt im Java Programm folgende Methode:

```
public int foo( int a, int b )
{
   int z = 0;
   for( ;a < b; ++b )
     z *= b + a;
   return z;
}
```
Wenn der Interpreter auf diesen Code stößt, muss er u.U. ganz schön viel machen. Er muss ständig Variablen umkopieren, was er im virtuellen Speicher tun muss, er muss seine Methoden für Schleifenabwicklung aufrufen, er muss womöglich den Stack kopieren, er muss Methoden für Addition, Multiplikation usw. aufrufen.

Was der JIT jetzt tun kann ist, die gesamte Methode in nativen code zu kompilieren. Den code bunkert er sich irgendwo und jedesmal, wenn diese Methode aufgerufen wird, schlicht den nativen code zu rufen. Dadurch fällt der gesamte interne overhead weg.

Das Beispiel funktioniert so natürlich nicht, aber vielleicht hilft es, das ganze besser zu verstehen.


----------



## SchonWiederFred (3. Aug 2008)

Deprecated hat gesagt.:
			
		

> Was also ist am Maschinencode des JIT anders als am Maschinencode des Interpreters?


Wenn Du eine Methode 1.000.000 Mal aufrufst, dann muss der Interpreter 1.000.000 Mal den Bytecode in Maschinencode übersetzen, bevor er den Maschinencode ausführen kann. Der JIT übersetzt den Bytecode nur einziges Mal in Maschinencode, und dieser bleibt dann über die Methodenaufrufe hinweg erhalten. Du sparst Dir also 999.999 Übersetzungen.


----------



## Saxony (4. Aug 2008)

Hiho,

also ich habe mal ein Bild dazu gemalt:







Erklärung:

Interpreter:
Der Interpreter liest BytCode für ByteCode und führt diesen sofort innerhalb seiner eigenen Umgebung aus. D.h. es wird bei einem Interpreter kein eigenständiger Maschinencode erzeugt. Sondern die Anweisungen werden vom Interpreter selbst ausgeführt - somit wird eigentlich der Maschinencode des Interpreters ausgeführt. Der Interpreter interpretiert den ByteCode immer wieder und wieder.

JIT
Der JIT interpretiert ebenso ByteCode für ByteCode führt diesen aber im Gegensatz zum Interpreter nicht sofort aus.
Hier wird der ByteCode erst für die Zielplattform zur Laufzeit in "echten" MaschinenCode compiliert und dann auf der Zielplattform ausgeführt. Der JIT interpretiert, compiliert und führt code auch immer wieder und wieder aus - es findet keine Persistierung des MaschinenCodes im Speicher statt. Der Vorteil bis hierher im Vergleich zum Interpreter ist, dass hier nun Maschinencode auf der Zielplattform ausgeführt wird.
Hat der JIT noch zusätzlich eine eingebaute HotSpot Optimierung, dann werden zu optimierende Codeabschnitte im Speicher gehalten, um nicht jedes mal neu intepretiert und compiliert werden zu müssen.

Das wars dann auch schon! 

Zusammenfassung:

Viele von euch vorgebrachten Vorteile eines JITs sind in Wahrheit nur Vorteile, wenn der JIT eine HotSpot Optimierung integriert hat. Weil Code Persistierung und Code Optimierung macht ein JIT Compiler alleine nicht.
Ein JIT alleine macht im Endeffekt das gleiche wie der Interpreter nur, dass er den interpretierten Code noch in Maschinencode umcompiliert und dieser dann ausgeführt wird. Und das macht auch ein JIT immer und immer wieder!  Erst die HotSpot Integrierung bringt dann "etwas" Intelligenz mit ins Spiel.

bye Saxony


----------



## Saxony (4. Aug 2008)

SchonWiederFred hat gesagt.:
			
		

> Deprecated hat gesagt.:
> 
> 
> 
> ...



Was du hier darstellst gilt nur für JIT-Compiler, welche eine HotSpot Optimierung unterstützen. Ein JIT-Compiler alleine optimiert dies so nicht!

bye Saxony


----------



## Guest (4. Aug 2008)

Ohne die Erkennung von "Hotspots" macht ein JIT-Compiler aber keinen Sinn, da er einfach NIE compliert. Der JIT kompiliert eben nicht immer sofort alles, sondern nur die Teile die als HotSpot identifiziert wurden.

Zum Programmstart interpretiert die VM immer alles. Erst im Laufe der Zeit werden Hotspots erkannt und diese dann kompiliert.


----------



## Saxony (4. Aug 2008)

Anonymous hat gesagt.:
			
		

> Ohne die Erkennung von "Hotspots" macht ein JIT-Compiler aber keinen Sinn, da er einfach NIE compliert.



Aha interessant! Dann steht das Wort Compiler bei JITC also nur als Lückenfüller damit es besser aussieht?
Man muss genrell davon ausgehen, dass ein JITC ohne HotSpot läuft, sonst gäbe es keine JITCs! Dann hätte man es gleich JITCWHS (JITC with HotSpot) genannt.

Ich bin mal so frei und zitiere Wikipedia zur JVM:



			
				Wikipedia hat gesagt.:
			
		

> Um die Geschwindigkeit ("performance") der Programmausführung zu erhöhen, setzen die meisten Java-VMs sogenannte JIT-Compiler (JITC) ein, die unmittelbar während des Programmablaufs den Bytecode „Just In Time“ („Genau rechtzeitig“, oder „Gerade zur rechten Zeit") in Maschinencode übersetzen. Eine Weiterentwicklung dieses Ansatzes, der Hotspot-Optimizer von Sun, behebt weitgehend den Geschwindigkeitsnachteil der JVM, der hohe Speicherbedarf bleibt jedoch bestehen.



Dort steht klar und deutlich: Ein JITC alleine kompiliert auch ohne HS und HS ist eine Weiterentwicklung/Optimierung des JITC.



			
				Anonymous hat gesagt.:
			
		

> Der JIT kompiliert eben nicht immer sofort alles, sondern nur die Teile die als HotSpot identifiziert wurden.



Ja genau! Also ist ein Standalone JITC ohne HS ein normaler Interpreter? Ich dachte der JITC kompiliert auch ohne HS ByteCode in Maschinencode.



			
				Anonymous hat gesagt.:
			
		

> Zum Programmstart interpretiert die VM immer alles. Erst im Laufe der Zeit werden Hotspots erkannt und diese dann kompiliert.



Ja erst wird alles interpretiert und so gut wie alles in Maschinencode kompiliert. Erst mit Integration eines HotSpot Optimieres werden die zu kompilierenden Stellen ausgewählt und nicht mehr alles kompiliert.



			
				eng. Wikipedia hat gesagt.:
			
		

> One possible optimization, used by Sun's HotSpot Java Virtual Machine, is to combine interpretation and JIT compilation. The application code is initially interpreted, but the JVM monitors which sequences of bytecode are frequently executed and translates them to machine code for direct execution on the hardware. For bytecode which is executed only a few times, this saves the compilation time and reduces the initial latency



Es kann natürlich auch sein, dass die Leute, welche die Wikipedia Beiträge geschrieben haben, keine Ahnung hatten was sie tun. In diesem Fall nehme ich alles zurück und ermutige dich zum Abändern der deutschen und englischen Wikipedia in Bezug auf Interpreter, JITC, JVM und HotSpot Optimierung.

bye Saxony


----------



## Guest (4. Aug 2008)

Ich zitiere mal aus der Wikipedia zum Thema Hotspot-Optimierung:



> Damit schlägt der Übersetzungsvorgang selbst, neben der eigentlichen Laufzeit des Programms, in Form von CPU-Zyklen und längerer Laufzeit zu Buche. Da dieser (Compilier-)Vorgang allerdings nur adaptiv, d. h. bei einigen Methoden (so genannten Hotspots) angewandt wird, dieser Aufwand einmalig und insbesondere bei langlaufenden Serveranwendungen sehr kurz verglichen mit der Programm-Ausführungszeit ist, wird dieser Mehraufwand schnell durch die höhere Qualität des erzeugten Maschinencodes kompensiert.





> Zu Beginn der Laufzeit eines Java-Programms wird der gesamte Bytecode ausschließlich interpretiert. Das geschieht in einer Interpreterschleife, die Bytecode für Bytecode abarbeitet. Die Besonderheit von Hotspot-Compilern liegt nun darin, nur häufig benutzte Codeabschnitte – so genannte „Hotspots“ – sukzessive in Maschinencode zu übersetzen. Die Hotspots werden unter anderem auch lokal, d. h. innerhalb von Methoden entdeckt, die kleinste Compiliereinheit ist aber in jedem Fall die Methode. Da die Übersetzung nur bei besonders häufig genutzten oder langlaufenden Methoden und nicht mit dem gesamten Code geschieht, ist der Mehraufwand speziell bei langlaufenden Anwendungen vernachlässigbar.



Es ist natürlich denkbar, daß es einen JIT-Compiler gibt, der einfach alles zur Laufzeit kompiliert. Das hat dann aber nichts mit dem JIT-Compiler der JVM zu tun, um den es hier wohl geht.


----------



## Saxony (4. Aug 2008)

Hiho

Jeder JIT (ohne HS) kompilliert so gut wie alles zur Laufzeit!

Deine WikiZitate sagen ja, dass erst der HS die Kompilierung einschränkt auf "rechenintensiven" Code.
Ohne HS würde so gut wie alles zu Maschinencode was vorher in der Interpreterschleife interprtiert wurde.



> *Die Besonderheit von Hotspot-Compilern liegt nun darin, nur häufig benutzte Codeabschnitte  – so genannte „Hotspots“ – sukzessive in Maschinencode zu übersetzen.*


Dort steckt das Detail, welches JITC von JITC mit integriertem HS unterscheidet.

Ausserdem, wenn wir von JIT i.V.m. HS reden, reden wir immer von der SUN JVM, da HS eine Erfindung von SUN zum SUN JVM JITC ist.

Das der JVM JITC nun ein JITC mit HS ist die eine Sache.
Dennoch ist es falsch zu sagen ein JITC optimiert nach Verfahren blafasel die zu kompilierenden Stellen eines ByteCodes.

Richtig heißt es:
Der JITC mit integriertem HS optimiert blafasel...

bye Saxony


----------



## Guest (4. Aug 2008)

HotSpot wurde von der Firma Longview Technologies, LLC "erfunden", welche später von Sun gekauft wurde.

Ein JIT-Compiler macht erstmal nichts anderes als zur Laufzeit Quell- oder Byte-Code in Maschinencode zu übersetzen. Dabei können verschiedene Optimierungsverfahren eingesetzt werden, das hat nichts mit HotSpot zu tun.



> Dennoch ist es falsch zu sagen ein JITC optimiert nach Verfahren blafasel die zu kompilierenden Stellen eines ByteCodes.



Das ist also falsch (bzw. doch richtig).

Das besondere an der HotSpot-Technologie ist, diese Hotspots durch Profiling festzustellen und den JIT-Compiler "anzuwerfen", wenn so eine Stelle erkannt wird. Optimieren tut jedoch ausschließlich der JIT-Compiler.

Wie gesagt, wir können über einen theoretischen JIT-Compiler philosophieren, der immer alles kompiliert. Diesen gibt es so jedoch nicht in der JVM, sondern nur die spezielle Ausprägung als HotSpot-JIT-Compiler.


----------



## Saxony (5. Aug 2008)

@deprecated
So da weißte nun bescheid!  Oder ist noch eine Frage offen?

[edit]
Kapitel 3 der "Java HotSpot Performance Engine Architecture"

Dort steht nochmal alles für den JVM JITC. Unter anderem aber wieder auch, dass ein JITC alleine generell alles kompiliert:



> Just-in-time (JIT) compilers are essentially fast traditional compilers that translate the Java technology bytecodes into native machine code on the fly. A JIT running on the end user's machine actually executes the bytecodes and compiles each method the first time it is executed.



Kurz und knapp entnehme ich dem darauf folgendem Text:
Wir lassen den JIT mal alles kompilieren, da die Optimierung in NutzerZeit viel zu lange dauert.

Dann wird im Text ein Bogen geschlagen und die Notwendigkeit von HS beschrieben.
[/edit]

bye Saxony


----------



## Deprecated (7. Aug 2008)

Saxony hat gesagt.:
			
		

> So da weißte nun bescheid!  Oder ist noch eine Frage offen?


Leider ja. Trotzdem danke an alle, die geantwortet haben...



			
				didjitalist hat gesagt.:
			
		

> Stell es dir ganz einfach vor. Es gibt im Java Programm folgende Methode:
> Wenn der Interpreter auf diesen Code stößt, muss er u.U. ganz schön viel machen.
> Er muss ständig Variablen umkopieren, was er im virtuellen Speicher tun muss, er muss seine Methoden für Schleifenabwicklung aufrufen, er muss womöglich den Stack kopieren, er muss Methoden für Addition, Multiplikation usw. aufrufen.


Sehe ich alles ein. Aber warum muss das der JIT denn nicht? Wenn der native Code durch den JIT generiert wurde und nun durchlaufen wird, dann muss dort doch ebenfalls eine Schleifenabwicklung erfolgen, sowie generierte Methoden zur Addition bzw. Multiplikation durchlaufen werden...  



			
				Saxony hat gesagt.:
			
		

> Der Vorteil bis hierher im Vergleich zum Interpreter ist, dass hier nun Maschinencode auf der Zielplattform ausgeführt wird.


Hm, ist mir als Argument etwas zu oberflächlich, denn der Interpreter führt ja letztendlich ebenfalls Maschinencode auf der Zielplatform aus, der sogar den gleichen Effekt hat.


Vielleicht klappt es anhand eines ganz banalen Beispieles:


```
int add(int a, int b) {
     return a+b;
}
```

Jetzt stelle ich mir vor, dass der Interpreter auf diese Methode stößt.
Er liest die Rumpfzeile ein, stellt fest dass da die Inhalte zweier Speicheradressen addiert werden sollen und das Resultat zurückgegeben werden muss.
Er springt also zu einer Methode, die beide Integerwerte in Register kopiert. Anschließend springt er zu einer Additionsinstruktion, lässt also den Rechner die Addition durchführen. Dann geht er an eine Stelle, an der das Ergebnis aus einem der Register als Rückgabewert an eine bestimmte Speicherstelle kopiert wird.

Nun der JIT. Er stößt auf die Methode und generiert Maschinencode.
Zuerst erzeugt er Instruktionen, die die beiden Speicherinhalte in Register kopieren.
Dann hängt er eine Addition an. Danach erzeugt er eine Instruktion um das Ergebnis zurück in den Speicher zu kopieren.

Kurz gesagt: bei meiner Logik generiert der JIT die gleichen Maschineninstruktionen, die der Interpreter bereits enthält und ausführt. Mit dem Unterschied, dass der JIT-Code in einem Zug ausführbar ist, während beim Interpreter zeitaufwändige Sprünge von der einen Methode zur anderen die Ausführung unterbrechen.

Nun hat meine Logik ja irgendwo einen Haken. Die Frage ist nur wo. Sind es etwa wirklich nur die o.g. Sprünge zwischen den unterschiedlichen Methoden, die den Interpreter langsamer machen? Vielleicht kann mir jemand noch ein letztes Mal anhand dieses oder eines anderen Beispiels erklären, wo der Unterschied liegen würde.
Wenn ich es dann nicht raffe, gebe ich auf, versprochen ;-)


----------



## Saxony (8. Aug 2008)

Deprecated hat gesagt.:
			
		

> Saxony hat gesagt.:
> 
> 
> 
> ...



Naja das ist etwas falsch. Bei einem JITC wird (eigenständiger) MaschinenCode auf der Zielplattform ausgeführt. Bei einem Interpreter wird der interpretierte Code innerhalb des Interpreters ausgeführt.
Bei dem Interpreter wird nur InterpreterCode auf der Zielplattform ausgeführt, wohin gegen bei einem JITC der JITC *und* der on-the-fly erzeugte MaschinenCode ausgeführt werden.


```
for(int i = 0; i < 1000; i++) sysout(i);
```

Der Interpreter würde diese Schleife 1000 mal interpretieren.
D.h. hier liest der Interpreter den Schleifenkopf, üperprüft die Bedingung, verarbeitet den Schleifenrumpf und erhöht dann i - und das 1000 Mal.

Der JITC kompiliert die einmal in MaschinenCode und lässt diesen von der Zielplattform ausführen.
D.h. hier liest der JITC den Schleifenkopf und den zugehörigen Schleifenrumpf kompiliert den in MaschinenCode und reicht diesen dann weiter an das OS zur Ausführung.

Das nun bei der Verarbeitung von 1000 (Interpretier)Zyklen und - auf der anderen Seite - dem einmaligen Kompilieren und Ausführen von MaschinenCode ein klitzekleiner Zeitvorteil entsteht ist hierbei nun das Ergebnis.



			
				Deprecated hat gesagt.:
			
		

> Vielleicht klappt es anhand eines ganz banalen Beispieles:
> 
> 
> ```
> ...



Um es ganz einfach zu machen:

Der Interpreter macht nichts anderes, als diese Methode zu lesen und innerhalb seines eigenen Codes neu abzubilden.
Das ist ungefähr so als würdest du einen Funktionsparser schreiben. Das Gelesene wird in eigene Syntax transformiert.

Mal extrem vereinfacht dargestellt:


```
codezeile = bytecode.nextzeile();
if (codezeile.contains(+)) {
    
    operanden = codezeile.split(+);

    addiere(operanden[0], operanden[1]);
}
```

Nun zum JITC:
Dieser liest ebenfalls diese Methode, macht aber folgendes daraus (mal in ASM):


```
;  ohne Rücksicht auf Sprünge und Adressbereiche
mov ax, a
mov bx, b
add ax, bx
; das waren damals mal 7 CPU Zyklen ;)
```

und reicht diesen MaschinenCode an die Zielplattform zum Ausführen weiter.

Noch eins:


```
sysout("Hallo Welt!");
```

Interpreter:

```
codezeile = bytecode.nextzeile();
if (codezeile.contains(sysout)) {
    
   print(inhalt_von_sysout(codezeile));
}
```

JITC:

```
100 db 0a,0d,"Hallo Welt!",0a,0d,"$"
101 mov ah,9
102 mov dx,0100
103 int 21
104 mov ah,0
105 int 21
```

Keine Ahnung wie das jetzt bei Assembler ist - ich habs damals nur zu DOS Zeiten gemacht! Dient aber auch nur als einfaches Beispiel. 


bye Saxony


----------

