C++ und moderne Parallelisierung unvereinbar?
-
Nach aktuellem Stand ist Threading in C++ broken aber nur weil die Implementierer der Bibliotheken nicht fähig genug sind
-
Es ist sicherlich richtig, dass die Konstrukte aus C++11 viel zu low-level sind um damit allein ohne viele Kenntnisse und Aufwand eine extrem skalierbare Anwendung zu schreiben. Viele Dinge die man dafür bräuchte kommen aber vermutlich mit C++17.
-
std schrieb:
Nach aktuellem Stand ist Threading in C++ broken aber nur weil die Implementierer der Bibliotheken nicht fähig genug sind
Der Punkt ist, dass der Standard broken ist (die VC++-Bibliotheken natürlich auch, aber das war ja sowieso zu erwarten).
TNA schrieb:
Es ist sicherlich richtig, dass die Konstrukte aus C++11 viel zu low-level sind um damit allein ohne viele Kenntnisse und Aufwand eine extrem skalierbare Anwendung zu schreiben.
Ich würde sagen, es ist unmöglich, weil viel zu fehleranfällig.
Viele Dinge die man dafür bräuchte kommen aber vermutlich mit C++17.
Nicht wirklich. Das sind Ad-Hoc-Lösungen, die genauso wenig funktionieren, weil C++ im Kern nicht dafür geeignet ist.
-
Ich muss sagen, dass ich den Artikel noch nicht ganz durch habe, aber ich arbeite dran. Was genau fehlt denn im Kern. Und was genau funktioniert denn an den vorgeschlagenen Lösungen nicht? Welche Sprache macht das besser?
Ich fand z.B. dieses Video recht ermutigend:
-
Also async ist tatsächlich völlig broken, da stimmte ich dir zu. future ist semi-broken, mit C++14 kann man es zumindest in Anwendungen gut einsetzen bei denen es nicht so auf die Performance ankommt. Probleme mit mutex, thread und condition_variable sehe ich jetzt so eigentlich nicht. Dass etwas zu low-level ist interessiert mich erst mal auch nicht, solange man sich den restlichen Kram draus bauen kann. Ein quasi immutable gibt's doch? (const) pure als function qualifier wäre natürlich schön, aber es ist jetzt nicht so dass man mit einer Sprache generell nicht ordentlich multi threading betreiben kann weil das fehlt?
Der Artikel hat sicher in vielen Punkten recht*, aber das Fazit kann ich nicht nachvollziehen:
I believe that the C++ language and its philosophy are in direct conflict with the requirements of parallel programming.
Dafür müsste schon irgendetwas Fundamentales im Memory-Model broken sein, das wichtige Optimierungen generell verhindert o.Ä.
* Ich wäre z.B. auch für einen backwards-compatibility-break, zumindest Teilweise, so dass man noch mit altem Code Interagieren kann, man aber prinzipiell alles was deprecated ist abstellen kann. Auch mit safe/unsafe sections aus D kann ich mich anfreunden.
-
Der Punkt ist wohl, dass bei echt funktionalen Sprachen ein gewisser Grad an auto parallelisierung möglich ist, da dort garantiert ist, dass Funktionen keine Nebenwirkungen haben. Das ist aber halt ein völlig anderer Ansatz.
-
cooky451 schrieb:
I believe that the C++ language and its philosophy are in direct conflict with the requirements of parallel programming.
Dafür müsste schon irgendetwas Fundamentales im Memory-Model broken sein, dass wichtige Optimierungen generell verhindert o.Ä.
Mal ein Beispiel: STM
In Haskell kannst du Funktionen ausführen und alle Writes auf Variablen zwischenspeichern (mit Monaden). Am Schluss machst du ein geordnetes Commit.
In C++ hast du keine Möglichkeit zu garantieren, dass alles mitgeloggt wird. Wenn das aktuell State of the Art ist, dann wird klar, dass das aktuell zu nichts führen kann. Vielleicht ist es möglich das mit Handarbeit nachzubauen in dem jede Funktion mit Write- und Read-Traits arbeitet, aber dann ruft jemand mal eine Funktion auf, die das zufällig nicht macht und schon hat man Bugs. Das von Hand zu machen ist schlimmer als OOP in C.
-
cooky451 schrieb:
Ein quasi immutable gibt's doch? (const)
Ich müsste nochmal darüber nachdenken, aber dürfte der Compiler mit immutable nicht noch mehr optimieren dürfen? (Achtung Spekulation:) sequential consistency, cache coherence, etc. sind ja recht aufwendig sicherzustellen, wenn die Daten bei jedem Funktionsaufruf geändert werden dürfen.
-
shreadder schrieb:
Ich würde dem so zustimmen und behaupte, C++ ist für Rechner mit >32 Kernen kaum noch geeignet. Ab dann ist mehr Abstraktion von Seiteneffekten zwingend notwendig und das ist in C++ nicht sinnvoll umsetzbar. Oder doch?
Ich verstehe den ersten Satz nicht. In dem Post ging es ja um Task-Based Parallelism und dass C++11 nur Thread-bases parallelism erlaubt. Ich sehe nicht, wo da die Anzahl der Kerne reinspielt. Wenn man viele Tasks hat und nur wenige Kerne sollten bei dem Thread-Modell die Anzahl der Probleme abnehmen, wenn man mehr Kerne an board holt, eben weil dann das Verhältnis Tasks<->Threads besser stimmt.
Was natürlich fehlt ist bei vielen Threads das "wait for any future to finish". Ich bin mir aber nicht sicher, ob da wirklich alle Hoffnung verloren ist, oder ob man da nicht eventuell noch nachträglich einen future_container reinbasteln kann.
Den Rest der Argumentation teil ich nicht. Das list sich mehr nach: "Weil Haskell restriktiver ist, stellen sich bim Threading viele Probleme nicht und daher ist Threading in Haskell geiler". Die Meinung kann man teilen, aber imho ist das nicht viel anders als in einer C++ Threading bibliothek zu sagen: "Funktionen dürfen keine Seiteneffekte haben.". Der einzige Unterschied ist, dass Haskell dieses garantiert. Das ist ein wichtiger Unterschied aber nicht game-changing, weil man dies auch durch einen Contract lösen kann.
-
otze schrieb:
Den Rest der Argumentation teil ich nicht. Das list sich mehr nach: "Weil Haskell restriktiver ist, stellen sich bim Threading viele Probleme nicht und daher ist Threading in Haskell geiler". Die Meinung kann man teilen, aber imho ist das nicht viel anders als in einer C++ Threading bibliothek zu sagen: "Funktionen dürfen keine Seiteneffekte haben.". Der einzige Unterschied ist, dass Haskell dieses garantiert. Das ist ein wichtiger Unterschied aber nicht game-changing, weil man dies auch durch einen Contract lösen kann.
Da würde ich dir prinzipiell zustimmen. Ich würde aber schon sagen: Wenn man weiß, dass eine Funktion keine Seiteneffekte haben darf, wäre es schön, dem Compiler sagen zu können, dass er das sicherstellen soll, ähnlich wie const bei einer Methode.
-
shreadder schrieb:
Das von Hand zu machen ist schlimmer als OOP in C.
Na ja ich weiß nicht, Kommunikation zwischen Threads ist im Normalfall doch relativ dünn, ich kann nicht beurteilen wie das in high-performance Servern aussieht, aber generell scheint mir das kein großes Problem zu sein.
shreadder schrieb:
Ich müsste nochmal darüber nachdenken, aber dürfte der Compiler mit immutable nicht noch mehr optimieren dürfen? (Achtung Spekulation:) sequential consistency, cache coherence, etc. sind ja recht aufwendig sicherzustellen, wenn die Daten bei jedem Funktionsaufruf geändert werden dürfen.
Nun, das schreiben in const-qualifizierte Objekte ist UB, insofern kann der Compiler hier gleich optimieren. Ein Problem ist eher, dass eine const& nicht zwangsläufig auch auf eine Konstante zeigen muss.
object o; foo(o); // takes const object& bar(o); // did o change?Wenn man jetzt davon ausgeht dass bar(o), foo(o) performanter wäre, und weiter davon ausgeht dass foo nicht durchsichtig ist, dann könnte das uU eine Optimierung verhindern. Aber, diese Kombination ist schon sehr unwahrscheinlich.
Doofer istvoid foo(object& a, const object& b) { for (stuff) { a.bar(); // Could change b! b.bar(); } }Weil man da vielleicht Sachen wieder aus dem Speicher laden muss, anstatt alles in Registern zu lassen. Den Fall wird es schon öfter geben (Erschreckend oft, eigentlich. Aber zumindest wird das in den meisten Fällen wohl geinlined, dann ist es egal.), aber das ist eher ein generelles aliasing Problem und hat nur indirekt etwas mit Threading zu tun. Nichts davon ließe sich nicht prinzipiell innerhalb von C++ fixen, insofern sehe ich da auch nicht den Weltuntergang wie der Autor des Artikels.
-
otze schrieb:
Die Meinung kann man teilen, aber imho ist das nicht viel anders als in einer C++ Threading bibliothek zu sagen: "Funktionen dürfen keine Seiteneffekte haben.".
Du verstehst nicht. Es geht nicht nur um pure.
Es geht darum, dass es in Haskell eine Abstraktion für Seiteneffekte aller Art gibt.Ich kann eine Monade schreiben, die es nur erlaubt, irgendwas in eine Queue zu pushen. Und alles was Funktionen machen können, die diese Monade bekommen ist etwas in eine Queue zu pushen. Was dabei passiert ist Sache der Monade.
Eben gerade das ermöglicht STM und richtige Tasks.
In C++ ist es gar nicht mal möglich, Tasks von einem Thread in einen anderen zu verschieben, wegen thread_local (was in eigentlich Quatsch ist, wenn wir von Tasks reden).
-
Microsoft's std::mutex is built on top the Concurrency Runtime. Which is a complete rewrite of the operating system primitives, emphasizing cooperative instead of pre-emptive threading.
-
shreadder schrieb:
wegen thread_local (was in eigentlich Quatsch ist, wenn wir von Tasks reden).
Ja, aber genau an dem Punkt geht deine und die Argumentation des Textes an dem Problem vorbei. C++ definiert nur Thread-Parallelismus und keinen Task Parallelismus. Jetzt zu argumentieren, dass thread_local probleme macht, wenn man tasks machen will ist ziemlich blöd, weil das gar nicht das Ziel der _Threading_ Bibliothek ist. Man kann sich darüber streiten ob nun Threads oder Tasks die bessere Abstraktion sind oder ob eventuell tasks mithilfe von Threads als weiterer Abstraktionslayer implementiert werden können (und damit meine ich nicht, wie der Artikel versucht, die C++-Threading Bibliothek anders zu implementieren, sondern eine Task Bibliothek auf der Threading-Bibliothek aufzubauen), aber das alles sind Nebenkriegsschauplätze.
Und was die Monaden angeht: ja stimmt, aber das sagt der Artikl nicht, der nennt explizit pure als Argument. Monade kommt exakt 0 mal vor. Threading in funktionalen Sprachen ist nebenbei auch kein Selbstläufer. Ein Kollege von mir arbeitet an einem Forschungsprojekt(70m Luftlinie von meinem Büro) wo es um parallelisierung von Haskellcode geht (und nein, nicht auto-parallelisierung). Aus den theoretischen Vorteilen praktische zu machen ist eben auch nicht so einfach.
-
shreadder schrieb:
...
Evtl vorher mal selber Struppis Bibel lesen?
-
otze schrieb:
Man kann sich darüber streiten ob nun Threads oder Tasks die bessere Abstraktion sind oder ob eventuell tasks mithilfe von Threads als weiterer Abstraktionslayer implementiert werden können [...] aber das alles sind Nebenkriegsschauplätze.
Man sieht ja wohin das führt:
Regel 1: Verwende kein thread_local
Regel 2: Verwende nicht direkt thread/mutex/...
Regel 3: Verwende kein async/future, weil broken
Regel 4: Schreibe nicht in nicht-task-lokale Variablen
Regel 5: Rufe keine Funktion auf, die in nicht-lokale-Variablen schreibt (da fängts schon an)
Regel 6: vermeide const in Tasks, mach Objekte entweder const-only (immutable) oder mutable-only (d.h. ohne const Zugriff), da sonst const != konstant
...Also eine typische C++-Verbotsrichtline.
Aus den theoretischen Vorteilen praktische zu machen ist eben auch nicht so einfach.
Parallelisierung war noch nie einfach, aber C++ kommt da an eine Grenze, wo selbst einfache Task-Modelle nicht vernünftig (ohne Garantien und nur mit Verboten) umsetzbar sind.
-
Was du uns also eigentlich sagen willst ist nicht, dass C++ irgendein besonderes fundamentales Problem mit Threading hätte, sondern dass du lieber in einer Sprache arbeiten würdest in der deine "Regeln" schon integriert sind?
-
cooky451 schrieb:
Was du uns also eigentlich sagen willst ist nicht, dass C++ irgendein besonderes fundamentales Problem mit Threading hätte, sondern dass du lieber in einer Sprache arbeiten würdest in der deine "Regeln" schon integriert sind?
Es ist wie mit C und OOP/Templates: Prinzipiell machbar, aber ohne Sprachunterstützung einfach unpraktikabel.
-
shreadder schrieb:
cooky451 schrieb:
Was du uns also eigentlich sagen willst ist nicht, dass C++ irgendein besonderes fundamentales Problem mit Threading hätte, sondern dass du lieber in einer Sprache arbeiten würdest in der deine "Regeln" schon integriert sind?
Es ist wie mit C und OOP/Templates: Prinzipiell machbar, aber ohne Sprachunterstützung einfach unpraktikabel.
Ich mach's mal wie im Projekte-Forum.
Um als Anti-C++-Troll qualifizierte Antwort im C++-Forum zu bekommen, musst Du einen angemessenen Stundenlohn anbieten. Wir lösen Dein Problem, wenn Du bereit bist, zu zahlen.
-
Ich wiederhole meine Frage von vorhin: Wie würde STM in C++ aussehen?