Schneller durch JIT?



  • Artchi schrieb:

    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?

    Bei Java... ja. Wenn man mal ausser Acht lässt dass es ja Arrays aus eingebauten Typen gibt, womit man fast ohne Overhead arbeiten kann. (Was aber natürlich oft unpraktisch ist und zu Pfui-Code führt.)

    Bei C# bzw. .NET allgemein: maximal jain. Denn bei "Value Types" bezahlt man soweit ich weiss nicht grundsätzlich per Instanz. Natürlich müssen auch die Metadaten für die Value Types irgendwo liegen, nur so lange man die nicht angreift, sollte man dafür maximal einmalig beim Laden der Assembly bezahlen müssen. Und natürlich mit leicht erhöhtem Speicherverbrauch. Auf jeden Fall aber nix wo ich gleich fix davon ausgehen würde dass es andere Vorteile die man durch den JIT bekommt nicht wieder aufwiegen können. Und... ein Teil dieser Daten ist auch für den GC erforderlich, damit der weiss wo in einem Objekt Referenzen stehen und wo nicht. Und da der GC wiederrum den Overhead bei der Speicherverwaltung verringern kann, kann man das Vorhandensein dieser Daten mMn. nicht als reinen Overhead werten. Man muss da halt gucken wie sich das alles zusammen bei einem jeweils betrachteten Programm auswirkt.



  • Zeus schrieb:

    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/

    Boah, da war ich mal wieder ein paar Jahre hinten 😃 Danke.

    ps: Da ist dauernd von Profiler die Rede. Macht die Runtime das auch von selbst zum Zwecke der Optimierung, oder nur per Request wenn ein Profiler im IL Code rumgefummelt hat?



  • hustbaer schrieb:

    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".

    Ich bin da anscheinend tatsächlich überhaupt nicht up to date, du hast Recht. Die Hotspot VM macht das nicht mehr, früher waren doppelt indirekte Handles und das Freigeben das Speichers war dann einfacher.
    Wobei ich echt nicht weiß, ob ich jetzt von der Variante, alle Referenzen zu aktualisieren tatsächlich so begeistert sein sollte.
    Und klar, beim wichtigen Fall Array von Objekten hast du immer noch doppelte Zeiger.



  • hustbaer schrieb:

    Zeus schrieb:

    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/

    Boah, da war ich mal wieder ein paar Jahre hinten 😃 Danke.

    ps: Da ist dauernd von Profiler die Rede. Macht die Runtime das auch von selbst zum Zwecke der Optimierung, oder nur per Request wenn ein Profiler im IL Code rumgefummelt hat?

    -.- nur im Profiler Modus, der Rejiter hat zuviele bekannte Problem!

    // #Known issues with REJIT at this time:
    //   NGEN inlined methods will not be properly rejitted
    //   Exception callstacks through rejitted code do not produce correct StackTraces
    //   Live debugging is not supported when rejit is enabled
    //   Rejit leaks rejitted methods, RejitInfos, and SharedRejitInfos until AppDomain unload
    //   Dump debugging doesn't correctly locate RejitInfos that are keyed by MethodDesc
    //   Metadata update creates large memory increase switching to RW (not specifically a rejit issue)
    


  • Na gut, dann bin ich beruhigt. Hab's zwar nicht dazugeschrieben, aber ich dachte schon an praktikables reJITen zur Laufzeit zwecks Optimierung unter "production" Bedingungen. Und da meine ich mich zu erinnern dass auch das bald kommen soll. Hoffentlich 🙂



  • Sehr interessant mit C#. Wen es interessiert das nennt sich dann .NET_Native und für Windows10 Apps soll das wohl schon gehen und soll die Anwendungen bis zu 60% schneller laufen lassen.



  • Mechanics schrieb:

    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.

    Ich kann mir nicht wirklich vorstellen, dass doppelte Indirektion auf modernen x86_64-Maschinen allzu sehr ins Gewicht fällt. Der Standard-Addressierungsmodus im Protected Mode hat da sowieso schon 4-Fache Indirektion auf den "Richtigen" RAM, in einer Virtuellen Maschine mit Nested Paging sogar 6-Fache, und theoretisch kann man beliebigfache haben, siehe https://www.reddit.com/r/programming/comments/18zvoh/x86_mmu_fault_handling_is_turing_complete/ (wenn dieses dumme Forum die URL nicht wieder scrambled).

    Ansonsten: Sobald man eine Runtime bereitstellt geht es immer darum, Rechenleistung gegen Gedankenarbeit einzutauschen. Bereits ein Assembler tauscht gewisse Optimierungsmöglichkeiten (zugegeben, sehr krude) gegen einen menschenlesbaren Befehlssatz ein, ein Compiler tauscht ein genau angepasstes Code-Layout ein gegen einfachere Lesbarkeit und Portabilität, C tauscht ein klares einfaches Memory-Layout aus gegen die Möglichkeit, mittels Zeigern in Objekten statt Zahlen zu denken. Google "Abstraction Optimization Tradeoff". Wenn man nicht gerade auf embedded devices arbeitet, sondern auf einem modernen Rechner, der genug RAM hat, dann will man einfach auf bestimmte Sprachfeatures (zum Beispiel Array-Bounds-Checking) nicht verzichten.

    Die JVM ist auch deutlich besser als ihr Ruf. Der schlechte Ruf der JVM kommt daher, dass SUN Java ursprünglich für RISC-Architekturen wie PPC und SPARC entwickelt hat. Die hatten unter Anderem viel mehr Register, im Gegensatz zu alten x86-processoren, außerdem hatten sie einen schlanken Befehlssatz, sodass der Unterschied zum Bytecode nicht so groß war.

    Aber man sollte sich hier nichts vormachen: Moderne x86_64-Processoren haben effektiv selbst bereits eine virtuelle Maschine (cf Microcode), so von wegen "on the metal". Und es gibt sogar Processoren, die Java Bytecode direkt unterstützen.



  • [quote="Ramelucke"]Ich kann mir nicht wirklich vorstellen, dass doppelte Indirektion auf modernen x86_64-Maschinen allzu sehr ins Gewicht fällt.quote]

    Ist ja schon geklärt, ich war nicht up to date. Ist schon lang umgebaut worden, grade weil das sehr teuer war.

    Ramelucke schrieb:

    sondern auf einem modernen Rechner, der genug RAM hat, dann will man einfach auf bestimmte Sprachfeatures (zum Beispiel Array-Bounds-Checking) nicht verzichten.

    Hä? "Array Bound Checking" ist völlig nutzlos, das will ich ganz sicher nicht haben. Ich hatte noch nie Probleme damit, und ich habe auch noch nie erlebt, dass da irgendjemand Probleme damit gehabt hätte. Das sind absolut triviale Probleme, da will ich nicht ständig Overhead für nichts haben. Und abfangen kann Array Bound Checking auch nicht wirklich was, dann fliegt halt auch nur eine saubere Exception statt UB, kaputt ist dein Programm trotzdem und du musst es richtig schreiben. Und wenn das erstmal richtig ist, bringt dir das "Feature" wieder absolut gar nichts.



  • Mechanics schrieb:

    Hä? "Array Bound Checking" ist völlig nutzlos, das will ich ganz sicher nicht haben. Ich hatte noch nie Probleme damit, und ich habe auch noch nie erlebt, dass da irgendjemand Probleme damit gehabt hätte. Das sind absolut triviale Probleme, da will ich nicht ständig Overhead für nichts haben. Und abfangen kann Array Bound Checking auch nicht wirklich was, dann fliegt halt auch nur eine saubere Exception statt UB, kaputt ist dein Programm trotzdem und du musst es richtig schreiben. Und wenn das erstmal richtig ist, bringt dir das "Feature" wieder absolut gar nichts.

    Waren nicht Pufferüberläufe immernoch eine der häufigsten Ursachen für Sicherheitslücken, erst so langsam eingeholt durch Rassenkonditionen?

    Aber egal, das ist off-topic. Man will halt gerne Late Binding haben, das wird durch JIT halt wirklich schneller, weil man zum Beispiel nachträglich funktionen inlinen kann. Man will Garbage Collection, die wird leichter wenn man Object Introspection hat, und die wird leichter, wenn man genau weiß was wann wo welches Objekt wie aufruft, und das kann man besser optimieren, wenn man JIT benutzt. Generell möchte man sich eigentlich möglichst wenig um den Stack kümmern müssen, und um die Eigenheiten des Systems, und man möchte möglichst leicht Shims injecten können um APIs nachträglich kompatibel nachzubessern; also zumindest sind das doch immer so Standardgründe die Leute angeben, Java statt C++ zu benutzen, wird also wohl was dran sein. You get the idea.

    Das Meiste davon kriegt man im Prinzip auch statisch hin, macht halt nur keiner, ist vmtl auch schwieriger, hat man ja an so Experimenten wie XPCOM gesehen. Und ein hochoptimiertes C-Programm wird sicherlich die meisten JITs schlagen, aber das zu schreiben und stabil hinzubekommen kostet halt einfach mehr, als man für ein paar mehr Server in seinem Rack ausgeben müsste.

    Was man nicht statisch hinkriegt sind so sachen wie Java RMI, die aber meines Wissens inzwischen extrem unpopulär sind, obwohl die Grundidee von RMI, also einen Algorithmus auf viele Knoden in einem heterogenen Netzwerk transparent zu verteilen, ja eigentlich genial ist.



  • @Mechanics
    Bounds-Violations sind ja bloss eine der schlimmsten und grössten Quellen von Exploits überhaupt. Dass das nicht mehr ganz so schlimm ist wie vor 10-15 Jahren liegt hauptsächlich daran dass aktuelle Compiler und Betriebssystem einiges unternehmen um Exploits durch unchecked Buffer zu erschweren bzw. verhindern.



  • Ramelucke schrieb:

    Man will Garbage Collection

    Wer ist man? GC hat ziemlich viel verdorben. Also mir sind Destruktoren und RAII lieber.



  • hustbaer schrieb:

    @Mechanics
    Bounds-Violations sind ja bloss eine der schlimmsten und grössten Quellen von Exploits überhaupt. Dass das nicht mehr ganz so schlimm ist wie vor 10-15 Jahren liegt hauptsächlich daran dass aktuelle Compiler und Betriebssystem einiges unternehmen um Exploits durch unchecked Buffer zu erschweren bzw. verhindern.

    Das ist richtig. Nur würde ich nicht gerade behaupten, dass zusätzliche Prüfungen zur Laufzeit durch die Runtime etwas wären, worauf ich nicht mehr verzichten mag. Ist ja nicht so, dass ich hunderte neue out of bounds Bugs pro Tag produzieren würde, die ich ohne die Runtime Unterstützung nicht im Griff hätte.



  • Du hast "völlig nutzlos" geschreiben.



  • JIT auf Java kann viel bringen, weil die umgebung ziemlich abgekapsel und restriktiv definiert ist, dadurch hast du einen allwissenden compiler. Wenn du neue module reinlaedst, musst das natuerlich neu gemacht werden.
    JIT optimieren eher selten waehrend der laufzeit, weil dadurch die wertvollen CPU-resourcen verbrannt werden wuerden, es ist eher iterativ, bei jedem start kann der JIT die evaluierungen vom letzten lauf nehmen und neu generien. (Es gibt natuerlich manchmal hintergrundscompilierung, aber das ist nicht um die letzten 10% zu holen).

    Kam das in der Praxis schon mal vor?

    Nicht gerade Java, aber ich finde der "geilste" JIT ist Project Denver von NVidia:

    Denver implements an innovative process called Dynamic Code Optimization, which optimizes frequently used software routines at runtime into dense, highly tuned microcode-equivalent routines. These are stored in a dedicated, 128MB main-memory-based optimization cache. After being read into the instruction cache, the optimized micro-ops are executed, re-fetched and executed from the instruction cache as long as needed and capacity allows.

    https://blogs.nvidia.com/blog/2014/08/11/tegra-k1-denver-64-bit-for-android/


Anmelden zum Antworten