Intel TBB 3.0 mit neuen Funktionen und VS2010-Support
Schon letzte Woche erreichte mich eine E-Mail, in der die aktualisierte Version der Intel Threading Building Blocks angekündigt wurde. Tja, und da genau heute Intels Bibliothekensammlung für C und C++ offiziell vorgestellt wird, gibt es dazu natürlich auch einen entsprechenden Blogbeitrag.
Auf den ersten Blick bietet Intel TBB 3.0 zwei wesentliche Verbesserungen: die volle Kompatibilität mit Visual Studio 2010 und .NET 4 sowie die Unterstützung des künftigen ISO/ANSI-Standards für C++, der noch unter dem Arbeitsnamen C++0x geführt wird. Aber auch andere Features wie eine neue Containerklasse und verbesserte Debugger-Merkmale flossen in die neue Version der Intel TBB 3.0 ein. Im Detail sieht das wie folgt aus:
Optionaler C++0x-Support
- die neue Funktion parallel_pipeline() verbessert die alte Warteschlange-Funktion mithilfe von Lambda-Support und anderen Features
- TBB_USE_CAPTURED_EXCEPTION() verbessert das Exception Handling
- verbesserte Synchronisierung auf Basis konditioneller Variablen
Handbuch “Pattern Design”
Dieses von TBB-Entwickler Arch Robison geschriebene Handbuch zeigt, wie Pattern Design funktioniert
Kompatibel mit Microsoft Visual Studio 2010
- Support der Concurrency Runtime
- kompatibel mit der Parallel Pattern Library
- kompatibel mit reader_writer_lock() and critical_section_lock()
Neue Container-Klasse
concurrent_unordered_map() (natürlich C++ 0x-kompatibel)
Verbessertes Debuggen
- Task Scheduling, Vorhersagen und Reaktionszeiten wurden verbessert
- voneinander unabhängiges Verwalten von Tasks für mehrere Masterthreads
- verbesserte Vermeidung von Deadlocks
- weiterentwickelte Hinweise für Parallel Amplifier und Parallel Inspector
Scheduler-Support (task::enqueue)
- unterstützt FIFO-Warteschlagen
- nützlich für das Emulieren von Task-Prioritäten und die Interaktion mit GUI-basierten Threads
Leistungsverbesserungen
- Beschleunigung der Funktion numerable_thread_specific() und Kombinationen davon
- verbesserte Speicherverteilung bei großen Speicherblöcken
und einiges mehr.
Wer mit Intel Threading Building Blocks Software entwickeln und parallelisieren möchte, kann dies in zweierlei Hinsicht tun: entweder bezahlt man 299 Dollar für die Windows- oder Linux-Variante und erwirbt damit das kommerzielle Paket von TBB 3.0. Dazu gehört auch ein professioneller Support seitens Intel. Oder man begibt sich auf www.threadingbuildingblocks.org, besorgt sich die Open-Source-Variante des Pakets und bekommt damit eine GPLv2-Lizenz. Diese ist natürlich kostenlos.
Von Intel TBB 2.1 auf Version 2.2 unfallfrei umsteigen
Erst am Dienstag hat Intel seine neueste Version der Threading Building Blocks vorgestellt. Und gerade mal einen Tag später hat Terry Wilmarth von Intel ein Rezept online gestellt, mit dessen Hilfe der Umstieg von 2.1 auf 2.2 halbwegs unfallfrei gelingen sollte. Zu den wichtigsten Empfehlungen zählen:
- Da die parallel arbeitenden Warteschlangen mit der Version 2.2 sowohl begrenzt (bounded) als auch unbegrenzt (unbounded) arbeiten, sollten Programmierer möglichst die begrenzten Warteschlangen benutzen.
- Da sich die Rückgabewerte der Vektor-Funktionen grow_by, grow_to_at_least und push_back geändert haben, ist der Funktionsaufruf ein wenig einfacher geworden. So wird aus std::copy(begin, end, x.begin()+x.grow_by(end-begin)); jetzt std::copy(begin, end, x.grow_by(end-begin));
- In Intel TBB 2.2 ist auto_partitioner() als Standard gesetzt und löst damit simple_partitioner() ab.
- Der Begriff der Task-Tiefe spielt in TBB 2.2 keine Rolle mehr. Daher sind depth_type und die Methoden depth(), set_depth() und add_to_depth().
Mehr Infos und weitere Codebeispiele sind im angegebenen Blogbeitrag erhältlich.
Neue Version der Intel Threading Building Blocks
Intel hat heute anlässlich der Siggraph 2009 die neueste Version 2.2 seiner C++-Bibliothek Threading Building Blocks vorgestellt. Die wesentlichen Verbesserungen im Vergleich zur Vorgängerversion 2.1 sind zwei Dinge: Lambda-Funktionen gemäß des künftigen C++0x-Standards werden jetzt vollständig unterstützt, und das Lizenzmodell wurde erheblich vereinfacht.
Das soll vor allem Software-Entwicklern die Möglichkeit geben, Intel TBB bei der Programmierung und Optimierung von Multithread-Sourcecode von Anfang an einsetzen zu können. So umfasst beispielsweise die Unreal Engine von Epic Games ab sofort eine kommerzielle Lizenz der Intel TBB, Autodesk stellt in Maya Intel TBB standardmäßig zur Verfügung.
Neben diesen Neuerungen gibt es weitere Dinge, die hinsichtlich mehr Leistung optimiert wurden:
- Der Taks-Scheduler wurde erheblich überarbeitet, sodass optimierte Anwendungen auf Multicore-Plattformen besser skalieren. Dazu gehören eine automatische Initialisierung bei paralleler Ausführung bestimmter Codeabschnitte und das Zusammenfassen mehrerer Tasks zu einer Gruppe.
- Der Memory Allocator wurde ebenfalls verbessert, was den standardmäßigen Allocator des Betriebssystems ersetzen kann. Folge: bessere Skalierbarkeit der vorhandenen Speicherressourcen und weniger Datenkollisionen.
- Darüber hinaus gibt es neue und verbesserte Konstrukte. Neu sind parallel_invoke and parallel_for_each, mit deren Hilfe sich parallele Konstrukte noch einfacher erstellen lassen. Zudem wurde das Erstellen der häufig benutzten parallel_for-Konstrukts vereinfacht.
Ach ja: Für ein besseres Verständnis, was es mit den optimierten Lambda-Funktionen auf sich hat, empfehle ich zwei Blogposts (Nummer 1 und Nummer 2) des Kollegen Reinders, in denen er sehr anschaulich erklärt, was es bringt und was sich dadurch ändert.
Optimierte Parallel-Programmiersprachen braucht das Land
Gerade habe ich auf Technologyreview.com einen sehr interessanten Artikel zum Thema Multicore gefunden. Bereits sein Untertitel zeigt, worunter die Multicore-Programmierer derzeit am meisten leiden:
We need languages that take full advantage of multicore processing
Genau darum geht es heute. Nicht die Hardware ist der Hemmschuh, sondern die Software, sprich die Programmiersprachen, die derzeit zur Verfügung stehen. Oder wie es der Autor passend formuliert:
Computer evolution, however, is now headed down an entirely new path: instead of simply becoming faster, our computer processors are being conjoined to work together. That new computer architecture requires a serious evolution in computer programming. Without it, we can only scratch the surface of what multicore computing can really do.
Das ist die Kernthese, mit der sich auch dieses Blog immer wieder beschäftigt, denn die Zeiten der GHz-Spirale sind endgültig vorbei, und eine neue Ära des “Parallel Computing” ist angebrochen – sogar im Mainstream-Markt. Denn was die HPC-Gemeinde seit Jahren vormacht, ist auf dem gemeinen Desktop-PC und Notebook jetzt auch möglich, wird nur zu selten richtig eingesetzt.
Eine weitere interessante These stellt der TR-Artikel in Sachen “Umdenken” auf: So wie seinerzeit diverse Software-Entwickler den Trend der objektorientierten Programmierung verschlafen haben, droht heute die Gefahr erneut, da das Schreiben von parallel-orientiertem Sourcecode ebenfalls einen völlig neuen Denkansatz erfordert. Dies betrifft vor allem den gemeinsamen Zugriff auf Daten, dessen Koordinierung in parallelen Systemen bedeutend anspruchsvoller ist als im seriellen Universum.
Mit Intel Threading Building Blocks parallel programmieren
Über die Intel Threading Building Blocks haben wir schon das ein oder andere Mal berichtet, aber eine gründliche Betrachtung stand bisher noch aus. Bisher!
Die Intel TBB lassen sich in sechs fundamentale Einzelteile zerlegen:
1. Algorithmen
2. Container
3. Memory Allocator
4. Mutual Exclusion
5. Timer
6. Task Scheduler
1. Die TBB-Algorithmen richten sich vor allem an parallel ausführbare Schleifenkonstrukte, aber auch Sortieralgorithmen lassen sich mithilfe der TBB recht problemlos parallelisieren. Die zugehörigen Funktionen nennen sich parallel_for, parallel_reduce, parallel_scan, parallel_while, parallel_pipeline und parallel_sort. Eine genauere Betrachtung dieser Funktionen findet auf dem Software Dev Blog in einem späteren Beitrag statt.
2. Die Intel TBB setzen sich aus drei Containern zusammen: queue, vector und hash table. Diese Container findet man zwar auch in den Standard Template Libraries von C++ und Fortran, diese sind allerdings weniger thread-sicher als die der Intel TBB.
3. Der Memory Allocator der Intel TBB ist äußerst skalierbar und daher in der Lage, gemeinsam genutzten Speicher auf mehrere Threads so zu verteilen, dass sich diese nicht gegenseitig behindern. Damit kümmert sich der Allocator der TBB um das gleichzeitige Ausführen der wichtigsten Funktionen wie malloc, new und delete.
Software-Projekte mit Multicore-Bibliotheken optimieren
Gerade auf Entwickler von Multimediaanwendungen wartet ein großes Optimierungspotenzial in Sachen Multicore-Programmierung. Ein gutes Beispiel sind Videoschnittprogramme wie PowerDirector 7 von CyberLink, in denen ein hohes Maß an Datenparallelität steckt, da beispielsweise beim Rendern sämtliche Bildpunkte simultan manipuliert werden können.
Wie gut, dass es für die Parallelisierung solcher Anwendungen die passenden Tools gibt, die Intel unter dem Begriff „Performance Libraries“, also Hochleistungsbibliotheken, anbietet. Dazu gehören die schon besprochenen Intel Threading Building Blocks, aber auch spezielle C++-Bibliotheken namens Intel Integrated Performance Primitives (Intel IPP) und Intel Math Kernel Library (Intel MKL).
Die Intel IPP wenden sich an all diejenigen, die programmierseitig viel mit En- und Dekodern für Audio- und Videoinhalte zu tun haben, aber auch mit Datenkompression, Spracherkennung und Bildbearbeitung. Für all diese (und weitere) Anwendungen bieten die Intel IPP nämlich die passenden Bibliotheken, die sich mit wenig Aufwand in das eigene Softwareprojekt integrieren lassen. Man muss ja schließlich das Rad nicht jedes Mal neu erfinden. Außerdem kann man sicher sein, dass die IPP in höchstem Maße multithreaded sind.
Programmiermethoden: Daten- und Aufgabenparallelität
Bei der parallelen Programmierung unterscheidet man grundsätzlich zwischen drei Formen, die letztlich darüber entscheiden, welche Programmiermethoden man anwenden sollte.
Die eine Form der parallelen Programmierung orientiert sich an den zu verarbeitenden Daten, die zur Laufzeit der Anwendung anfallen. Ein gutes Beispiel für die Datenparallelität ist das Rendern eines Bildes, das aus vielen tausend Bildpunkten besteht, die sich unabhängig voneinander berechnen, zeichnen oder mit einem Filter versehen lassen.
Darin steckt also eine Menge an Multicore-Potenzial, wofür sich der Einsatz von OpenMP oder Intel TBB empfiehlt. Ein weiteres gutes Beispiel ist die Rechtschreibprüfung eines Textverarbeitungsprogramms. Hier können sämtliche Wörter simultan überprüft werden, da sie zueinander in keiner Abhängigkeit stehen.
Auf der anderen Seite existiert der Aufgabenparallelismus, der voneinander getrennte Aufgaben betrachtet, die sich vortrefflich parallel ausführen lassen. Ein gutes Beispiel hierfür ist Outlook, das aus unterschiedlichen Modulen besteht (Adressen, Kalender, E-Mail etc.), die sich grundsätzlich unabhängig voneinander ausführen lassen und damit auf separaten Prozessorkernen laufen können.
Fünf Multicore-Programmierregeln auf einen Blick
Oft hört man die Frage, was beim parallelen Programmieren alles zu beachten sei. Deshalb folgen an dieser Stelle die fünf wichtigsten Multicore-Regeln für angehende Parallel-Entwickler:
Multicore-Regel Nummer 1: Denke parallel! Am allerbesten ist ein paralleles Konzept von Anfang an. Dies vermeidet ein nachträgliches Implementieren parallelen Quellcodes, wie das beispielsweise mit den Intel Threading Building Blocks möglich ist. Optimalerweise legen Sie sich eine Strategie zurecht, wie Sie Schritt für Schritt zu einer multicore-optimierten Anwendung kommen.
Multicore-Regel Nummer 2: Entwickle task-orientiert! Parallel programmierter Code skaliert am besten, wenn er nicht für eine bestimmte Zahl an Prozessoren programmiert wurde, sondern sich an der bestmöglichen Zahl an parallel ausführbaren Aufgaben orientiert. Denken Sie daher nicht in Threads, dass erledigen Tools wie die Intel TBB für Sie. Deren Scheduler verteilt die auszuführenden Aufgaben auf die vorhandenen Prozessorkerne. Und je mehr davon zur Verfügung stehen, desto paralleler läuft die Anwendung.
Multicore-Regel Nummer 3: Auf die Tools kommt es an! Parallele Threads auf Basis von POSIX oder Windows-APIs programmieren zu wollen, ist ein hartes Brot, denn Sie müssen sich selbst um die Parallelisierung des Quellcodes kümmern. Viel einfacher ist es, mit den richtigen Tools der Anwendung paralleles Leben einzuhauchen.
Multithreading-Konzepte: OpenMP, APIs und Intel TBB
Für die parallele Programmierung bieten sich diverse Alternativen an, von denen hier drei vorgestellt werden sollen: OpenMP, Threading-APIs und Intel Threading Building Blocks.
OpenMP: Diese Threading-Methode ist relativ simpel zu handhaben, da in den Quellcode sogenannte Pragmas eingebaut werden, die der jeweilige Compiler richtig interpretieren kann – oder auch nicht. OpenMP eignet sich vor allem für die Parallelisierung einfacher Schleifenkonstrukte und prozeduraler Datenstrukturen. Aufgrund seiner Historie lässt sich OpenMP gut in nativen Programmierumgebungen (C++ und Fortran) einsetzen, dafür leider gar nicht in managed Code (C#, .NET).
Threading-APIs: Spezielle Threading-APIs wie Win32- oder POSIX eignen sich nicht so gut zum Implementieren von Parallel-Code. Dabei werden die Multithreading-Konstrukte in eigenen Funktionen verpackt. Die Ergebnisse werden dann als Pointer übergeben, und hier steckt die Schwierigkeit bei der Arbeit mit Threading-APIs: Funktionsprototypen und Datenstrukturen müssen aufwendig modifiziert werden, was häufig die Abstraktion des Codes und das Programmdesign zerstört. Außerdem muss man sich als Programmierer selbst um das Erzeugen, Verwalten und Synchronisieren der Threads kümmern, was sehr mühsam ist. Daneben unterstützen Threading-APIs C++ nur sehr schlecht und oft gar nicht.
Intel Threading Building Blocks: Hierbei handelt es sich um eine C++-Bibliothek, die ähnlich wie die Standard Template Library eines herkömmlichen Compilers aufgebaut ist. Die Intel TBB stellen hochentwickelte Abstraktionsmodelle zur Verfügung, um eine flexible Programmierung zu ermöglichen. Das bedeutet, dass beispielsweise Iteratoren, die in Container verpackt sind, mithilfe der Intel TBB relativ einfach parallelisiert werden können.
Die Idee hierbei ist recht simpel: Anstatt Threads spezifiziert man einzelne Aufgaben (Tasks), die der TBB Scheduler auf die vorhandenen Hardware-Threads abbildet. Man muss sich also nicht mehr um die Synchronisation der Threads und ähnliches kümmern. Außerdem skaliert eine mithilfe der Intel TBB parallelisierte Anwendung sehr gut, da je nach verfügbaren Prozessorkernen und damit verbundenen Threads die Software um ein Vielfaches schneller ausgeführt werden kann.
Nützliche und kostenlose Entwickler-Tools
Kennen Sie eigentlich die Code- und Download-Seite des Intel Software Network? Nein?! Dann wird’s aber höchste Zeit. Auf dieser sehr nützlichen Webseite finden Sie diverse Tools, aber auch Sourcecode-Beispiele, die Ihre tägliche Arbeit rund um die Software-Entwicklung erleichtern und verbessern sollen.
Finden Sie zum Beispiel mit dem Concurrency Checker heraus, ob Ihre Software tatsächlich parallel programmiert wurde und wie sich der Multicore-Code zur Laufzeit verhält. So spüren Sie relativ einfach Fehler im Quellcode auf. Anhand der Ergebnisse wissen Sie dann schon mal ziemlich genau, wo es bei Ihrer Software in Sachen Multithreading hakt. Anschließend können Sie mithilfe der Intel Threading Analysis Tools die Fehler beheben und aus Ihren Anwendungen echte Multicore-Anwendungen machen.
Aber auch kostenlose Entwickler-Werkzeuge für nicht-kommerzielle Softwareprojekte finden Sie dort. Dazu gehören beispielsweise die Intel-Compiler (C++ und Fortran) für Linux sowie der VTune Performance Analyzer für Linux. Aber auch die C++-Multithread-Bibliothek Intel Threading Building Blocks lässt sich für nicht-kommerzielle Zwecke gratis nutzen.

