Java schneller als C++?!



  • Gibt es beim Visual Studio nicht von vorneherein zwei Projekteinstellungen: Debug und Release, wobei Debug vorausgewählt ist? Wählt einfach mal Release. Allein das sollte schon deutlich an geschwindigkeit bringen.

    Wenn du weiter tunen willst solltest du natürlich speziell auf deinen Prozessor optimieren lassen.

    Das ist doch mal ein Beitrag der konstruktiv ist :).

    Leider ist das Programm in C++ auf "Release" immernoch langsamer als Java. Und immer speziell auf die CPU zu optimieren, wäre ja auch nicht gerade das Wahre.



  • net schrieb:

    Optimizer schrieb:

    ...ob Java nicht bei bestimmten Dingen schneller sein kann als C++ lautet die Antwort: Ja natürlich, und was ist jetzt daran so toll?

    hast du einen beispielcode, bei dem java definitiv schneller ist als c++?

    Codebeispiele sind sinnlos, weil es immer ein Zusammenspiel von Compiler, plattformspezifischer Optimierung, Programmarchitektur und hunderten weiteren Sachen ist.

    In Java kann ich nen String 20mal rumreichen und an hunderte Objekte als Datenelement zuweisen und es kostet praktisch nichts. In C++ geht das natürlich auch. std::string ist aber meistens nicht als shared buffer implementiert und du musst z.B. mit C-Strings arbeiten oder ne andere Klasse nutzen. Dann hast du beim sharen aber wieder das Speicherverwaltungsproblem, außer du nimmst den Overhead von refcounting in Kauf.
    Mit genügend großem Aufwand wirst du schon auf eine Lösung kommen, mit der C++ ohne Probleme dann mithalten kann, du kannst aber nicht immer genügend großen Aufwand betreiben.

    Wenn du dynamisch sehr viele kurzlebige Objekte allokierst, kann der GC sehr rocken, wenn du für C++ nen eigenen Allokator speziell für diesen Zweck schreibst, hast du wieder geilen Aufwand (frag volkard, ob er dir seinen gibt. ach ne, den hat er gekickt, weil er doch nicht zufrieden war) und kannst dann wieder mithalten. Für realistische Fälle = realistische Entwicklungszeit/Kosten gibt es immer wieder Fälle, wo du mit Java schneller bist. Und natürlich auch, wo du langsamer bist.
    Der eigentliche Witz ist aber, dass man in C++ dann nicht die selbe Architektur wählt, die man für Java wählt. Deshalb ist es nie wirklich vergleichbar.

    Keine Ahnung, warum das so unvorstellbar ist. Wenn ne Firma mehrere Mannjahre inverstiert, um irgendwas zu optimieren, kann es doch durchaus sein, dass du mit weniger Mannjahren auf eine weniger optimale Lösung kommst.

    Ich stoße immer wieder auf Sachen, wenn ich überhaupt mir die Mühe mach, das zu messen. Du denkst, ich will mich jetzt herausreden, aber ich tu mir keinen Gefallen, wenn ich jetzt irgendwas poste. Irgendjemand (der ich sein könnte) kommt dann her und sagt:
    - dein Code ist nicht praxisrelevant
    - in C++ würde man das anders machen
    - mit meinem Compiler ist das anders
    - mit meinem Allokator ist das anders / ich hätte dafür nen eigenen Allokator geschrieben
    - ...

    Wieso bist du auf der einen Seite so korrekt und auf der anderen Seite wieder nich? Warum ist es dein unbrauchbares Fallbeispiel?

    - Du kannst anscheinend nicht perfekt mit deinem Compiler umgehen
    - Die Zeitmessung, die du verwendest, ist fürn Arsch
    - Dein Beispiel ist nicht so praxisrelevant. Ich berechne nicht ständig PI

    Es gibt so viele Möglichkeiten. Standard C++ fordert strikt IEEE konforme Berechnungen, Java nicht. Der JIT-Compiler kann dadurch Optimierungen durchführen, die der C++ Compiler hier vielleicht grad nicht macht. Die Liste der möglichen Umstände ist endlos. Java ist hier möglicherweise vielleicht gerade schneller, bei was anderem ist es wieder anders rum.

    Normal hätte man doch vermuten müssen, dass C++ schneller ist, allein schon weil es keine "Virtuelle Maschiene" hat.

    Ich vermute das nicht pauschal.

    Optimieren musst du über Mathematik, über Datenstrukturen. Mit nem Wechsel von Java auf C++ oder umgekehrt holst du nicht das selbe raus.



  • kurze ergänzung zu Optimizers Post, dem ich vollkommen zustimme:

    im Prinzip vergleicht der Code lediglich die implementierung von sqrt und pow. Die Schleife ist ja absolut trivial.



  • Optimizer schrieb:

    Es gibt so viele Möglichkeiten. Standard C++ fordert strikt IEEE konforme Berechnungen, Java nicht. Der JIT-Compiler kann dadurch Optimierungen durchführen, die der C++ Compiler hier vielleicht grad nicht macht.

    Einspruch:

    Gerade bei Dingen wie Sinusberechnungen, Cosinusberechnungen usw. hat Java bisher eher auf Genauigkeit als auf Geschwindigkeit gesetzt. Teilweise werden diese Funktionalitäten in Software nachgebaut, weil die Hardwarehersteller es nicht so genau mit der Genauigkeit nehmen. Die Behauptung, dass Java nicht so sehr auf die Genauigkeit achtet, ist also einfach falsch. (Wobei sich das in Zukunft möglicherweise ändern könnte, was IMHO durchaus gut ist: Vergleicht mal die Performance der trigonometrischen Funktionen von (Sun's) Java und einer C++ Implementierung).



  • IMHO ist dieser Grundsatz inzwischen nicht mehr so ganz gültig. Seit strictfp wird standardmäßg nicht IEEE-konformer Code generiert.
    Ok, bei Sachen wie sin weiß ich es nicht. Da ist die Spezifikation eindeutig.
    Aber Prozessoren kennen ja zum Beispiel Befehle wie multiply-add, die nicht dem IEEE Standard entsprechen. Die benutzt der JIT-Compiler AFAIK standardmäßig.



  • Ok, vielleicht hab ich was falsch verstanden. Betrifft strictfp jetzt nur Überläufe?

    Java Language Specification schrieb:

    It follows that an expression is not FP-strict if and only if it is not a compile-time constant expression and it does not appear within any declaration that has the strictfp modifier.

    Within an FP-strict expression, all intermediate values must be elements of the float value set or the double value set, implying that the results of all FP-strict expressions must be those predicted by IEEE 754 arithmetic on operands represented using single and double formats. Within an expression that is not FP-strict, some leeway is granted for an implementation to use an extended exponent range to represent intermediate results; the net effect, roughly speaking, is that a calculation might produce "the correct answer" in situations where exclusive use of the float value set or double value set might result in overflow or underflow.



  • Java ist fast immer schneller als C++, nur Swing nicht.



  • Welchen C++ Compiler hast du denn benutzt? OK, VC++ .NET... ist es die 2003er Version? Ist es die Standard-Version oder die Pro-Version?

    Java hat ja (muß man ja sagen) einen Vorteil: der Hotspot kann zur Laufzeit den Code optimieren, und bei solchen Dingen kann ich mir einen Speedvorteil vorstellen.

    Ich habe aber auch in 5 Jahren Java-Erfahrung festgestellt, das vieles in Java grotten langsam ist. Ich sage nur XSLT-Verarbeitung, der Horror!

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



  • Es gibt so viele Möglichkeiten. Standard C++ fordert strikt IEEE konforme Berechnungen, Java nicht. Der JIT-Compiler kann dadurch Optimierungen durchführen, die der C++ Compiler hier vielleicht grad nicht macht.

    Jup, daran liegt es. mit dem gcc 3.4 ohne -ffast-math: etwas über 5 Sekunden, mit -ffast-math 'ne knappe halbe Sekunde auf meinem System. Java 1.5 braucht etwas über 3.



  • ColdSharp schrieb:

    Ergebnis Java 1.5.0:
    PI: 3.141592653552359
    Zeit: 2.75s
    
    Ergebnis Visual C++ .NET:
    PI: 3.141592653552359
    Zeit: 4.578s
    
    Ergebnis Borland C++ 5.5:
    PI: 3.141592653552337
    Zeit: 3.844s
    

    Mir ist gerade mal folgendes aufgefallen:

    1. Das Ergebnis des Borland-Compilers unterscheidet sich von den anderen beiden.

    2. Viel schlimmer:

    Laut www.wikipedia.de sind die ersten Stellen von Pi 3,141 592 653 589 793. Es scheint hier also massive Abweichungen vom tatsächlichen Wert zu geben. Woran liegt es? Am Algorithmus? Ich habe mir den Algorithmus nicht angeguckt.

    ...und was kommt beim gcc mit "-ffast-math" raus?



  • PS: Ich sehe gerade, dass der Algorithmus tatsächlich Mist ist. Es liegt garantiert daran und ist kein generelles Problem.



  • Ich glaube, du missverstehst was. Dein Algorithmus berechnet eine Näherung für PI. Diese muss sich ab irgendeiner Stelle von PI unterscheiden. Die Frage ist nun ab welcher Stelle das der Fall ist. Dein Algorithmus ist in der Tat so langsam in seiner Annäherung, dass er in 10000000 Iterationen nur 9 Stellen hinter dem Komma berechnen konnte. Es gibt Algorithmen mit wesentlich besserer Konvergenz.

    Wenn du sowas machst, musst du dir auch über die Grenzen von Fließkommaarithmetik bewusst sein. Diese verhindern, dass du mit double überhaupt dich näher an PI heranschleichen kannst, als eine bestimmte Stelle.

    Der Algorithmus selbst ist nicht nur langsam, sondern auch langsam implementiert. Man könnte unter Beibehaltung der Struktur folgendes machen:

    public double calculatePI(double n) 
       { 
             double untersumme = 0.0; 
    
             for(double i = 1.0; i < n; i++)  {
                double step = i/n;
                untersumme += Math.sqrt(1 - step * step); 
             }
             return (2*untersumme + 1.0/n) * 2.0 / n; 
       }
    


  • Komischer Thread bzw komischer Test. 🙂
    Java schneller als C++ ? Wenn man messen würde wie lange es dauert Java und C++ Projekte zu entwickelt und zu warten würde ich doch glatt Geld auf Java setzen 😉 :p
    Aber einen 3D Shooter in Java ... k.A. Mir hat mal jemand erzählt dass Java im schnitt 20% langsamer sein sollen als C++. Und das kann ich mir auch gut vorstellen. Aber mit Java als erstes langsame Anwendungen zu verbinden, was in unserer großen weiten Welt voller kleinen Trollen recht häufig vorkommt, zeugt meiner Meinung nach von Unwissenheit.
    [edit] uf, Rechtschreibfehler ohne Ende 😮



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


Anmelden zum Antworten