Performance C++ vs. Java



  • @wob sagte in Performance C++ vs. Java:

    Vielleicht wurde einfach das falsche Konstrukt verwendet

    Über den folgenden Code bin ich vor kurzem gestolpert.

    string s = ""; // ODS XML
    
    s += "<document>";
    // ... 60 MByte Aktiendaten
    s += "</document>";
    

    Bis ich den C# StringBuilder nutzte.

    Der Code ist dummerweise C++ und C# kompatibel. Dahinter stehen aber unterschiedliche Konzepte der mutable, immutable Strings.



  • @Quiche-Lorraine Du kannst auch string.reserve aufrufen. Aber es ist natürlich nachfragenswert, warum man sowas in einem String haben will...



  • @It0101 sagte in Performance C++ vs. Java:

    Wenn man z.B. einen vector-artigen Container vergleicht kann allein durch den Allokator im Hintergrund ein großer Unterschied auftreten. Wenn bei 10000 Elementen der eine vector alle 10 Elemente jeweils 10 neue allokiert und der andere direkt 10000 Elemente auf einen Schlag, dann ist allein dadurch ein deutlich spürbarer Unterschied gegeben.

    Es geht noch viel subtiler. Man muss nur Speicher irgend wie allozieren und dann z.B. SIMD-Code darauf loslassen, und dann stimmt die Performance nicht, weil man das unpassende Alignment genutzt hat.



  • @wob sagte in Performance C++ vs. Java:

    Aber es ist natürlich nachfragenswert, warum man sowas in einem String haben will...

    Der Code war ein Schnellschuss für eine Ausgabe nach LibreOffice ODS. Und ich meinte, ich habe zuerst den Dateiinhalt bestimmt, diesen dann nach UTF8 gewandelt und dann erst in die XML Datei geschrieben.

    Aber der Knackpunkt war die langsame XML Ausgabe in C#. Die Ursache lag an der Nutzung von string in Kombination mit dem += Operator. Da unter C# immutable sind, wird pro += Aufruf ein neuer String erzeugt.

    Und da lag mein Denkfehler. Unter C++ ist ein string ein Container, unter C# ein unveränderlicher String.

    Aber wie schon gesagt, unter C# löste StringBuilder mein Problem. Unter C++ hatte ich das Problem noch nie.


  • Gesperrt

    @Timmi87 : Schreib einen vernünftigen Test (um die CPU/ eine Schleife zu testen, und nicht das Speichermedium...), und vergleiche dann die Ausgaben:

    #include <limits.h>
    #include <math.h>
    #include <iostream>
    #include <chrono>
    
    bool is_p(long long int test)
    {
        if (test % 2 == 0) {
            return false;
        }
        long long int s = sqrt(test);
        for (long long int i = 3; i <= s; i += 2) {
            if (test % i == 0) {
                return false;
            }
        }
        return true;
    }
    
    int main(int argc, char* argv[])
    {
        using namespace std;
        chrono::time_point<chrono::high_resolution_clock> t1 = chrono::high_resolution_clock::now();
        for (long long int i = LLONG_MAX;; i--) {
            bool p = is_p(i);
            cout << i << " : " << p << endl;
            if (p) {
                break;
            }
        }
        chrono::time_point<chrono::high_resolution_clock> t2 = chrono::high_resolution_clock::now();
        cout << chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count() << endl;
        return 0;
    }
    

    Das sollte keins der Sprachen optimieren können. Ausgabe bei mir:

    a.exe
    9223372036854775807 : 0
    9223372036854775806 : 0
    9223372036854775805 : 0
    9223372036854775804 : 0
    9223372036854775803 : 0
    9223372036854775802 : 0
    9223372036854775801 : 0
    9223372036854775800 : 0
    9223372036854775799 : 0
    9223372036854775798 : 0
    9223372036854775797 : 0
    9223372036854775796 : 0
    9223372036854775795 : 0
    9223372036854775794 : 0
    9223372036854775793 : 0
    9223372036854775792 : 0
    9223372036854775791 : 0
    9223372036854775790 : 0
    9223372036854775789 : 0
    9223372036854775788 : 0
    9223372036854775787 : 0
    9223372036854775786 : 0
    9223372036854775785 : 0
    9223372036854775784 : 0
    9223372036854775783 : 1
    17067
    

    Schaffst du es, das Java-Äquivalent zu formulieren?


    Unterm Strich werden aber beide in etwa "gleichschnell" sein.



  • @EinNutzer0 sagte in Performance C++ vs. Java:

    bool is_p(long long int test)
    

    Nenn die Funktion doch ruhig is_prime und spendiere ihr noch den Check auf den Spezialfall test == 2, damit sie auch richtig wird 🙂

    Signifikant schneller wirds z.B. so:

    bool is_prime(long long int test)
    {
        if (test % 2 == 0 || test % 3 == 0) {
            return test == 2;
        }
        long long int s = sqrt(test);
        for (long long int i = 5; i <= s; i += 4) {
            if (test % i == 0) {
                return false;
            }
            i += 2;
            if (test % i == 0) {
                return false;
            }
        }
        return true;
    }
    

    Und dann kann man mal anfangen, die Schleife zu optimieren. Vielleicht stören ja die beiden if-Bedingungen? Also:

        for (unsigned long long int i = 5; i <= s; i += 6) {
            if ((test % i == 0) + (test % (i + 2) == 0) != 0) return false;
        }
    

    Siehe da, das ist bei mir schneller (in Kombi mit s und i als unsigned).

    Was lernen wir: Erst den Algorithmus optimieren!



  • @wob sagte in Performance C++ vs. Java:

    bool is_prime(long long int test)
    {
        if (test % 2 == 0 || test % 3 == 0) {
            return test == 2;
        }
    

    Bei test == 3 kommt da aber nicht das raus was ich mir erwarten würde...



  • @Jockelx sagte in Performance C++ vs. Java:

    @hustbaer sagte in Performance C++ vs. Java:

    Das halte ich für ein Gerücht.

    Möglich, zumindest finde ich da auch nichts zu. Meine Firma hat mir mal ein Online-Seminar von Rainer Grimm spendiert und da hat er das erzählt. Ist keine schlechte Quelle und daher hab ich das ungeprüft mal so weiter gegeben.

    Spätestens jetzt wäre interessant was er da genau gesagt hat.
    vector::push_back ist bei MSVC auf jeden Fall OK und war schon sehr lange OK.
    vector::insert (speziell range-insert) bin ich nicht ganz so sicher.

    Beim Erstellen eines vector aus dem Inhalt zweier Iteratoren bin ich auch nicht 100% sicher dass hier nicht unnötig re-allocated wurde. Ich bin sicher dass nicht linear re-allocated wurde, aber es wäre möglich dass da unnötigerweise exponentiell re-allocated wurde.



  • @hustbaer sagte in Performance C++ vs. Java:

    Bei test == 3 kommt da aber nicht das raus was ich mir erwarten würde...

    😱 😭 😭

    Verdammt. Vor allem, wo ich doch noch darauf Wert gelegt habe... Hast natürlich völlig recht. Kommt davon, wenn man an anderen Stellen am code rumspielt. Ist aber ja einfach zu fixen.


  • Gesperrt

    @wob Ich verstehe den Vorteil deiner Variante noch nicht so ganz... gerechnet wird doch gleich oft.

    Und unsigned long long int hab ich Absichtlich nicht gewählt, obwohl es vielleicht interessant wäre, weil Java mit so großen vorzeichenlosen Datentypen nicht umgehen kann. Oder man nimmt BigInteger, aber dann ist die Performance ganz im Keller wegen der Objektinstanziierungen...

    In Java ist char der einzige vorzeichenlose primitive Ganzzahldatentyp...

    Also, um die Vergleichbarkeit sicherzustellen, besser das unsigned streichen. 😀



  • @EinNutzer0 sagte in Performance C++ vs. Java:

    Ich verstehe den Vorteil deiner Variante noch nicht so ganz... gerechnet wird doch gleich oft.

    Weil ich am Anfang einmal checke, ob durch 2 oder durch 3 geteilt werden kann, dann ich danach sämtliche Vielfache von 2 und 3 ausschließen. Du machst das ja praktisch auch, indem du alle Vielfachen von 2 ausgeschlossen hattest. Wenn ich die Vielfachen von 2 und 3 ausshließen will, muss ich abwechselnd +=2 und +=4 rechnen. Damit erspare ich mir 1/3 der Checks auf Kosten einer längeren Loop.

    Oder sprichst du die andere Variante an? Es ist gut, wenn man nur wenige Datenabhängigkeiten und wenige Branches hat. Im ersten Fall muss ich testen, ob das Teilen aufgeht. Dann 2 addieren. Dann wieder testen. Im 2. Fall ist der Code bei mir ca. 1 Sekunde schneller - ich habe hier nur ein if und eine Rechnung, die der Compiler schon ordentlich für mich optimieren wird. Aber da ist immer etwas "black magic" bei 🙂

    Und das andere: du willst 2 Sprachen vergleichen, aber bestimmte Features nicht erlauben? (ich war überrascht, dass das was half, habe aber auch nicht so genau geguckt - also es schadet bestimmt nicht, wenn du diese Änderungen auch bei dir versuchst nachzuvollziehen und testest, ob die bei dir auch helfen)

    PS: und Zeile 4 natürlich korrigieren. Der Fehler ärgert mich immer noch.


Anmelden zum Antworten