Open Source im professionellen Einsatz

© Gerti G., photocase.com.

Parallelprogrammierung in C++ mit der OMPTL

Doppel-Herz

Die einen lieben, die anderen hassen sie: die flexible, aber komplexe C++-Standardbibliothek STL. Entwickler des zweiten Lagers können ihren Quellcode nun fast umsonst parallelisieren. Den Werkzeugkasten dazu liefert OMPTL - vorausgesetzt der Compiler beherrscht den OpenMP-Standard.

Mittlerweile geht kaum noch ein PC über die Ladentheke, in dem nicht mindestens zwei Prozessorkerne schlagen. Die Kraft der zwei Herzen hat die Programmierlandschaft jedoch im Halbschlaf überrollt. Auf der einen Seite überschütten Intel und AMD selbst schon Privatkunden mit Quadcores (Abbildung 1), während am anderen Ufer die Software-Entwickler mit ihren linearen Sprachen und Konzepten winken.

Abbildung 1: Supercomputing-Power für den Desktop: Layout eines Vier-Kerne-Prozessors.

Abbildung 1: Supercomputing-Power für den Desktop: Layout eines Vier-Kerne-Prozessors.

In mühsamer Kleinarbeit müssen daher die Programmierer parallele Aufgaben identifizieren, per Hand in eigene Threads und Prozesse auslagern und die Ergebnisse wieder aufwändig zusammenführen - vom anschließenden, teilweise hirnverdrehenden Debugging einmal ganz abgesehen.

Für Nutzer der C++-Standardbibliothek gibt es jedoch einen Lichtblick. Er hört auf den Namen OpenMP Multi-Threaded Template Library, kurz OMPTL. Diese kleine, aber pfiffige Bibliothek ersetzt einfach sämtliche Algorithmen sowie den numerischen Teil der C++-Standardbibliothek durch eigene parallelisierte Varianten. Der Programmierer muss lediglich ein paar Funktionsaufrufe leicht überarbeiten.

Stolperdraht

Bei so viel Komfort gibt es wie immer auch einen Haken. Im Fall der OMPTL liegt dieser beim Compiler: Die Bibliothek stützt sich vollständig auf den OpenMP-Standard ([1], [2]). Diese offene Spezifikation beschert C- und C++-Compilern neue Befehle, über die sich Codeblöcke recht einfach parallelelisieren lassen. Das klappt freilich nur, wenn der Übersetzer den OpenMP-Standard korrekt und vollständig beherrscht. Auf den Intel-Compiler trifft dies schon seit geraumer Zeit zu [3]. Leider darf die Linux-Version nur für private Zwecke kostenfrei genutzt werden.

Die beliebte GNU Compiler Collection kann erst in der brandneuen Version 4.2 mit OpenMP-Anweisungen umgehen. Wer nicht auf den Intel-Compiler ausweichen kann oder mag, muss folglich entweder den aktuellen G++ per Hand nachrüsten oder auf die allerneuesten Distributionen umsteigen. Bereits einsatzfertig haben ihn sowohl Ubuntu 7.10 als auch Open Suse 10.3 an Bord. Lediglich Red Hat hat für seine Community-Distribution Fedora die OpenMP-Funktionalität in ältere GCC-Versionen zurückportiert. Eine OpenMP-freie Variante der OMPTL-Bibliothek stellt der Kasten "Kleine Schwester" vor.

Kleine Schwester

Auf der Homepage der OMPTL ist noch die Multi Processing Template Library, kurz MPTL, anzutreffen. Sie ist ähnlich gestrickt wie ihre großer Schwester, arbeitet aber intern mit herkömmlichen Posix-Threads statt OpenMP. Die Programmierung erfolgt analog zur OMPTL-Variante. Aus dem Code in Listing 1 beispielsweise würde in der MPTL-Variante:

#include <vector>
#include <mptl.h>
#include <mptl_algo.h>
#include <mptl_qsort.h>
int main (int argc, char * const argv[])
{
        mptl::setNumThreads(2);
        std::vector<int> zahlen(100000);
        mptl::sort(zahlen.begin(), zahlen.end());
        return 0;
}

Im Gegensatz zur OMPTL-Bibliothek muss der Entwickler bei der MPTL zu Beginn seines Programms explizit angeben, auf wie viele Threads sich die Bearbeitung verteilen soll. Da die MPTL auf den Posix-Threads aufsetzt, muss er bei der Übersetzung eine passende Bibliothek hinzulinken, beispielsweise die »libpthread«:

g++ -I/Pfad/MPTL/ -lpthread sortieren.cpp

Die MPTL erreicht eine Parallelisierung der Standard-Algorithmen mit jedem beliebigen C++-Compiler, und das sogar ohne OpenMP-Support. Umso trauriger stimmt es daher, dass die bei Redaktionsschluss aktuelle Version schon fast ein Jahr auf dem Buckel hat. Es ist also zu vermuten, dass sie zugunsten ihrer großen Schwester langfristig in der Versenkung verschwinden soll - auch wenn das Ende der Bibliothek noch nicht offiziell verkündet ist. Den Sprung in die CVMLCPP hat sie jedenfalls nicht geschafft.

Groß oder klein?

So bewaffnet lädt sich der angehende Multicore-Programmierer von [4] das aktuelle Archiv herunter. Dessen Installation könnte nicht einfacher sein. Es genügt, den Inhalt in ein Verzeichnis eigener Wahl zu entpacken. Der alternative Weg führt über die CVMLCPP-Bibliothek ([5], Common Versatile Multi-purpose Library for C++). Sie erweitert die C++-Standardbibliothek um ein paar nützliche Funktionen.

Unter ihrem Dach wird mittlerweile auch die OMPTL weiterentwickelt, die sich innerhalb des CVMLCPP-Pakets im Unterverzeichnis »omptl« befindet. Dessen Inhalt ist wiederum mit dem Archiv auf der OMPTL-Homepage identisch - aber nur in der jeweils aktuellen Version. Nach der Installation der OMPTL-Bibliothek geht es daran, das eigene Programm algorithmisch zu parallelisieren.

Listing 1 zeigt in Anlehnung an [4] ein kleines Sortierbeispiel als Ausgangspunkt. Es legt zunächst einen »vector« mit 100 000 Elementen an, die es anschließend sortiert. Sowohl die Datenstruktur als auch der Algorithmus stammen aus der C++-Standardbibliothek. Um daraus eine parallele Version zu klöppeln, genügen zwei kleine Änderungen. Zunächst ersetzt der Programmierer den Header »algorithm« durch sein OMPTL-Gegenstück

#include <omptl/omptl_algorithm>

und verwendet anschließend den Sortieralgorithmus aus dem Namespace »omptl«:

omptl::sort(zahlen.begin(), zahlen.end());

Den vollständigen Programmcode zeigt Listing 2.

Mit Hilfe des Makros »_OPENMP« prüft der Compiler, ob die OpenMP-Unterstützung tatsächlich zur Verfügung steht. Im negativen Fall definiert der Quellcode das Symbol »OMPTL_OFF«, das wiederum die OMPTL-Funktionen dazu veranlasst, ihre parallele Verarbeitung abzuschalten. Das Ergebnis verhält sich wie ein herkömmlicher Aufruf der Standardbibliothek. Zudem ist der Quellcode mit einem C++-Compiler ohne OpenMP-Unterstützung übersetzbar.

Die Fallunterscheidung ist folglich immer dann sinnvoll, wenn man das Programm an andere Personen mit einem unbekannten Zielsystem weitergeben möchte. Selbstverständlich lässt sich »OMPTL_OFF« auch direkt dem Compiler mitgeben:

g++ -I/Pfad/omptl -DOMPTL_OFF sortieren.cpp

Den modifizierten Quellcode übersetzt ein Intel-Compiler mit:

icc -I/Pfad/omptl -openmp sortieren.cpp

GCC-4.2-Nutzer greifen zu dem folgenden Äquivalent:

g++ -I/Pfad/omptl -fopenmp sortieren.cpp

Nach dem Programmstart und der Erzeugung des Vektors verteilt die OMPTL die Sortieraufgabe auf mehrere OpenMP-Threads. Deren Anzahl hängt von der Anzahl der vorhandenen Prozessoren beziehungsweise der Prozessorkerne ab. Auf einem Core 2 Duo würde die Sortierung folglich auf genau zwei Threads aufgeteilt.

Falls nötig oder gewünscht, kann der Anwender über die zu OpenMP gehörende Umgebungsvariable »OMP_NUM_THREADS« beim Start des Programms eine bestimmte Thread-Anzahl erzwingen. Mehr Threads zu erzeugen, als Kerne vorhanden sind, führt allerdings eher zur Verlangsamung des parallelisierten Programms.

Sobald die Threads ihre Berechnungen beendet haben, führt die OMPTL deren Ergebnisse automatisch wieder zu einem sortierten Vektor zusammen. Nach außen hin bleibt dies alles vollkommen transparent, der Programmierer kann anschließend direkt mit dem fertig sortierten Vektor weiterarbeiten.

Listing 1: Quicksort mit
100 000 Zufallszahlen

01 #include <vector>
02 #include <algorithm>
03 
04 int main (int argc, char * const argv[])
05 {
06         std::vector<int> zahlen(100000);
07         std::sort(zahlen.begin(), zahlen.end());
08         return 0;
09 }

Listing 2: Mit OMPTL
parallelisiertes Quicksort

01 #include <vector>
02 
03 #ifndef _OPENMP
04 #define OMPTL_OFF
05 #endif
06 
07 #include <omptl/omptl_algorithm>
08 
09 int main (int argc, char * const argv[])
10 {
11         std::vector<int> zahlen(100000);
12         omptl::sort(zahlen.begin(), zahlen.end());
13         return 0;
14 }

Diesen Artikel als PDF kaufen

Express-Kauf als PDF

Umfang: 3 Heftseiten

Preis € 0,99
(inkl. 19% MwSt.)

Als digitales Abo

Als PDF im Abo bestellen

comments powered by Disqus

Ausgabe 07/2013

Preis € 6,40

Insecurity Bulletin

Insecurity Bulletin

Im Insecurity Bulletin widmet sich Mark Vogelsberger aktuellen Sicherheitslücken sowie Hintergründen und Security-Grundlagen. mehr...

Linux-Magazin auf Facebook