Schneller durch JIT?



  • Es wird ja immer gesagt, das Java durch den JITter theoretisch schneller sein könnte als etwas fest kompiliertes. Kam das in der Praxis schon mal vor?


  • Mod

    Zählen als "Praxis" auch künstlich getunte Benchmarks, die diese Eigenschaft besonders hervor heben und dafür gewisse Nachteile wenig zum Zuge kommen lassen?



  • Dort, wo Performance wichtig ist, wird sowieso auf der GPU ausgelagert oder man nimmt SIMD-Libraries.
    Dort, wo die Performance weniger wichtig ist, da wird JIT gegenüber nativen Lösungen nicht viel bringen. Was bringt mir das, wenn ich nur in 1% der Fälle ein SSE 4.2 Befehl nutzen kann? Dafür sind Sprachen wie Java in vielen anderen Fällen langsamer. JIT sind Gut-Genug-Lösungen. Niemand nimmt Java, weil er maximale Performance will. Java wird aus anderen Gründen genommen.



  • SeppJ schrieb:

    Zählen als "Praxis" auch künstlich getunte Benchmarks, die diese Eigenschaft besonders hervor heben und dafür gewisse Nachteile wenig zum Zuge kommen lassen?

    Von mir aus.



  • sfdsdfs schrieb:

    Es wird ja immer gesagt, das Java durch den JITter theoretisch schneller sein könnte als etwas fest kompiliertes. Kam das in der Praxis schon mal vor?

    Wie soll etwas schneller laufen, wenn die Bytecode Kompilierung in die Runtime der Anwendung dazugehört. Natürlich hat die Virtuelle Maschine sehr schlaue Mechanismen (Code Cache, partiale Optimierung, ...) um mit einem JIT Optimum an Performance rauszuholen. Die Möglichkeit von dynamischen Optimierung als Anpassung auf das CPU Target hätte ein sehr großes Potential, aber ich hab bis jetzt nix gelesen, dass diese Fähigkeit in der JVM/.NET implementiert wären - aber vielleicht bin ich auch nur zu faul oder zu dumm xD



  • Also für .NET s. Understanding .NET Just-In-Time Compilation, d.h. je nach Target gibt es unterschiedliche JIT-Compiler, welche dann die spezifischen Optimierungen vornehmen.



  • Ich stelle mir das so vor, dass z.B. eine Schleife, mit sagen wir mal 10.000 Iterationen, durchlaufen wird. Die ersten 3 Runden laufen langsam ab, da der JIT sich erst anschauen muss was da genau live passiert. Mit diesen Infos optimiert er immer weiter. Aber ab dann läuft der Schleifenkörper schneller ab, weil der JIT eine Lösung gefunden hat, die einem statischen Compiler vielleicht entgangen wäre.

    Soweit meine theoretische Überlegung zu dem Thema. In der Praxis habe ich keine Ahnung ob sowas wirklich funktioniert.



  • Th69 schrieb:

    Also für .NET s. Understanding .NET Just-In-Time Compilation, d.h. je nach Target gibt es unterschiedliche JIT-Compiler, welche dann die spezifischen Optimierungen vornehmen.

    Viel zu Allgemein und keine Untersuchung oder Belege für die Aussage.
    Außerdem gibst nur x86, x64 und ARM(?).
    Das du jeweilige Code Generatoren für die Target CPU brauchst, ist ja klar, weil ein ARM kein x86/x64 oder andersrum versteht. Das diese jeweilige andere Optimierung macht (sollte klar sein), aber das sind auch im diesen Sinne keine dynamischen Optimierungen.

    Ein Beispiel ist, du lässt dein .NET App auf dein alt System laufen optimal, dann testet du dei App auf deine neuste Maschine, wunders dich über die Schnelligkeit, weil die neuste Instruktion der CPU auch vom JIT mitbenutzt können. Theoretisch möglich, praktisch bei ein Befehlsatz der 9x% gleich ist - keine Ahnung, ob es den Entwickler der JVM oder CLR die Komplexität wert ist, dass zu implementieren.



  • Wir hatten hier doch auch irgendwann einen Fall, bei dem Java schneller war und es konnte sich erstmal keiner erklären. Dann war die Vermutung, dass der JIT Compiler ein bestimmtes Muster "erkannt" hat und daraufhin optimiert hat. Das war quasi zufällig schneller, weil die Daten in bestimmter Reihenfolge kamen. Kaum waren die Daten zufällig sortiert, schon war diese Annahme falsch und die Java Variante war langsamer. Kann mich jetzt aber nicht mehr ganz genau dran erinnern.



  • sfdsdfs schrieb:

    Es wird ja immer gesagt, das Java durch den JITter theoretisch schneller sein könnte als etwas fest kompiliertes. Kam das in der Praxis schon mal vor?

    Was ist für dich Praxis? Ich habe zumindest von wissenschaftlichen Projekten gehört, bei der im wesentlichen immer die gleichen Algorithmen zur Anwendung kamen, bei dem das der Fall sein sollte. Selbst bin ich nicht in dem Bereich tätig um das zu verifizieren.



  • Zee++ schrieb:

    Ich stelle mir das so vor, dass z.B. eine Schleife, mit sagen wir mal 10.000 Iterationen, durchlaufen wird. Die ersten 3 Runden laufen langsam ab, da der JIT sich erst anschauen muss was da genau live passiert. Mit diesen Infos optimiert er immer weiter. Aber ab dann läuft der Schleifenkörper schneller ab, weil der JIT eine Lösung gefunden hat, die einem statischen Compiler vielleicht entgangen wäre.

    Soweit meine theoretische Überlegung zu dem Thema. In der Praxis habe ich keine Ahnung ob sowas wirklich funktioniert.

    Deine Überlegung ist korrekt, jedoch ist das auch in statisch kompilierten Sprachen wie z.B. C++ möglich, nennt sich profile-guided optimization. In meinen performancekritischen Projekten konnte der GCC damit etwa weitere 15% 'rauskitzeln (nach -O3 -march=native !).



  • Kannte ich nicht, danke für den Tipp.

    Aber ein JIT kann diese Optimierung immer durchführen und so auf Veränderungen reagieren, also z.b. anders optimieren wenn 1Mio Durchläufe gebraucht werden und bei 10 den Loop ausrollen oder so. Oder schauen ob alles im Cache berechnet werden kann oder ob Zugriffe aufs langsame RAM von Nöten sind und dementsprechend anders optimieren.

    Theoretisch finde ich so einen JIT-Gedanken schon gut, aber das bringt ja alles nichts wenn es in der Praxis dann doch Geschwindigkeitsnachteile gegenüber fest kompilierten nativen Sprachen wie C++ gibt.



  • Zee++ schrieb:

    Kannte ich nicht, danke für den Tipp.

    Aber ein JIT kann diese Optimierung immer durchführen und so auf Veränderungen reagieren, also z.b. anders optimieren wenn 1Mio Durchläufe gebraucht werden und bei 10 den Loop ausrollen oder so. Oder schauen ob alles im Cache berechnet werden kann oder ob Zugriffe aufs langsame RAM von Nöten sind und dementsprechend anders optimieren.

    Theoretisch finde ich so einen JIT-Gedanken schon gut, aber das bringt ja alles nichts wenn es in der Praxis dann doch Geschwindigkeitsnachteile gegenüber fest kompilierten nativen Sprachen wie C++ gibt.

    man darf halt nicht vergessen, dass der C++ Compiler Zeit (viel Zeit ....) hat über die Optimierungen nachzudenken.
    Der JIT Compiler hingegen muss den Code möglichst schnell liefern.



  • Stimmt auch wieder.



  • fsdfsfsdfs schrieb:

    man darf halt nicht vergessen, dass der C++ Compiler Zeit (viel Zeit ....) hat über die Optimierungen nachzudenken.
    Der JIT Compiler hingegen muss den Code möglichst schnell liefern.

    Wobei der JIT bei "typischen" Businessanwendungen durchaus Zeit für eine Nachoptimierung hätte - nur sollte dann das Compilat für nachfolgende Starts aufgehoben werden, wenn sich nichts relevantes am System verändert hat. Zu guter letzt lassen viele JIT-Sprachen auch eine native Compilierung zu.



  • Richtig, es gibt ja z.B. für .NET seit ca. 2005 (müsste ich genauer nachschauen) den ngen.exe-Compiler. Der kompiliert .NET-Code standalone in nativen Code, bevor das Programm startet. Also ein statischer Build. Aber natürlich weiterhin mit allen nötigen Managed-Informationen (Reflection, GC usw.).

    Prominentester Nutzer dessen ist Paint.NET: während man dieses Programm installiert (mit dem Install-Wizard), dauert es eine gewisse und ungewöhnliche Zeit bis es fertig ist. Der Grund ist, das beim Installieren der ngen.exe alles schon kompiliert. Danach muß der der JIT nichts mehr machen.

    Auch das gesamte .NET-Framework wird nach der Installation im Hintergrund mit ngen kompiliert. Das kann zwar mehrere Tage dauern, erklärt aber auch, warum die Festplatte immer in den Ruhepausen dann doch aktiv wird.

    Für das kommende Java 9 ist ein ngen-ähnliches Feature übrigens auch geplant.

    Das sind dann aber keine JIT-Kompilate mehr. Es ist wie ein C++-Build ein statisches Kompilat, das auf die Zielplattform direkt kompiliert wurde.

    Die Frage war aber ob JIT-Kompilate unbedingt schneller sind? Hier muss man den Overhead der prominenten JIT-Sprachen mit einbeziehen. Z.B. haben Smalltalk, C# und Java sehr viel Objekt-Informationen für Reflection. Das gibt es bei C++ nur im Debug-Build. Bei den JIT-Sprachen ist das aber ein Release-Feature. Da man hier dem Coder Arbeit abnehmen will. Das kann aber Laufzeitperformance kosten, wenn diese Features nicht überall genutzt werden. Sobald man aber in C++ ähnliche Features braucht, muss man die programmatisch hinzufügen und dann kostet es auch Performance. Würde dann C++ immer noch gegen JITs gewinnen?

    Oder was wäre, wenn man ein C++-Programm (ohne Debug-Infos) generell per JIT kompilieren würde? Würde man noch mehr raus holen können?



  • Artchi schrieb:

    Hier muss man den Overhead der prominenten JIT-Sprachen mit einbeziehen. Z.B. haben Smalltalk, C# und Java sehr viel Objekt-Informationen für Reflection.

    Du vergisst beim Overhead z.B., dass man in Java beim Speicherzugriff immer doppelte Indirektion und schlecht cache locality hat. Allein das kann schon einiges kosten und verbaut grundsätzlich Optimierungsmöglichkeiten.



  • @Artchi
    .NET kann soweit ich weiss sowieso (noch) nicht reJITen. *
    Das soll kommen, aber aktuell wird 1x geJITed, und zwar bevor der Code das 1. mal ausgeführt wird. Bzw. halt über ngen gleich präventiv. Aber auf jeden Fall ohne dass irgend welche Statistiken verfügbar wären, weil der Code ja noch nie gelaufen ist.

    Das einzige wo .NET also aktuell (theoretisch) die Nase vorn haben könnte bezüglich Optimierung, ist Optimierung auf die Features/Eigenheiten der konkret vorhandenen CPU. Theoretisch, weil ich selbst da nicht sicher bin ob das ausgenutzt wird.

    *: Bzw. wenn es geht, dann ist es brandneu.



  • Mechanics schrieb:

    Du vergisst beim Overhead z.B., dass man in Java beim Speicherzugriff immer doppelte Indirektion (...)

    Wie, immer doppelte Indirektion?
    Bei Arrays aus Klassen, ja. Aber immer ...? 😮
    Ich dachte schon dass bei Java die Referenzen direkt die Adresse enthalten... und wenn ein Objekt verschoben werden muss, dann wird halt "die Welt umgeschrieben".

    Natürlich sammelt man trotzdem schnell viele Indirektionen. Besonders wenn man brav ist und z.B. Komposition statt Verrebung einsetzt oder eben besagte Arrays aus Klassen. Weswegen halbwegs gut optimierter Java Code auch oft viel mehr Pfui ist als halbwegs gut optimierter C++ Code 🙂



  • hustbaer schrieb:

    .NET kann soweit ich weiss sowieso (noch) nicht reJITen. *

    *: Bzw. wenn es geht, dann ist es brandneu.

    ReJit ist ab .NET 4.5 drin.
    Quelle: https://blogs.msdn.microsoft.com/davbr/2011/10/12/rejit-a-how-to-guide/


Log in to reply