Veröffentlicht am: 12 Februar 2019
Experte:
Klaas van Gend MSc
Trainer
Lesen Sie mehr über Klaas van Gend
Aktie

In der Praxis ist das Schreiben paralleler Software immer noch eine schwierige Aufgabe. Man stößt immer wieder auf unvorhergesehene Probleme, wenn man nicht jede einzelne Ebene des Problems versteht, sagt Klaas van Gend.

Im Jahr 2019 sollte es einfacher denn je sein, Multicore-Software zu schreiben. Moderne Programmiersprachen wie Scala und Rust reifen heran, Programmierrahmen werden immer einfacher zu verwenden und C # und das gute alte C ++ nehmen Parallelität als Teil ihrer Standardbibliotheken an.

In der Praxis ist es jedoch immer noch ein chaotischer Prozess. Das Ganze erweist sich als schwierig zu synchronisieren und wenn die Software einmal funktioniert, läuft sie auf einem Multicore-Prozessor meist nur wenig oder gar nicht schneller. Und zu allem Übel neigt sie auch noch dazu, alle möglichen schwer fassbaren Fehler nachzuweisen.

Parallele Programmierung ist einfach ein sehr schwieriges Thema, bei dem man auf alle möglichen subtilen, unerwarteten Effekte stößt, wenn man nicht versteht, was auf allen Ebenen passiert, erklärt Klaas van Gend, Softwarearchitekt bei Sioux. Ich habe gehört, dass man Knoten auf einem Supercomputer mit Hilfe virtueller Maschinen teilen kann. Aber sie ruinieren sich gegenseitig den Prozessor-Cache; sie kommen sich nur in die Quere.

'At university it was all about Dijkstra, which means mutexes, locks and condition variables. But the moment you turn on a lock, you only ensure that the code is executed on one core whilst the others temporarily do nothing. So, you really only learn how not to program for multicore.'

Van Gend zufolge liegt das Problem darin, dass viele Entwickler während ihrer Informatikausbildung keine pädagogisch fundierte Grundlage erhalten haben. An der Universität ging es nur um Dijkstra, d.h. Mutexe, Sperren und Bedingungsvariablen. Aber in dem Moment, in dem Sie eine Sperre einschalten, sorgen Sie nur dafür, dass der Code auf einem Kern ausgeführt wird, während die anderen vorübergehend nichts tun. Man lernt also eigentlich nur, wie man nicht für Multicore programmiert“, sagt er.

Aus diesem Grund hat Van Gend die Multicore-Schulung seines alten Arbeitgebers Vector Fabrics aus der Mottenkiste geholt. Bis vor ein paar Jahren konzentrierte sich Vector Fabrics auf die Entwicklung von Werkzeugen, um einen Einblick in die Gefahren von paralleler Software zu geben. Zusammen mit cto Jos van Eijndhoven und anderen Mitarbeitern bot Van Gend Schulungen zu diesem Thema an. Das Unternehmen ging 2016 in Konkurs, aber Van Gend hat in seiner jetzigen Beschäftigung erkannt, dass das Problem immer noch aktuell ist. Nachdem er den Kurs bei seiner jetzigen Anstellung noch einmal gegeben hat, bietet er ihn jetzt auch unter der Flagge des High Tech Institute für Dritte an.


Klaas van Gend ist der Dozent der 3-tägigen Schulung‚Multicore-Programmierung in C++‚.

Ein Problem auf jeder einzelnen Ebene

Einer der wichtigsten Punkte beim Schreiben von paralleler Software ist es, herauszufinden, wie man es schafft, dass sie auf mehreren Ebenen funktioniert, erklärt Van Gend. Er macht diesen Punkt immer mit einem einfachen Beispiel deutlich: Conway’s Game of Life, der zelluläre Automat, bei dem die Kästchen in einem Gitter mit jeder neuen Runde schwarz oder weiß werden, je nach dem Status ihrer unmittelbaren Nachbarn. Auf der untersten Ebene Ihres Programms müssen Sie überprüfen, welchen Status Ihre Nachbarzellen haben. Das können Sie mit zwei for-Läufen/Schleifen tun. Und dann haben Sie eine Schleife für eine komplette Zeile und darüber für den kompletten Satz von Zeilen.‘

Die meisten Programmierer beginnen mit der Parallelisierung bei den unteren Schleifen. Das ist ganz natürlich, denn das ist ein Teil des Codes, den Sie noch verstehen können, der noch in Ihren Kopf passt. Aber es macht viel mehr Sinn, auf einer höheren Ebene anzusetzen und die äußere Schleife zu nehmen. Dann teilen Sie das Feld in mehrere Blöcke von Zeilen auf und Ihre Arbeitslast pro Kern ist viel größer.‘

Wenn Sie die Dinge auf diese Weise betrachten, wird schnell klar, dass es viele Dinge gibt, auf die Sie achten müssen. Es gibt auch Programme, bei denen die Belastung variabel ist. Wir haben zum Beispiel eine Übung zur Berechnung der ersten hundert Primzahlen. Zwischen der Primzahl zehn und der Primzahl neunundneunzig liegt bereits mehr als ein Faktor von hundert. Dann müssen Sie den Lastausgleich berechnen.‘

Es gibt auch Unterschiede bei dem, was Sie parallelisieren können: die Daten oder die Aufgabe. Die Datenparallelität eignet sich im Allgemeinen für sehr spezifische Anwendungen, aber ansonsten finden Sie schnell eine Art Dekomposition Ihrer Aufgabe. Dies kann mit einem Akteursmodell oder mit einem Kahn-Prozessnetz geschehen, wobei die Datenparallelität wiederum ein Teil davon sein kann. In der Praxis werden Sie sehen, dass Sie immer mit Mischformen enden.‘ Seit einiger Zeit geht es nicht mehr nur um Algorithmen, sondern die zugrunde liegende Hardware spielt eine wichtige Rolle. Wenn der Programmierer zum Beispiel die Caching-Mechanismen des Prozessors nicht berücksichtigt, kann das Problem der falschen Aufteilung entstehen. Ich habe gesehen, wie riesige Anwendungen in die Knie gezwungen wurden“, sagt Van Gend. ‚Nehmen wir an, Sie haben zwei Threads, die beide Metriken sammeln. Wenn Sie diese unordentlich aufteilen, können Zähler von verschiedenen Threads in derselben Cache-Zeile landen. Die beiden Prozessoren müssen dann gleichzeitig mit demselben Cache arbeiten und Ihr Cache-Mechanismus schleppt die Zeilen ständig hin und her. Das senkt die Leistung erheblich.‘ Aus diesem Grund ist Van Gend auch skeptisch, was den Einsatz von Hochsprachen in Multicore-Designs angeht. Sie haben die Tendenz, die Details des Speicherlayouts zu abstrahieren. Bei einer Sprache wie C ++ ist es immer noch sehr klar, dass Sie mit grundlegenden Primitiven arbeiten, und Sie können das deutlich sehen. Aber hochrangige Sprachen überfliegen oft vorschnell die Details der Datentypen, was bedeutet, dass das System nie wirklich reibungslos laufen kann.‘

'If you only partially understand the model, then you will run into problems. It works well for certain specific situations, but it can’t be used everywhere.'

Van Gend ist auf jeden Fall der Meinung, dass neue Sprachen kein Wundermittel für das Multicore-Problem sind. In der Regel gehen sie von einem bestimmten Ansatz aus, der gar nicht gut /notwendig gut zur Anwendung passen muss. Sprachen wie Scala oder Rust stützen sich stark auf das Actor-Modell, um das Threading zu erleichtern. Wenn Sie das Modell nur teilweise verstehen, werden Sie auf Probleme stoßen. Es funktioniert gut für bestimmte Situationen, aber es kann nicht überall eingesetzt werden.‘

Die falsche Annahme

Die modernen Versionen von C ++ bieten auch Ergänzungen, die eine parallele Programmierung ermöglichen. Die ‚Atomics sind jetzt zum Beispiel vollständig eingebunden. Damit können Sie oft Daten austauschen, ohne etwas zu stoppen. Wir arbeiten auch an einer Bibliothek, in der das Sperren für den Benutzer überhaupt nicht mehr sichtbar ist. Wenn es notwendig ist, geschieht es, ohne dass der Benutzer es sieht und auch mit dem kürzest möglichen Umfang, so dass die Sperre so schnell wie möglich aufgehoben wird‘, sagt Van Gend. Auch hier ist es wichtig, dass Sie verstehen, was Sie tun. Van Gend ist zum Beispiel weniger begeistert von der Erweiterung der Standardbibliothek um die Ausführungsrichtlinien in C++17. Damit können eine Reihe grundlegender Algorithmen wie Find, Count, Sort und Transform parallel ausgeführt werden, indem einfach ein zusätzlicher Parameter im Funktionsaufruf hinzugefügt wird. Aber das funktioniert nur für einige akademische Beispiele; in der Praxis wird es nicht funktionieren“, sagt Van Gend. Diese Api’s basieren auf einer falschen Grundannahme. Und bei der C # api haben sie denselben Fehler wieder gemacht.‘

Das Problem ist, dass Sie bei diesem Ansatz nur einzelne Schritte machen können. Es stimuliert die individuelle Parallelisierung der einzelnen Operationen. Sie teilen Ihren Datensatz bei jeder Operation neu auf, tun etwas, machen ihn dann wieder ganz und gehen zur nächsten Operation über. Es ist immer parallel, sequentiell, parallel, sequentiell, und so weiter. Das ist konzeptionell sehr klar, aber Sie müssen die ganze Zeit warten, bis alle Threads fertig sind und dann weitermachen. Das ist eine komplette Zeitverschwendung. Mit einer Bibliothek wie Openmp hingegen wird der gesamte Satz von Operationen einfach auf die Threads verteilt. Das bedeutet also, dass Sie nicht unnötig warten müssen.‘

'The funny thing is that Microsoft also played a large part in the Par Lab at the University of Berkeley. This has resulted in a fairly large collection of design patterns for parallel programming, which I deal with extensively in the training course.'

Der gcc-Compiler bietet keine Unterstützung für diese parallelen Funktionen. Visual Studio schon, denn die Zusätze kommen schließlich von Microsoft. Das Lustige daran ist, dass Microsoft auch eine große Rolle im Par Lab an der Universität von Berkeley gespielt hat. Daraus ist eine ziemlich große Sammlung von Entwurfsmustern für die parallele Programmierung entstanden, die ich in der Schulung ausführlich behandle. Microsoft hat gezeigt, dass sie genau wissen, wie man es richtig macht.‘

Dieser Artikel wurde von Pieter Edelman geschrieben, Tech-Redakteur von Bits&Chips.

Recommendation by former participants

By the end of the training participants are asked to fill out an evaluation form. To the question: 'Would you recommend this training to others?' they responded with a 8.6 out of 10.

Das High Tech Institute organisiert ein- bis zweimal im Jahr die Schulung Multicore-Programmierung in C++. Außerdem ist es für firmeninterne Schulungen verfügbar.