Videosessions: TechTalks mit Intel und Microsoft

veröffentlicht von Michael Hülskötter am 27. Juli 2009 (0) Kommentare

Der Juni 2009 stand bei Intel und Microsoft ganz im Zeichen der parallelen Programmierung (woran sich im Juli natürlich nichts geändert hat). Daher fanden zu diversen Terminen in diversen Städten sogenannte TechTalks statt, die erfahrungsgemäß immer sehr gut besucht sind.

Ich war auf der Veranstaltung in München und konnte mich selbst davon überzeugen: der Raum war proppenvoll, da das Thema wohl für viele sehr spannend ist. Der Bedarf nach mehr Wissen zum Thema Parallelprogrammierung ist sehr groß, und mindestens genauso groß sind die Lücken, die sich dazu bei vielen Entwicklern auftun.

Nun, diese Defizite konnten Intel und Microsoft hoffentlich ein wenig beheben. Und wer selbst nicht die Gelegenheit hatte, sich vor Ort ein eigenes Bild davon zu machen, was es mit der parallelen Programmierung auf sich hat, dem seien die drei Videoclips empfohlen, die Microsoft online gestellt hat.

Allerdings sollte man sich viel Zeit nehmen, denn die Videokollektion umfasst mehr als drei Stunden Anschauungsmaterial zu diversen Themen:

  • Warum wird Parallel Computing immer wichtiger?

Deshalb: Popcorn und Coke bereitstellen, Füße hochlegen und Videos gucken. Und zwar welche der lehrreichen Sorte, Powerpoint-Folien inklusive.

Kategorien : Multicore Tags : , ,

Screencasts zur Parallelen Programmierung unter .NET 4

veröffentlicht von Michael Hülskötter am 24. Juni 2009 (0) Kommentare

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:

Task Parallel Library: Tasks

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!

Kategorien : Multicore Tags : , ,

Workshops und Tutorials für Parallelprogrammierer

veröffentlicht von Michael Hülskötter am 6. Mai 2009 (0) Kommentare

Was mir als Software-Dev-Blogger wirklich am Herzen liegt, ist ein möglichst hoher Nutzwert, den meine Blog-Beiträge bieten sollen. Daher haben sich im Laufe der letzten gut sechs Monate einige Workshops und Tutorials angesammelt, die zeigen, wie die parallele Programmierung vonstattengeht. Und damit diese Tipps und Tricks nicht in Vergessenheit geraten, folgt jetzt die ultimative Tutorial-Sammelliste:

>> Los ging es mit den bekannten Multithreading-Konzepten OpenMP, APIs und Intel Threading Building Blocks und der Frage, welche der drei Methoden sich zum Threaden am besten eignet.

>> Dann folgten fünf Multicore-Programmierregeln, die zeigen sollen, mit welchen Anforderungen Programmierer und Software-Entwickler konfrontiert werden, wenn sie sequenziellen Code in parallele Anwendungen überführen wollen. Ich sage nur: Denke parallel!

>> Ein wichtiges, weil fundamentales Konzept der Multicore-Programmierung ist der Unterschied zwischen Daten- und Aufgabenparallelität. Diesen zu verstehen ist die erste Programmiererpflicht, wenn es darum geht, skalierbare Multithread-Anwendungen zu erstellen.

>> Wie viele Schritte muss man gehen, um aus seriellem Quellcode parallel ablaufenden zu machen? Genau vier. Rein abstrakt betrachtet zumindest. Auch hierfür habe ich den passenden Workshop parat, der die vier Stufen der Parallelprogrammierung genauer beleuchtet.

>> Was aber, wenn ich zwar weiß, wie ich parallel programmieren soll, ich aber keinen Schimmer davon habe, welche Stolperfallen dabei auf mich warten? Da heißt es dann meinen Beitrag lesen, welche Fehler bei der Parallelprogrammierung der Entwicklergemeinde drohen und wie sich diese (die Fehler, nicht die Entwickler) umgehen lassen.

weiterlesen…

Kategorien : Multicore Tags : , ,

OpenMP: Schleifen anpassen für Multithreading-Ausführung

veröffentlicht von Michael Hülskötter am 15. April 2009 (1) Kommentar

Gestern habe ich mit einer neuen Serie angefangen, die sich intensiv mit dem Thema OpenMP beschäftigt. Im ersten Teil ging es sehr fundamental um die Voraussetzungen, die eine Schleife erfüllen muss, um per OpenMP multithreading-tauglich zu sein. Heute geht es um die Frage, welche Dinge zu beachten sind, damit eine Schleife ordnungsgemäß in mehrere Threads aufgeteilt werden kann.

Zunächst einmal: Das Threaden von Schleifenkonstrukten bedeutet nichts anderes, als dass unabhängige Schleifeniterationen auf mehreren Threads gleichzeitig ausgeführt werden können, was natürlich Rechenzeit pro Takt spart. Hierzu wird die Schleife in eine neue Form gebracht, die das Parallelisieren derselben überhaupt erst ermöglicht. Dies ist aber nur umsetzbar, wenn die Schleife keine Abhängigkeiten aufweist.

Daher muss man als Entwickler zunächst einmal mit einem passenden Tool wie VTune Performance Analyzer diejenige Schleife finden, die insgesamt die meiste Rechenzeit verschlingt. Anschließend wird diese umstrukturiert, um festzustellen, dass keine iterationsübergreifenden Abhängigkeiten bestehen. Erst dann sollte diese Schleife mithilfe eines OpenMP-Pragmas parallelisiert werden.

weiterlesen…

Kategorien : Multicore Tags : , ,

OpenMP: fünf Bedingungen für parallele Schleifen

veröffentlicht von Michael Hülskötter am 14. April 2009 (1) Kommentar

Das Thema OpenMP steht bei vielen Lesern dieses Blogs hoch im Kurs. Das liegt zum einen an der Parallel-Computing-Seite von MSDN, auf der ein entsprechender Beitrag lange verlinkt war. Zum anderen findet Google meine OpenMP-Bemühungen wohl ganz nett, zumindest steht besagter Artikel im deutschsprachigen Index auf dem fünften Platz.

Daher starten wir heute mit einer Serie, die sich mit kleineren und größeren Aspekten der OpenMP-basierten Programmierung beschäftigt. Also mit solchen Fragen wie:

  • Welche Bedingungen müssen erfüllt sein, damit OpenMP-basierende Schleifen überhaupt in parallelen Threads ausgeführt werden können?
  • Welche Dinge sind zu beachten, damit eine Schleife ordnungsgemäß in mehrere Threads aufgeteilt werden kann?
  • Wie lässt sich möglicher Threading-Overhead vermeiden?
  • Wie kann man das Optimum aus OpenMP herausholen?
  • Wie lassen sich sinnvoll OpenMP-Bibliotheksfunktionen und -Umgebungsvariablen einsetzen?
  • Wie geschieht das Kompilieren und Debuggen mithilfe von OpenMP?
  • Wovon hängt eine maximale Multithread-Leistung auf OpenMP-Basis ab?

Welche Bedingungen müssen erfüllt sein, damit OpenMP-basierende Schleifen überhaupt in parallelen Threads ausgeführt werden können?

1. In der Version 2.5 müssen Schleifenvariablen vom Typ vorzeichenbehafteter Integer sein. Mit der OpenMP-Spezifikation 3.0 ist diese Beschränkung weggefallen.

2. Die Vergleichsoperation muss in der Form loop_variable <, <= oder >= loop_invariant_integer sein.

3. Der Inkrementteil (z.B. i++) der for-Schleife muss additiv oder substraktiv sein, und zwar mit einem schleifeninvarianten Wert.

4. Ist die Vergleichsoperation vom Typ < oder <=, muss die die Schleifenvariable bei jeder Iteration erhöht werden. Umgekehrt muss die Variable dekrementiert werden (also bei > oder >=).

5. Die Schleife muss zwingend einen Eintritt und einen Austritt haben. Daher sind nicht erlaubt: Sprünge aus der Schleife heraus bzw. von außen in die Schleife hinein. Das gilt beispielsweise für goto- oder break-Anweisungen oder für Ausnahmebehandlungen. Eine Ausnahme dieser Regel stellt die exit-Anweisung dar, die die komplette Anwendung beendet.

Diese Bedingungen müssen aus Kompilierungsgründen eingehalten werden. Andernfalls kann keine automatische Parallelisierung erfolgen.

Kategorien : Multicore Tags : , ,

Workshop: Alles über .NET-Threads – Teil 4

veröffentlicht von Michael Hülskötter am 16. März 2009 (0) Kommentare

Eine neue Woche beginnt, und im selben Atemzug endet meine vierteilige Serie zum Thema .NET-Threads. So habe ich im ersten Kapitel über das Erzeugen von Threads geschrieben, im zweiten Teil mich über das Verwalten derselben ausgelassen, und am letzten Donnerstag war der ThreadPool und dessen Möglichkeiten dran. Und heute?! Nun, heute geht es um die Synchronisierung mehrerer Threads und atomare Aktionen.

Zunächst einmal kann man festhalten, dass die Thread-Synchronisierung im .NET-Framework ähnlich funktioniert wie im Win32- oder Pthreads-Umfeld. Es geht also um den gegenseitigen Ausschluss sowie um atomare Aktionen auf spezielle Variablen. Wie bei der von C# bekannten Methode lock wird ein Codeabschnitt mithilfe der geschweiften Klammern geblockt, sodass zu dieser Zeit nur ein einziger Thread darauf zugreifen kann. Hierfür bietet das .NET-Framework eine ähnliche Konstrukte:

Monitor.Enter ( this )
try
{
……. shared_var = other_shared_var +1;
……. other_shared_var = 0;
}
finally
{
…… Monitor.Exit ( this )
}

Mit der Klasse Monitor wird der entsprechende Codeabschnitt blockiert. Mit Enter() wird der Abschnitt gesperrt und mit Exit() wieder freigegeben. Praktisch an Monitor ist auch dessen Möglichkeit, Datenstrukturen als Parameter zu übergeben.

Bei Monitor.Enter() geschehen übrigens zwei Dinge: Erstens wird eine Warteschlange eingerichtet, die auf diejenigen Threads verweist, die gesperrt werden sollen und eine zweite Queue mit Threads, die darüber informiert werden wollen, wenn eine Speere verfügbar ist. Monitor.Exit() sorgt dafür, dass der erste verfügbare Thread in Warteschlange #1 gesperrt wird.

weiterlesen…

Kategorien : Multicore Tags : , ,

Workshop: Alles über .NET-Threads – Teil 3

veröffentlicht von Michael Hülskötter am 12. März 2009 (2) Kommentare

Teil eins und Teil zwei meines Minispecials zum Thema .NET-Threads stehen bereits online, und heute folgt sehr chronologisch der dritte Abschnitt. Dieser handelt von den Thread-Pools, mit deren Hilfe eine größere Anzahl von notwendigen Threads mithilfe des .NET-Frameworks verwaltet werden können.

Zunächst einmal sollte man sich klar machen, dass das Erzeugen und Verwalten von .NET-Threads mit einem immensen administrativen Aufwand verbunden ist: Lokaler Thread-Speicher muss angelegt und die Systemstrukturen für die Thread-Verwaltung müssen eingerichtet werden. Darüber hinaus nimmt die Komplexität des Quellcodes deutlich zu, sobald mehrere Threads mit Bordmitteln verwaltet werden sollen.

Aus diesem Grund hat Microsoft dem .NET-Framework ein Ressource spendiert, die sich ThreadPool nennt. Dabei wird zunächst eine bestimmte Anzahl von Threads generiert, außerdem wird eine Art Arbeitswarteschlange erstellt. Sobald eine auszuführende Aufgabe in diese Warteschlange befördert wird, wird ein Thread für diesen Task aktiviert und ihm die Aufgabe zugewiesen. Dies erledigt das .NET-Framework automatisch. Allerdings ist dabei zu beachten, dass der ThreadPool nur dann sinnvoll eingesetzt werden kann, wenn ein Programm aus mehreren Threads besteht, die immer wieder benötigt werden.

weiterlesen…

Kategorien : Multicore Tags : , ,

Workshop: Alles über .NET-Threads – Teil 2

veröffentlicht von Michael Hülskötter am 11. März 2009 (0) Kommentare

Meine kleine .NET-Serie zum Thema Threads geht heute in die zweite Runde. Nachdem es gestern um das Erzeugen von Threads ging, handelt Teil zwei von der Thread-Verwaltung. Dabei unterscheidet man zwischen Beenden, Warten, Anhalten und Fortsetzen.

Beenden von Threads: Dies geschieht am einfachsten, indem ein Thread ganz regulär während des Programmablaufs verlassen wird und die Anwendung wieder zum Masterthread zurückkehrt. Dies gibt der Common Language Runtime (CLR) die Möglichkeit, die notwendigen Aufräumarbeiten durchzuführen. Allerdings muss manchmal ein anderer als der gerade laufende Thread beendet werden. Daher hat Microsoft den .NET-Thread-APIs eine Methode spendiert, die sich Abort() nennt, mit deren Hilfe ein laufender Thread abgebrochen werden kann. Beim Aufruf von Abort() wird automatisch eine ThreadAbortException ausgelöst.

Der Aufruf von Abort() zieht übrigens eine Reihe verschiedener Dinge nach sich. Dazu gehört die Fähigkeit des Threads, den eigenen Abbruch zu vereiteln, indem er innerhalb des Exception-Handlers die Methode System.Threading.Thread.ResetAbort aufruft. Daneben besteht die Möglichkeit, innerhalb eines Codeblocks mithilfe von finally weiteren Quellcode ausführen zu lassen, was im ungünstigsten Fall eine beachtliche Verzögerung des Thread-Abbruchs nach sich zieht. Aus diesen beiden Gründen sollte man zur Laufzeit überprüfen, ob ein bestimmter Thread auch vollständig abgebrochen wurde. Hierfür steht die Join-Methode zur Verfügung.

weiterlesen…

Kategorien : Multicore Tags : , ,

Workshop: Alles über .NET-Threads – Teil 1

veröffentlicht von Michael Hülskötter am 10. März 2009 (0) Kommentare

Ende Januar, im Anschluss an die OOP 2009, habe ich hier eine kleine Miniserie mit dem Titel “Multicore-Programmierung im .NET-Umfeld” veröffentlicht. Was dabei ein wenig zu kurz kam sind die technischen Aspekte, die daraus resultieren. Wie beispielsweise Threads mithilfe des .NET-Frameworks erzeugt, verwaltet und synchronisiert werden. Und über die vorhandenen Thread-Pools habe ich ebenfalls zu wenig erzählt.

Das alles werde ich ab heute in Form einer Miniserie mit der Überschrift “Alles über .NET Threads” nachholen. Der erste Teil beschäftigt sich mit dem Thema “Threads erzeugen”.

Da die .NET-APIs etwas “schlanker” sind als ihre Win32-Brüder und -Schwestern, gestaltet sich das Erzeugen eines .NET-Threads relativ einfach. Dies sieht wie folgt aus:

using System.Threading;

[Definition von Variablen]

Thread t = new Thread( new ThreadStart( ThreadFunc ));

Der Aufruf von ThreadStart() erzeugt einen neuen Thread. Der Parameter ist eine Delegat namens ThreadFunc. Übrigens: In C# ist ein Delegat identisch mit der Adresse einer Funktion in C. Der Thread endet dann an der Stelle, sobald ThreadFunc() endet. Doch damit nicht genug. Für eine fehlerfreie Ausführung des Threads muss weiterer Code implementiert werden:

t.Start() — Dies startet den Thread unter .NET explizit (was z. B. unter Win32 nicht erforderlich ist).

Thread.Sleep( 40 ) — Dies unterbricht den (seriellen) Hauptthread für die Dauer der Zeiteinheit in der Klammer, die in Millisekunden angegeben wird. Dies kann in der Praxis jedoch erheblich von der tatsächlich benötigten Zeit abweichen.

[aufzurufende Funktion] — Diese Funktion wird parallel auf den vorhandenen Prozessorkernen ausgeführt.

Thread.Sleep( 0 ) Dies beendet den parallelen Thread und reaktiviert den “schlafenden” Hauptthread.

Kategorien : Multicore Tags : , ,

Mögliche Thread-Modi und deren Folgen

veröffentlicht von Michael Hülskötter am 27. Februar 2009 (1) Kommentar

Multicore-Programmierung ist sicherlich kein einfaches Ding. Zu viel kann dabei passieren und schiefgehen, wenn man bestimmte Regeln nicht beachtet. Daher starten wir heute einen Mehrteiler, der sich mit den Details der Multicore-Programmierung beschäftigt. Im ersten Teil geht es um die fundamentale Frage, welche Zustände ein Thread während des Programmablaufs annehmen kann.

Im ersten Schritt wird beispielsweise vom Thread Manager ein Thread erzeugt und in den Bereit-Zustand versetzt (”ready”). Dort verharrt der Thread so lange, bis er eine Anweisung, eine Funktion oder ähnliches ausführen soll. Darum kümmern sich entsprechende Programmanweisungen wie zum Beispiel die Pragma-Methoden der OpenMP-basierten Programmierung.

Im zweiten Schritt kommt es zur Ausführung der entsprechenden Quellcodestelle. Währenddessen muss der betreffende Thread möglicherweise auf einen anderen Thread oder ein Datum eines parallel ablaufenden Threads warten. Dann begibt er sich in den Wartend-Modus, bis der benötigte Thread beendet ist oder dessen Ergebnis vorliegt. Anschließend wird der angehaltene Thread weiter ausgeführt oder wieder in den Bereit-Zustand versetzt, da der Thread zu einem späteren Zeitpunkt nochmals benötigt wird.

Ach ja: Gerade dieser Wartend-Modus bereitet vielen Programmierern erhebliche Probleme, da hierbei Dead Locks, Data Races oder andere unvorhersehbare Ereignisse auftreten können. Wie gut, dass es hierfür Tools wie den Thread Checker oder Parallel Inspector gibt, die solche Schwachstellen aufspüren können.

weiterlesen…

Kategorien : Mobile Tags : , ,