Avatar von cdonat
  • cdonat

mehr als 1000 Beiträge seit 21.10.2002

Re: Coroutinen hat noch nie was revolutioniert

Menos schrieb am 25.03.2020 17:29:

cdonat schrieb am 25.03.2020 14:34:

Ich kann es dir sagen, was dann passiert: Dann fliegt die Exception und das File-Objekt liegt herrenlos im Speicher, bis der GC vorbei kommt und es abräumt. Dabei wird das File dann vom GC auch geschlossen. Das kann aber sehr viel später passieren und bis dann leakst du das Filehandle.

Weiterhin nein. ;) Es gibt in JS keine Möglichkeit ein Dateihandle zu leaken, es gibt nicht mal ein file.close in irgend einer Form das ich aufrufen könnte.

Wenn ich natürlich gar nicht auf den Inhalt der Datei zugreifen kann, bevor sie komplett geladen ist, dann kann man das File natürlich auch schliessen, bevor mein Code läuft. Dann kann ich aber die Anforderung, nur einen Teil der Datei überhaupt einzulesen erfüllen. Du hast also schon bei der Wahl des Werkzeugs versagt, die gegebenen Requirements zu erfüllen. Warum hast du auch überhaupt an der Stelle auf JavaScript zurück gegriffen in den Kommentaren zu einem Artikel über die neuesten Features von C++?

Dann kann der Browser natürlich auch die gewünschte Funktionalität nicht implementieren

Richtig. Es ging mir ja auch einzig und allein nur darum eine Idee zu demonstrieren ohne sich stundenlang Gedanken über Exceptions u.Ä. machen zu müssen. ;)

Hättest du die Sprache verwendet, um die es auch im Artikel geht, also C++, dann hättest du die Diskussion auch gleich von Anfang an dank RAII vermeiden können.

Gut, dann zählen wir noch mal durch, wie viele Proframmierer es gibt, die hauptsächlich funktional programmieren und wie viele es gibt, die hauptsächlich imperativ (egal, ob prozedural, oder OO) programmieren.

Ich habe keine Ahnung ob es dazu Statistiken gibt, aber das ist auch relativ egal. Wenn man in einer Firma ist wo funktional programmiert wird kann man noch so sehr auf so eine Statistik pochen, das wird den Chef herzlich wenig interessieren.

Dann schauen wir noch mal, in wie vielen Firmen funktional programmiert wird. Als Proxy dafür können wir z.B. die Stellenausschreibungen zuziehen, in denen explizit OO, bzw. funktionale Programmierung gefordert wird.

Seit mindestens vierzig Jahren sind einige Leute sicher, dass spätestens zehn Jahre nach der Aussage alle nur noch funktional programmieren. Ist bis jetzt nicht passiert und wird auch jetzt nicht passieren. Nicht weil funktionale Programmierung schlecht wäre - auch C++ übernimmt ja Teile davon, aber für ein allgemeines Werkzeug ist es halt zu eingeschränkt, so wie z.B. reine OO auch zu eingeschränkt ist.

Aber du wolltest uns ja beweisen, dass Coroutines keine Vorteile haben, also wie machst du das?

Ich habe nur gesagt das Co-Routinen nichts revolutionieren weil es Alternativen gibt. Das hast du ja mit deinem Code auch schön demonstriert. Es kann natürlich andere Vorteile geben, auch die hast du alle aufgelistet, nur sind das halt Nieschensituationen und keine Revolution.

Ich habe dir nicht bei der Aussage widersprochen, dass Coroutines nichts völlig neues sind. Ich habe dir bei der Behauptung widersprochen, dass sie überall nur ein Nieschendarseien fristen würden. Meine beiden Gegenargumente sind 1. in Python werden sie viel und gerne verwendet und 2. die Vorteile von Coroutines in C++ sind gross genug, dass sie nach meiner Einschätzung eine ähnliche Verbreitung finden werden, wie in Python, also Allgegenwärtig werden.

Die einzige wirkliche Alternative wäre ja, eine eigene Range mit eigenen Iteratoren gewesen. Dafür ist der Aufwand aber sehr gross, so dass man es meistens oft lieber doch meidet. Mit Coroutines werden wir viel mehr Ranges verwenden, weil sie ja so einfach hergestellt werden können.

Dein Vorschlag war deshalb keine gute Alternative, weil er für den User deiner Funktion eine API anbietet, die sich nirgendwo in die etablierten C++-Idiome einfügt. Deshalb kann man mit deinem Vorschlag keine STL-Algorithmen, keine Range-Based-For-Loops, keine Range-Adaptoren und Views, etc. verwenden. Man kann damit auch z.B. keinen Vektor initialisieren, was mit einer Range eben durchaus geht.

Ich sehe bei dieser API Schwächen in Sachen Erweiterbarkeit und Composability, aber das ist hier ja nicht das Thema.

Die sind übrigens ganz bewusst vorhanden, damit die Leute Streams nicht für Dinge benutzen für die sie gar nicht gedacht sind. Aber das nur so am Rande.

Ich behaupte, dass das eine Schutzbehauptung ist, weil die Sprache keine besseren Möglichkeiten hergibt, eine DSL zu bauen.

Die Stärke einer API zeigt sich dann, wenn jemand sie für Sachen einsetzt, die dem ursprünglichen Autor der API nicht eingefallen ist. Die neue C++20 Ranges-API z.B. hat dieses Kriterium erfüllt, als eine Forschergruppe sie verwendet hat, um die Range-Ausdrücke komplett in GPU-Code compiliert hat. Eric Niebler war vorher der Ansicht, dass das wahrscheinlich nicht möglich sein wird und ist aus allen Wolken gefallen, als er den Artikel gelesen hat.

Wenn man z.B. grosse Cloud-Anwendungen für richtig viele User schreibt, dann können z.B. 1% Performanceunterschied schon ganz ordentlich Kohle bringen.

Völlig klar. Aber wie bei jeder Performance-Optimierung gilt: man muss zunächst nachweisen, dass eine nennenswerte Anzahl Rechenleistung an einer Stelle verbraucht wird.

Dafür misst man, aber wer misst, misst Mist. Microbenchmarking ist nicht so einfach, wie viele sich das vorstellen. Selbst dann nicht, wenn man ein etabliertes Microbenchmarking-Framework verwendet, wie z.B. das von Google.

Malloc/dealloc sind Dinge die so gut optimiert sind, dass man schon krasse Sachen damit veranstalten muss um damit überhaupt 1% Performance rausholen zu können.

Google hat sein eigenes malloc(), das wesentlich schneller ist, auf Multicore-Maschinen wesentlich besser skaliert als die übliche Standard-Implementation und auch NUMA-aware ist und damit hilft, den inter-Node-Traffic zu minimieren.

Wenn ich mal wieder als Performance-Engineer unterwegs bin sehe ich das auch ständig: da diskutiert der eine stundenlang mit mir darüber aus welche Form der For-Schleife denn jetzt die beste Performance hat und das man das unbedingt beachten müsse, aber wenn ich dann darauf scheiße und am Algorithmus ansetze und alles auf ein mal 100x schneller läuft wird doof geguckt.

Ich bin an der Stelle komplett auf deiner Seite. Auch wenn man sich auch mal vertun kann, wenn man die algorihtmische Komplexität als alleinigen Masstab verwendet. Der Klassiker ist natürlich die Tatsache, dass bei relativ kurzen Listen bubblesort schneller ist als quicksort. Noch mehr bin ich aus den Latschen gekippt, als ich den Talk von Barne Stroustrup gesehen habe, wo er zeigt, wie ein vorhersagbares Speicherzugriffspattern eine viel schlechtere algorithmische Komplexität auch für relativ grosse Datnestrukturen überlagern können: https://www.youtube.com/watch?v=OB-bdWKwXsU&feature=youtu.be&t=2678

Bei den beiden hier besprochenen Alternativen war die algorithmische Komplexität aber ja identisch. Das bedeutet, dass die Geschwindigkeit der einzelnen Schrittte die Performancecharakteristik dominiert. Hier sind Speicherzugriffe einer der wichtigsten Einflussfaktoren. Durch die Wiederverwendung des Speichers in meinem Code, operieren wir also in den meisten Durchgängen komplett im Cache. Wenn du jedes mal neuen Speicher allozierst, dann hast du vergleichsweise viele Cache-Misses. Dazu kommt eben der Overhead für die zusätzlichen malloc()s und realloc()s.

Bewerten
- +