CppCon 2018

Dieses Jahr war ich das erste Mal auf der CppCon in Bellevue (Seattle). Hier sind meine Eindrücke.

In Pocket speichern vorlesen Druckansicht
Lesezeit: 9 Min.
Von
  • Rainer Grimm
Inhaltsverzeichnis

Dieses Jahr war ich das erste Mal auf der CppCon in Bellevue (Seattle). Hier sind meine Eindrücke.

Ich hielt einen der 2-Tages-Workshops, die vor der Konferenz stattgefunden haben. Andere Workshops wurden unter anderem von Nicolai Josuttis, Alexandrei Alexandrescu und Scott Meyers gehalten. Mein Workshop "Concurrency with Modern C++" wurde sehr gut angenommen, sodass ich mehr als 30 Teilnehmer hatte. Am ersten Tag ging es um die Grundlagen wie Threads, Tasks, Locks und Bedingungsvariablen. Der zweite Tage war deutlich experimenteller, denn nach der Suche in die Breite stand die in die Tiefe an. Es ging um atomare Variablen und das Speichermodell. Darüber hinaus führte ich das interaktive Werkzeug CppMem ein, mit dem sich lock-freier Code verifizieren lässt. Zum Abschluss ging ich noch auf die Parallele Standard Template Library ein und gab einen Ausblick auf die concurrent-Zukunft (C++20/23) von C++.

Nun aber zur Konferenz. Dies sind die Eindrücke der Vorträge, die mich am meisten beeindruckt haben. In ein paar Wochen werden sie auf YouTube verfügbar sein.

Bjarne präsentierte in seiner Keynote die zentralen Ideen zum Design von Concepts. Los ging es mit ein wenig Geschichte und Motivation zu Concepts. Concepts besitzen eine lange Geschichte in C++. Den stärksten Einfluss besitzt Alexander Stepanovs' Buch Elements of Programming. Alexander Stepanov ist der Vater der Standard Template Library.

Natürlich ging Bjarne auf die Vorteile von Concepts ein. Hier sind seine Punkte:

  • spezifischere Fehlermeldungen
  • selbsterklärende Interfaces
  • besser lesbarer Code
  • zusätzliches Optimierungspotential für den Compiler

Concepts sind Prädikate, die zur Compilezeit ausgewertet werden. Sie sollten nicht minimale Anforderungen wie HasAdd modellieren, sondern auf Interoperabilität ausgerichtet sein. Concepts sind keine Typklassen wie in Haskell, denn sie können sowohl Typen als auch Werte annehmen. Bleibt die Frage: Wie können Concepts konkret aussehen? Sie können mathematische Ideen wie Gruppen oder Ringe oder aber konkrete Ideen wie Zahlen oder Sortable abbilden. Natürlich kannst du auch eigene Concepts oder neue Concepts auf der Grundlage bestehender Concepts definieren.

Darüber hinaus beantwortete Bjarne die Fragen, die ihren Designprinzipien zugrunde liegen:

  • Provide good interfaces.
  • Look for semantic coherence.
  • Don't force users to do what machines do better.
  • Keep simple things simple.

Gerne wiederhole ich mich. Dies war ein großartiger Überblick zu den zentralen Ideen von Concepts.

Im Gegensatz zur Keynote, war Bryce' Talk sehr technisch und relativ anspruchsvoll. Bryce erklärt in seinem Vortrag Begriffe, die für das Verständnis eines single-threaded und insbesondere eines multithreaded Programms wesentlich sind. Dabei schloss er explizit die berühmt-berüchtigte Speicherordnung std::memory_order consume aus.

Zuerst einmal gibt es die as-if rule. Diese besagt, dass das System (Compiler, Prozessor und Speicher) alle Optimierungen durchführen darf, die das beobachtbare Verhalten eines single-threaded Programms nicht ändern. Bryce stellte das Speichermodell als eine abstrakte Maschine vor, ging auf Threads genauer ein und definierte dabei die Begriffe expression, evaluation, side-effect, sequenced-before, execution step und forward progress. Gerne will ich tiefer auf den zentralen Begriff sequenced-before eingehen. Im single-threaded Programm bewirkt eine sequence-before-Relation eine happens-before-Relation. Im multithreaded Fall, wird eine happens-before-Relation durch eine synchronize-with-Relation etabliert. Im Wesentlichen bedeutet dies, dass ein multithreaded Programm ohne happens-before-Relation ein data-race besitzt.

Bryce' Talk war zwar anspruchsvoll, jedoch gut verständlich für mich, da ich diese Begriffe in meinen Präsentationen oder auch Workshops zum Speichermodell verwende.

Um mich kurz zu halten. Toms' Präsentation war nach meinem Geschmack. Zuerst einmal war der Talk anspruchsvoll aber nicht zu anspruchsvoll. Darüber hinaus lernte ich Einiges zur Implementierung von std::function. std::function ist ein polymorpher Funktionswrapper für aufrufbare Einheiten wie Funktionen, Funktionsobjekte und Lambda-Funktionen.

Tom Poole ist ein Maintainer für das JUCE Open Source, ein Cross-Platform-C++-Framework für Audio-Applikationen. Aufgrund der besonderen Performanz- und Speicheranforderungen und der Tatsache, dass std::function nicht auf allen von JUCE unterstützten Plattformen zur Verfügung steht, bietet JUCE eine eigene Implementierung an. Tom stellt die Herausforderungen vor, die es bei einer eigenen Implementierung zu bewältigen gilt. Diese Herausforderungen umfassen die Vermeidung von Speicherallokationen, Locks, aber auch type erasure. Letztendlich besitzt JUCE eine Implementierung, die ähnliche Performanzcharakteristiken wie std::function aufweist.

Mithilfe der Begriffe sane und safe unterteilte Peter Datentypen in vier Kategorien.

Mit leeren Datentypen (empty types) oder value-Datentypen (value types oder regular types), befinden wir uns in der Kategorie sane und safe. Dies gilt aber nicht für Zeiger, polymorphe Typen oder built-in-Datentypen wie bool, int oder double. Diese sind in der Kategorie in-sane und in-safe.

Natürlich gehören Zeiger in diese Schublade. Ich denke aber, du wunderst dich, dass built-in-Datentypen dazu gehören. Der Grund ist einfach. Sie können Opfer von integral promotion oder numerischen Konvertierungen sein und besitzen viele spezielle Werte wie +Inf, -Inf oder NaN. Damit die built-in-Datentypen sane und safe werden, solltest du das Whole Value Pattern von Ward Cunningham anwenden. Nun ist die Frage: Welche Datentypen sind sane und in-safe.

Zum Beispiel sind Zeiger-Typen wie Iteratoren, Smart Pointer, Referenzen, Referenz-Wrapper oder auch Views (std::string_view) sane und in-safe.

Mein Resümee zu Peters Vortrag ist kurz und bündig: Dieser Vortrag sollte Bestandteil jeder C++-Schulung sein.

Dank einer Einladung von Jon Kalb war ich am Donnerstag zusammen mit Jason Turner und Dan Saks Gastgeber einer Diskussionsrunde. In dieser 60-Minuten-Runde beantworteten wir viele Fragen der Zuhörer zu unseren Schulungen.

Warum bin ich auf diesen sehr theorielastigen Talk gegangen? Der Grund ist, dass eine wohl-definierte Begrifflichkeit ein Schlüsselelement für Mulithreading-Programme darstellt. Oft entstehen Probleme mit Multithreading-Programmen dann, wenn Entwickler ein unterschiedliches Verständnis von den gleichen Begriffen besitzen.

Zuerst einmal verwendete Geoffrey den Begriff API race anstelle von data race, der vom C++-Standard verwendet wird.

  • API race: The program performs two concurrent operations on the same object when the object's API doesn't permit those operations to be concurrent.

Ein API race tritt dann auf, wenn zwei Operationen auf einem Objekt zeitgleich ausgeführt werden, obwohl dies nicht erlaubt ist. Diese Definition unterscheidet sich deutlich von der Definition eines data race, denn bei einem data race darf zu einem Zeitpunkt keine Lese- und Schreiboperation gleichzeitig auf einer Speicherstelle ausgeführt werden. Dies schließt zwei Schreiboperationen zum gleichen Zeitpunkt ein.

Der Fokus seiner Vorstellung lag hauptsächlich auf Datentypen und nicht auf Funktionen. Jeder Datentyp gehört in eine der drei Kategorien.

  1. If a live object has a thread-safe type, it can't be the site of an API race.
  2. If a live object has a thread-compatible type, it can't be the site of an API race if it's not being mutated.
  3. The remaining objects are not thread-compatible.

Objekte, die in die erste Kategorie fallen, sind zum Beispiel atomare Variablen. Objekte der zweiten Kategorie unveränderliche Daten.

An dieser Stelle wird Geoffrey konkret in seinem Vortrag: Du sollst thread-safe-Datentypen oder unveränderliche Datentypen verwenden, falls dies notwendig ist.

Obwohl meine Zusammenfassung des 30-Minuten-Vortrags relativ kurz ist, fand ich den Talk sehr interessant, denn mir wurde deutlich klar: Wir benötigen eine wohl-definierte Begrifflichkeit für unsere Datentypen, wenn wir die Korrektheit von Multithreading-Programmen analysieren wollen.

Meinen 2-Tages-Workshop vor der Konferenz zu halten und die 5-tägige Konferenz zu besuchen, dies war sehr beeindruckend und um ehrlich zu sein auch sehr anstrengend. Klar, viele der Vorträge waren großartig und ich lernte sehr viel, aber das ist nicht mein Fazit. Vor allem genoss ich die vielen Diskussionen, die ich während und nach der Konferenz geführte habe. Dies vor allem, da ich zum ersten Mal persönlichen Kontakt mit meinen Diskussionspartnern hatte. Dafür bin ich Jon sehr dankbar, der mich zu CppCon eingeladen hat.

Am 12.11 und 13.11 dieses Jahres halte ich den selben Workshop, den ich auf der CppCon 2018 gehalten haben, als Schulung in deutscher Sprache in Rottenburg. Es sind noch Plätze frei und ich freue mich auf diese anspruchsvolle Schulung.

Hier gibt es mehr Details: Multithreading mit modernem C++. ()