Java schneller als C++?!



  • stevg schrieb:

    Mir hat mal jemand erzählt dass Java im schnitt 20% langsamer sein sollen als C++.

    Im Schnitt? Nein.
    An bestimmten Stellen und in bestimmten Situationen: Vielleicht.



  • Weil's grad zum Thema passt, geb ich euch mal Zunder (zum flamen):

    http://www.idiom.com/~zilla/Computer/javaCbenchmark.html schrieb:

    And In Theory: Maybe Java Should be Faster
    Java proponents have stated that Java will soon be faster than C. Why? Several reasons (also see reference [1]):

    1. Pointers make optimization hard
      This is a reason why C is generally a bit slower than Fortran.

    In C, consider the code

    x = y + 2 * (...)
    *p = ...
    arr[j] = ...
    z = x + ...

    Because p could be pointing at x, a C compiler cannot keep x in a register and instead has to write it to cache and read it back -- unless it can figure out where p is pointing at compile time. And because arrays act like pointers in C/C++, the same is true for assignment to array elements: arr[j] could also modify x.

    This pointer problem in C resembles the array bounds checking issue in Java: in both cases, if the compiler can determine the array (or pointer) index at compile time it can avoid the issue.

    In the loop below, for example, a Java compiler can trivially avoid testing the lower array bound because the loop counter is only incremented, never decremented. A single test before starting the loop handles the upper bound test if 'len' is not modified inside the loop (and java has no pointers, so simply looking for an assignment is enough to determine this):

    for( int i = 0; i < len; i++ ) { a[i] = ... }

    In cases where the compiler cannot determine the necessary information at compile time, the C pointer problem may actually be the bigger performance hit. In the java case, the loop bound(s) can be kept in registers, and the index is certainly in a register, so a register-register test is needed. In the C/C++ case a load from memory is needed.
    2) Garbage collection- is it worse...or better?
    Most programmers say garbage collection is or should be slow, with no given reason- it's assumed but never discussed. Some computer language researchers say otherwise.

    Consider what happens when you do a new/malloc: a) the allocator looks for an empty slot of the right size, then returns you a pointer. b) This pointer is pointing to some fairly random place.

    With GC, a) the allocator doesn't need to look for memory, it knows where it is, b) the memory it returns is adjacent to the last bit of memory you requested. The wandering around part happens not all the time but only at garbage collection. And then (depending on the GC algorithm) things get moved of course as well.
    The cost of missing the cache

    The big benefit of GC is memory locality. Because newly allocated memory is adjacent to the memory recently used, it is more likely to already be in the cache.

    How much of an effect is this? One rather dated (1993) example shows that missing the cache can be a big cost: changing an array size in small C program from 1023 to 1024 results in a slowdown of 17 times (not 17%). This is like switching from C to VB! This particular program stumbled across what was probably the worst possible cache interaction for that particular processor (MIPS); the effect isn't that bad in general...but with processor speeds increasing faster than memory, missing the cache is probably an even bigger cost now than it was then.

    (It's easy to find other research studies demonstrating this; here's one from Princeton: they found that (garbage-collected) ML programs translated from the SPEC92 benchmarks have lower cache miss rates than the equivalent C and Fortran programs.)

    This is theory, what about practice? In a well known paper [2] several widely used programs (including perl and ghostscript) were adapted to use several different allocators including a garbage collector masquerading as malloc (with a dummy free()). The garbage collector was as fast as a typical malloc/free; perl was one of several programs that ran faster when converted to use a garbage collector. Another interesting fact is that the cost of malloc/free is significant: both perl and ghostscript spent roughly 25-30% of their time in these calls.

    Besides the improved cache behavior, also note that automatic memory management allows escape analysis, which identifies local allocations that can be placed on the stack. (Stack allocations are clearly cheaper than heap allocation of either sort).
    3) Run-time compilation
    The JIT compiler knows more than a conventional "pre-compiler", and it may be able to do a better job given the extra information:

    * The compiler knows what processor it is running on, and can generate code specifically for that processor. It knows whether (for example) the processor is a PIII or P4, if SSE2 is present, and how big the caches are. A pre-compiler on the other hand has to target the least-common-denominator processor, at least in the case of commercial software.

    * Because the compiler knows which classes are actually loaded and being called, it knows which methods can be de-virtualized and inlined. (Remarkably, modern java compilers also know how to "uncompile" inlined calls in the case where an overriding method is loaded after the JIT compilation happens.)

    * A dynamic compiler may also get the branch prediction hints right more often than a static compiler.



  • Mir ist da etwas nicht klar:
    ein C compiler kann Zeiger nicht gut optimieren, weil sie überall hin zeigen können. Soweit so klar. Kann man in C ja mit restricted umgehen, aber ok.

    In Java hat man doch das selbe Problem, oder?
    Ich meine, eine Referenz ist ja ein Zeiger auf einen Zeiger. Und das echte Objekt kann ja von dem GC hin und hergeschoben werden, oder? Und es kann auch 2 'Zeiger' auf das selbe Objekt geben.

    Um das C Beispiel aufzugreifen:

    Class x=new Class();
    Class p=x;
    x.read();
    p.change();
    x.read(); //kann nicht in einem register liegen
    

    Übersehe ich etwas?

    das GC und JIT ein Vorteil sein können ist aber wiederum klar.



  • In Java hast du zumindest einen entscheidenden Vorteil:
    Variablen, die im Stack liegen, können nicht von Referenzen referenziert werden, sind also alles Kandidaten für Eliminierung. Bei einer Schleife ist es evtl. nicht ganz unwichtig, dass der Zähler nicht im Speicher liegt. Ich seh das Problem in C++ aber auch nicht so dramatisch. Wahrscheinlich wollte der Kerl nur sagen, dass sich ein Compiler mit Java leichter tun kann, weil die Sprache weniger Freiheiten lässt.
    Das Verschieben von Objekten durch den GC ist übrigens kein Problem, weil der JIT-Compiler für den GC Metadaten anlegt nach dem Motto "in register x liegt gerade eine Referenz auf ein Objekt" bzw. es gibt Codebereiche in denen der GC anspringen darf und andere, wo er es nicht darf.



  • Optimizer schrieb:

    Wahrscheinlich wollte der Kerl nur sagen, dass sich ein Compiler mit Java leichter tun kann, weil die Sprache weniger Freiheiten lässt.

    Ok, das klingt logisch.

    Das Verschieben von Objekten durch den GC ist übrigens kein Problem, weil der JIT-Compiler für den GC Metadaten anlegt nach dem Motto "in register x liegt gerade eine Referenz auf ein Objekt" bzw. es gibt Codebereiche in denen der GC anspringen darf und andere, wo er es nicht darf.

    Was aber auch wiederum nicht gratis ist 😉



  • Metadaten sind IMHO schon gratis. Der GC, wenn er anspringt, schaut halt, wo der Instruction Pointer ist und schaut dann in den Metadaten, in welchen Registern sich gerade eine wegoptimierte Referenz befindet.
    Das mit den safe points hab ich eh noch nicht ganz kapiert. 🤡



  • BTW: Das paßt hier zwar nicht wirklich, aber vielleicht interessiert es ja wen:

    Es gibt Neuigkeiten bezüglich der "wahrgenommenen" Swing-Performance:

    http://weblogs.java.net/blog/zixle/archive/2005/04/no_more_gray_re_1.html

    Das "Gray-Rect" Problem wurde jetzt also beseitigt, wobei der Fix bisher nur unter Windows läuft, aber das ist wohl nur eine Frage der Zeit. Da ich kein Windows habe, konnte ich es noch nicht testen. Ich kann nur bestätigen, das der Fix unter Linux noch nicht läuft. 😃

    Die Swing-Performance unter Mustang scheint also, auch wenn man die Fortschritte an der OpenGL-Pipeline betrachtet, recht vielversprechend zu werden. Ich bin mal gespannt.



  • Optimizer schrieb:

    Weil's grad zum Thema passt, geb ich euch mal Zunder (zum flamen):

    wie immer müll.
    mag ja alle korrekt sein, was da drin steht, aber es zeigt nur, daß java theoretisch schneller als C oder schneller als C/C++ sein kann. C/C++ gibt es nicht und C ist veraltet und keine Gegner.



  • --



  • Artchi schrieb:

    Aber auch C++ schläft nicht und MS will ja mit VC++2005 so ein neues intelligentes Compile- und Link-Verfahren raus bringen, das C++ nochmal deutlich in der Performance pushen soll. Leider habe ich den langen englischen Text zu dem Verfahren nicht ganz verstanden... hat sich aber spektakulär gelesen. 😉

    Du denkst dann, dass dann C++ wieder ein Vorsprung gegenüber Java hat. Falsch gedacht! In welcher Sprache ist wohl Java programmiert?! Genau, in C++. Und wenn C++-Compiler an Geschwindigkeit zulegen, dann tut das Java auch; so einfach ist das.
    Zudem soll Java 1.6 noch mal schneller werden weil die VM multithreaded läuft.



  • Die VM hat immer mehrere Threads. Was neues kommen soll ist was anderes. Normal läuft jedes Java-Programm in einer eigenen Instanz der JVM. Nun soll jedes neue Programm in der selben, einzigen Instanz laufen, also praktisch nicht mehr als ein weiterer Thread in der aktuellen VM sein, anstatt ein eigener Prozess. Das kann natürlich enorm Speicher-Overhead reduzieren und auch JIT-Compilierzeit sparen. Dann darf die JVM aber echt nicht abstürzen, sonst sind _alle_ laufenden Java-Programme futsch.

    Ob das allerdings für 1.6 schon fertig wird, davon weiß ich noch nichts.



  • Allerdings ist deine Schlussfolgerung, dass Java mit C++ automatisch schneller wird, falsch. Die JVM mag schneller werden, aber dass sie aus dem Bytecode dadurch besseren Native Code generiert, ist wohl nicht der Fall. Hier muss man schon den JIT-Compiler verbessern.



  • Optimizer schrieb:

    Die JVM mag schneller werden...

    Genau das meinte ich. Nicht mehr und nicht weniger.

    LG



  • Optimizer schrieb:

    Normal läuft jedes Java-Programm in einer eigenen Instanz der JVM. Nun soll jedes neue Programm in der selben, einzigen Instanz laufen, also praktisch nicht mehr als ein weiterer Thread in der aktuellen VM sein, anstatt ein eigener Prozess. Das kann natürlich enorm Speicher-Overhead reduzieren und auch JIT-Compilierzeit sparen. Dann darf die JVM aber echt nicht abstürzen, sonst sind _alle_ laufenden Java-Programme futsch.

    Das ist schlecht, weil's schwierig wird unter Systemen die kein 'native Threading' kennen. Die Java-Entwickler scheinen das Ganze für Windows optimieren zu wollen, haben aber dabei nicht beachtet, daß die Ressourcen und der Managementaufwand, die/den ein Prozeß belegt verschwindend gering sind gegenüber dem eine Threads.



  • Javaianer schrieb:

    haben aber dabei nicht beachtet, daß die Ressourcen und der Managementaufwand, die/den ein Prozeß belegt verschwindend gering sind gegenüber dem eine Threads.

    Äh, es geht nicht Prozess Versus Thread sondern eine VM pro Anwendung.
    Und das ist sehr wohl eine Katastrophe.

    Momentan geht das noch ganz gut, weil man kaum 2 Java Programme gleichzeitig offen hat. Aber wenn ich auf meinem Notebook Netbeans und JEdit öffne, dann geht nichts mehr, deshalb ist JEdit geflogen und durch einen anderen Editor ersetzt worden.

    Die Optimierung dass nur eine VM für alle Anwendung nötig ist, bringt verdammt viel. Das ganze ist für das Java Programm aber unsichtbar, denn für das Programm ist es ja egal ob es seine eigene VM hat oder sie sich teilt.

    Ich verstehe deinen Einwand daher nicht.



  • Shade Of Mine schrieb:

    Äh, es geht nicht Prozess Versus Thread sondern eine VM pro Anwendung.
    Und das ist sehr wohl eine Katastrophe.

    Eigentlich nicht, da der ausführbare Code der VM nur einmal in den Speicher geladen werden muß. Es liegt daran, daß die VMs von vorn herein einen großen Working Set anlegen, der nicht geshared werden kann. Sicher, eine VM pro System würde dieses Problem verringern, allerdings zu einem Preis, den Optimizer schon genannt hat. Im Endeffekt hätte man dann eine abgewandelte Version des Tomcats.



  • JavaKid schrieb:

    Eigentlich nicht, da der ausführbare Code der VM nur einmal in den Speicher geladen werden muß.

    Ja, aber bedenke: du hast pro Anwendung den gesamten VM Overhead.
    dh, es können keine Resourcen zwischen den Anwendungen geshared werden. Man hat die ganzen Initialisierungskosten der VM jedesmal (schau dir mal an wie schnell eine .NET Anwendung im Gegensatz zu einer Java Anwendung startet).

    Es macht durchaus sehr viel Sinn die VM nur einmal zu starten.
    Natürlich ist es komplizierter, aber IMHO ist es das Wert.

    Mal abgesehen dass es bei Java vermutlich sinnlos sein wird, dass zu tun. Denn fast jede Anwendung bringt ihre eigene JRE mit. An dieser Stelle hat Sun IMHO versagt irgendetwas einheitliches zu machen. Vielleicht wäre es nicht möglich gewesen diese Entwicklung zu stoppen, aber Tatsache ist, dass dies ein gehöriger Nachteil für Java geworden ist.

    Denn meistens hat ein User nun 5mal die JRE drauf und updaten tut er logischerweise keine. Das ist echt ungut 😞

    Somit ist das Feature: nur eine VM pro Anwendung zwar im Prinzip super, nur für Java in der momentanen Situation vernachlässigbar.



  • @Shade: Weil es sich gerade so angehört hat, das i.A. schnellere Starten einer .Net Anwendung liegt nicht daran, dass die Instanz des Frameworks geshared würde (was AFAIK nicht der Fall ist), sondern daran, dass die ganze Klassenbibliothek des Frameworks bei der Installation zu native code vorcompiliert wird. Eine Java-Anwendung dagegen compiliert bei jedem Starten java.lang.String neu. Genau dieser Punkt könnte aber bedeutungsloser werden, denn durch die "shared VM" würde nur noch die erste Java-Anwendung deswegen langsamer starten.

    Wahrscheinlich suckt es aber dann schon wieder, wenn ich Eclipse starte, Eclipse beende (keine andere Anwendung laufen habe) und wieder starte. Da ist der native cache irgendwie ein Vorteil bei der JIT-Compilierzeit.
    Trotzdem sehe ich dieses .Net Feature irgendwie mit gemischten Gefühlen, ich glaube zum Beispiel, dass Inlining der Bibliotheksmethoden in den eigenen Programmcode damit unmöglich geworden ist.

    Kann natürlich auch sein, dass der JIT-Compiler von Microsoft einfach geiler ist. 😃 Irgendwie ham die das Compilerbauen schon drauf. Den VC++ - Compiler find ich um Welten besser als den g++ und der C#-Compiler ist nur noch unfassbar schnell. Da rollen sich einem echt die Zehennägel auf.



  • Übrigens full ack bei dem Desaster mit der JRE. Ich verstehe auch nie so ganz, warum die Leute das machen. Man kann heute noch Java 1.0 Code auf der 1.5 VM ausführen und das war auch klar, dass das immer gehen wird.



  • Shade Of Mine schrieb:

    Denn fast jede Anwendung bringt ihre eigene JRE mit.

    Was dummerweise verhindert, daß das Betriebssystem die JVMs sharen kann, da es verschiedene sind.

    Optimizer schrieb:

    Übrigens full ack bei dem Desaster mit der JRE. Ich verstehe auch nie so ganz, warum die Leute das machen.

    Vielleicht wollen sie absolut sicher gehen, daß ihre Anwendung überall gleich läuft und aussieht


Anmelden zum Antworten