Seminararbeit GPU Architektur und Programmiermöglichkeiten für GPGPU-Anwendungen

Größe: px
Ab Seite anzeigen:

Download "Seminararbeit GPU Architektur und Programmiermöglichkeiten für GPGPU-Anwendungen"

Transkript

1 Seminararbeit GPU Architektur und Programmiermöglichkeiten für GPGPU-Anwendungen Marius Gräfe University of Kaiserslautern, Embedded Systems Group m graefe10@cs.uni-kl.de 25. Oktober 2012 Alone we can do so little; together we can do so much. Helen Keller

2 2 Zusammenfassung Auf der stetigen Suche nach mehr Rechenleistung können Programmierer seit den letzten Jahren vermehrt auf die Möglichkeiten moderner Grafikbeschleuniger zurückgreifen. Diese entwickeln sich weg von der ursprünglichen Spezialhardware für Spieler hin zu allgemeinen Rechenbeschleunigern. Dieses Paper soll über die Architektur moderner GPUs informieren und einen Überblick über die Möglichkeiten und Besonderheiten der Programmierung dieser geben.

3 3 Inhaltsverzeichnis 1 Einleitung 4 2 GPU Architektur Einleitung Allgemeiner Aufbau Konkreter Aufbau (AMD Radeon HD 7970) Stärken und Schwächen CUDA Einleitung Prozessmodell Parallelisierung Speicherhierarchie CUDA C Kompilierung Kerneldefinition und -aufruf Umsetzung der Speicherhierarchie Synchronisation Datentypen PTX Assembler OpenCL Einleitung Prozessmodell Parallelisierung Speicherhierarchie Workflow OpenCL C Kerneldefinition und Speicherhierarchie Synchronisation Datentypen Die OpenCL API (Host-Code) Vergleich zwischen OpenCL und CUDA Grundlegendes Vergleich der Performance Abschließende Bemerkungen 26

4 4 1 Einleitung Ursprünglich waren Grafikbeschleuniger genau das, was der Name aussagt: Sie sollten bei grafikintensiven Anwendungen die restlichen Komponenten des Computers entlasten indem sie einen Teil des Berechnungsaufwands übernehmen. Da Grafikberechnungen zum Großteil aus einfachen Gleitkommaadditionen und -Multiplikationen bestehen, wurden diese Berechnungsvorgänge von den Herstellern massiv optimiert und parallelisiert. Jedoch waren diese Fähigkeiten nur im Rahmen der sogenannten Fixed-Function-Pipeline verfügbar. Diese erhielt vom Host-Programm nur Geometriedaten und einige Parameter (wie Lichteinstellungen, Texturen etc.) und erzeugte nach einem unveränderlichen Ablauf das Bild auf dem Monitor. Mit der Zeit wurde die Grafikpipeline immer mehr generalisiert und schließlich komplett programmierbar gestaltet, heute lässt sich ein Grafikprozessor auch für Berechnungen nutzen, welche nichts mit Computergrafik zu tun haben. Mit der Veröffentlichung des CUDA SDK durch NVIDIA Anfang 2007 wurden Grafikbeschleuniger schließlich einfacher für GPGPU Anwendungen nutzbar, allerdings nur mit Grafikkarten von NVIDIA (siehe Kapitel 3, Seite 7) wurde die Spezifikation von OpenCL 1.0 als offener Standard (ähnlich wie OpenGL) veröffentlicht und mittlerweile existieren Implementierungen für eine ganze Reihe unterschiedlicher Geräte (siehe Kapitel 4, Seite 16).

5 5 2 GPU Architektur 2.1 Einleitung Eine GPU ist ein massiver Parallelrechner, die aktuellen Spitzenmodelle (Mitte 2012) besitzen 1536 (Nvidia GTX 680) bzw (AMD RADEON HD 7970) Rechenkerne. Da jeder dieser Kerne (im Kontext der Computergrafik auch Shadereinheiten genannt) parallel rechnet, lassen sich beachtliche theoretische Leistungen von ungefähr 3700 GFLOPS 1 erzielen (als Vergleich: ein aktueller Intel Core i7-3930k leistet maximal 307 GFLOPS 2, ein Pentium 4 mit 3,2 GHz lieferte seinerzeit maximal 6,4 GLOPS). Dieses Kapitel beschreibt, wie eine GPU allgemein und, bezogen auf die HD 7970, konkret aufgebaut ist. 2.2 Allgemeiner Aufbau Analog zur CPU spricht man auf der GPU ebenfalls von Threads, wenn man parallel ablaufende Programmteile meint. Das Programm (bzw. der Code) eines Threads nennt man Kernel, der Thread selber ist demnach eine Kernel-Instanz. Eine Kernel-Instanz führt immer den exakt gleichen Programmcode aus, der Programmfluss und die Berechnungsergebnisse hängen somit nur von den eingelesenen Daten ab. Eine GPU ist ein paralleler Streamprozessor, was bedeutet, dass Grafikkarten darauf ausgelegt sind, viele einfache (d.h. skalare) Berechnungen nach einem festen, möglichst verzweigungsfreien und für jeden Datensatz gleichen Schema auszuführen. Sie lässt sich grob in vier Bereiche einteilen: Hauptspeicher, Rechenkerne, Caches und aufgabenspezifische Hardwarebausteine. Der Hauptspeicher, welcher, obwohl der mittlerweile fast identischen Kapazität, nicht mit dem allgemeinen Hauptspeicher des Host-Computers zu verwechseln ist, stellt den Aufbewahrungsort für die zur Berechnung benötigten Daten dar. Zwar ist die Anbindung dieses Speichers an die GPU häufig um ein vielfaches schneller als die Anbindung des RAM s an die CPU des Rechners, trotzdem eignet er sich aufgrund seiner im Vergleich hohen Zugriffszeiten nicht gut für kleine, temporäre Berechnungsergebnisse. Die Rechenkerne sind einfache, skalare Gleitkommaprozessoren welche häufig zu Gruppen zusammengefasst werden. Innerhalb der Gruppe teilen sie sich zum Beispiel Integer-, Branching-, Cache-, Texturund Schedulingeinheiten, auch Register werden häufig geteilt, was bedeutet, dass ein Rechenkern keinesfalls als vollwertiger Prozessor bezeichnet werden kann. Die Caches sind im Vergleich zu denen der CPU extrem klein, sie bewegen sich im Kilobyte-Bereich, wohingegen eine CPU häufig mehrere Megabytes besitzt. Außerdem ist ihre Anzahl, bezogen auf die Menge der Kerne, viel kleiner, es gibt häufig keinen L3, sondern nur einen globalen L2-Cache, und mehrere Kerne teilen sich einen L1-Cache. Aufgabenspezifische Hardwareteile sind nicht programmierbar. Sie übernehmen sowohl allgemeine Managementaufgaben (wie z.b. Cache-Controller, Command-Processor oder Bus-Interfaces), als auch die (wenigen) vorgeschriebenen Schritte der Grafikpipeline, wie die Rasterisierung. 1 AMD Radeon HD 7970, Single Precision 2 Single Precision

6 6 2.3 Konkreter Aufbau (AMD Radeon HD 7970) Die Shadereinheiten der HD 7970 sind zu insgesamt 32 Compute Units zusammengefasst. Eine Compute Unit besteht aus vier Vector Units, welche wiederum aus 16 Shadereinheiten bestehen. Jeder Vector Unit steht ein Vector Register von 64 Kilobyte zur Verfügung. Alle Vector Units, bzw. die darin enthaltenden Shadereinheiten, teilen sich einen Scheduler, eine Scalar Unit sowie das zugehörige Scalar-Register (4 Kilobyte), vier Texture Filter Units, 16 Texture Fetch/Store Units, eine Branch & Message Unit, einen Local Data Share sowie einen L1-Cache von 16 Kilobyte. Jeweils vier Compute Units wird ein 16 Kilobyte großer Instruction Cache, ein 32 Kilobyte großer Scalar Data Cache sowie eine komplette Rastereinheit zur Verfügung gestellt. Alle Einheiten teilen sich einen L2-Cache sowie den 3 Gigabyte großen Hauptspeicher. 2.4 Stärken und Schwächen Trotz der großen Rechenleistung ist eine Grafikkarte nicht für alle Aufgaben geeignet. Allem voran muss der auszuführende Algorithmus massiv parallelisierbar sein, sequenzielle Algorithmen sind schlichtweg ungeeignet. Eine GPU kann nicht gut mit Verzweigungen umgehen, zum einen fehlen moderne CPU- Features wie Branch Prediction 3, andererseits kann ein dynamischer Branch eine (langsame) Resynchronisation der Threads zur Folge haben. Wegen der eher spartanischen Cache-Architektur sollte ein einzelner Thread nicht zu viele Daten auf einmal aus dem Hauptspeicher lesen. Threads, welche die gleichen, bzw. ähnliche Speicheradressen ansprechen, sollten gleichzeitig ablaufen um einen Vorteil aus den kleinen Caches zu ziehen und Hauptspeicherzugriffe zu vermeiden. Ein optimaler GPGPU-Algorithmus führt also viele einfache Berechnungen auf wenigen, kleinen Daten parallel aus und ist Branch-frei. Als zusätzliche Schwäche ist das Rechnen mit Gleitkommazahlen bei doppelter Genauigkeit (doubleprecision) zu nennen, vor allem die für Spieler gedachten GPUs sind hier erheblich langsamer als bei einfacher Genauigkeit 4. GPUs für ein professionelles Umfeld (z.b. NVIDIA Tesla oder AMD FireStream ) unterscheiden sich, neben einem oft wesentlich größeren Speicher und/oder Fehlerkorrekturverfahren, durch ihre höhere Geschwindigkeit bei doppelter Genauigkeit von den normalen Modellen, allerdings auch zu einem erheblich höheren Preis. 3 Methode um das Ergebnis einer Branch-Operation mithilfe von Heuristiken vorherzusagen um so Pipeline-Stalls zu verhindern 4 AMD RADEON HD 7970: Faktor 1/4 (DP/SP), NVIDIA GTX 680: Faktor 1/24, NVIDIAs Kepler-Architektur hat allerdings auch bei den professionellen Modellen keinen besseren Faktor, die vorherige Generation Fermi erreichte im professionellen Umfeld 1/2.

7 7 3 CUDA 3.1 Einleitung CUDA (Compute Unified Device Architecture) ist eine von NVIDIA entwickelte Architektur zur parallelen Berechnung auf NVIDIA Grafikkarten. Die in der Programmiersprache CUDA C geschriebenen Programme werden mittels eines von NVIDIA bereitgestellten Compilers kompiliert, und können anschließend mithilfe normaler Programmiersprachen (allen vorran C/C++) angesprochen, also ausgeführt werden. Aufgrund seiner hardwarespezifischen und proprietären Herkunft besitzt CUDA ein großes Potenzial für Optimierungen und bietet dem Programmierer viele speziell auf die Hardware abgestimmte Möglichkeiten. Der Nachteil ist allerdings die Beschränkung auf die herstellereigene Hardware, das heißt CUDA Programme können nur auf NVIDIA Hardware ausgeführt werden. Da die Multiplikation großer Matrizen ein häufig herangezogenes Beispiel ist komme ich auch hier häufig darauf zurück. 3.2 Prozessmodell Das einfachste Prozessmodell unter CUDA sieht vier Schritte vor: 1. Die Prozessdaten werden vom Hauptspeicher des Host-Rechners in den Speicher der Grafikkarte kopiert. 2. Die CPU beaufragt die GPU mit der Berechnung. 3. Die GPU führt das Programm parallel in CUDA Threads aus. 4. Das Resultat wird zur Weiterverarbeitung beziehungsweise Auswertung vom Hauptspeicher der Grafikkarte in den Hauptspeicher des Host-Rechners übertragen. 3.3 Parallelisierung Wie bei allen GPGPU Anwendungen muss das Gesamtproblem auch bei CUDA in gleichartige Teilprobleme zerlegt werden, welche dann parallel in sogenannten CUDA Threads ausgeführt werden. Diese Zerlegung kann, basierend auf realen Problemen, in ein, zwei oder drei Dimensionen erfolgen. Zur Berechnung einer eindimensionalen Fouriertransformation (z.b. bei digitaler Audioverarbeitung) bietet sich also eine Dimension an, bei Matrizenmultiplikationen zwei und so weiter. Der Gedanke dahinter ist dass, geometrisch gesprochen, nah beieinanderliegende Threads auf nah beieinanderliegenden (im Sinne der Speicheradressen) Daten operieren sollen, um so die Cache-Architektur besser auszunutzen. Das Programm zur Lösung eines Teilproblems, also, um als Beispiel bei der Matritzenmultiplikation zu bleiben, die Berechnung eines Eintrags der Ergebnismatrix, wird Kernel genannt. Ein Kernel wird von vielen CUDA Threads parallel ausgeführt, man spricht daher auch von Kernel-Instanzen. Außerdem werden mehrere CUDA Threads zu CUDA Thread Blocks gruppiert, die Gesamtheit der Blöcke wird Grid genannt. Die Dimension der Blöcke ist von der Dimension des Grids unabhängig, jedoch besitzen alle Blöcke untereinander die gleiche Dimension (d.h. es können zweidimensionale Blöcke in einem dreidimensionalen Grid ausgeführt werden, aber niemals ein zwei- und ein dreidimensionaler Block gleichzeitig); für eine Beispielhafte Aufteilung siehe Abbildung 1, Seite 8. Innerhalb eines Kernels kann über vordefinierten Variablen (siehe Tabelle 1, Seite 9) unter anderem auf die Position des Threads (welcher die Kernel-Instanz ausführt) bzw. des Blocks innerhalb des Grids zugegriffen werden. Der Multiprozessor (die GPU) erstellt, verwaltet und führt Threads in Gruppen von 32 Stück aus, diese

8 8 werden bei CUDA Warp genannt. Wenn die GPU einen oder mehr Blöcke ausführen soll, so partitioniert sie diese in Warps, welche dann von einem Warp Scheduler zur Ausführung eingeteilt werden. Ein Warp führt immer nur eine gemeinsame Instruktion gleichzeitig aus. Falls die Threads innerhalb eines Warps (z.b. aufgrund eines if-statements) unterschiedliche Ausführungspfade nehmen, also divergieren, so werden die unterschiedlichen Pfade sequentiell ausgeführt. Gegeben sei der folgende Pseudocode: if condition then do something else do something different end if Angenommen für die Threads T true gilt condition = wahr und für die Threads T f alse gilt condition = f alsch (T true T f alse = Warp, T true /0, T f alse /0), dann werden zuerst alle Threads in T true parallel ausgeführt und anschließend alle Threads in T f alse (bzw. andersherum). Es können aber innerhalb eines Warps niemals zwei Threads t 1 T true und t 2 T f alse gleichzeitig ausgeführt werden, die volle Leistung wird also nur erreicht wenn alle Threads des Warps den gleichen Pfad nehmen. Block (0,0) Block (1,0) (0,0) (1,0) Grid (0,0) (1,0) (0,1) (1,1) (0,1) (1,1) Block (0,1) Block (1,1) (0,0) (1,0) (0,0) (1,0) (0,1) (1,1) (0,1) (1,1) Abbildung 1: Beispielhaftes, zweidimensionales Block- und zweidimensionales Threadlayout bei CUDA. In den Klammern stehen jeweils (blockidx.x, blockidx.y) bzw. (threadidx.x, threadidx.y). Dieses Layout währe zum Beispiel für die Multiplikation zweier 8 8 Matrizen geeignet, wobei jeder Thread einen Eintrag der Ergebnismatrix berechnet. 3.4 Speicherhierarchie In CUDA gibt es (wie auch in OpenCL) unterschiedliche Speicherarten. Diese unterscheiden sich wesentlich in ihrer Größe, Geschwindigkeit und den Zugriffsbeschränkungen. Zu allererst ist der Speicher des Host-Programms zu nennen: Dies ist der normale Arbeitsspeicher des Computers, ein Kernel- Programm (bzw. Thread) hat hierauf keinen direkten Zugriff, die Daten müssen erst in den Device Memory übertragen werden. Der Device Memory ist der Hauptspeicher der Grafikkarte und lässt sich von jedem Thread sowohl lesen als auch schreiben. Er ist zwar groß, aber auch vergleichsweise langsam. Als Sonderfall gilt der Constant Memory. Dieser ist ebenfalls von jedem Thread lesbar, kann aber nur

9 9 Name Typ Beschreibung griddim dim3 a Größe des Grids, sodass griddim.x griddim.y griddim.z = #BlöckeImGrid ist blockidx uint3 b Block-Index innerhalb des Grids blockdim dim3 Größe eines Blocks, sodass blockdim.x blockdim.y blockdim.z = #T hreadsproblock ist threadidx uint3 Thread-Index innerhalb des Blocks warpsize int Die Größe eines Warps, in Threads Tabelle 1: Vordefinierte Variablen bei CUDA. Diese sind nur in Funktionen welche auf der Grafikkarte ausgeführt werden gültig, d.h. nur innerhalb eines Kernel-Programms. a Vektortyp wie uint3, unspezifizierte Komponenten werden auf 1 initialisiert. b Vektortyp mit 3 Komponenten vom Typ unsigned integer. Zugriff über var.[x y z]. vom Host-Programm geschrieben werden, außerdem ist er meist sehr viel kleiner als der Device Memory. Der Constant Memory ist für die Speicherung von unveränderlichen Konstanten gedacht, der Zugriff auf diese ist stark gecached und daher schnell. Für jeden Thread Block existiert ein Shared Memory. Dieser ist erheblich kleiner als die bisher genannten Speicher aber auch sehr viel schneller. Er lässt sich nur von Threads innerhalb desselben Thread Blocks sowohl lesen als auch schreiben, und stellt somit eine schnelle Möglichkeit der Kommunikation zwischen Threads desselben Blocks dar. Schließlich existiert für jeden Thread ein Register Memory. Dieser ist mit den Registern einer CPU zu vergleichen, extrem schnell, aber auch extrem klein. Außerdem lässt er sich nur vom zugehörigen Thread ansprechen. Für weitere Details bezüglich üblicher Speichergrößen, sowie eine grafische Aufarbeitung, siehe Tabelle 2, Seite 9 und Abbildung 2, Seite 10. Die optimale (Aus-)Nutzung der Speicherhierarchie hat einen maßgeblichen Einfluss auf die Performance des Programms und ist daher immens wichtig. Name Beschreibung Zugriff Typ. Größe host Speicher des Host-Programms, Nur Host-Programm 2-16 GB also RAM des Rechners global Hauptspeicher der Grafikkarte Jeder Thread 1-8 GB constant Kann nur von Host-Programm geschrieben werden, für Threads read-only Jeder Thread 64 kb shared Kleiner, aber schneller Speicher Nur der zugehörige kb Thread-Block registers Noch kleinerer, noch schnellerer Speicher Nur zugehöriger Thread 16 kb Tabelle 2: Speicherhierarchie bei CUDA.

10 10 host Host Memory(RAM) CUDA Device PCIe Global Memory Constant Memory CUDA Thread Block CUDA Thread Block Shared CUDA Thread Block Shared CUDA Thread CUDA Thread Block CUDA Thread Shared CUDA CUDA Thread Thread Register CUDA CUDA Thread Thread Shared Register CUDA CUDA Thread Thread Register Register CUDA CUDA Thread Thread Register Register CUDA CUDA Thread Thread Register Register CUDA CUDA Thread Thread Register Register CUDA Thread Register Register CUDA Thread Register Register Register Register Abbildung 2: Speicherhierarchie bei CUDA. 3.5 CUDA C Kompilierung Als Erweiterung der Programmiersprache C ist es in CUDA C möglich Kernel zu definieren, welche nach einem speziellen Aufruf n-mal von n CUDA Threads ausgeführt werden. In CUDA C bestehen die Quelltexte (Dateiendung.cu) aus Host-Code, zur Ausführung auf der CPU des Host-Rechners, und Device-Code (Kernel), zur Ausführung auf der GPU. Die Quelldateien werden mittels des von NVIDIA bereitgestellten Compilers Nvcc kompiliert. Nvcc selbst kann sowohl vollständig kompilierte Objektdateien (Endung.o oder.obj), zur Eingabe in einen normalen Linker, als auch ANSI C Quelltexte (Endung.c), welche mit einem anderen Compiler weiterverarbeitet werden können, ausgeben. Nvcc ist nicht komplett eigenständig, sondern benötigt einen installierten C-Compiler; unter Linux wird standardmäßig von GCC ausgegangen, unter Windows von cl (Teil von Microsoft Visual Studio) Kerneldefinition und -aufruf CUDA C definiert neue Schlüsselwörter, Konstanten und Funktionsaufrufe zum Aufruf der Kernelfunktionen. Zur Definition eines Kernels wird dem Funktionskopf das Schlüsselwort global vorange-

11 11 stellt, so währe global void matsq(const float *mat, float *out){... } bereits ein gültiger Kernel; dieser ließe sich aus einer Host-Funktion mittels einer neuen Befehlsform mit dreifachen spitzen Klammern starten: Kernelname<<<dim3 Dg, dim3 Db, size_ t Ns, cudastream_ t S>>>( Parameter ); Der Parameter Dg bestimmt hier die Größe des Grids, Db die Größe eines Blocks, Ns die Größe des, zusätzlich zum statisch reservierten, dynamisch für jeden Block zu reservierenden gemeinsamen Speichers (shared-memory), und S ein Stream, auf welchen hier nicht näher eingegangen wird. Nötig sind nur die ersten beiden Parameter, Ns und S sind optional und standardmäßig 0. Innerhalb der Runden Klammern stehen Funktionsparameter anlog zu einem normalen Funktionsaufruf. Um unseren Kernel matsq aufzurufen genügt die Syntax: matsq<<<1, N>>>(A, B). Funkionen, welche innerhalb eines Kernels aufgerufen werden sollen, werden mit dem Schlüsselwort device ausgezeichnet, diese werden dann ebenfalls auf der GPU ausgeführt. Das Schlüsselwort host kennzeichnet Funktionen welche auf dem Host laufen, und nur von anderen Host-Funktionen aufgerufen werden können, allerdings ist es optional, Funktionen ohne device oder global werden immer als normale C/C++ Funktion (und damit als Host-Funktion) interpretiert. Speicherbereiche im Global Memory (wie im Falle des Beispiels die Arrays float *mat und float * out) müssen per cudamalloc() reserviert werden bevor sie per cudamemcpy() mit Daten gefüllt werden können; Kernelprogamme können nicht auf den Speicher des Hosts zugreifen. Nach Aufruf des Kernels lässt sich das Ergebnis schließlich mit cudamemcpy() vom Speicher der Grafikkarte in den Speicher des Hosts transferieren (siehe Kapitel 3.2, Seite 7) Umsetzung der Speicherhierarchie Die Speicherhierarchie in CUDA (siehe Kapitel 3.4, Seite 8) wird mittels der Schlüsselwörter device (global), constant (constant) und shared (shared) realisiert. Lokale Variablen, welche innerhalb einer Kernel-Funktion (bzw. innerhalb einer device Funktion) deklariert werden, und mit keinem dieser Schlüsselwörter ausgezeichnet sind, werden automatisch dem Register-Speicher zugewiesen. Zusätzlich existiert das Schlüsselwort restrict, welches dem Compiler, analog zu dem im C99 Standard eingeführten C-Schlüsselwort restrict, helfen soll die Anzahl an Speicherzugriffen bei Zeigerauflösungen zu reduzieren. Wenn mehrere Zeiger mit restrict ausgezeichnet sind so dürfen diese nicht auf sich überlappende Speicherbereiche zeigen. Man betrachte die folgenden beiden Funktionen in CUDA C: device void foo ( const float * a, const float * b, float * c) { c[0] = a [0] * b [0]; c[1] = a [0] * b [0]; } device void bar ( const float * a, const float * b, float * c) { float atimesb = a [0] * b [0]; c[0] = atimesb ; c[1] = atimesb ; }

12 12 Auf den ersten Blick sind diese Funktionen äquivalent. Es ist jedoch möglich dass die Zeiger a und c (bzw. b und c) auf den selben Speicherbereich verweisen, wodurch (in foo) eine Modifikation an c[0] auch den Wert von a[0] (bzw. b[0]) modifiziert. Der Compiler kann also in foo den Wert von a[0] nicht cachen, sondern muss jedes mal eine Operation zum lesen von a[0] einfügen, welche anschließend nur noch von dem ausführenden Prozessor dynamisch zur Laufzeit optimiert werden kann. Das Schlüsselwort restrict (bzw. restrict bei C99) verbietet überlappende Zeiger, und erlaubt dem Compiler so, den Code wie in bar zu optimieren. Die optimierte (und zu bar äquivalente) Funktion währe demnach: device void foobar ( const float * restrict a, const float * restrict b, float * restrict c) { c[0] = a [0] * b [0]; c[1] = a [0] * b [0]; } Da die Speicherbereiche nicht mehr überlappen dürfen, ist es dem Compiler erlaubt, das Ergebnis der Operation a[0] * b[0] innerhalb der Funktion als konstant anzusehen. Damit sind nur noch zwei Lesezugriffe und eine Multiplikation nötig Synchronisation Naturgemäß existieren bei CUDA sehr wenige Synchronisationsmöglichkeiten. Sie beschränken sich im wesentlichen auf Threads des selben Thread Blocks. Die Funktion void synchthreads() wartet bis alle Threads des Thread-Blocks diesen Aufruf erreichet haben und alle Speicherzugriffe auf dem globalen sowie shared-speicher für alle Threads des Blocks sichtbar sind. synchthreads() ist in konditionalem Code (z.b. if-statement) nur erlaubt, wenn die Kondition in allen Threads des Blocks das gleiche Ergebnis liefert. Als Beispiel siehe Listing 1, Seite 13. Bei GPUs welche CUDA 2.x und höher unterstützen existieren noch weitere ähnliche Funktionen: int synchthreads_count(int predicate) ist identisch mit synchthreads(), zusätzlich wird predicate für alle Threads des Blocks ausgewertet und die Anzahl an Threads zurückgegeben, für die predicate ungleich Null ist. int synchthreads_and(int predicate) gibt einen Wert ungleich Null zurück, wenn (und nur wenn) predicate für alle Threads ungleich Null ist, int synchthreads_or(int predicate) gibt analog dazu nur einen Wert ungleich Null zurück, wenn (und nur wenn) predicate für irgendeinen Thread ungleich Null ist. Eine Synchronisation von Threads verschiedener Blöcke ist nur über den Host möglich, also durch den Aufruf einer zweiten Kernelfunktion. Wenn die Threads Daten austauschen sollen, so schreibt die erste Kernelfunktion Daten in den globalen Speicher. Der zweite Kernel liest die Daten im Anschluss aus und verarbeitet sie weiter. Aufgrund des großen Overheads empfiehlt es sich, dies zu vermeiden und möglichst die nativen Synchronisationsmechanismen zu nutzen. Speicherbarrieren (Memory-Fence genannt) werden durch die Funktionen void threadfence_block(), void threadfence() sowie void threadfence_system() realisiert. Sie stellen sicher, dass Zugriffe des aufrufenden Threads auf den global und shared-speicher für, in der selben Reihenfolge, alle Threads des Blocks, alle Threads des Grids, oder alle Threads des Grids inklusive Lesezugriffen des Hosts, sichbar sind. CUDA unterstützt unter Umständen die Ausführung eines Kernels und gleichzeitige Lese/Schreiboperationen durch den Host, siehe hierzu [4, Kapitel Overlap of Data Transfer and

13 13 Kernel Execution ]. Da ein Warp immer nur eine gemeinsame Instruktion gleichzeitig ausführt (siehe Kapitel 3.3, Seite 7), sind die Threads innerhalb eines Warps implizit (d.h. auf Befehlsebene) synchronisiert. Dadurch kann eine (langsame) Synchronisation der Threads unter Umständen vermieden werden. 1 // Example for two threads per block 2 global void foo ( const float *a, float * b) 3 { 4 shared float sh [2]; // declare shared memory 5 int id = threadidx. x; // we only use two threads, so id is either 0 or 1 6 sh[id] = a[id] * a[id ]; // calculate "our" result 7 synchthreads (); // synchronize threads 8 int otherid = ( id +1) % 2; // switch ids (0 -> 1, 1 -> 0) 9 b[ id] = sh[ otherid ]; // write the result calculated by the other thread 10 } Listing 1: Beispielhafter CUDA Kernel bei welchem zwei Threads jeweils die Berechnungsergebnisse des anderen benutzen und daher synchronisiert werden müssen Datentypen CUDA C definiert neue Vektordatentypen: [u]charn, [u]shortn, [u]intn, [u]longn, [u]longlongk, floatn und doublek. Der optionale Präfix u steht jeweils für die nicht-vorzeichenbehafteten Varianten, n ist entweder 1, 2, 3 oder 4, und k ist entweder 1 oder 2. Der Zugriff auf einzelne Komponenten geschieht durch die Member x, y, z und w, wobei bei einem Datentyp mit n Komponenten natürlich nur die ersten n der genannten Member gültig sind. Jeder Datentyp hat außerdem einen Konstruktorfunktion der Form make Typname, also zum Beispiel float3 make_float3(float x, float y, float z);. CUDA C definiert eine Menge an neuen Funktionen, welche das Arbeiten mit den Vektordatentypen erleichtern, siehe hierzu [3, Kapitel 5.57] PTX Assembler CUDA benutzt für Kernelprogramme intern PTX (Parallel Thread Execution) Assemblercode. Da eine vollständige Abdeckung der PTX ISA (Instruction Set Architecture) den Rahmen dieses Dokumentes sprengen würde, sei an dieser Stelle auf [5] verwiesen; hier wird lediglich ausgeführt wie der Programmierer PTX Assemblercode direkt in CUDA-C-Programme einfügen kann. Die normale Syntax eines Inline-PTX Befehls lautet wie folgt: asm(" template - string " : " constraint "( output ) : " constraint "( input )); Der Template-String enthält dabei den Befehl an sich sowie die nummerierten Template-Parameter, Output enhält die Ausgabevariablen, Input die Eingabevariablen. Die Constraints beziehen sich auf den Typ der genutzten Register. Es folgt ein einfaches Beispiel: int i; int j = 5; int k = 5; asm("add. s32 %0, %1, %2; " : "=r"(i) : "r"(j), "r"(k)); //i is now 10

14 14 Hier beziehen sich die Platzhalter %0 bis %2 auf die nachfolgenden Variablen i, j und k. "r" bzw. "=r" bezieht sich auf den Registertyp.u32, das Gleichheitszeichen kennzeichnet Variablen in die geschrieben wird. Es existieren die folgenden Registertypen: constraint Typ Beschreibung und CUDA C Typ h.u16.s16 16-Bit Ganzzahl ([unsigned] short int) r.u32.s32 32-Bit Ganzzahl ([unsigned] int) l.u64.s64 64-Bit Ganzzahl ([unsigned] long long) f.f32 32-Bit Fließkommazahl (float) d.f64 64-Bit Fließkommazahl (double) Zu beachten ist, dass beispielsweise der Registertyp.u32 sowohl für vorzeichenbehaftete als auch vorzeichenlose Ganzzahlen verwendet werden kann, da u-register mit s-registern kompatibel sind sofern sie die selbe Größe bestitzen. Als was der Registerinhalt schließlich interpretiert wird bestimmt die Operation, so wurde im Beispiel oben add.s32 verwendet, was einer vorzeichenbehafteten Ganzzahladdition entspricht. Aus diesen Gründen wird bei den Constraints nicht zwischen vorzeichenbehaftet und vorzeichenlos unterschieden, sie dienen in erster Linie nur der Bestimmung der Größe eines Parameters. Das asm-statement aus dem Beispiel erzeugt den Code: ld.s32 r1, [j]; ld.s32 r2, [k]; add.s32 r3, r1, r2; st.s32 [i], r3; Zwar mag die Syntax des asm-statements etwas verwirrend wirken, es lassen sich jedoch mehrere Instruktionen nacheinander angeben, wobei die Template-Parameter mehrfach genutzt werden können. Als Beispiel die Funktion cube, welche die dritte Potenz einer Ganzzahl berechnet: device int cube ( int x) { return x* x* x; } Diese Funktion lässt sich mittels PTX Assembler in folgende umschreiben: device int cube ( int x) { int y; asm (". reg. u32 t1;" // temporal register t1 " mul.lo.u32 t1, %1, %1;" // t1 = x * x " mul.lo.u32 %0, t1, %1;" // y = t1 * x : "=r"(y) : "r"(x) ); // output : y, input : x return y; } Die drei Anweisungszeilen werden hier mittels der normalen C/C++ String-Syntax aneinandergereiht. Falls die Funktion cube vom Compiler inlined 5 wird, so kann es zu Namenskonflikten kommen, da das Register t1 mehrfach deklariert wird. Um dies zu vermeiden können die PTX Anweisungen (analog zum Scope bei C/C++) mit geschweiften Klammern eingefasst werden. Da das asm()-statement nicht überprüft in welchem Speicherbereich ein Register liegt, obliegt es dem Nutzer die korrekten Befehle zu benutzen. 5 Inlining einer Funktion: Der Compiler fügt an die Stelle des Aufrufs den Inhalt der Funktion ein, anstatt einen Sprungbefehl zu setzen.

15 15 1 // Device code 2 global void mmult ( const float *A, const float *B, float *C, 3 int wa, int wb) 4 { 5 int i = blockidx. x * blockdim. x + threadidx. x; 6 int j = blockidx. y * blockdim. y + threadidx. y; 7 8 float val = 0; 9 for ( int k = 0; k < wa; k ++ ) 10 val += A[wA*j+k] * B[wB*k+i]; 11 C[wA*j+i] = val ; 12 } // Host code 15 void RandomInit ( float * data, int size ) { 16 for ( int i = 0; i < size ; ++ i) 17 data [i] = rand () / ( float ) RAND_MAX ; 18 } int main ( int argc, char ** argv ) 21 { 22 // Define sizes of matrices, A (1024 x512 ), B (512 x2048 ) => C (512 x512 ) 23 int wa = 1024; int ha = 512; 24 int wb = ha; int hb = 2048; 25 int wc = ha; int hc = wb; 26 size_t sizea = wa * ha * sizeof ( float ); 27 size_t sizeb = wb * hb * sizeof ( float ); 28 size_t sizec = wc * hc * sizeof ( float ); // Allocate matrices in host memory 31 float * h_a = ( float *) malloc ( sizea ); 32 float * h_b = ( float *) malloc ( sizeb ); 33 float * h_c = ( float *) malloc ( sizec ); // Initialize input vectors 36 RandomInit (h_a, wa*ha); 37 RandomInit (h_b, wb*hb); // Allocate vectors in device memory 40 float * d_a, d_b, d_c ; 41 cudamalloc (( void **)&d_a, sizea ); 42 cudamalloc (( void **)&d_b, sizeb ); 43 cudamalloc (( void **)&d_c, sizec ); // Copy vectors from host memory to device memory 46 cudamemcpy ( d_a, h_a, sizea, cudamemcpyhosttodevice ); 47 cudamemcpy ( d_b, h_b, sizeb, cudamemcpyhosttodevice ); // Invoke kernel 50 dim3 threads (16, 16) ; 51 dim3 grid ( wc / threads.x, hc / threads. y ); 52 mmult<<<grid, threads>>>( d_a, d_b, d_c, wa, wb); // Copy result from device memory ( d_c) to host memory ( h_c) 55 cudamemcpy ( h_c, d_c, sizec, cudamemcpydevicetohost ); 56 } Listing 2: CUDA Kernel und Host-Programm zur Multiplikation zweier Matrizen. Kernel und Host- Code stehen in der selben Datei. Fehlerbehandlung und Speicherfreigabe sind zwecks Übersichtlichkeit entfernt worden.

16 16 4 OpenCL 4.1 Einleitung OpenCL (Open Computing Language) ist ein ursprünglich von Apple entwickelter, offener Standard für parallele Berechnungen auf uneinheitlichen Parallelrechnern. Im Gegensatz zu CUDA ist OpenCL also mit allen Geräten kompatibel für die eine konforme Implementierung existiert. NVIDIA bietet eine Implementierung an, welche auf CUDA basiert, AMD eine, welche auf ihrer GPGPU-Schnittstelle ATI-Stream basiert. Außerdem existieren Implementierungen für x86 CPUs, DSPs und Cell Prozessoren, was OpenCL zu einer sehr flexiblen Technologie macht. In direkter Konkurrenz zu CUDA ist festzustellen, dass es OpenCL aufgrund seiner platformunabhängigen Natur an Low-Level Zugriffen auf die Hardware fehlt 6, allerdings ist OpenCL im Moment auch die einzige bequeme Möglichkeit sowohl Platform- als auch Geräteunabhängige GPGPU-Anwendungen zu entwickeln. 4.2 Prozessmodell Das Prozessmodell in OpenCL ist, bis auf die Umbenennung von CUDA Thread in Work-Item identisch zu dem in CUDA (siehe Kapitel 3.2, Seite 7). 4.3 Parallelisierung Die Parallelisierung unter OpenCL unterscheidet sich nicht wesentlich von der in CUDA (siehe Kapitel 3.3, Seite 7), auch in OpenCL wird das Gesamtproblem in mehreren Dimensionen in Teilprobleme zerlegt. Mehrere Work Items werden hierbei zu Work Groups zusammengefasst, dies entspricht der Zusammenfassung von CUDA Threads zu CUDA Thread Blocks, ein Work-Item führt einen Kernel also genau einmal aus. Innerhalb der Kernelfunktionen gibt es in OpenCL Funktionen zum Abfragen der Position innerhalb einer Work Group, der Position bezüglich aller Work Items usw. Die große Anzahl dieser Funktionen kann dem Programmierer etwas Arbeit abnehmen, so kann man die in CUDA häufig anzutreffende Berechnung blockidx.x * blockdim.x + threadidx.x durch den Funktionsaufruf get_global_id(0) abkürzen. 4.4 Speicherhierarchie OpenCL kennt fünf unterschiedliche Arten Speicher: Host, Global, Constant, Local und Private. Der Host-Speicher ist hierbei der normale Arbeitsspeicher des Host-Rechners, Global der Hauptspeicher der Grafikkarte (bzw. des Devices da OpenCL nicht auf GPUs beschränkt ist), Constant ein Konstantenspeicher, Local der lokale Speicher der Work Group und Private der private Speicher eines Work Items. Die typischen Größen sind, zumindest auf Grafikkarten, dieselben wie bei CUDA, und auch die Zugriffsbeschränkungen sind identisch, einzig die Benennung ist etwas anders: Der Local-Memory heißt bei CUDA Shared und der Private-Memory heißt Register; daher siehe auch Tabelle 2, Seite 9. Für eine angepasste Grafik der Speicherhierarchie siehe Abbildung 3, Seite wenn man von herstellereigenen Erweiterungen des Standards absieht

17 17 host Host Memory(RAM) Device PCIe Global Memory Constant Memory Work-Group Work-Group Local Work-Group Local Work-Item Work-Group Work-Item Local Work-Item Private Work-Item Local Private Work-Item Private Private Work-Item Private Private Work-Item Private Private Work-Item Private Private Work-Item Private Private Work-Item Private Private Private Private Abbildung 3: Speicherhierarchie bei OpenCL. 4.5 Workflow Die API und der Workflow von OpenCL unterscheidet sich wesentlich von dem von CUDA. Zur Einbindung in ein in C/C++ geschriebenes Programm werden sowohl die C-Headerdateien als auch eine vom Hersteller der Implementierung bereitgestellte, statisch gelinkte Bibliothek (.lib) benötigt. Im Kontext von GPU-Anwendungen ist die Bibliothek lediglich eine Brücke zwischen dem Programm und der eigentlichen Implementierung im Grafikkartentreiber, so ist die von AMD bereitgestellte Bibliothek OpenCL.lib mit Grafikkarten (bzw. deren Treibern) von NVIDIA kompatibel. Da die OpenCL API als normale Programmbibliothek eingebunden wird, ist kein besonderer Compiler (wie bei CUDA) nötig. Kernelfunktionen werden der Implementierung zur Laufzeit als Quellcode (Stringbasiert) übergeben und Just-In-Time für genau das Gerät, für welches die OpenCL-Implementierung geschrieben wurde, kompiliert. Dies hat den Vorteil, dass der Programmierer zur Entwicklungszeit nicht zwingend wissen muss, auf welchen Geräten sein Code später ausgeführt wird. Solange er standardkonformen Code schreibt, kann er davon ausgehen, dass sein Programm korrekt läuft. Häufig wird der Grafikkartentreiber beim Programmstart mit der Kompilierung der Kernel beauftragt, was zwangsläufig eine (gegenüber dem vorkompilierten Code von CUDA) längere Startphase der Applikation zur Folge hat. Es besteht die Möglichkeit die Kernel beim ersten Programmstart kompilieren zu lassen und den vom Grafikkartentreiber produzier-

18 18 ten Binärcode in einer Datei abzuspeichern, sodass bei folgenden Starts nur diese Datei geladen werden muss (falls sich an der Systemkonfiguration nichts geändert hat). Ob sich dieser Aufwand lohnt ist von Fall zu Fall unterschiedlich, die Kompilierung einfacher Kernelfunktionen benötigt häufig weniger als eine Sekunde Zeit. Die OpenCL API ist in ihrer Syntax an die OpenGL API angelehnt, der Kompilierungsworkflow ähnelt der Kompilierung von GLSL 7 -Shadern. 4.6 OpenCL C Analog zu CUDA ist die zum Entwickeln von Kernelfunktionen genutzte Sprache OpenCL C eine Abwandlung der Programmiersprache C, genauer eine Abwandlung des C99 Standards, und definiert im Wesentlichen neue Schlüsselwörter, Datentypen und Funktionen. Anders als bei CUDA wird OpenCL C nur für die Kernelfunktionen ( Device-Code ), und nicht für Host-Code benutzt. Dies hat einen etwas komplexer anmutenden Host-Code zur Folge; ein gesondert einzurichtender und -stellender Compiler entfällt aber. OpenCL C ist keine Obermenge von C99, da es einige Einschränkungen definiert: Unter anderem werden Zeiger auf Funktionen, Bit-Felder, Arrays mit variabler Länge sowie Rekursion nicht unterstützt. Für Details sei der Leser auf [6, Kapitel 6.8 Restrictions ] verwiesen Kerneldefinition und Speicherhierarchie Ein Kernel wird durch das vorangestellte Schlüsselwort kernel definiert. Falls die Funktionsparameter Zeiger enthalten, so müssen diese mit einem Speicherbereichsqualifizierer ausgezeichnet werden: kernel void matsq ( global const float *A, global float *B) {... } In OpenCL C existieren die folgenden Speicherbereichsqualifizierer: global, constant, local und private, diese beziehen sich auf die in Kapitel 4.4 genannte Speicherhierarchie. Lokale Variablen innerhalb des Funktionsrumpfes einer Kernelfunktion werden automatisch zu private. Da die Kernelfunktionen bei OpenCL gesondert kompiliert werden, und nicht zusammen mit dem Host-Code wie bei CUDA, existiert kein Schlüsselwort um Funktionen zu kennzeichnen, welche aus einer Kernelfunktion aufgerufen werden sollen (wie device bei CUDA). Eine zur Ausführung auf dem Device bestimmte Funktion erhält kein besonderes Schlüsselwort, kernel kommt lediglich zur Anwendung wenn die Funktion vom Host aus als Kernel gestartet werden soll. Eine Auflistung der Work-Item Funtionen in OpenCL C ist der Tabelle 3, Seite 21 zu entnehmen. Für ein Beispiel einer Kernelfunktion in OpenCL sei auf Listing 3, Seite 19 verwiesen Synchronisation Die Synchronisation von Work-Items innerhalb einer Work-Group wird über die Funktion void barrier ( cl_ mem_ fence_ flags flags ) realisiert. Alle Work-Items einer Work-Group müssen den Aufruf an barrier() getätigt haben bevor einem von ihnen erlaubt ist weiterzuarbeiten. Zusätzlich zur Synchronisation wird auch sichergestellt, dass Speicherzugriffe für andere Work-Items der selben Work-Group sichtbar sind, ob dies nur auf dem Local-Memory oder auch auf dem Global-Memory geschieht kann gesteuert werden, indem flags auf die Konstante CLK_LOCAL_MEM_FENCE oder CLK_GLOBAL_MEM_FENCE gesetzt wird. Das Pendant zu CUDA s 7 OpenGL Shading Language

19 19 synchthreads() währe somit barrier(clk_global_mem_fence). Speicherbarrieren werden über die Funktion void mem_ fence ( cl_ mem_ fence_ flags flags ) gesetzt, für flags lassen sich die gleichen Konstanten wie bei barrier einsetzen. mem_fence stellt sicher, dass alle Speicherzugriffe, die vor dem Aufruf von mem_fence angeordnet wurden, vor Speicherzugriffen, welche nach dem Aufruf angeordnet werden, abgeschlossen sind, ordnet also die Speicherzugriffe. Zusätzlich imlementieren die Funktionen void read_ mem_ fence ( cl_ mem_ fence_ flags flags ) und void write_ mem_ fence ( cl_ mem_ fence_ flags flags ) eine Anordnung der Speicherzugriffe exklusiv bei Lese- bzw. Schreibzugriffen. Wie auch bei CUDA ist eine Synchronisation von Work-Items verschiedener Work-Groups nur über den Host möglich, also über den Aufruf einer anderen Kernelfunktion Datentypen OpenCL C definiert eine Reihe an neuen Datentypen, die Wichtigsten sind hierbei die Vektordatentypen charn, ucharn, shortn, ushortn, intn, uintn, longn, ulongn und floatn, n gibt jeweils die Anzahl an Elementen an und kann 2, 3, 4, 8 oder 16 betragen. Die Datentypen sind bis auf floatn alle Ganzzahltypen, deren Größen betragen aufsteigend 8, 16, 32 und 64 Bit pro Element, der Präfix u steht für unsigned und kennzeichnet vorzeichenlose Ganzzahlen. Als Ausnahme ist floatn ein 32-bit Gleitkommatyp. Bei Operationen auf den Vektordatentypen setzt der Compiler automatisch die für seine Architektur passenden Instruktionen ein, falls möglich sollten sie gegenüber skalarer Rechnung bevorzugt werden. Für eine komplette Auflistung der Datentypen sowie den Zugriffsmethoden und -beschränkungen siehe [6, Kapitel 6.1 Supported Data Types ]. Zusätzlich zu den Datentypen führt OpenCL C eine Reihe an Funktionen ein, die es erleichtern mit den Typen zu arbeiten, wie zum Beispiel eine Kreuz- und Skalarproduktfunktion. Für eine vollständige Liste sei auf [6, Kapitel 6.11 Built-in Functions ] verwiesen. 1 kernel void mmult ( global const float *A, 2 global const float *B, 3 global float *C, 4 int wa, int wb) 5 { 6 int i = get_ global_ id (0) ; 7 int j = get_ global_ id (1) ; 8 9 float val = 0; 10 for ( int k = 0; k < wa; k ++ ) 11 val += A[wA*j+k] * B[wB*k+i]; 12 C[wA*j+i] = val ; 13 } Listing 3: OpenCL C Kernel zur Anwendung bei einer Matrizenmultiplikation.

20 Die OpenCL API (Host-Code) Da OpenCL im Kern lediglich eine API ist, und keinen gesonderten Compiler mit sich bringt, ergeben sich einige Unterschiede zu CUDA. In OpenCL werden alle Elemente des Programms, also Kernel, Speicherpuffer und so weiter, als Objekte gehandhabt. Da die OpenCL API auf C basiert liefert sie, analog zu OpenGL, sogenannte Handles, also Referenzen auf die Objekte zurück. Da OpenCL Funktionen meist sehr viele Parameter besitzen, gebe ich hier nur die Funktionsnamen an, für Details siehe [6]. Über clgetdeviceids() können die im System vorhandenen (bzw. von der Implementierung unterstützten) Devices abgefragt werden, ein Parameter device_type spezifiziert hierbei die gewünsche Art des Devices, so z.b. CL_DEVICE_TYPE_CPU oder CL_DEVICE_TYPE_GPU. Als nächstes kann über clcreatecontext() ein für das Device gültiger OpenCL-Kontext erstellt werden, nur innerhalb diesem ist die nachfolgende Erstellung von Objekten gültig. Anschließend kann z.b. über clcreateprogramwithsource() ein Programmobjekt aus OpenCL C Quelltext erstellt werden, dies wird dann mittels clbuildprogram() kompiliert. Ein Programmobjekt kann mehrere Kernelfunktionen enthalten, ein auf eine einzige Kernelfunktion bezogenes Kernelobjekt erhält man aus dem Programmobjekt über clcreatekernel(). Speicherpuffer auf dem Device werden mittels clcreatebuffer() beantragt. Um auf den erstellten Objekten Operationen auszuführen, also z.b. das Füllen eines Speicherpuffers oder die Ausführung eines Kernels, ist eine Command Queue nötig; diese führt die beantragten Operationen (auch Commands genannt) dann, meist asynchron, aus. Eine Applikation kann mehrere Command-Queues erstellen und so mehrere Aufgaben asynchron ausführen lassen, ohne dass die Applikation selbst (Betriebssystem-)Threads auf dem Host erstellen müsste 8. Eine Command-Queue lässt sich über clcreatecommandqueue() erstellen. Über Funktionen mit dem Präfix clenqueue*() lassen sich Aufträge in die Queue einreihen, so kann z.b. über clenqueuewritebuffer() und clenqueuereadbuffer() das Befüllen bzw. Auslesen eines Speicherpuffers beantragt werden. Da die Queue asynchron arbeitet, sollte vor der Weiterverarbeitung von Berechnungsergebnissen clfinish() auf der Queue aufgerufen werden. Dieser Aufruf blockiert, bis alle in der Queue befindlichen Aufträge fertiggestellt wurden. Da es bei OpenCL keine spezielle Befehlsform zum Aufruf von Kerneln (wie die dreifachen spitzen Klammern bei CUDA) gibt, müssen die Funktionsparameter vor dem Aufruf über clsetkernelarg() gesetzt werden, anschließend kann die parallele Ausführung des Kernels mittels der Funktion clenqueuendrangekernel() beantragt werden. Die Funktion nimmt hierbei unter anderem die gewünschte Anzahl an Dimensionen, die Gesamtanzahl der Work-Items und die Anzahl der Work-Items in einer Work-Group (jeweils in den gewünschten Dimensionen) entgegen. Ein Programm in Pseudo-C++ könnte also wie folgt ablaufen: device = clgetdeviceids ( CL_ DEVICE_ TYPE_ GPU ); context = clcreatecontext ( device ); queue = clcreatecommandqueue ( context ); program = clcreateprogramwithsource ( context, " kernel foo ( global float *a) { a[ get_global_id (0)] = 1.0f; }" ); clbuildprogram ( program ); kernel = clcreatekernel ( program, " foo"); a_ device = clcreatebuffer ( context, sizeof ( float ) * 4096) ; float a_host [4096]; clsetkernelarg ( kernel, 0, & a_device ); clenqueuendrangekernel ( queue, kernel, 4096, 128) ; 8 Dies gilt nur falls sich die Queues keine Objekte Teilen, also jede Queue auf ihrem eigenen Satz an Objekten operiert.

21 21 Funktion Beschreibung CUDA Pendant uint get work dim() size t get global size(uint dimid) size t get global id(uint dimid) size t get local size(uint dimid) size t get local id(uint dimid) size t get num groups(uint dimid) size t get group id(uint dimid) size t get global offset(uint dimid) Anzahl der momentan genutzten Dimensionen Gesamtanzahl der Work- Items. Eindeutige globale ID des Work-Items Anzahl der Work-Items in einer Work-Group Eindeutige lokale ID des Work-Items innerhalb der Work-Group Gesamtanzahl der Work- Groups Globale, eindeutige ID der Work-Group Ein optionaler Offset- Parameter welcher beim starten des Kernels angegeben werden kann griddim.α * blockdim.α blockidx.α * blockdim.α + threadidx.α blockdim.α threadidx.α griddim.α blockidx.α Tabelle 3: Vordefinierte Work-Item Funktionen bei OpenCL C. dimid indiziert jeweils die Dimension die abgefragt werden soll, also 0 für die x-achse, 1 für die y-achse und 2 für die z-achse. Anders als bei CUDA wird nicht.x/.y/.z verwendet da OpenCL auch für mehr als drei Dimensionen spezifiziert ist (auch wenn nicht jedes Gerät dies unterstützen muss). Der Selektor α steht für x, y oder z, je nachdem welchen Wert dimid hat. clenqueuereadbuffer ( queue, a_device, a_host, sizeof ( float ) * 4096) ; Dieser Pseudocode soll lediglich den prinzipiellen Ablauf einer Anwendung andeuten. Es wurden zwecks Verständnis viele Funktionsparameter weggelassen oder umgeordnet. Ein vollständiges und korrektes Beispielprogramm in C++ ist in Listing 4, Seite 22 abgebildet. Um mit den in genannten Datentypen im Host-Code zu arbeiten, definiert die OpenCL API für fast jeden Datentyp aus dem Device-Code ein Pendant im Host-Code. Gegenüber dem Device-Code erhalten die Datentypen den Präfix cl, also z.b. cl float4 anstatt float4.

22 22 1 const char kernel_src [] = 2 " kernel void mmult ( global const float *A, " 3 " global const float *B, " 4 " global float *C, " 5 " int wa, int wb) " 6 "{ " 7 " int i = get_global_id (0) ; " 8 " int j = get_global_id (1) ; " 9 " " 10 " float val = 0; " 11 " for ( int k = 0; k < wa; k ++ ) " 12 " val += A[wA*j+k] * B[wB*k+i]; " 13 " C[wA*j+i] = val; " 14 "} "; void RandomInit ( float * data, int size ) { 17 for ( int i = 0; i < size ; ++ i ) 18 data [ i] = rand () / ( float ) RAND_MAX ; 19 } int main ( int argc, const char * argv []) 22 { 23 // define sizes of matrices, A (1024 x512 ), B (512 x2048 ) => C (512 x512 ) 24 int sa [] = {1024, 512}; 25 int sb [] = {sa [1], 2048}; 26 int sc [] = {sa [1], sb [0]}; 27 size_t sizea = sa [0] * sa [1] * sizeof ( float ); 28 size_t sizeb = sb [0] * sb [1] * sizeof ( float ); 29 size_t sizec = sc [0] * sc [1] * sizeof ( float ); // get platform 32 cl_uint num_ platforms ; cl_ platform_ id platform ; 33 cl_int err = clgetplatformids (1, & platform, & num_ platforms ); // get device 36 cl_device_id device ; 37 clgetdeviceids ( platform, CL_DEVICE_TYPE_GPU, 1, & device, 0); // create context, command queue, program and kernel 40 cl_context context = clcreatecontext (0, 1, & device, 0, 0, & err); 41 cl_ command_ queue cmd_queue = clcreatecommandqueue ( context, device, 0, 0); 42 cl_program program = clcreateprogramwithsource ( context, 1, & kernel_src,0,& err ); 43 clbuildprogram ( program, 0, 0, 0, 0, 0); 44 cl_kernel kernel = clcreatekernel ( program, " mmult ", & err); // create memory objects 47 float * hosta = new float [ sizea ]; 48 float * hostb = new float [ sizeb ]; 49 float * hostc = new float [ sizec ]; 50 RandomInit (hosta, sa [0]* sa [1]) ; 51 RandomInit (hostb, sb [0]* sb [1]) ; 52 cl_mem deva = clcreatebuffer ( context, CL_MEM_READ_ONLY, sizea, 0, 0); 53 cl_mem devb = clcreatebuffer ( context, CL_MEM_READ_ONLY, sizeb, 0, 0); 54 cl_mem devc = clcreatebuffer ( context, CL_MEM_WRITE_ONLY, sizec, 0, 0); 55

23 23 56 // set memory objects as kernel arguments 57 clsetkernelarg ( kernel, 0, sizeof ( cl_mem ), & deva ); 58 clsetkernelarg ( kernel, 1, sizeof ( cl_mem ), & devb ); 59 clsetkernelarg ( kernel, 2, sizeof ( cl_mem ), & devc ); // calculate work sizes 62 size_t ws_global [] = { sc [0], sc [1]}; 63 size_t ws_local [] = {16, 8}; // 128 items per group // transfer input matrices from host to device, 66 // calculate result and transfer result from device to host 67 clenqueuewritebuffer ( cmd_queue, deva, CL_FALSE, 0, sizea, hosta, 0, 0, 0); 68 clenqueuewritebuffer ( cmd_queue, devb, CL_FALSE, 0, sizeb, hostb, 0, 0, 0); 69 clenqueuendrangekernel ( cmd_queue, kernel, 2, 0, ws_global, ws_local, 0, 0, 0); 70 clenqueuereadbuffer ( cmd_queue, devc, CL_FALSE, 0, sizec, hostc, 0, 0, 0); 71 clfinish ( cmd_queue ); // The Result is now in hostc and ready for further processing. 74 return 0; 75 } Listing 4: Matrizenmultiplikation mittels OpenCL. Speicherfreigabe und Fehlerbehandlung sind entfernt worden. Man beachte dass dieser Quellcode mit einem normalen C++ Compiler kompiliert werden würde.

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

CUDA. Moritz Wild, Jan-Hugo Lupp. Seminar Multi-Core Architectures and Programming. Friedrich-Alexander-Universität Erlangen-Nürnberg CUDA Seminar Multi-Core Architectures and Programming 1 Übersicht Einleitung Architektur Programmierung 2 Einleitung Computations on GPU 2003 Probleme Hohe Kenntnisse der Grafikprogrammierung nötig Unterschiedliche

Mehr

Proseminar. GPU-Computing Cuda vs. OpenCL. SS 2013 Alexander Stepanov

Proseminar. GPU-Computing Cuda vs. OpenCL. SS 2013 Alexander Stepanov Proseminar GPU-Computing Cuda vs. OpenCL SS 2013 Alexander Stepanov Inhaltsverzeichnis 1. Einführung: Warum GPU Computing? CPU vs. GPU GPU Architektur 2. CUDA Architektur Beispiel Matrix Multiplikation

Mehr

GPGPU Architectures - Compiler Techniques and Applications SS 2012

GPGPU Architectures - Compiler Techniques and Applications SS 2012 Seminar on GPGPU Architectures - Compiler Techniques and Applications SS 2012 Embedded Systems Group Department of Computer Science University of Kaiserslautern Preface The widespread use of so-called

Mehr

OpenCL. Programmiersprachen im Multicore-Zeitalter. Tim Wiersdörfer

OpenCL. Programmiersprachen im Multicore-Zeitalter. Tim Wiersdörfer OpenCL Programmiersprachen im Multicore-Zeitalter Tim Wiersdörfer Inhaltsverzeichnis 1. Was ist OpenCL 2. Entwicklung von OpenCL 3. OpenCL Modelle 1. Plattform-Modell 2. Ausführungs-Modell 3. Speicher-Modell

Mehr

GPU Architektur CUDA - OpenCL

GPU Architektur CUDA - OpenCL GPU Architektur und Programmiermöglichkeiten für GPGPU-Anwendungen kernel void matsq( global const float *mat, global float *out ) { int dim = get_global_size(0); //Matrix dimension int i = get_global_id(0);

Mehr

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

Praxiseinheit: Realisierung einer hardwarebeschleunigten Disparitätenberechnung zur automatischen Auswertung von Stereobildern Praxiseinheit: Realisierung einer hardwarebeschleunigten Disparitätenberechnung zur automatischen Auswertung von Stereobildern Institut für Betriebssysteme und Rechnerverbund TU Braunschweig 25.10., 26.10.

Mehr

Compute Unified Device Architecture CUDA

Compute Unified Device Architecture CUDA Compute Unified Device Architecture 06. Februar 2012 1 / 13 Gliederung 2 / 13 : Compute Unified Device Architecture entwickelt von Nvidia Corporation spezifiziert Software- und Hardwareeigenschaften Ziel:

Mehr

Parallele Algorithmen mit OpenCL. Universität Osnabrück, Henning Wenke,

Parallele Algorithmen mit OpenCL. Universität Osnabrück, Henning Wenke, Parallele Algorithmen mit OpenCL Universität Osnabrück, Henning Wenke, 203-04-24 Was bisher geschah Host Device Platform Führt aus Führt aus Device Context Applikation Java, C++, Kernel (OpenCL C) Memory

Mehr

Rheinisch-Westfälische Technische Hochschule Aachen. Seminararbeit

Rheinisch-Westfälische Technische Hochschule Aachen. Seminararbeit Rheinisch-Westfälische Technische Hochschule Aachen Seminararbeit Analyse von General Purpose Computation on Graphics Processing Units Bibliotheken in Bezug auf GPU-Hersteller. Gregori Kerber Matrikelnummer

Mehr

GPGPU-Programmierung

GPGPU-Programmierung 12 GPGPU-Programmierung 2013/04/25 Diese Folien enthalten Graphiken mit Nutzungseinschränkungen. Das Kopieren der Graphiken ist im Allgemeinen nicht erlaubt. Motivation (1) General Purpose Computing on

Mehr

Grundlagen von CUDA, Sprachtypische Elemente

Grundlagen von CUDA, Sprachtypische Elemente Grundlagen von CUDA, Sprachtypische Elemente Stefan Maskanitz 03.07.2009 CUDA Grundlagen 1 Übersicht 1. Einleitung 2. Spracheigenschaften a. s, Blocks und Grids b. Speicherorganistion c. Fehlerbehandlung

Mehr

GPGPU-Programmierung

GPGPU-Programmierung 12 GPGPU-Programmierung 2014/04/29 Diese Folien enthalten Graphiken mit Nutzungseinschränkungen. Das Kopieren der Graphiken ist im Allgemeinen nicht erlaubt. Motivation (1) General Purpose Computing on

Mehr

Einführung. GPU-Versuch. Andreas Schäfer Friedrich-Alexander-Universität Erlangen-Nürnberg

Einführung. GPU-Versuch. Andreas Schäfer Friedrich-Alexander-Universität Erlangen-Nürnberg GPU-Versuch andreas.schaefer@cs.fau.de Friedrich-Alexander-Universität Erlangen-Nürnberg Praktikum Parallele Rechnerarchitekturen SS2014 Outline 1 Einführung 2 Outlook 1 Einführung 2 Eine kurze Geschichte

Mehr

GPGPU-Architekturen CUDA Programmiermodell Beispielprogramm Organiosatorisches. Tutorial CUDA. Ralf Seidler

GPGPU-Architekturen CUDA Programmiermodell Beispielprogramm Organiosatorisches. Tutorial CUDA. Ralf Seidler Friedrich-Alexander-Universität Erlangen-Nürnberg 05.10.2010 Outline 1 GPGPU-Architekturen 2 CUDA Programmiermodell 3 Beispielprogramm 4 Organiosatorisches Outlook 1 GPGPU-Architekturen 2 CUDA Programmiermodell

Mehr

Parallele Algorithmen mit OpenCL. Universität Osnabrück, Henning Wenke,

Parallele Algorithmen mit OpenCL. Universität Osnabrück, Henning Wenke, Parallele Algorithmen mit OpenCL Universität Osnabrück, Henning Wenke, 2013-04-17 Kapitel I OpenCL Einführung Allgemeines Open Compute Language: API für einheitliche parallele Programmierung heterogener

Mehr

GPGPU-Architekturen CUDA Programmiermodell Beispielprogramm. Einführung CUDA. Ralf Seidler. Friedrich-Alexander-Universität Erlangen-Nürnberg

GPGPU-Architekturen CUDA Programmiermodell Beispielprogramm. Einführung CUDA. Ralf Seidler. Friedrich-Alexander-Universität Erlangen-Nürnberg Einführung CUDA Friedrich-Alexander-Universität Erlangen-Nürnberg PrakParRA, 18.11.2010 Outline 1 GPGPU-Architekturen 2 CUDA Programmiermodell 3 Beispielprogramm Outlook 1 GPGPU-Architekturen 2 CUDA Programmiermodell

Mehr

GPU-Programmierung: OpenCL

GPU-Programmierung: OpenCL Seminar: Multicore Programmierung Sommerstemester 2009 04.06.2009 Inhaltsverzeichnis 1 GPU-Programmierung von Grafikkarten von GPU-Computing 2 Architektur Spracheigenschaften Vergleich mit CUDA Beispiel

Mehr

Masterpraktikum Scientific Computing

Masterpraktikum Scientific Computing Masterpraktikum Scientific Computing High-Performance Computing Thomas Auckenthaler Wolfgang Eckhardt Prof. Dr. Michael Bader Technische Universität München, Germany Outline Organisatorisches Entwicklung

Mehr

Gliederung. Was ist CUDA? CPU GPU/GPGPU CUDA Anwendungsbereiche Wirtschaftlichkeit Beispielvideo

Gliederung. Was ist CUDA? CPU GPU/GPGPU CUDA Anwendungsbereiche Wirtschaftlichkeit Beispielvideo Gliederung Was ist CUDA? CPU GPU/GPGPU CUDA Anwendungsbereiche Wirtschaftlichkeit Beispielvideo Was ist CUDA? Nvidia CUDA ist eine von NvidiaGPGPU-Technologie, die es Programmierern erlaubt, Programmteile

Mehr

Yilmaz, Tolga MatNr: Mesaud, Elias MatNr:

Yilmaz, Tolga MatNr: Mesaud, Elias MatNr: Yilmaz, Tolga MatNr: 157317 Mesaud, Elias MatNr: 151386 1. Aufbau und Funktionsweise einer Grafikkarte 2. CPU vs. GPU 3. Software 4. Beispielprogramme Kompilierung und Vorführung 5. Wo wird Cuda heutzutage

Mehr

Cuda Speicherhierarchie

Cuda Speicherhierarchie Cuda Speicherhierarchie Threads eines Blocks können über Shared Memory kommunizieren Der Shared Memory ist klein aber sehr schnell Alle Threads können nur über Global Memory kommunizieren Der Global Memory

Mehr

OpenCL. Multi-Core Architectures and Programming (Seminar) Apelt, Nicolas / Zöllner, Christian

OpenCL. Multi-Core Architectures and Programming (Seminar) Apelt, Nicolas / Zöllner, Christian OpenCL Multi-Core Architectures and Programming (Seminar) Apelt, Nicolas / Zöllner, Christian Hardware-Software-Co-Design Universität Erlangen-Nürnberg Apelt, Nicolas / Zöllner, Christian 1 Was ist OpenCL?

Mehr

Masterpraktikum Scientific Computing

Masterpraktikum Scientific Computing Masterpraktikum Scientific Computing High-Performance Computing Thomas Auckenthaler Wolfgang Eckhardt Technische Universität München, Germany Outline Entwicklung General Purpose GPU Programming (GPGPU)

Mehr

Eine kurze Geschichte der Grafikkarten

Eine kurze Geschichte der Grafikkarten 3.1 Einführung Eine kurze Geschichte der Grafikkarten ursprünglich: Graphics Card steuert Monitor an Mitte 80er: Grafikkarten mit 2D-Beschleunigung angelehnt an Arcade- und Home-Computer frühe 90er: erste

Mehr

Programmierbeispiele und Implementierung. Name: Michel Steuwer E-Mail: michel.steuwer@wwu.de

Programmierbeispiele und Implementierung. Name: Michel Steuwer E-Mail: michel.steuwer@wwu.de > Programmierbeispiele und Implementierung Name: Michel Steuwer E-Mail: michel.steuwer@wwu.de 2 > Übersicht > Matrix Vektor Multiplikation > Mandelbrotmenge / Apfelmännchen berechnen > Kantendetektion

Mehr

Interaktive Globale Beleuchtung nach dem Antiradiance-Verfahren mittels der Open Computing Language (OpenCL)

Interaktive Globale Beleuchtung nach dem Antiradiance-Verfahren mittels der Open Computing Language (OpenCL) Interaktive Globale Beleuchtung nach dem Antiradiance-Verfahren mittels der Open Computing Language (OpenCL) Verteidigung der Belegarbeit Andreas Stahl Zielstellung Globales Beleuchtungsverfahren für die

Mehr

Zum Aufwärmen nocheinmal grundlegende Tatsachen zum Rechnen mit reelen Zahlen auf dem Computer. Das Rechnen mit Gleitkommazahlen wird durch den IEEE

Zum Aufwärmen nocheinmal grundlegende Tatsachen zum Rechnen mit reelen Zahlen auf dem Computer. Das Rechnen mit Gleitkommazahlen wird durch den IEEE Zum Aufwärmen nocheinmal grundlegende Tatsachen zum Rechnen mit reelen Zahlen auf dem Computer. Das Rechnen mit Gleitkommazahlen wird durch den IEEE 754 Standard festgelegt. Es stehen sogenannte einfach

Mehr

GPGPU-Programming. Constantin Timm Informatik 12 TU Dortmund 2012/04/09. technische universität dortmund. fakultät für informatik informatik 12

GPGPU-Programming. Constantin Timm Informatik 12 TU Dortmund 2012/04/09. technische universität dortmund. fakultät für informatik informatik 12 12 GPGPU-Programming Constantin Timm Informatik 12 TU Dortmund 2012/04/09 Diese Folien enthalten Graphiken mit Nutzungseinschränkungen. Das Kopieren der Graphiken ist im Allgemeinen nicht erlaubt. Motivation

Mehr

GPGPU-Architekturen CUDA CUDA Beispiel OpenCL OpenCL Beispiel. CUDA & OpenCL. Ralf Seidler. Friedrich-Alexander-Universität Erlangen-Nürnberg

GPGPU-Architekturen CUDA CUDA Beispiel OpenCL OpenCL Beispiel. CUDA & OpenCL. Ralf Seidler. Friedrich-Alexander-Universität Erlangen-Nürnberg CUDA und OpenCL Friedrich-Alexander-Universität Erlangen-Nürnberg 24. April 2012 Outline 1 GPGPU-Architekturen 2 CUDA 3 CUDA Beispiel 4 OpenCL 5 OpenCL Beispiel Outlook 1 GPGPU-Architekturen 2 CUDA 3 CUDA

Mehr

OpenCL. OpenCL. Boris Totev, Cornelius Knap

OpenCL. OpenCL. Boris Totev, Cornelius Knap OpenCL OpenCL 1 OpenCL Gliederung Entstehungsgeschichte von OpenCL Was, warum und überhaupt wieso OpenCL CUDA, OpenGL und OpenCL GPUs OpenCL Objekte Work-Units OpenCL Adressbereiche OpenCL API Codebeispiel

Mehr

Parallele Algorithmen mit OpenCL. Universität Osnabrück, Henning Wenke, 2013-05-08

Parallele Algorithmen mit OpenCL. Universität Osnabrück, Henning Wenke, 2013-05-08 Parallele Algorithmen mit OpenCL Universität Osnabrück, Henning Wenke, 2013-05-08 Aufräumen Ressourcen in umgekehrter Abhängigkeitsreihenfolge freigeben Objekte haben Reference-Count (RC), initial 1 clrelease

Mehr

7 Funktionen. 7.1 Definition. Prototyp-Syntax: {Speicherklasse} {Typ} Name ({formale Parameter});

7 Funktionen. 7.1 Definition. Prototyp-Syntax: {Speicherklasse} {Typ} Name ({formale Parameter}); S. d. I.: Programieren in C Folie 7-1 7 Funktionen 7.1 Definition Prototyp-Syntax: Speicherklasse Typ Name (formale Parameter); der Funktions-Prototyp deklariert eine Funktion, d.h. er enthält noch nicht

Mehr

CUDA. Axel Jena, Jürgen Pröll. Multi-Core Architectures and Programming. Friedrich-Alexander-Universität Erlangen-Nürnberg Axel Jena, Jürgen Pröll 1

CUDA. Axel Jena, Jürgen Pröll. Multi-Core Architectures and Programming. Friedrich-Alexander-Universität Erlangen-Nürnberg Axel Jena, Jürgen Pröll 1 CUDA Axel Jena, Jürgen Pröll Multi-Core Architectures and Programming Axel Jena, Jürgen Pröll 1 Warum Tesla? Traditionelle Graphikkarten Getrennte Prozessoren für Vertex- / Pixelberechnungen - Nachteil:

Mehr

> High-Level Programmierung heterogener paralleler Systeme

> High-Level Programmierung heterogener paralleler Systeme > High-Level Programmierung heterogener paralleler Systeme Projektseminar im SoSe 2012 Prof. Sergei Gorlatch, Michel Steuwer, Tim Humernbrum AG Parallele und Verteilte Systeme, Westfälische Wilhelms-Universität

Mehr

GPUs. Arbeitsbereich Wissenschaftliches Rechnen Fachbereich Informatik Fakultät für Mathematik, Informatik und Naturwissenschaften Universität Hamburg

GPUs. Arbeitsbereich Wissenschaftliches Rechnen Fachbereich Informatik Fakultät für Mathematik, Informatik und Naturwissenschaften Universität Hamburg GPUs Arbeitsbereich Wissenschaftliches Rechnen Fachbereich Informatik Fakultät für Mathematik, Informatik und Naturwissenschaften Universität Hamburg Vorgelegt von: Johannes Coym E-Mail-Adresse: 4coym@informatik.uni-hamburg.de

Mehr

Inhaltsverzeichnis. Kapitel i: Schnelleinstieg 13. Kapitel 2: Was sind Programme? 17. Kapitel 3: Wie erstellt man eigene Programme?

Inhaltsverzeichnis. Kapitel i: Schnelleinstieg 13. Kapitel 2: Was sind Programme? 17. Kapitel 3: Wie erstellt man eigene Programme? Liebe Leserin, lieber Leser 10 Kapitel i: Schnelleinstieg 13 Kapitel 2: Was sind Programme? 17 Was ist ein Programm? 18 Sprechen Sie Computer? 18 Von der Idee zum Programm 19 Von Windows, Fenstern und

Mehr

GPGPU WITH OPENCL. Praktikum Parallele Rechnerarchitekturen, 2015w Franz Richter-Gottfried

GPGPU WITH OPENCL. Praktikum Parallele Rechnerarchitekturen, 2015w Franz Richter-Gottfried GPGPU WITH OPENCL Praktikum Parallele Rechnerarchitekturen, 2015w Franz Richter-Gottfried INFRASTRUCTURE Enqueue interactive job srun --gres --pty bash Graphics cards available for tesla_k20,

Mehr

Zeiger. C-Kurs 2012, 2. Vorlesung. Tino Kutschbach 10.

Zeiger. C-Kurs 2012, 2. Vorlesung. Tino Kutschbach  10. Zeiger C-Kurs 2012, 2. Vorlesung Tino Kutschbach tino.kutschbach@campus.tu-berlin.de http://wiki.freitagsrunde.org 10. September 2012 This work is licensed under the Creative Commons Attribution-ShareAlike

Mehr

Programmierung von Graphikkarten

Programmierung von Graphikkarten Programmierung von Graphikkarten Stefan Lang Interdisziplinäres Zentrum für Wissenschaftliches Rechnen Universität Heidelberg INF 368, Raum 532 D-69120 Heidelberg phone: 06221/54-8264 email: Stefan.Lang@iwr.uni-heidelberg.de

Mehr

2. Programmierung in C

2. Programmierung in C 2. Programmierung in C Inhalt: Überblick über Programmiersprachen, Allgemeines zur Sprache C C: Basisdatentypen, Variablen, Konstanten Operatoren, Ausdrücke und Anweisungen Kontrollstrukturen (Steuerfluss)

Mehr

Organisatorisches. Folien (u.a.) auf der Lva-Homepage Skriptum über MU Online

Organisatorisches. Folien (u.a.) auf der Lva-Homepage Skriptum über MU Online Organisatorisches Folien (u.a.) auf der Lva-Homepage Skriptum über MU Online Nächste Woche VO und UE am Dienstag, den 30.10.! UE im CR IL/IT Wissensüberprüfung am Zettel 25.10.2018 IT I - VO 3 1 Organisatorisches

Mehr

Memory Models Frederik Zipp

Memory Models Frederik Zipp Memory Models Frederik Zipp Seminar: Programmiersprachen für Parallele Programmierung (SS 2010) Fakultät für Informatik - IPD SNELTING LEHRSTUHL PROGRAMMIERPARADIGMEN 1

Mehr

C++ - Einführung in die Programmiersprache Header-Dateien und Funktionen. Leibniz Universität IT Services Anja Aue

C++ - Einführung in die Programmiersprache Header-Dateien und Funktionen. Leibniz Universität IT Services Anja Aue C++ - Einführung in die Programmiersprache Header-Dateien und Funktionen Leibniz Universität IT Services Anja Aue Modularisierung Logische Gliederung von Programmteilen mit Hilfe von Namensräumen. Aufteilung

Mehr

Physikalische Berechnungen mit General Purpose Graphics Processing Units (GPGPUs)

Physikalische Berechnungen mit General Purpose Graphics Processing Units (GPGPUs) Fakultätsname XYZ Fachrichtung XYZ Institutsname XYZ, Professur XYZ Physikalische Berechnungen mit General Purpose Graphics Processing Units (GPGPUs) im Rahmen des Proseminars Technische Informatik Juni

Mehr

CUDA Workshop. Ausblick. Daniel Tenbrinck

CUDA Workshop. Ausblick. Daniel Tenbrinck CUDA Workshop Ausblick Daniel Tenbrinck Computer Vision and Pattern Recognition Group Institut für Informatik Westfälische Wilhelms-Universität Münster 03.Juli 2009 Folie: 1 / 10 Daniel Tenbrinck CUDA

Mehr

Organisatorisches. Folien (u.a.) gibt's auf der Lva-Homepage zum Download

Organisatorisches. Folien (u.a.) gibt's auf der Lva-Homepage zum Download Organisatorisches Folien (u.a.) gibt's auf der Lva-Homepage zum Download Diesen Mi erstes Tutorium (15-17) Ab nächster Woche montags 10-12 (jeweils im Computerraum) 17.10.2017 IT I - VO 3 1 Organisatorisches

Mehr

C++ - Einführung in die Programmiersprache Zeiger, Referenzen und Strukturen. Leibniz Universität IT Services Anja Aue

C++ - Einführung in die Programmiersprache Zeiger, Referenzen und Strukturen. Leibniz Universität IT Services Anja Aue C++ - Einführung in die Programmiersprache Zeiger, Referenzen und Strukturen Leibniz Universität IT Services Anja Aue Zeiger (Pointer) Verweis auf eine Speicherstelle. Speicherung einer Speicheradresse.

Mehr

Software Engineering für moderne parallele Plattformen 9. GPGPUs: Grafikkarten als Parallelrechner

Software Engineering für moderne parallele Plattformen 9. GPGPUs: Grafikkarten als Parallelrechner Software Engineering für moderne parallele Plattformen 9. GPGPUs: Grafikkarten als Parallelrechner Dipl.-Inform. Korbinian Molitorisz M. Sc. Luis Manuel Carril Rodriguez KIT Universität des Landes Baden-Württemberg

Mehr

Automatische OpenCL-Code-Analyse zur Bestimmung von Speicherzugriffsmustern

Automatische OpenCL-Code-Analyse zur Bestimmung von Speicherzugriffsmustern Automatische OpenCL-Code-Analyse zur Bestimmung von Speicherzugriffsmustern Bachelorarbeit Moritz Lüdecke 8. Juli 2014 INSTITUT FÜR TECHNISCHE INFORMATIK - LEHRSTUHL FÜR RECHNERARCHITEKTUR UND PARALLELVERARBEITUNG

Mehr

Projekt 3 Variablen und Operatoren

Projekt 3 Variablen und Operatoren Projekt 3 Variablen und Operatoren Praktisch jedes Programm verarbeitet Daten. Um mit Daten programmieren zu können, muss es Möglichkeiten geben, die Daten in einem Programm zu verwalten und zu manipulieren.

Mehr

Programmiertechnik. Teil 4. C++ Funktionen: Prototypen Overloading Parameter. C++ Funktionen: Eigenschaften

Programmiertechnik. Teil 4. C++ Funktionen: Prototypen Overloading Parameter. C++ Funktionen: Eigenschaften Programmiertechnik Teil 4 C++ Funktionen: Prototypen Overloading Parameter C++ Funktionen: Eigenschaften Funktionen (Unterprogramme, Prozeduren) fassen Folgen von Anweisungen zusammen, die immer wieder

Mehr

Computergrafik Universität Osnabrück, Henning Wenke,

Computergrafik Universität Osnabrück, Henning Wenke, Computergrafik Universität Osnabrück, Henning Wenke, 2012-06-25 Kapitel XV: Parallele Algorithmen mit OpenCL 15.1 Parallele Programmierung Quellen: V.a. Wikipedia. Leistungsdaten unter Vorbehalt. Bitte

Mehr

Funktionen. mehrfach benötigte Programmteile nur einmal zu schreiben und mehrfach aufzurufen

Funktionen. mehrfach benötigte Programmteile nur einmal zu schreiben und mehrfach aufzurufen Funktionen Funktionen erlauben, dem Programmcode hierarchisch zu strukturieren ein Hauptprogramm steuert dabei die Abfolge von Schritten, die einzelnen Schritte können durch Funktionen realisiert werden

Mehr

Multicore-Architekturen

Multicore-Architekturen Universität Erlangen- Nürnberg Technische Universität München Universität Stuttgart Multicore-Architekturen Vortrag im Rahmen der Ferienakademie 2009 Kurs 1: Programmierkonzepte für Multi-Core Rechner

Mehr

Ferienakademie Erik Muttersbach

Ferienakademie Erik Muttersbach Ferienakademie 2009 - Erik Muttersbach 1. Einführung 2. Kernels, Threads, Blocks 3. CUDA Execution Model 4. Software Stack 5. Die CUDA Runtime API 6. Speichertypen/ Zugriff 7. Profiling und Optimierung

Mehr

OpenCL Implementierung von OpenCV Funktionen

OpenCL Implementierung von OpenCV Funktionen Multi-Core Architectures and Programming OpenCL Implementierung von OpenCV Funktionen julian.mueller@e-technik.stud.uni-erlangen.de Hardware/Software Co-Design August 18, 2011 1 Table of content 1 OpenCL

Mehr

Wertebereich und Genauigkeit der Zahlendarstellung

Wertebereich und Genauigkeit der Zahlendarstellung Wertebereich und Genauigkeit der Zahlendarstellung Sowohl F als auch C kennen bei ganzen und Floating Point-Zahlen Datentypen verschiedener Genauigkeit. Bei ganzen Zahlen, die stets exakt dargestellt werden

Mehr

Grafikkarten-Architektur

Grafikkarten-Architektur > Grafikkarten-Architektur Parallele Strukturen in der GPU Name: Sebastian Albers E-Mail: s.albers@wwu.de 2 > Inhalt > CPU und GPU im Vergleich > Rendering-Pipeline > Shader > GPGPU > Nvidia Tesla-Architektur

Mehr

Paralleler Cuckoo-Filter. Seminar: Implementierungstechniken für Hauptspeicherdatenbanksysteme Jeremias Neth München, 21.

Paralleler Cuckoo-Filter. Seminar: Implementierungstechniken für Hauptspeicherdatenbanksysteme Jeremias Neth München, 21. Paralleler Cuckoo-Filter Seminar: Implementierungstechniken für Hauptspeicherdatenbanksysteme Jeremias Neth München, 21. November 2017 1 Paralleler Cuckoo-Filter Cuckoo-Hashtabelle Serieller Cuckoo-Filter

Mehr

Einleitung Typsystem Typisierung Zusammenfassung Literatur. Typisierung. Effiziente Programmierung. Thomas Schnieders

Einleitung Typsystem Typisierung Zusammenfassung Literatur. Typisierung. Effiziente Programmierung. Thomas Schnieders Typisierung Effiziente Programmierung Thomas Schnieders Fachbereich Informatik Fakultät für Mathematik, Informatik und Naturwissenschaften Universität Hamburg 2018-04-26 Thomas Schnieders Typisierung 1

Mehr

Volumenrendering mit CUDA

Volumenrendering mit CUDA Volumenrendering mit CUDA Arbeitsgruppe Visualisierung und Computergrafik http://viscg.uni-muenster.de Überblick Volumenrendering allgemein Raycasting-Algorithmus Volumen-Raycasting mit CUDA Optimierung

Mehr

General Purpose Computation on GPUs

General Purpose Computation on GPUs General Purpose Computation on GPUs Matthias Schneider, Robert Grimm Universität Erlangen-Nürnberg {matthias.schneider, robert.grimm}@informatik.stud.uni-erlangen.de M. Schneider, R. Grimm 1 Übersicht

Mehr

Ein kleiner Blick auf die generische Programmierung

Ein kleiner Blick auf die generische Programmierung TgZero Technik.Blosbasis.net June 3, 2013 1 Inhaltsverzeichnis 1 Vorwort 3 2 Ein kleines Beispiel 3 3 Templates 3 4 Verschiedene Datentypen 4 5 Variadic Templates 5 6 Unterschied zwischen den Programmiersprachen

Mehr

Parallele Algorithmen mit OpenCL. Universität Osnabrück, Henning Wenke,

Parallele Algorithmen mit OpenCL. Universität Osnabrück, Henning Wenke, Parallele Algorithmen mit OpenCL Universität Osnabrück, Henning Wenke, 2013-05-29 Kapitel Parallelität [1]: Parallel Programming (Rauber, Rünger, 2007) [2]: Algorithms Sequential & Parallel A Unified Approach

Mehr

Objekte werden eindeutig beschrieben durch ihren Typ und einen beliebig wählbaren Bezeichner.

Objekte werden eindeutig beschrieben durch ihren Typ und einen beliebig wählbaren Bezeichner. Grundlegende Deklarationen Seite 1 von 6 Jedes Programm benötigt Objekte. Ein Objekt ist ein reservierter Bereich im Systemspeicher in welchem Informationen abgelegt werden. Informationen sind z.b. Zahlen,

Mehr

Einführung in die Programmiersprache C

Einführung in die Programmiersprache C Einführung in die Programmiersprache C 11 Was bisher verschwiegen wurde Alexander Sczyrba Robert Homann Georg Sauthoff Universität Bielefeld, Technische Fakultät Type qualifier Typen können mit folgenden

Mehr

Unterlagen. CPP-Uebungen-08/

Unterlagen.  CPP-Uebungen-08/ Unterlagen http://projects.eml.org/bcb/people/ralph/ CPP-Uebungen-08/ http://www.katjawegner.de/lectures.html Kommentare in C++ #include /* Dies ist ein langer Kommentar, der über zwei Zeilen

Mehr

Einführung in die Programmiersprache C

Einführung in die Programmiersprache C Einführung in die Programmiersprache C 11 Was bisher verschwiegen wurde Alexander Sczyrba Robert Homann Georg Sauthoff Universität Bielefeld, Technische Fakultät Type qualifier Typen können mit folgenden

Mehr

C++ - Funktionen und mehr -

C++ - Funktionen und mehr - C++ - Funktionen und mehr - Friedrich-Schiller-Universität Jena Kerstin Gößner und Ralf Wondratschek Prof. Dr. habil. Wolfram Amme Dipl.-Inf. Thomas Heinze Inhaltsverzeichnis 1 Einleitung 3 2 Deklaration,

Mehr

Funktionen in JavaScript

Funktionen in JavaScript Funktionen in JavaScript Eine Funktion enthält gebündelten Code, der sich in dieser Form wiederverwenden lässt. Es können ganze Programmteile aufgenommen werden. Mithilfe von Funktionen kann man denselben

Mehr

Methoden und Wrapperklassen

Methoden und Wrapperklassen Methoden und Wrapperklassen CoMa-Übung IV TU Berlin 06.11.2012 CoMa-Übung IV (TU Berlin) Methoden und Wrapperklassen 06.11.2012 1 / 24 Themen der Übung 1 Methoden 2 Wrapper-Klassen CoMa-Übung IV (TU Berlin)

Mehr

Einführung in die Programmierung 1

Einführung in die Programmierung 1 Einführung in die Programmierung 1 Einführung (S.2) Einrichten von Eclipse (S.4) Mein Erstes Programm (S.5) Hallo Welt!? Programm Der Mensch (S.11) Klassen (S.12) Einführung Wie Funktioniert Code? Geschriebener

Mehr

JavaScript. Dies ist normales HTML. Hallo Welt! Dies ist JavaScript. Wieder normales HTML.

JavaScript. Dies ist normales HTML. Hallo Welt! Dies ist JavaScript. Wieder normales HTML. JavaScript JavaScript wird direkt in HTML-Dokumente eingebunden. Gib folgende Zeilen mit einem Texteditor (Notepad) ein: (Falls der Editor nicht gefunden wird, öffne im Browser eine Datei mit der Endung

Mehr

Einführung in die Programmiersprache C

Einführung in die Programmiersprache C Einführung in die Programmiersprache C 4 Storage classes Alexander Sczyrba Robert Homann Georg Sauthoff Universität Bielefeld, Technische Fakultät Compilation units Compilierung eines mehrteiligen Programms:

Mehr

Einführung in die Programmiersprache C

Einführung in die Programmiersprache C Einführung in die Programmiersprache C 4 Storage classes Alexander Sczyrba Robert Homann Georg Sauthoff Universität Bielefeld, Technische Fakultät Compilation units Compilierung eines mehrteiligen Programms:

Mehr

RST-Labor WS06/07 GPGPU. General Purpose Computation On Graphics Processing Units. (Grafikkarten-Programmierung) Von: Marc Blunck

RST-Labor WS06/07 GPGPU. General Purpose Computation On Graphics Processing Units. (Grafikkarten-Programmierung) Von: Marc Blunck RST-Labor WS06/07 GPGPU General Purpose Computation On Graphics Processing Units (Grafikkarten-Programmierung) Von: Marc Blunck Ablauf Einführung GPGPU Die GPU GPU Architektur Die Programmierung Programme

Mehr

2. Programmierung in C

2. Programmierung in C 2. Programmierung in C Inhalt: Überblick über Programmiersprachen, Allgemeines zur Sprache C C: Basisdatentypen, Variablen, Konstanten, Operatoren und Ausdrücke Anweisungen und Kontrollstrukturen (Steuerfluss)

Mehr

Cell and Larrabee Microarchitecture

Cell and Larrabee Microarchitecture Cell and Larrabee Microarchitecture Benjamin Grund Dominik Wolfert Universität Erlangen-Nürnberg 1 Übersicht Einleitung Herkömmliche Prozessorarchitekturen Motivation für Entwicklung neuer Architekturen

Mehr

Programmierung mit C Zeiger

Programmierung mit C Zeiger Programmierung mit C Zeiger Zeiger (Pointer)... ist eine Variable, die die Adresse eines Speicherbereichs enthält. Der Speicherbereich kann... kann den Wert einer Variablen enthalten oder... dynamisch

Mehr

OpenCL. Seminar Programmiersprachen im Multicore-Zeitalter Universität Siegen Tim Wiersdörfer tim.wiersdoerfer@student.uni-siegen.

OpenCL. Seminar Programmiersprachen im Multicore-Zeitalter Universität Siegen Tim Wiersdörfer tim.wiersdoerfer@student.uni-siegen. OpenCL Seminar Programmiersprachen im Multicore-Zeitalter Universität Siegen Tim Wiersdörfer tim.wiersdoerfer@student.uni-siegen.de Abstract: In diesem Dokument wird ein grundlegender Einblick in das relativ

Mehr

Wintersemester Maschinenbau und Kunststofftechnik. Informatik. Tobias Wolf Seite 1 von 29

Wintersemester Maschinenbau und Kunststofftechnik. Informatik. Tobias Wolf  Seite 1 von 29 Kapitel 2 Einführung in C++ Seite 1 von 29 C++ Zeichensatz - Buchstaben: a bis z und A bis Z. - Ziffern: 0 bis 9 - Sonderzeichen: ; :,. # + - * / % _ \! < > & ^ ~ ( ) { } [ ]? Seite 2 von 29 Höhere Elemente

Mehr

Inhalt. 1 Einstieg in die Welt von C Erste Schritte in C 31. Vorwort... 15

Inhalt. 1 Einstieg in die Welt von C Erste Schritte in C 31. Vorwort... 15 Vorwort... 15 1 Einstieg in die Welt von C 17 1.1 Die Sprache C... 17 1.2 Die C-Standardbibliothek... 18 1.3 Die nötigen Werkzeuge für C... 21 1.4 Übersetzen mit der Entwicklungsumgebung... 23 1.5 Übersetzen

Mehr

Einführung Sprachfeatures Hinweise, Tipps und Styleguide Informationen. Einführung in C. Patrick Schulz

Einführung Sprachfeatures Hinweise, Tipps und Styleguide Informationen. Einführung in C. Patrick Schulz Patrick Schulz patrick.schulz@paec-media.de 29.04.2013 1 Einführung Einführung 2 3 4 Quellen 1 Einführung Einführung 2 3 4 Quellen Hello World in Java Einführung 1 public class hello_ world 2 { 3 public

Mehr

Parallel Computing in der industriellen Bildverarbeitung

Parallel Computing in der industriellen Bildverarbeitung SOLUTIONS FOR MACHINE VISION Parallel Computing in der industriellen Bildverarbeitung Dipl.-Inform. Alexander Piaseczki Research and Development Sirius Advanced Cybernetics GmbH Tools & Solutions für die

Mehr

Speicherklassen (1) Lokale Variablen

Speicherklassen (1) Lokale Variablen Speicherklassen (1) Lokale Variablen Lokale Variablen beschränken sich auf die Funktionen, in denen sie definiert werden Sind in der Funktion gekapselt können also nur in der Funktion verändert werden

Mehr

Vorlesung Programmieren

Vorlesung Programmieren Vorlesung Programmieren Speicherverwaltung und Parameterübergabe Prof. Dr. Stefan Fischer Institut für Telematik, Universität zu Lübeck http://www.itm.uni-luebeck.de/people/fischer Gültigkeitsbereich von

Mehr

Programmieren 1 C Überblick

Programmieren 1 C Überblick Programmieren C Überblick. Einleitung 2. Graphische Darstellung von Algorithmen 3. Syntax und Semantik 4. Einstieg in C: Einfache Sprachkonstrukte und allgemeiner Programmaufbau 5. Skalare Standarddatentypen

Mehr

ÜBUNGS-BLOCK 7 LÖSUNGEN

ÜBUNGS-BLOCK 7 LÖSUNGEN ÜBUNGS-BLOCK 7 LÖSUNGEN Aufgabe 1: Gegeben ist folgender Code: Auto[] array = new Auto[3]; // Alle Autos im Array tunen: for (int i = 1; i

Mehr

Implementieren von Klassen

Implementieren von Klassen Implementieren von Klassen Felder, Methoden, Konstanten Dr. Beatrice Amrhein Überblick Felder/Mitglieder (Field, Member, Member-Variable) o Modifizierer Konstanten Methoden o Modifizierer 2 Felder und

Mehr

Übungspaket 23 Mehrdimensionale Arrays

Übungspaket 23 Mehrdimensionale Arrays Übungspaket 23 Mehrdimensionale Arrays Übungsziele: Skript: Deklaration und Verwendung mehrdimensionaler Arrays Kapitel: 49 Semester: Wintersemester 2016/17 Betreuer: Kevin, Matthias, Thomas und Ralf Synopsis:

Mehr

C++ - Objektorientierte Programmierung Konstante und statische Elemente

C++ - Objektorientierte Programmierung Konstante und statische Elemente C++ - Objektorientierte Programmierung Konstante und statische Elemente hat eine Kantenlänge hat eine Füllfarbe Kantenlänge setzen Füllfarbe lesen Volumen berechnen Leibniz Universität IT Services Anja

Mehr

Grundlagen der OO- Programmierung in C#

Grundlagen der OO- Programmierung in C# Grundlagen der OO- Programmierung in C# Technische Grundlagen 1 Dr. Beatrice Amrhein Überblick Visual Studio: Editor und Debugging Die Datentypen Methoden in C# Die Speicherverwaltung 2 Visual Studio 3

Mehr

Repetitorium Programmieren I + II

Repetitorium Programmieren I + II Repetitorium Programmieren I + II Stephan Gimbel Johanna Mensik Michael Roth 6. März 2012 Agenda 1 Operatoren 2 Datentypen Gleitpunkt Zahl Typkonvertierung 3 Strommanipulatoren 4 Bedingungen if-else switch-case

Mehr

Einführung in C. Alexander Batoulis. 5. Mai Fakutltät IV Technische Universität Berlin

Einführung in C. Alexander Batoulis. 5. Mai Fakutltät IV Technische Universität Berlin Fakutltät IV Technische Universität Berlin 5. Mai 2014 Inhaltsverzeichnis 1 2 3 4 5 6 7 Überblick Beispielprogramm in Java Beispielprogramm in C 1 2 3 4 5 6 7 Beispielprogramm in Java Beispielprogramm

Mehr