The "C is Efficient" Language Fallacy



  • 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)?
    🙂



  • Man kann ja einfach schauen ob sich [trg, trg+N] mit [src, src+N] schneidet, da die Daten ja sequentiell im Speicher liegen.

    Siehe http://www.ddj.com/cpp/184401611



  • rüdiger schrieb:

    Man kann ja einfach schauen ob sich [trg, trg+N] mit [src, src+N] schneidet, da die Daten ja sequentiell im Speicher liegen.

    bei so primitiven sachen: ja.
    aber nicht bei komplexeren statements.



  • Tim schrieb:

    Das verstehe ich irgendwie nicht.

    Hab ich doch gleich gesagt, aber ~john hats dir ja jetzt erklärt.



  • lollol schrieb:

    Tim schrieb:

    Das verstehe ich irgendwie nicht.

    Hab ich doch gleich gesagt, aber ~john hats dir ja jetzt erklärt.

    quatsch, beide gingen von anderen voraussetzungen aus.
    🙂



  • @ShadeOfMine
    Warum sollte es bei komplexen Beispielen nicht funktionieren? Der Compiler kann ja selbst wenn er nicht inlined verschiedene Varianten einer Funktion anlegen und aufrufen. Sicher wird ein Array auch implizit zu einem Pointer. Aber das heißt nicht, dass er die Information bei einem Aufruf verlieren muss. Solange er in der statischen Analyse beweisen kann, dass der konkrete Aufruf frei ist von Überschneidungen kann er direkt eine dafür optimierte Version aufrufen.



  • für solche kopierorgien gibts unter Java 'System.arraycopy()'.

    Jaja, darum ging es nicht. In C gibt es ja auch memcpy und in C++ std::copy.

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

    Du kannst eine spezialisierte Funktion schreiben, die vorraussetzt, dass die beiden Arrays sich nicht ueberlappen. Wenn du diese einsetzt, dann ist der Benutzer der FUnktion fuer das Einhalten der Spezifikation verantwortlich.



  • rüdiger schrieb:

    Warum sollte es bei komplexen Beispielen nicht funktionieren? Der Compiler kann ja selbst wenn er nicht inlined verschiedene Varianten einer Funktion anlegen und aufrufen. Sicher wird ein Array auch implizit zu einem Pointer. Aber das heißt nicht, dass er die Information bei einem Aufruf verlieren muss. Solange er in der statischen Analyse beweisen kann, dass der konkrete Aufruf frei ist von Überschneidungen kann er direkt eine dafür optimierte Version aufrufen.

    Ja du kannst ein konservatives verhalten implementieren - keine frage. aber du kannst nicht anhand der parameter garantieren dass keine überappung stattfindet. nicht immer. manchmal kannst du es - aber du musst es zur laufzeit entscheiden und im zweifelsfall konservativ sein.

    @knivil:
    der compiler kann einfach nicht optimieren. sowas einfaches wie

    *a+=*b;
    *c+=*b;

    ist nicht parallelisierbar vom compiler. der overhead um zu checken ob es überlappungen gibt wäre größer als der nutzen. du kannst nämlich nur zur laufzeit checken.

    deshalb hat man in c restrict. das behebt dieses problem. in c++ fehlt es bitterlcih, aber man will wohl nicht so low levelig werden...


Anmelden zum Antworten