Parallele Programmierung

Die funktionale Programmierung mit Haskell gibt uns verschieden Möglichkeiten an die Hand, mehrere Rechenkerne zu nutzen. Dabei ist genau zwischen paralleler und nebenläufiger Programmierung zu unterscheiden:

Parallele Programmierung

Ziel jeder parallelen Berechnung ist einzig und allein Geschwindigkeit. Das Ergebnis ist deterministisch, aber durch die Verteilung einer Berechnung auf mehrere Prozessoren erhält man dieses schneller. Diese Idee ist konzeptuell einfach. Dennoch gilt es schwierige Fragen bei der Programmierung zu beantworten. Die zwei wichtigsten sind:

1. Wie verteilt man die Arbeit ausgewogen auf die zur Verfügung stehenden Kerne? Fehler hierbei führen zu einer Situation, in der manche Prozessoren keine Arbeit mehr haben, während andere noch nicht fertig sind. Die Ausgabe des Ergebnisses wird hierdurch verzögert.

2. Wie weit muß jeder Kern rechnen, um das Ergebnis zu produzieren? Falsche Einschätzung in dieser Frage führen dazu, daß eventuell zu viel berechnet wird, obwohl das Ergebnis schon mit weniger Rechenaufwand zu produzieren wäre. Oder zu wenig, wodurch die verfügbaren Kerne dann nicht optimal ausgelastet werden.

Mit Hilfe funktionaler Programmierung haben wir mehrer Techniken, diese Probleme anzugehen:

  • Flat and nested data parallelism
  • Evaluation strategies
  • Data flow parallelism

Welche Technik sich am besten eignet, wird immer an Hand der konkreten Aufgabe entschieden. Zudem haben wir mit Threadscope ein mächtiges Tool, daß uns bei der Analyse des Laufzeitverhaltens unterstützt. Die Abbildung zeigt eine aufwendige Berechnung auf vier Kernen:

Nebenläufige Programmierung

Nebenläufigkeit wird als Mittel zur Strukturierung unabhängiger Teile eines Programmes eingesetzt, zum Beispiel, wenn eine Benutzeroberfläche reaktiv bleiben soll, währen im Hintergrund eine andere Berechnung läuft. Für nebenläufige Programme benötigt man daher nicht unbedingt mehrere Prozessoren, da die Simulatiom von Multitasking auf einem Kern oft schon das gewünschte Ergebnis bringt.

Schwierigkeiten bei der Programmierung nebenläufiger Systeme entsteht oft durch Nicht-Determinismus. Dead-Locks, Race-Conditions und partiell durchgeführte Operationen sind häufige Probleme. Hier können wir eine Technik namens Software Transactional Memory anwenden. Sie orientiert sich an den von der Datenbanktechnologie her bekannten Prinzipien Isolation und Atomarität. Sie helfen, daß System auf einfache Weise konsistent zu halten. Nebenläufigkeit wird damit handhabbar.

iX-Artikel Starke Typen

Hier finden Sie die Haskell-Quellen und Ergänzungen zum Artikel Starke Typen, der im Juli 2011 in der Fachzeitschrift iX erschienden ist:

  • Ein Beispielprogramm: Main.hs
  • Die Bibliothek für die eingebettete domainspezifische Sprache (EDSL): Edsl.hs

Übersetzen können Sie die Quellen mit dem Glasgow Haskell Compiler. Dazu tippen Sie auf der Konsole:

$ ghc -threaded -rtsopts --make Main.hs -o main

Das entstehende Programme rufen Sie auf wie folgt:

$ ./main +RTS -Nx -RTS

wobei x hier die Zahl der zu verwendenen Prozessoren ist. Für einen Quad-Core würden man zum Beispiel -N4 als Option wählen.