Compute Unified Device Architecture (CUDA)



Ähnliche Dokumente
Compute Unified Device Architecture CUDA

CUDA. Moritz Wild, Jan-Hugo Lupp. Seminar Multi-Core Architectures and Programming. Friedrich-Alexander-Universität Erlangen-Nürnberg

Grundlagen von CUDA, Sprachtypische Elemente

OpenMP am Beispiel der Matrizenmultiplikation

Praxiseinheit: Realisierung einer hardwarebeschleunigten Disparitätenberechnung zur automatischen Auswertung von Stereobildern

Lineargleichungssysteme: Additions-/ Subtraktionsverfahren

Konzepte der Informatik

Programmierbeispiele und Implementierung. Name: Michel Steuwer

Einführung zum Arbeiten mit Microsoft Visual C Express Edition

Installation OMNIKEY 3121 USB

5 DATEN Variablen. Variablen können beliebige Werte zugewiesen und im Gegensatz zu

Informationen zur Verwendung von Visual Studio und cmake

Programme im Griff Was bringt Ihnen dieses Kapitel?

AGROPLUS Buchhaltung. Daten-Server und Sicherheitskopie. Version vom b

4D Server v12 64-bit Version BETA VERSION

Windows 8.1. Grundkurs kompakt. Markus Krimm, Peter Wies 1. Ausgabe, Januar inkl. zusätzlichem Übungsanhang K-W81-G-UA

Handbuch. NAFI Online-Spezial. Kunden- / Datenverwaltung. 1. Auflage. (Stand: )

Anleitung über den Umgang mit Schildern

Handbuch Fischertechnik-Einzelteiltabelle V3.7.3

Nutzung von GiS BasePac 8 im Netzwerk

1 Mathematische Grundlagen

Jederzeit Ordnung halten

Erstellen einer Collage. Zuerst ein leeres Dokument erzeugen, auf dem alle anderen Bilder zusammengefügt werden sollen (über [Datei] > [Neu])

Matrix42. Use Case - Sicherung und Rücksicherung persönlicher Einstellungen über Personal Backup. Version September

Anton Ochsenkühn. amac BUCH VERLAG. Ecxel für Mac. amac-buch Verlag

Microsoft Visual Studio Community 2015

Outlook. sysplus.ch outlook - mail-grundlagen Seite 1/8. Mail-Grundlagen. Posteingang

Ein kleiner Einblick in die Welt der Supercomputer. Christian Krohn

DIGITALVARIO. Anleitung Bootloader. Ausgabe 0.1 deutsch für Direkt-Digital-Vario. Firmware ab Hardware 01 Seriennummer ab 0003

Primzahlen und RSA-Verschlüsselung

Handbuch ECDL 2003 Basic Modul 6: Präsentation Diagramm auf einer Folie erstellen

1. Einführung Erstellung einer Teillieferung Erstellung einer Teilrechnung 6

Ist Excel das richtige Tool für FMEA? Steve Murphy, Marc Schaeffers

1 topologisches Sortieren

Eine eigene Seite auf Facebook-Fanseiten einbinden und mit einem Tab verbinden.

OpenGL. (Open Graphic Library)

Fachbericht zum Thema: Anforderungen an ein Datenbanksystem

Daten-Synchronisation zwischen dem ZDV-Webmailer und Outlook ( ) Zentrum für Datenverarbeitung der Universität Tübingen

Monitore. Klicken bearbeiten

Lehrer: Einschreibemethoden

Binäre Bäume. 1. Allgemeines. 2. Funktionsweise. 2.1 Eintragen

Anzeige von eingescannten Rechnungen

Konfiguration von Igel ThinClients fu r den Zugriff via Netscaler Gateway auf eine Storefront/ XenDesktop 7 Umgebung

Musterlösungen zur Linearen Algebra II Blatt 5

Tutorium Informatik 1. Aufgabe 2: Formatierte Ein- und Ausgabe

Satzhilfen Publisher Seite Einrichten

Lizenzierung von SharePoint Server 2013

Speicher in der Cloud

Zwischenablage (Bilder, Texte,...)

Hilfen zur Verwendung der Word-Dokumentvorlage des BIS-Verlags

Zählen von Objekten einer bestimmten Klasse

MORE Profile. Pass- und Lizenzverwaltungssystem. Stand: MORE Projects GmbH

OECD Programme for International Student Assessment PISA Lösungen der Beispielaufgaben aus dem Mathematiktest. Deutschland

Elexis-BlueEvidence-Connector

Internet online Update (Mozilla Firefox)

Installationsanleitung für Update SC-Line

Diese Unterlage bezieht sich auf Excel 2010 (auf Deutsch). Die Benutzeroberfläche kann in anderen Versionen der Software erheblich anders aussehen.

4 Aufzählungen und Listen erstellen

Arbeiten mit UMLed und Delphi

Informationen zum neuen Studmail häufige Fragen

Software- und Druckerzuweisung Selbstlernmaterialien

Einführung in die Programmierung (EPR)

Installation und Inbetriebnahme von Microsoft Visual C Express

Microsoft Access 2010 Navigationsformular (Musterlösung)

Zahlen auf einen Blick

Einführung in die C++ Programmierung für Ingenieure

Feiertage in Marvin hinterlegen

Projektmanagement in der Spieleentwicklung

Stundenerfassung Version 1.8 Anleitung Arbeiten mit Replikaten

4. BEZIEHUNGEN ZWISCHEN TABELLEN

Aufklappelemente anlegen

Verbesserungsdetails: PTC Mathcad Prime 3.0. Copyright 2013 Parametric Technology Corporation. weiter Infos unter

Step by Step Softwareverteilung unter Novell. von Christian Bartl

Viele Bilder auf der FA-Homepage

Gemeinsamer Bibliotheksverbund: Übertragung von Datenexporten für den Verbundkatalog Öffentlicher Bibliotheken

Systeme 1. Kapitel 6. Nebenläufigkeit und wechselseitiger Ausschluss

WinVetpro im Betriebsmodus Laptop

Lineare Gleichungssysteme

Easy Share Anleitung Februar 2014

Erweiterung der Aufgabe. Die Notenberechnung soll nicht nur für einen Schüler, sondern für bis zu 35 Schüler gehen:

(C)opyright 2009 by Jochen Vajda

Datenbanken Kapitel 2

Ihr IT-Administrator oder unser Support wird Ihnen im Zweifelsfall gerne weiterhelfen.

Bereich METIS (Texte im Internet) Zählmarkenrecherche

Whitepaper. Produkt: combit Relationship Manager / address manager. Dateiabgleich im Netzwerk über Offlinedateien

Masterpraktikum Scientific Computing

Inhalt. 1 Einleitung AUTOMATISCHE DATENSICHERUNG AUF EINEN CLOUDSPEICHER

Hilfe Bearbeitung von Rahmenleistungsverzeichnissen

1) Farbsteuergerät in der Nikobus-Software unter Modul zufügen hinzufügen.

GEZIELT MEHR SICHERHEIT MIT 4I ACCESS SERVER & 4I CONNECT CLIENT

Die Arbeitsweise von Flash

Automatisierung ( Fernsteuerung ) von Excel unter Microsoft Windows Tilman Küpper (tilman.kuepper@hm.edu)

! Tipps und Tricks Sie können den Windows Explorer am einfachsten mit der Tastenkombination Windows+ E öffnen.

Synchronisations- Assistent

Wo möchten Sie die MIZ-Dokumente (aufbereitete Medikamentenlisten) einsehen?

Text Formatierung in Excel

Print2CAD 2017, 8th Generation. Netzwerkversionen

Zeichen bei Zahlen entschlüsseln

Advoware mit VPN Zugriff lokaler Server / PC auf externe Datenbank

BELIEBIG GROßE TAPETEN

Transkript:

Compute Unified Device Architecture (CUDA) Thomas Koller 12. Februar 2012 Zusammenfassung Diese Ausarbeitung beschäftigt sich mit der Programmierung von Grafikkarten mittels CUDA. Bei bestimmten Berechnungen können Grafikkarten eine wesentlich höhere Rechenleistung erzielen als Hauptprozessoren. Das Programmiermodell von Grafikkarten und Hauptprozessoren unterscheidet sich grundlegend. CUDA versucht die Programmierung zu vereinfachen und an das gewohnte Programmieren in C bzw. C++ anzupassen. 1

Inhaltsverzeichnis 1 Einleitung 3 2 Die Grafikkarte 3 3 CUDA 5 3.1 Überblick............................. 5 3.2 Aufbau und Konzept....................... 6 4 Programmiermodell 7 4.1 Ausführungselemente....................... 7 4.2 Datenparallelität......................... 8 4.3 Speicherverwaltung und Synchronisation............ 11 4.4 Compute Capabilities...................... 13 5 Werkzeuge 14 5.1 Entwicklungswerkzeuge..................... 14 5.2 Bibliotheken............................ 14 6 Fazit 15 2

1 Einleitung Die Compute Unified Device Architecture (CUDA) beschreibt eine von der NVIDIA Corporation entwickelte Architektur, die neben der Softwarearchitektur auch die wichtigsten Hardwareeigenschaften spezifiziert. Ziel dieser Architektur ist es die Grafikkarte als allgemeiner Parallelrechner nutzen zu können. Im Englischen ist dieses Konzept unter dem Begriff GPGPU General purpose computation on Graphics Processor Units bekannt. Das Nutzen der Grafikkarte zum Berechnen allgemeiner Aufgaben ist ein recht neuer Trend und wurde vorher durch das eingeschränkte Programmiermodell von Grafikkarten behindert. Grafikkarten dienten alleine zur Grafikberechnung, bzw. zur Berechnung des auf dem Monitor anzuzeigenden Bildes. Da die Berechnung der einzelnen Bildelemente recht aufwendig ist und zur flüssigen Darstellung der Bilder hohe Aktualisierungsraten notwendig sind, wurden viele Algorithmen direkt in Hardware implementiert. Daher erfolgte die Programmierung hardwarenah und der Befehlssatz war eingeschränkt. [WM10] Ein weiteres Problem war die Beschränkung auf spezielle Datentypen, die auf die Anforderungen im Grafikbereich ausgelegt sind. Unterstützt wurden fast ausschließlich Fest- und Fließkommazahlen, die für die Grafikberechnungen ausreichend genau sind, aber eine Umrechnung auf in der CPU genutzten Datentypen wie Integer erschweren. [BM07] In den letzten Jahren wurden diese Probleme durch zahlreiche Verbesserungen behoben. Einerseits gab es Hardwareverbesserungen wie die Nutzung von mehr parallelen Einheiten, mehr und schnelleren lokalen Speicher und höheren Bandbreiten beim Zugriff auf den Hauptspeicher (PCI Express). Andererseits wurde auch das Programmiermodell der Grafikkarte immer weiter verallgemeinert und der Abstraktionsgrad erhöht. Startpunkt dieser Entwicklung war der Wunsch aufwändigere 3D-Effekte in Spielen programmieren zu können. Die Möglichkeit der (eingeschränkten) Programmierbarkeit der Recheneinheiten, die so genannten Shader, begann auf Windowssystemen mit DirectX 8 und plattformunabhängig mit OpenGL 2.0. [WM10] Inzwischen sind Grafikkarten soweit frei programmierbar, dass sie für allgemeine Berechnungen eingesetzt werden können. Da inzwischen nahezu jeder Rechner eine vergleichsweise leistungsstarke Grafikkarte besitzt, die außer bei 3D-Anwendungen kaum gefordert ist, wird GPU-fähige Software immer interessanter für den Heimgebrauch. [PV09] 2 Die Grafikkarte Der Aufbau von Grafikkarten (GPU) unterscheidet sich grundsätzlich vom Aufbau eines Hauptprozessors (CPU). Dies liegt an den unterschiedlichen Aufgabengebieten für die sie entwickelt wurden. Bis vor einigen Jahren besaß 3

Abbildung 1: Vergleich Architektur CPU und GPU [NV11] eine CPU nur einen Kern. Die Leistung des Hauptprozessors wurde in erster Linie durch Takterhöhungen gesteigert. Dies brachte bei den zu dieser Zeit rein sequenziellen Programmen immer einen neuen Geschwindigkeitsschub. Mit der Zeit wurde die Erhöhung der Leistung durch reine Taktsteigerung und kleinen Korrekturen am Design immer schwieriger. Um dennoch die Geschwindigkeiten der Programme erhöhen zu können, wurden Prozessoren mit mehr als einem Kern entwickelt. Aktuell werden für den Heimgebrauch zwei bis sechs Kerne im Prozessor verwendet. Im Serverbereich werden CPU mit bis zu 16 Kernen genutzt. Gleichzeitig ist dort der Einsatz von mehreren CPUs üblich. Dieses so genannte Multicore-Konzept unterscheidet sich immer noch erheblich von dem Manycore-Konzept auf das Grafikkarten setzen (Vgl. Abb. 1). Das Multicore-Konzept der CPUs ist auf die möglichst schnelle Ausführung von sequenziellen Programmen ausgelegt. Der größte Teil der heute eingesetzten Software kann von mehreren Kernen nicht profitieren. Daher besitzen CPUs eine große Kontrolllogik um den sequenziellen Code schneller ausführen zu können. Dabei helfen Designtricks wie out-of-order-execution das sequenziellen Code in Hardware parallelisieren kann ohne die sequenzielle Struktur aufzuheben. Zusätzlich helfen große Caches Verzögerungszeiten beim Datenzugriff zu minimieren und die geringen Speicherbandbreiten zu verbergen. [KD10] Das Manycore-Konzept von GPUs benötigt Kontrolllogik und Caches nicht in diesem großen Umfang. Stattdessen wird der verfügbare Platz auf der Chipfläche in eine große Anzahl einfacher Rechenkerne investiert. Die Kontrolllogik ist auf das Nötigste reduziert und die Caches sind recht klein. Da GPUs für die Grafikberechnung entwickelt wurden, besitzen sie seit jeher eine große Speicherbandbreite, um z.b. Texturen schnell verarbeiten zu können. [KD10] Die aktuell schnellste Grafikkarte, die für CUDA-Berechnungen eingesetzt werden kann, die GeForce GTX 580, besitzt 512 Kerne und eine Speicherbandbreite von über 192GB/s. [AW10] Im Vergleich dazu besitzt der aktuell schnellste Hauptprozessor für den Heimgebrauch, der Intel Sandy 4

Bridge-E, sechs bis acht Kerne und eine Speicherbandbreite von 35,3GB/s, wenn vier DDR-1600-Module verwendet werden. [RV11] Diese architektonischen Unterschiede begründen, weshalb die CPU eher für allgemeine Anwendungen und die GPU eher für spezielle Anwendungen ausgelegt sind. Diese speziellen Anwendungen für die GPU nutzen die Single Instruction, Multiple Data -Arbeitsweise (SIMD) vorzugsweise mit großen Datenmengen und Datenparallelität (siehe Kapitel 4.2 auf Seite 8). [PV09] Ein weiterer interessanter Aspekt ist die Effizienz bei Berechnungen in Bezug auf den Bedarf an elektrischer Energie (Umgangssprachlich Stromverbrauch ). Grafikkarten haben bei optimierten Programmen eine wesentlich höhere Rechenleistung pro Watt. So leistet eine NVIDIA Fermi C2050 bei 32bit Berechnungen 4,14 GFlop/Watt, ein Intel Nehalem x5550 0,85 GFlop/Watt. [FR11] Abbildung 2: Vergleich Rechenleistung CPU und GPU [FR11] 3 CUDA 3.1 Überblick CUDA setzt auf die grundlegenden Konzepte zweier weit verbreiteter Programmierschnittstellen. Einerseits auf Open Multi-Processing (OpenMP) als Standard für Shared-Memory-Programmierung, andererseits auf das Message Passing Interface (MPI), in dem die Kommunikation zwischen den Prozessen über Nachrichtenaustausch geregelt wird. CUDA nutzt das Konzept des gemeinsamen Speichers von OpenMP. Wegen einer aufwändigen Threadverwaltung skaliert OpenMP bei einer wachsenden Anzahl von Thread nicht sehr gut. CUDA nutzt dagegen eine Threadverwaltung mit wenig Overhead, sodass auf einer GeForce GTX 580 über 24000 Threads möglich sind. Weiterhin benötigt OpenMP cache-kohärente Hardware. Dies ist bei CUDA nicht notwendig. [KD10] 5

Das MPI-Konzept wird bei CUDA als one-sided message passing mit limited shared memory capability [KD10, S. 13] aufgegriffen. Der Programmierer muss sich um den Datentransfer vom Hauptprozessor (Arbeitsspeicher) zur Grafikkarte (der lokale DRAM auf der Karte) kümmern, danach die Berechnungen auf der GPU durchführen und nach der Berechnung die Daten wieder von der Grafikkarte in den Arbeitsspeicher kopieren. [KD10] 3.2 Aufbau und Konzept Programme für CUDA werden in der Sprache CUDA C geschrieben. Alternativ ist die Sprache auch unter C for CUDA bekannt. Dabei handelt es sich um die Sprache C, die mit CUDA-Spezifischen Schlüsselwörtern erweitert wurde. Weiterhin gehört eine Laufzeitbibliothek zu CUDA C. CUDA trennt zwischen Host- und Device-Code. Der Host-Code läuft weiterhin auf dem Hauptprozessor (CPU) und der Device-Code wird auf der Grafikkarte (GPU) ausgeführt. Dies kann gleichzeitig und asynchron geschehen. Abbildung 3: Sequenzieller Ablauf [FR11] Abbildung 4: Paralleler Ablauf [FR11] CPU und GPU besitzen ihren eigenen Speicher. Die CPU nutzt wie gehabt den Arbeitsspeicher und die GPU ihren lokalen Speicher auf der Grafikkarte. Der Speicher auf der Grafikkarte muss vom Programmierer manuell verwaltet werden. Daten, die zur Berechnung benötigt werden, können nicht automatisch auf die Grafikkarte kopiert werden. Allgemein lässt sich die Programmierung der Grafikkarte mit CUDA folgendermaßen zusammenfassen: 6

Die generelle Programmlogik wird weiterhin auf dem Host ausgeführt. Der zur Berechnung benötigte GPU-Speicher wird allokiert. Die Daten werden vom Host zum Device kopiert. Die Berechnungen werden auf dem Device durchgeführt. Das Ergebnis wird vom Device zum Host kopiert. Der allokierte Speicher wird wieder freigeben. Somit kann man die Grafikkarte mit CUDA als eigenständigen Co- Prozessor mit eigenem Arbeitsspeicher und Cache ansehen, der parallel zur CPU arbeitet. Der Compiler für CUDA heißt Nvidia C-Compiler, kurz nvcc. Er unterscheidet anhand der Dateiendungen zwischen Host- und Device-Code. Dateien mit der Endung.cu, die später auf der Grafikkarte laufen sollen, kompiliert er selbst. C oder C++ Dateien für den Host reiche er an einen C bzw. C++ Compiler wie dem GNU C Compiler weiter. 4 Programmiermodell 4.1 Ausführungselemente Funktionen, die auf der Grafikkarte ausgeführt werden, werden in CUDA Kernel genannt. Der Code in einem Kernel wird von jedem Thread genau einmal ausgeführt. Jeder Thread führt somit denselben Code aus. Das folgende Beispiel [NV11] dient zur Addition zweier Vektoren. In der main-funktion wird mit VecAdd<<<1, N>>>(A, B, C) der Kernel mit N Threads aufgerufen. Die Anzahl der Threads muss hierbei gleich der Länge des Vektors sein. Der Kernel wird mit dem Schlüsselwort global definiert und erwartet drei Vektoren. Wichtig ist das Schlüsselwort void. Kernel können keine Werte zurückgeben. Die Rückgabe erfolgt über das Zurückkopieren des entsprechenden Speicherbereichs von der Grafikkarte in den Hauptspeicher (in diesem Fall den Vektor C). 1 // Kernel definition 2 global void VecAdd ( float * A, float * B, float * C) 3 { 4 int i = threadidx. x; 5 C[i] = A[i] + B[i]; 6 } 7 int main () 8 { 9... 10 // Kernel invocation with N threads 7

11 VecAdd <<<1, N >>>(A, B, C); 12 } Kernels können mit den Schlüsselwörtern global, device und host definiert werden. Mit Hilfe dieser Schlüsselwörter wird unterschieden, wo eine Funktion ausgeführt werde muss, um eine andere Funktion aufrufen zu dürfen. global bedeutet, dass die Funktion, die auf dem Device läuft, von einer Funktion, die auf dem Host läuft, aufgerufen werden muss. Im Beispiel ruft die main-funktion, die auf dem Host läuft, die auf dem Device laufende Funktion VecAdd auf. Funktionen mit dem Schlüsselwort device dürfen nur andere device -Funktionen aufrufen. host -Funktionen dürfen nur von anderen host -Funktionen aufgerufen werden. [KD10] 4.2 Datenparallelität Der in Abbildung 4, Kapitel 3.2, dargestellte Ablauf nennt sich Taskparallelität. Zwar unterstützt CUDA Taskparallelität, aber Rechenleistung von Grafikkarten kann nur bei Datenparallelität ausgenutzt werden. Bei Datenparallelität werden die Daten aufgeteilt und auf jedem dieser Datenteile wird die gleiche Berechnung durchgeführt. Dazu erlaubt es CUDA die Kernel in Blöcke und Threads zu unterteilen. Jede Berechnung wird einem Thread zugeordnet und die Blöcke dienen dazu diese Threads strukturiert zu ordnen. Ein Kernel führt ein Grid aus. Dieses Grid besteht aus Blöcken, die in der Regel dreidimensional angeordnet sind. In Abbildung 5 wird eine zweidimensionale Anordnung gewählt, z.b. Block(1,1). Diese Blöcke wiederum beinhalten Threads, die bis zu drei Dimensionen haben können. Je nach Anwendung müssen nicht alle Dimensionen benutzt werden. So ist es auch möglich 1 bis N Blöcke in einer Dimension zu nutzen oder im Extremfall nur einen Block zu verwenden. Genauso können Threads zweidimensional und eindimensional verwendet werden. Die Dimensionen von Blöcken und Threads werden beim Kernelaufruf festgelegt. Ein Kernelaufruf erfolgt nach dem Schema KernelFunction<<<dimGrid, dimblock>>>(...);. dimgrid und dimblock sind zwei dreidimensionale Integer-Variablen vom Typ dim3. Erstere bezeichnet die Anordnung der Blöcke, letztere die Anordnung der Threads in den Blöcken. Diese Anordnung der Threads ist in allen Blöcken des Grids gleich und kann genauso wie die Anordnung der Blöcke zur Laufzeit des Kernels nicht mehr verändert werden. Die in Abbildung 5 gezeigte Anordnung von Blöcken und Threads wird über folgenden Code aufgerufen: 1 dim3 dimgrid (2, 2, 1); 2 dim3 dimblock (4, 2, 2); 3 KernelFunction <<< dimgrid, dimblock > > >(...) ; 8

Abbildung 5: Organisation von Grids, Blöcken und Threads [KD10] Je nach unterstützter Compute Capability des Grafikchips (siehe Kapitel 4.4), wird eine unterschiedliche Anzahl von Threads und Blöcken unterstützt. Vor Version 2.x war die Dimension der Blöcke auf zwei begrenzt. Seit dieser Version können auch Blöcke dreidimensional sein. Dennoch musste eine dim3-variable verwendet werden. Die letzte Stelle der Variablen wurde dabei ignoriert. Weiterhin war die Anzahl der Threads in einem Block auf 512 limitiert. Nun dürfen es 1024 sein. [NV11] Warum diese Aufteilung in Blöcken und Threads erfolgt, wird nun an einem Beispielen zur Multiplikation zweier Matrizen veranschaulicht. Zur Vereinfachung wird nur ein Block verwendet. In Abbildung 6 ist neben einem C-Quelltext zur sequenziellen Berech- 9

nung auf der CPU auch die Verwendung der Variablen M, N, P, i, j und k dargestellt. Die Matrizen sind quadratisch und die Anzahl der Zeilen bzw. der Spalten ist mit dem Parameter WIDTH angegeben. Abbildung 6: einfache Matrixmultiplikation [KD10] Der folgende Kernel [KD10] multipliziert die beiden Matrizen auf der Grafikkarte: 1 // Matrixmultiplication kernel - thread specification 2 global void MatrixMulKernel ( float * Md, float * Nd, float * Pd, int Width ) 3 { 4 // 2D Thread ID 5 int tx = threadidx. x; 6 int ty = threadidx. y; 7 8 // Pvalue stores the Pd element that is computed by the thread 9 float Pvalue = 0; 10 11 for ( int k = 0; k > Width ; ++k) 12 { 13 float Mdelement = Md[ ty * Width + k]; 14 float Ndelement = Nd[ k * Width + ty ]; 10

15 Pvalue += Mdelement * Nelement ; 16 } 17 18 // Write the matrix to device memory each thread writes one element 19 Pd[ ty * Width + tx] = Pvalue ; 20 } Im Gegensatz zum Standard C-Code fehlen hier die beiden for-schleifen mit den Variablen i und j. Diese wurden durch die Variablen threadidx.x und threadidx.y ersetzt. Da jeder Thread denselben Code ausführt, dienen in diesem Beispiel alleine diese beiden Variablen zur Unterscheidung der Threads. Der erste Thread (0, 0) bekommt automatisch für threadidx.x und threadidx.y jeweils eine 0 zugewiesen. Der zweite Thread (0, 1) hat threadidx.x = 0 und threadidx.y = 1, usw. Somit rechnet jeder Thread genau ein Element in der Matrix Pd aus, indem er nur auf seiner Zeile der Md-Matrix bzw. seiner Spalte der Nd-Matrix arbeitet. [KD10] Bei einer genügend großen Anzahl an Threads können prinzipiell, im Gegensatz zur sequenziellen Ausführung auf der CPU, alle Elemente der Pd-Matrix gleichzeitig ausgerechnet werden. Wenn neben Thread-Ids auch Blöcke benutzt werden, kann man jeden Thread in einem Grid mit Hilfe der Formeln 1 int i = blockidx. x * blockdim. x + threadidx. x; 2 int j = blockidx. y * blockdim. y + threadidx. y; 3 int k = blockidx. z * blockdim. z + threadidx. z; eindeutig identifizieren. Das Aufteilen in Blöcken ermöglicht es später während der Ausführung des Programms eine Art Hierarchiebildung durchzuführen. Das Ziel dabei ist eine optimale Auslastung auch auf anderen CUDA-Grafikkarten mit einer anderen Anzahl an Recheneinheiten (Vgl. Abb. 7). [NV11] 4.3 Speicherverwaltung und Synchronisation Die Speicherverwaltung auf Grafikkarten gestaltet sich komplizierter als bei der Programmierung auf CPUs. Wie in Kapitel 3.2 erwähnt, besitzt die Grafikkarte einen separaten eigenen Speicher. Um Berechnungen auf der Grafik- 11

Abbildung 7: Ausführung auf unterschiedlichen GPUs [NV11] karte durchführen zu können, müssen die benötigten Daten zuerst einmal in den Speicher der Grafikkarte kopiert werden. Dazu muss wie unter C zuerst der Speicher allokiert werden. Hierfür besitzt CUDA den Befehl cudamalloc (). Als nächstes kopiert man die Daten mit dem Befehl cudamemcpy() auf die Grafikkarte. Mit diesem Befehl können die Daten auch wieder zurück in den Arbeitsspeicher kopiert werden. Dazu gibt es vier Arten von cudamemcpy (), wovon eine als Parameter dem Befehl übergeben werden muss. Für Daten, die vom Host zum Device kopiert werden, ist cudamemcpyhosttodevice vorgesehen, für die andere Richtung gibt es cudamemcpydevicetohost. Weiterhin gibt es cudamemcpyhosttohost und cudamemcpydevicetodevice um Daten im gleichen Speicherbereich zu kopieren, wobei letzterer nicht für den Datentransfer zwischen zwei Grafikkarten in einem Computer verwendet werden kann. Nach dem Zurückkopieren kann mit cudafree() der anfangs allokierte Speicher auf der Grafikkarte wieder freigegeben werden. [ND11] Eine Synchronisation der Threads ist nur eingeschränkt möglich. Die einzige Möglichkeit, die CUDA zur Synchronisation unterstützt, ist die Funktion syncthreads(), die eine Barrieren-Synchronisation nutzt. Hier wird gewartet bis alle Threads in einem Block (nicht in einem Grid!) an dieser Stelle angelangt sind und den Befehl aufgerufen haben. [NV11] Bei der Synchronisation mit Barrieren ist auf einen möglichst guten Lastausgleich 12

zwischen den Threads zu achten, damit die Zeit, die Threads warten müssen, gering ist. 4.4 Compute Capabilities CUDA wird Software- und Hardwareseitig laufend weiterentwickelt. Um festlegen zu können welche Funktionen Hardwareseitig in welchem Umfang unterstützt werden, sind die Grafikchips in verschiedene Klassen eingeteilt. Diese Klassen werden bei CUDA Compute Capabilities genannt. Abbildung 8: Auszug Compute Capabilities [NV11] Unterschieden wird in eine major revision und eine minor revision. Major revision 1.x wurde mit der GeForce 8800 GTX (G80) eingeführt. Diese Version wurde mit der GeForce GTX 280 (GT200) bis zur Compute Capability 1.3 weiterentwickelt. Bei annähernd gleicher Kernarchitektur des Grafikchips wurden einige Funktionen hinzugefügt und beispielsweise atomare Funktionen auf Variablen im globalen Speicher (Compute Capability 1.1) oder die Unterstützung von Gleitkommazahlen in doppelter Genauigkeit (Compute Capability 1.3) ermöglicht. Mit der neuen Chiparchitektur Fermi, die mit der GeForce GTX 480 (GF100) eingeführt wurde, ist die major revision auf 2.x angehoben worden. Die wichtigsten Neuerungen waren einerseits die Möglichkeit nun auch drei- 13

dimensionale Blöcke nutzen zu können und andererseits die Verdopplung der maximalen Anzahl an Threads pro Block von 512 auf 1024. [NV11] 5 Werkzeuge 5.1 Entwicklungswerkzeuge Das wichtigste Entwicklungswerkzeug für CUDA ist das CUDA Toolkit. Es ist für Linux, Mac und Windows erhältlich und unterstützt unter Linux viele Distributionen wie Fedora, Ubuntu und OpenSUSE. Es beinhaltet den Compiler nvcc, der, wie in Kapitel 3.2 erwähnt, den Cuda-Code selbst kompiliert und den C bzw. C++ Code an einen Standard-Compiler wie dem GNU C Compiler weiterreicht. Zum Debuggen beinhaltet das Toolkit cuda -dgb und cuda-memcheck. cuda-memcheck wurde speziell für das Debuggen von Zugriffsfehlern auf den Speicher entwickelt. Weiterhin gibt es den Visual Profiler zum Optimieren des Programmcodes und verschiedene Bibliotheken für häufig verwendete Funktionen. [NT11] Nur für Windows Vista ab SP1 und Windows 7 und Windows HPC Server 2008 ist NVIDIA Parallel Nsight erhältlich. Es integriert sich in die IDE Microsoft Visual Studio 2008 oder 2010 (jeweils mit SP1) und enthält Funktionen wie debuggen, profiling und tracing. [NP11] 5.2 Bibliotheken Inzwischen gibt es für CUDA viele Bibliotheken. Wichtige Bibliotheken sind u.a. cufft, cublas und Thrust. cufft bietet optimierte Funktionen für Berechnungen mit reellen und komplexen Zahlen, u.a. für die schnelle Fourier- Transformation. cublas besitzt Funktionen für den Bereich der linearen Algebra. Thrust bietet im Vergleich zu Standard-CUDA vereinfachte Funktionen zum Erstellen und Kopieren von Datenstrukturen und optimierte Algorithmen für häufige Probleme wie sortieren, transformieren oder der Reduktion. Weitere Bibliotheken gibt es zum Beispiel zur Generierung von Zufallszahlen und anderen mathematischen Problemen. [NL11] 14

6 Fazit Die Programmierung von Grafikkarten für allgemeine Rechenaufgaben wurde mit der Entwicklung von CUDA stark vereinfacht. Allerdings steckt hinter GPGPU ein völlig anderes Programmierkonzept. Datenparallelität steht hier im Vordergrund. Eine vernünftige Aufteilung der Daten und ein darauf ausgelegter Programmcode sind für eine gute Performance essentiell. CUDA wird laufend weiterentwickelt. In der kürzlich erschienenen Version 4.1 wird ein neuer Compiler verwendet. Dieser soll zukünftig neben NVIDA-Grafikkarten auch andere Plattformen bzw. Prozessor-Architekturen wie Grafikchips von AMD und Intel unterstützen. [HD12] Bei Hochleistungsrechnern haben sich Grafikkarten zu Beschleunigung der Berechnungen längst etabliert. Der zweitschnellste Supercomputer, der Tianhe-1A - NUDT YH MPP aus China, setzt auf NVIDIA Tesla C2050 Grafikkarten. [TO11] Auch die Physiker der Universität Bielefeld setzen bei ihrem neuen Hochleistungscomputer auf NVIDIA Grafikkarten. [UA12] Mit der Zeit wird sich diese Entwicklung auch im Heimbereich durchsetzen. Multimediaanwendungen werden immer wichtiger und Grafikkarten immer leistungsfähiger. Gerade diese Anwendungen bieten großes Potential durch Parallelisierung beschleunigt zu werden. 15

Literatur [AW10] Andermahr, Wolfgang: Test: Nvidia GeForce GTX 580, 2010. http://www.computerbase.de/artikel/grafikkarten/2010/test-nvidiageforce-gtx-580/, Abruf: 18.01.2012 [BM07] Blunck, Marc: Grafkkarten - Programmierung. Hochschule Bremen, 2007 [FR11] Farber, Rob: CUDA Application Design and Development. Elsevier Inc., Waltham 2011 [HD12] heise Developer: Nvidia veröffentlicht CUDA 4.1 mit LLVM- Compiler. http://heise.de/-1423222, Abruf 27.01.2012 [KD10] Kirk, David: Programming massively parallel processors. Elsevier Inc., Burlington 2010 [ND11] NVIDIA: NVIDIA CUDA Library Documentation 4.0. http://developer.download.nvidia.com/compute/cuda/4_0/toolkit/docs/ online/index.html, Abruf 23.01.2012 [NL11] NVIDIA: GPU-Accelerated Libraries. http://developer.nvidia.com/gpu-accelerated-libraries, Abruf 26.01.2012 [NP11] NVIDIA: NVIDIA Parallel Nsight. http://developer.nvidia.com/nvidia-parallel-nsight, Abruf 26.01.2012 [NT11] NVIDIA: CUDA Toolkit 4.1. http://developer.nvidia.com/cuda-toolkit-41, Abruf 26.01.2012 [NV11] NVIDIA: NVIDIA CUDA C Programming Guide, Version 4.0. http://developer.download.nvidia.com/compute/cuda/4_0/toolkit/docs/ CUDA_C_Programming_Guide.pdf, Abruf: 19.01.2012 [PV09] Pankratius, Victor: Software Engineering für moderne, parallele Plattformen. Universität Karlsruhe (TH), 2009 [RV11] Rißka, Volker: Test: Intel Sandy Bridge-E, 2011. http://www.computerbase.de/artikel/prozessoren/2011/test-intel-sandybridge-e/, Abruf: 18.01.2012 [SJ10] Sanders, Jason; Kandrot, Edward: CUDA by Example: An Introduction to General-Purpose GPU Programming. Addison-Wesley Longman, Amsterdam 2010 [TO11] TOP500 List - November 2011. http://www.top500.org/list/2011/11/100, Abruf 27.01.2012 16

[UA12] uni.aktuell: Bielefelder Hochenergie-Physiker erhalten neuen Superrechner. http://ekvv.uni-bielefeld.de/blog/uniaktuell/entry/bielefelder _hochenergie_physiker_erhalten_neuen, Abruf 27.01.2012 [WM10] Wahl, Michael: Rechnerarchitekturen 2. Universität Siegen, 2010 17