The "C is Efficient" Language Fallacy



  • Das Problem ist, dass der Compiler quasi nichts parallelisieren kann. Das ist ein relevantes Problem.

    Nur zur Klarstellung: Was, wie und in welchem Massstab bedeutet hier parallelisieren? UV-Pipeline, Mehrkern, ... ?

    C ist halt nah an der Maschine. Entweder abstrahiert man von der Maschine oder spezialisiert man fuer eine Maschine. C oder auch C++ tut beides nicht. Das ist kein Nachteil. Warum soll C++ alles koennen? Wenn man so etwas haben moechte, dann parallelisiert man selbst oder man nimmt sich z.B. eine funktionale Sprache und laesst sich parallelen Code generieren. In beiden Faellen muss man Gehirnschmalz investieren, zum einen parallelisiert man selbst oder muss halt die neue Sprache lernen. Und wenn jemand behaupt, dass z.B. Java schneller sein soll als C++, dann hat er vielleicht hier und da etwas weniger Gehirnschmalz investiert. Und dann auf die super tolle Jit-Blub verweisen, ich weiss nicht ...

    Ich koennte ja noch 'ne Geschichte zum Besten geben mit Agent Nero in Mission Iso-Image brennen. Installer: 500 MByte ... die modernen Alleskoenner. Ich will sie nicht.



  • knivil schrieb:

    Nur zur Klarstellung: Was, wie und in welchem Massstab bedeutet hier parallelisieren? UV-Pipeline, Mehrkern, ... ?

    Jede Art von Parallelisierung.

    Das Problem ist nämlich, dass jedes Statement dass Pointer liest oder schreibt jedes andere Statement verändern kann. Egal welche Art der Parallelisierung - sogar in der Pipeline selber drinnen, ist mit Zeigern enorm problematisch.

    C ist halt nah an der Maschine. Entweder abstrahiert man von der Maschine oder spezialisiert man fuer eine Maschine. C oder auch C++ tut beides nicht. Das ist kein Nachteil. Warum soll C++ alles koennen? Wenn man so etwas haben moechte, dann parallelisiert man selbst oder man nimmt sich z.B. eine funktionale Sprache und laesst sich parallelen Code generieren.

    Es ist ein relevantes Problem dass C/C++ Code einfach in gewissen Situationen lahm ist. Da kannst du auch kein Gehirnschmalz oder sonstwas investieren - dieser Aspekt an C++ suckt einfach. Deshalb ist dringend restrict in C++ notwendig...

    Und wenn jemand behaupt, dass z.B. Java schneller sein soll als C++, dann hat er vielleicht hier und da etwas weniger Gehirnschmalz investiert. Und dann auf die super tolle Jit-Blub verweisen, ich weiss nicht ...

    Es ist aber so. In gewissen Situationen wird Java immer schneller sein als C++.



  • ist mit Zeigern enorm problematisch

    Vielleicht hilft es, Zeiger vorsichtig einzusetzen.

    In gewissen Situationen wird Java immer schneller sein als C++.

    Glauben ist gut, Kontrolle ist besser. Da ich wenig mit Java arbeite, kann ich nicht viel darueber sagen. Aber ich bin offen und schaue mir auch gern Beispiele an (sofern sie nicht allzu gross sind).



  • knivil schrieb:

    Glauben ist gut, Kontrolle ist besser. Da ich wenig mit Java arbeite, kann ich nicht viel darueber sagen. Aber ich bin offen und schaue mir auch gern Beispiele an (sofern sie nicht allzu gross sind).

    while(i<N) {
      trg[i]=src[i]);
      ++i;
    }
    

    das ist in java nämlich parallelisierbar. und es gibt nichts dass du in c++ tun kannst...

    PS:
    Zeiger vorsichtig einsetzen ist nicht. denn fast alles in C++ kommt irgendwie auf zeiger zurück. strings, arrays, jede datenstruktur. und das ist eben das problem.



  • Shade Of Mine schrieb:

    while(i<N) {
      trg[i]=src[i]);
      ++i;
    }
    

    das ist in java nämlich parallelisierbar. und es gibt nichts dass du in c++ tun kannst...

    doch, memcpy() nehmen.
    🙂



  • +fricky schrieb:

    Shade Of Mine schrieb:

    while(i<N) {
      trg[i]=src[i]);
      ++i;
    }
    

    das ist in java nämlich parallelisierbar. und es gibt nichts dass du in c++ tun kannst...

    doch, memcpy() nehmen.
    🙂

    👍

    Oder gleich std::copy<T>, welches sich auch noch spezialisieren lässt, und z.B. für bestimmte Datentypen einen speziellen Copy-Algo nutzen könnte (z.B. einen Blitter 😃 ).



  • +fricky schrieb:

    Shade Of Mine schrieb:

    while(i<N) {
      trg[i]=src[i]);
      ++i;
    }
    

    das ist in java nämlich parallelisierbar. und es gibt nichts dass du in c++ tun kannst...

    doch, memcpy() nehmen.
    🙂

    Und wenn der =operator ueberladen worden ist?



  • Obwohl memcpy eine Option ist, wollte ich es einfach mal wissen. Natuerlich ist das jetzt kein repraesentativer Benchmark, aber soweit entfernt von Java liegt C++ ohne als auch mit Optimierung nicht (32 bit System).

    g++ main.cpp: time ./a.out -> user 0m0.512s (so ganz ohne Optimierung)
    g++ -O main.cpp: time ./a.out -> user 0m0.212s (mit Optimierung)
    java -Xms512m -Xmx1024m Test -> 386 ms

    Interessanterweise wollte die JVM das Programm nicht ohne maximaler Heapsize von 1024 MByte starten. Dabei wurde das entsprechende Javapaket von Sun genutzt.

    C++ kommt irgendwie auf zeiger zurück. strings, arrays, jede datenstruktur. und das ist eben das problem

    Ja und Nein. Bei einer Stringzuweisung weiss ich, dass diese zwei Strings keinen gemeinsamen Speicher benutzen, ausser es handelt sich um das gleiche Objekt. Daher kann ich z.B. das Kopieren auch per Hand parallelisieren. Das gleich gilt fuer Arrays (als Typ).

    Java- und C++-Programme:

    class Test
    {
        public static void main(String[] args)
        {
            int n = 100000000;
            int i = 0;
            int a1[] = new int[n];
            int a2[] = new int[n];
            long time = System.currentTimeMillis();
            while(i<n)
            {
                a1[i] = a2[i];
                ++i;
            }
            System.out.println( System.currentTimeMillis() - time );
        }
    }
    
    const int n = 100000000;
    int a1[n];
    int a2[n];
    
    int main()
    {
        int i = 0;
        while(i<n)
        {
            a1[i] = a2[i];
            ++i;
        }
        return 0;
    };
    


  • Shade Of Mine schrieb:

    knivil schrieb:

    Nur zur Klarstellung: Was, wie und in welchem Massstab bedeutet hier parallelisieren? UV-Pipeline, Mehrkern, ... ?

    Jede Art von Parallelisierung.

    Das ist doch Unsinn. Es geht hier um die fehlende Möglichkeit Mikrooptimierungen durch den Compiler vorzunehmen. Will man ein Programm richtig parallelisieren, muß man egal in welcher Sprache ganz massiv von Hand eingreifen.

    Shade Of Mine schrieb:

    Es ist aber so. In gewissen Situationen wird Java immer schneller sein als C++.

    Wenn man sein Compilerhandbuch zum Thema Parallelisierung gelesen hat, dann kann man das getrost als Unsinn bezeichnen. Java bietet von Haus einige wenige Möglichkeiten, so daß der Compiler Mikrooptimierungen vornehmen kann, die man bei C++ via Pragmas ebenfalls vornehmen kann.



  • ~john schrieb:

    Shade Of Mine schrieb:

    knivil schrieb:

    Nur zur Klarstellung: Was, wie und in welchem Massstab bedeutet hier parallelisieren? UV-Pipeline, Mehrkern, ... ?

    Jede Art von Parallelisierung.

    Das ist doch Unsinn. Es geht hier um die fehlende Möglichkeit Mikrooptimierungen durch den Compiler vorzunehmen. Will man ein Programm richtig parallelisieren, muß man egal in welcher Sprache ganz massiv von Hand eingreifen.

    Wenn ein Compiler eine längere Schleife parallelisieren kann und dadurch Faktor 2 oder mehr rausholen kann sehe ich das nicht mehr wirklich als Mikrooptimierung. So oder so, "Unsinn" ist das nicht.



  • DEvent schrieb:

    +fricky schrieb:

    Shade Of Mine schrieb:

    while(i<N) {
      trg[i]=src[i]);
      ++i;
    }
    

    das ist in java nämlich parallelisierbar. und es gibt nichts dass du in c++ tun kannst...

    doch, memcpy() nehmen.
    🙂

    Und wenn der =operator ueberladen worden ist?

    dann haste pech, oder geschwindigkeit ist sowieso egal.

    knivil: für solche kopierorgien gibts unter Java 'System.arraycopy()'.
    🙂



  • 90:10 sag ich da nur zu euch Mikrooptimisten.



  • Tim schrieb:

    Wenn ein Compiler eine längere Schleife parallelisieren kann und dadurch Faktor 2 oder mehr rausholen kann sehe ich das nicht mehr wirklich als Mikrooptimierung. So oder so, "Unsinn" ist das nicht.

    Du holst mit so einem Fliegensch* niemals den Faktor zwei heraus! Erstmal muß man die Laufzeit in dieser Routine mit der Gesamtlaufzeit der Applikation in Relation setzen. Wenn es sich um eine wirklich kritische Routine handelt, dann kann man diese optimieren. Aber dann wird man das grundsätzlich von Hand machen und nicht dem Compiler überlassen.



  • Tim schrieb:

    ~john schrieb:

    Shade Of Mine schrieb:

    knivil schrieb:

    Nur zur Klarstellung: Was, wie und in welchem Massstab bedeutet hier parallelisieren? UV-Pipeline, Mehrkern, ... ?

    Jede Art von Parallelisierung.

    Das ist doch Unsinn. Es geht hier um die fehlende Möglichkeit Mikrooptimierungen durch den Compiler vorzunehmen. Will man ein Programm richtig parallelisieren, muß man egal in welcher Sprache ganz massiv von Hand eingreifen.

    Wenn ein Compiler eine längere Schleife parallelisieren kann und dadurch Faktor 2 oder mehr rausholen kann sehe ich das nicht mehr wirklich als Mikrooptimierung. So oder so, "Unsinn" ist das nicht.

    Du hast doch keine Ahnung.



  • ~john schrieb:

    Tim schrieb:

    Wenn ein Compiler eine längere Schleife parallelisieren kann und dadurch Faktor 2 oder mehr rausholen kann sehe ich das nicht mehr wirklich als Mikrooptimierung. So oder so, "Unsinn" ist das nicht.

    Du holst mit so einem Fliegensch* niemals den Faktor zwei heraus!

    Der Faktor bezog sich auf eine einzelne Schleife/Funktion.

    ~john schrieb:

    Erstmal muß man die Laufzeit in dieser Routine mit der Gesamtlaufzeit der Applikation in Relation setzen. Wenn es sich um eine wirklich kritische Routine handelt, dann kann man diese optimieren.

    Oder dem Compiler gleich die richtigen Hints geben. Dazu muss man nichtmal wissen ob der jeweilige Part zeitkritisch ist oder nicht, man macht es einfach. Im schlimmsten Fall bringt es halt nichts.
    Profilen ersetzt das sicher nicht, hat aber auch niemand behauptet. Auch wenn du das unterstellt.

    ~john schrieb:

    Aber dann wird man das grundsätzlich von Hand machen und nicht dem Compiler überlassen.

    Das verstehe ich irgendwie nicht. Wenn es der Compiler schon parallelisiert, warum sollte man es dann unbedingt noch von Hand machen? Das macht nur Sinn wenn man es auf anderen Compilern noch übersetzen will wo man weiss, dass er das eben nicht macht.

    @ lollol: Hier dein Keks.



  • Tim schrieb:

    Das verstehe ich irgendwie nicht. Wenn es der Compiler schon parallelisiert, warum sollte man es dann unbedingt noch von Hand machen?

    Weil die Compilerparallelisierung meistens nicht viel bringt. Bei Enterprise Anwendungen versaust Du mit so etwas den Gesamtdurchsatz der Maschine (Ein BigIron ist meist um die 90% ausgelastet, andernfalls hat man zu viel Geld für die Maschine ausgegeben. Ergo, kannibalisiert man mit diesen Mikrooptimierungen CPU Zeit von anderen Prozessen, dafür geht Zeit für die Thread Erzeugung, Vernichtung und den ganzen Synchronisierungsquatsch drauf. -> weniger Transaktionen), und bei SciTech Anwendungen bringt es meist nichts. Diese Mikrooptimierungen können nur auf einem SMP Knoten funktionieren, das bringt aber auf einem MPP Rechner überhaupt nichts. Der ganze Aufwand fürs Kopieren, Synchronisieren und die Kommunikation ist auf dem MPP einfach viel zu groß. Mal ein Beispiel die BlueGene Rechner kennen noch nicht einmal Threads! Und jeder Knoten führt exakt einen Prozeß aus.

    Bei einem SMP-Knoten gilt, nur dann wenn die anderen Kerne nicht ausgelastet sind bringt das etwas. Damit beschränkt man sich auf Desktop Rechner und kleine Server. Dem Desktop-Anwender fallen aber solche Mikrooptimierungen nie auf, dazu ist der Gewinn insgesamt vernachlässigbar. Und um auf das Thema Java vs. C oder C++ zurückzukommen. Vielfach ist ein C Programme geladen, ausgeführt und schon beendet bevor der Java Kram überhaupt geladen ist. Bei den anderen Sprache für VMs sieht es doch ganz ähnlich aus. Ergo, bringt das ganze nur etwas, wenn man über signifikante Geschwindigkeitsunterschiede spricht, und das bei entsprechend langen Laufzeiten in einer Routine.

    Soll etwas schnell sein, kommt man um Profilerläufer und Handoptimierungen nicht herum.

    P.S. Java GUI-Programme sind meist so träge, daß man sich als Nutzer mehr über die Oberfläche ärgert, als das einem diese Mikrooptimierungen auffallen würden.



  • Shade Of Mine schrieb:

    knivil schrieb:

    Glauben ist gut, Kontrolle ist besser. Da ich wenig mit Java arbeite, kann ich nicht viel darueber sagen. Aber ich bin offen und schaue mir auch gern Beispiele an (sofern sie nicht allzu gross sind).

    while(i<N) {
      trg[i]=src[i]);
      ++i;
    }
    

    das ist in java nämlich parallelisierbar. und es gibt nichts dass du in c++ tun kannst...

    *einmisch*

    zumindest bei Grunddatentypen wird das mit modernen C/C++ Compilern vektorisiert. Ich bin mir gar nicht so sicher ob ich eine "hoehere" Form der Parallelisierung (Thread- oder Prozesserstellung) haben moechte, sooo viel Rechenkerne haben aktuelle (Desktop)Maschinen noch gar nicht dass ich das aus der Hand geben moechte, wieviel davon ich beleg. Und wenn ich's wirklich parallel haben will, ist das mit OpenMP in einer Zeile parallelisiert 😉



  • Blue-Tiger schrieb:

    zumindest bei Grunddatentypen wird das mit modernen C/C++ Compilern vektorisiert.

    Du kannst nicht garantieren dass die beiden arrays nicht überlappen...



  • Shade Of Mine schrieb:

    Blue-Tiger schrieb:

    zumindest bei Grunddatentypen wird das mit modernen C/C++ Compilern vektorisiert.

    Du kannst nicht garantieren dass die beiden arrays nicht überlappen...

    Der ICC baut falls er es nicht garantieren kann, durchaus auch zwei Versionen und prüft dann zur Laufzeit welche er nehmen soll.



  • rüdiger schrieb:

    Shade Of Mine schrieb:

    Blue-Tiger schrieb:

    zumindest bei Grunddatentypen wird das mit modernen C/C++ Compilern vektorisiert.

    Du kannst nicht garantieren dass die beiden arrays nicht überlappen...

    Der ICC baut falls er es nicht garantieren kann, durchaus auch zwei Versionen und prüft dann zur Laufzeit welche er nehmen soll.

    woran erkennt er, welche die richtige ist (wenn vielleicht sogar beabsichtigt ist, daß sich die speicherbereiche überlappen)?
    🙂


Anmelden zum Antworten