ICSE 04.06.2012, 16:03 Uhr

Coding von Multi-Core-Prozessoren

Rasend schnell: Mehrkern-Prozessoren booten - im Prinzip - von jedem Kern ein eigenes OS. Aber die Software hinkt noch hinterher. Diese parallelen Techniken versprechen Erfolg.
Zu Beginn der Schweizer ICSE 2012 (International Conference on Software Engineering) gab es gleich einen harten Brocken zu schlucken. Victor Pankratius vom MIT referierte über "Multicore Software Engineering and Auto-Tuning", auf deutsch: Performance-Optimierung durch parallele Programmierung. Das Szenario: Nahezu alle neuen Desktop-PCs und Smartphones arbeiten heute mit Multi-Kern-Prozessoren: AMD Opteron 12 Cores, Sun Niagara 16 Cores, Intel SCC 48 Cores, nVida GTX >300 Cores. Ziehen alle Kerne an einem Strang und arbeiten parallel an einer Aufgabe, kann das die Performance von Apps extrem steigern. Nur hinken die Software-Entwickler zurzeit noch hinterher - und verschenken Optimierungspotenzial. Kein triviales Problem Die "Open Specifications for Multi-Processing" (OpenMP) etwa werden zurzeit in 46 Projekten erprobt. OpenMP eignet sich hervorragend für numerische Apps (Grafiken, Vektoren, Matrizen) und unterstützt die Programmiersprachen C, C++ und Fortran. Pankratius berichtet jedoch von zahlreichen, undokumentierten Fehlern in den Projekten. Oft seien falsche Annahmen über die Sichtbarkeit von Variabeln und die Ausführungsreihenfolge der Codezeilen der Grund, zugegeben in parallelem Soucecode ein nicht gerade triviales Problem. Microsoft hat für seine .NET-Sprachen die "Task Parallel Library" (TPL) vorgestellt. die Parallelität als Methode parallel.for einführt und ab .NET 3.5 funktioniert. Schleifendurchläufe etwa, in denen ein und derselbe Sourcecode zehntausend oder hunderttausend Mal in gleicher Weise abgearbeitet wird, bieten sich zur Optimierung an. Die klassische For-Schleife in C# wird normalerweise ganz brav sequentiell, also hintereinander abgearbeitet: for (int i = 0; i < 100000; i++) {     a[i] = a[i]*a[i];  } Durch die Parallelisierung der 100.000 Schleifendurchläufe gewinnen die zwei Zeilen Code auf elegante Weise erheblich an Tempo: Parallel.For(0, 100000, delegate(int i) {      a[i] = a[i]*a[i]; }); Microsofts "Parallel Library" sei noch in einem frühen Experimentierstadium, da könne sich noch jede Menge ändern, meint Pankratius. Abgesehen von Bibliotheken und Frameworks müssen Software-Entwickler auf dem Weg zu optimiertem, parallelem Sourcecode viele mentale Fallstricke vermeiden: zum Beispiel die Granularitätsfalle. Der Software-Guru erzählt von seinem ersten Stück Parallelcode, wo er akribisch alle theoretisch parallelisierbaren Threads auch programmiert habe. Mit dem Ergebnis: Der parallele, also auf mehrere Prozessorkerne verteilte Code lief langsamer als das simple sequentielle Programm. Pankratius hatte zu viel parallelisiert: Der kommunikative Overhead zwischen den Subroutinen zwang die Performance in die Knie. Nchste Seite: Erfolgsstrategien &amp; Chip-Design Der kommunikative Overhead zwischen den Threads kann einerseits den Performance-Gewinn eines optimierten Programms glatt zunichte machen, ist aber andererseits dringend notwendig. Zum Beispiel, um die gefürchteten Deadlocks zur vermeiden: zwei parallel laufende Software-Komponenten blockieren sich gegenseitig, die gesamte Programmausführung stoppt. Pankratius verweist auf das sogenannte Producer-Consumer-Pattern, eine weitere Erfolgsstrategie der parallelen Software-Entwicklung. Das Szenario: Ein Producer legt Elemente in einem Buffer ab, der x Elemente speichern kann. Ein Consumer entnimmt Elemente aus dem Puffer. Producer und Consumer takten in der Regel nicht synchron. Java: Deadlocks verhindern Ist nun der Buffer voll und der Producer am Zug, stockt die Programmausführung, denn der Producer kann kein neues Element in den Puffer legen (der ist ja schon voll). Durch Kommunikation zwischen beiden Komponenten, in Java durch die Funktion notifyAll(), lässt sich der Deadlock elegant vermeiden: Der Producer wartet, bis im Puffer eine Position frei wird, addiert ein Element und benachrichtigt danach alle Consumer-Threads: synchronized void post (Work w) { while (queue.isFull())) {this.wait(); }          queue.add(w); this.notifyAll(); } Der Consumer wartet, bis der Puffer mindestens ein Element enthält, entnimmt dieses und benachrichtigt danach alle Producer-Threads: synchronized Work get () { while (queue.isEmpty()) {this.wait(); }         queue.remove(); this.notifyAll(); } Chip-Produzenten wie Intel oder AMD rät Pankratius, bei aller Parallelisierung den herkömmlichen sequentiellen Code nicht zu vergessen. Die Zukunft liege im optimierten, hybriden Chip-Design. Denn Software ist immer nur bis zu einem gewissen Grad parallelisierbar; bestimmte Sourcecode-Anteile verlangen zwingend nach einer sequentiellen Bearbeitung. Sind dann 47 von 48 Prozessorkernen auf Parallelcode optimiert, dann lastet die sequentielle Abarbeitung auf einem einzigen Prozessorkern, und das ist definitiv zu wenig. Punkto parallele Software-Entwicklung hält der Software-Papst zwei Trends für wegweisend: Offline-Tuning, wo der Programmierer performancerelevante Parameter vorgibt und ein in das Betriebssystem (OS) integriertes Auto-Tuning, welches das OS zur Laufzeit der Software selbst vornimmt.



Das könnte Sie auch interessieren