Buchrezension: Parallelprogrammierung unter dotNET
Das Thema Paralllel Computing beschäftigt mich hier auf diesem Blog schon seit mehr als einem Jahr. Und so kurz vor Weihnachten hab ich die perfekte Geschenkempfehlung für alle dotNET-Entwickler, die sich selbst oder andere in Form eines Buches eine Freude machen wollen.
Sein Titel (also der des Buches): “Parallel Computing in .NET”. Sein Autor: Marc André Zhou. Sein Umfang: rund 360 Seiten. Seine ISBN: 978-3-86802-038-0. Seine Mission: Wie mache ich dotNET-Entwicklern klar, dass sie von der sequentiellen Programmierung umsteigen sollten auf die parallele. So gesehen finde ich den Titel ein wenig unglücklich gewählt, wenngleich es in dem Buch natürlich auch um Parallel Computing geht. Doch zuvorderst sollen die Leser dieses gedruckten Leitfadens etwas über Multithreaden und Co. lernen.
Und hierfür hat der Autor wirklich sehr viel an Informationen zusammengetragen. Das beginnt mit einer kleinen Aufklärungsrunde in Sachen Parallel Computing (sic!), die sogar die parallelen Programmiermodelle betrachtet. Anschließend wird es ein wenig theoretisch, indem Zhou so Dinge wie das Amdahlsche und das Gustafson-Gesetz betrachtet. Das hilft auf jeden Fall für ein grundsätzliches Verständnis, wann die Parallelprogrammierung sinnvoll ist – und wann nicht.
Nach weiteren allgemeinen Informationen rund um die Regeln des Parallel Computing, aber auch des Projektmanagement und den Modellierungsmöglichkeiten geht es dann nahtlos über in das Basiswissen rund um Threads unter .NET. Dies ist jedoch schon wieder fast veraltet, denn mit der Einführung von .NET 4.0 wird kein Mensch mehr von .NET-Threads und deren manuellem Erstellen reden.
Wie .NET-Entwickler mit VTune Bottlenecks finden können
Vergangene Woche auf der Microsoft TechEd Europe 2009 hatten wir die Gelegenheit, uns von Rami Radi zeigen zu lassen, wie .NET-Entwickler mithilfe des Profiling- und Samplingtools VTune Performance Analyzer Schwachstellen in ihren Anwendungen aufspüren und beheben können. Dabei geht es beispielsweise sehr oft um Schleifenkonstrukte, die unverhältnismäßig viel CPU-Zeit in Anspruch nehmen, was mit den richtigen Tricks gar nicht notwendig wäre. Für alle, die also in Zukunft mehr aus ihrer Software herausholen wollen, sei dieser Video-Workshop wärmstens empfohlen.
Screencasts zur Parallelprogrammierung unter .NET 4
Der Name Dariusz Parys ist regelmäßig wiederkehrenden Besuchern dieses Blogs ein bekannter Name: Entweder stellt er sich unseren Fragen oder veranstaltet gemeinsam mit Intel TechTalks oder nimmt Screencasts auf, die er zum Wohle aller online stellt. Und genau die haben es mir besonders angetan, da man in relativ kurzer Zeit eine Menge über das parallele Programmieren unter .NET 4 mithilfe von Visual Studio 2010 lernen kann.
Daher hat’s mich sehr gefreut, dass ich heute Morgen zwei “neue” Screencasts” auf Channel 9 gefunden habe. Das erste beschäftigte sich mit der Frage, wie sich die Klasse Parallel dazu nutzen lässt, mehrere Funktionsblöcke gleichzeitig auf mehreren Prozessorkernen ablaufen zu lassen. Hierzu stellt die Parallel-Klasse ein Methode zur Verfügung, die sich Invoke nennt.
Praktisch an dieser Form der Parallelisierung ist die Tatsache, das die gleichzeitig ablaufenden Funktionen nicht synchronisiert werden müssen. Der Mainthread wird nämlich erst dann fortgesetzt, wenn die parallelen Threads fertig sind. Darum kümmert sich die Concurrency Runtime.
Screencast Nummer 2 zeigt in nur sechs Minuten, wie sich Tasks definieren lassen, die einen Rückgabewert liefern. Auch das hat den großen Vorteil, dass der Mainthread nicht unnötig warten muss, bis ein oder mehrere parallel ablaufende Aufgaben fertig sind. Die Übergabe des Returnwertes sorgt nämlich für die Synchronisation des Programmablaufs. Schön daran ist zudem, dass sich auf diesem Weg mehrere Tasks verknüpfen lassen und so ein Folgetask mit dem Rückgabewert des Vorgängertasks “gefüttert” werden kann. Es lassen sich aber nicht nur einfache Werte, sondern auch Objekte übergeben.
Screencasts zur Parallelen Programmierung unter .NET 4
Auf Channel 9 habe ich gerade drei sehr interessante Screencasts entdeckt, die Dariusz Parys dort eingestellt hat. Anhand sehr anschaulicher Beispiele zeigt Dariusz folgende Dinge:
In diesem Screencast geht es vor allem um eine Kernaussage: Vergesst Threads und denkt ab sofort in Tasks, also in einer abstrahierten Form von Threads. Warum das so ist und welche Mechanismen der Threadpool des .NET-4-Frameworks hierfür bereithält, lernt ihr in dem Sechsminüter.
Task Parallel Library: Task Continuations
Dieser Screencast behandelt die Frage, wie sich einzelne Aufgaben (Tasks) mithilfe der Task-Klasse verketten lassen, um weitere Ereignisse möglichst einfach zu parallelisieren. Dies geschieht mithilfe des Aufrufs task.ContinueWith().
Task Parallel Library: Exception Handling
In diesem Screencast geht es um die Fehlerbehandlung innerhalb von Tasks. Hierzu gibt es die Möglichkeit, per AggregateException mögliche Fehler zur Laufzeit abzufangen.
Ihr seht also: drei gute Gründe, euch die Screencasts anzusehen. Viel Spaß dabei!
Zurück zum Multicore-Blogging: Post-Konferenzing
Die vorige Woche stand ganz im Zeichen der Multicore-Konferenzen. Los ging’s am Montagabend in Salzburg bei Bier und Brez’n im Pitterkeller des ehrwürdigen Crown Plaza, wo ich mit netten Kollegen zusammensaß und über dies und das geplaudert habe. Das Dienstagsprogramm rankte sich dann um das Thema Multicore-Programmierung und Parallel Studio, aber auch um andere Dinge wie das Nero-Engagement und Microsoft-Bemühungen.
Mittwoch ging es dann wieder nach München, damit ich am Donnerstag auch ja pünktlich auf der .NET-Multicore-Konferenz sein konnte, um von dort aus live zu bloggen. Was auch gut geklappt und zudem viel Spaß gemacht hat. Und es war nicht nur informativ, sondern auch sehr unterhaltsam, Ralf Westphal und Bernd Marquardt gemeinsam auf der Bühne zu erleben. Denn neben der geballten fachlichen Kompetenz konnten die zwei auch mit Entertainment-Qualitäten überzeugen. Stellenweise habe ich mich an meine Jugend erinnert gefühlt, in der Statler und Waldorf auf dem Muppet-Show-Balkon Ähnliches abgeliefert haben.
prio.powerday: Microsoft Concurrency Coordination Runtime
Direkt nach Bernds Vortrag ist Ralf Westphal wieder dran und erzählt etwas zur Concurrency Coordination Runtime (CCR). Im Gegensatz zu Bernd geht es bei Ralfs Vortrag weniger um die Leistungssteigerung parallel programmierter Anwendungen als vielmehr um die optimale asynchrone Datenverarbeitung im parallelen .NET-Umfeld.
Die CCR ermöglicht ein neues Programmiermodell für parallel laufende Anwendungen. Ein sehr schöner Ausspruch von Ralf lautet übrigens: “Vergesst Threads!”, da Threads synchronisiert werden müssen, was Ralf nicht wirklich mag. Um das besser verdeutlichen zu können, schreibt er in Echtzeit verschiedene kleinere Programme. Sehr schlau!
Das Besondere an der CCR ist das Warteschlangenprinzip: Jeder Befehl oder jedes Datum wird im Hintergrund aus der Warteschlange ausgelesen und verarbeitet, sobald eine Ressource frei ist. Damit müssen keine expliziten Threads erzeugt und wieder beendet werden, da sich die CCR automatisch darum kümmert. Hierfür steht die DispatcherQueue-Klasse zur Verfügung.
Die CCR erlaubt sowohl den Einsatz des Standard-Threadpools als auch eigene Threadpools auf Dispatcher-Basis.
prio.powerday: Parallele Programmierung unter .NET 3.5
Nach der Mittagspause ist Bernd Marquardt ohne sein Alter Ego an der Reihe. Sein Thema: Parallele Programmierung mithilfe des .NET-Frameworks 3.5, und zwar per TPL.
Folgende Elemente kommen zum Einsatz:
- Parallele Erweiterungen für LINQ = PLINQ
- Schleifen parallelisieren: Die hierzu gehörige Klasse heißt System.Threading.Parallel. Schleifen werden auf mehrere Threads aufgeteilt werden, aber nur, wenn die einzelnen Schleifendurchläufe unabhängig voneinander sind. Dabei müssem die Indezies alle gleich sein. Am Ende der Schleife werden alle Threads synchronisiert. Fazit: Schleifenparallelisierung lohnt sich nur bei komplexen und großen Schleifen! Wichtig ist auch die richtige Verteilung einer Schleife auf die vorhandenen Prozessorressourcen. Dies gelingt, indem die einzelnen Schleifendurchläufe in kleinen Portionen verarbeitet werden.
- Aggregationen: Hierbei geht es um das Zusammenfassen von Ergebnissen. Dabei ist Locking meist erforderlich. Zwischenwerte werden über sogenannte ThreadLocateState weitergegeben. Der Ablauf sieht folgendes vor: Initialisierung, Zwischenspeichern der einzelnen Thread-Ergenisse und das abschließende Zusammenführen der Zwischenwerte inklusive Locking. Übrigens: Aufgrund des Zusammeführens der einzelnen Thread-Zwischenergebnisse kann ist bei der parallelen Ausführung zu Rundungsfehlern kommen und das Ergebnis weicht vom sequentiellen Programm ab.
prio.powerday: Multithreading mit .NET-Bordmitteln
Und weiter geht’s. Bernd und Ralf gehen jetzt näher auf das Thema Multithreading mit .NET-Bordmitteln ein. Hierbei gibt es verschiedene Aspekte:
- Statisches Threading birgt vor allem ein Problem: Es gibt statische Variablen, die von mehreren Threads gleichzeitig schreibend genutzt werden können.
- Threading mit instanzierten Klassen: Jeder Thread hat seine eigene Instanz, also zum Beispiel seine eigenen Variablen, was natürlich ein wenig mehr Ressourcen kostet.
- Steuerung von Threads: Hierbei geht es um die Priorisierung von Threads sowie das Starten, Pausieren und Stoppen von Threads. Die Herausforderung ist hier der jeweils richtige Zeitpunkt, ein Thread zu manipulieren. Außerdem muss man feststellen können, ob ein Thread gewollte beendet wurde. Dies kann per Bool’scher Variable erreicht werden.
prio.powerday: Einführung in die parallele Programmierung
Jetzt sind mit kaum einer halben Stunde Verspätung Ralf Westphal und Bernd Marquardt dran. Sie wollen uns erst mal erzählen, was parallele Programmierung eigentlich bedeutet.
Zunächst geht es um echte Basics wie Multithreading mit Single- und Multicores. Fakt ist: Multithreading allein bringt gar nichts, dafür sind mehrere Prozessoren und die Zerlegung eines Programms in parallel zu verarbeitenden Bereiche notwendig.
Eine weitere Herausforderung sind Latenzzeiten, die sich aus asynchroner Prozessorverarbeitung ergeben. Das lässt sich mithilfe einer Kombination aus Multithreading und Multitasking auf mehreren Prozessoren optimieren.
Es gilt aber zugleich, den Durchsatz zu erhöhen. Dies gelingt ausschließlich auf Multicore-Systemen.
Es gibt ein grundsätzliches Problem bei der Parallelverarbeitung: den Zugriff auf gemeinsame Ressourcen. Diese Herausforderung meistern Zugriffskontrollen mithilfe explizierter Sperren. Hierfür sperren zum Beispiel einzelne Tasks gemeinsamen Speicher. Allerdings müssen die Zeitspannen und die Granularität dieser Sperren genau passen. Ein große Gefahr hierbei sind übrigens mögliche Deadlocks, die gerade bei parallel programmierten Anwendungen erst zur Laufzeit auftreten. Deadlocks lassen sich durch dieselbe Sperrreihenfolge vermeiden.
prio.powerday: Multicore-Programmierung unter .NET
Kaum dass ich aus Salzburg zurück bin, sitze ich hier im Konferenzhaus in der Lazarettstraße, München, und nehme an der Multicore-Konferenz für .NET-Entwickler teil, die im Rahmen der prio.powerdays stattfindet.
Den Anfang macht Rami Radi von Intel, der die Einführungsrede hält und die Anwesenden auf das Thema Multicore einstimmen will.
Rami beginnt seinen Vortrag mit Hardware, nicht mehr Software. Namentlich spricht er über den Core i7-Prozessor, der ja im höchsten Maße multithreading-tauglich ist. Core i7 verarbeitet nämlich pro Prozessorkern zwei Threads simultan.
Die nächste Folie zeigt die ganze Vielfalt der Intel-Entwicklertools, über die ich hier schon ausführlich berichtet habe, also über Compiler, VTune etc.
So, jetzt wird’s ernst. Rami spricht über die Parallelprogrammierung im .NET-Umfeld, was seiner Meinung nach nicht einfach ist (wären wir sonst hier?). Zu den Herausforderungen gehören unter anderem falsch genutzter Speicher, zu viele und zu wenige Threads, gemeinsam genutzte Speicherbereiche, Load Balancing und viele andere. Wie gut, dass es für diese “Issues” die passenden Tools gibt wie den VTune Performance Analyzer.
