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? Open Computing Language Programmiersprache OpenCL C Offener Standard zur parallelen und plattform-unabhängigen Programmierung bzw. Berechnung Einsatz auf heterogenen Plattformen CPUs GPUs DSPs Cell Prozessoren Apelt, Nicolas / Zöllner, Christian 2
Historie Ursprünglich durch Apple entwickelt Wurde erstmal mit Mac OS X 10.6 veroeffentlicht (August 2009) Khronos Group Industrie-Konsortium Erstellt und Verwaltet offene Standards im Multimedia-Bereich 16. Juni 2008: Bildung einer Arbeitsgruppe für offene Standards für heterogenes, paralleles Rechnen Veröffentlichte Dezember 2008 die OpenCL 1.0 Spezifikation Mit Beteiligung führender Hersteller, darunter: - AMD, IBM, Intel, NVIDIA, Apple, 3DLabs, ARM, EA, GE,... Apelt, Nicolas / Zöllner, Christian 3
Warum OpenCL? Technische Schwierigkeiten die Leistung durch höhere Taktraten zu steigern Tendenz zu mehr Prozessor-Kernen Sowohl bei CPUs als auch bei GPUs Ausnutzung dieser heterogenen Plattformen für rechenintensive Applikationen Portabler Code für unterschiedliche Geräte und Architekturen Apelt, Nicolas / Zöllner, Christian 4
Vergleich CPU GPU CPU wenig(er) ALUs Komplexer Befehlssatz Auf schnelles Abarbeiten von sequentiellen Aufgaben optimiert GPU Viele einfache ALUs Begrenzter Befehlssatz Auf (massive) Parallelisierung spezialisiert Apelt, Nicolas / Zöllner, Christian 5
Herausforderungen Unterschiedliche Programmier-Ansätze für Mehrkern-CPUs und -GPUs General-purpose computing on GPUs (GPGPU) Programmier-Modelle: Komplexe Speicherhierarchien Vektor-Operationen Plattform-, Hersteller- und Hardware-spezifisch Apelt, Nicolas / Zöllner, Christian 6
OpenCL C (1) Basiert auf ISO C99 Einschränkungen Keine Pointer auf Funktionen Keine Rekursionen Nur Arrays fester Länge Erweitert um Datentypen und Funktionen zur parallelen Verarbeitung Skalare Datentypen - bool, char, short, int, long, float, half, size_t, ptrdiff_t, intptr_t, void Vektor-Datentypen - char, short, int, long, float - 2-, 4-, 8- und 16-elementig Weitere Datentypen - image2d_t, image3d_t, sampler_t, event_t Apelt, Nicolas / Zöllner, Christian 7
OpenCL C (2) Code wird dem Compiler im Klartext übergeben Übersetzung erfolgt zur Laufzeit ( Just In Time ) Anschließend wird der Code an das jeweilige Gerät zur Ausführung eingereiht Apelt, Nicolas / Zöllner, Christian 8
OpenCL Framework Platform Layer Erlaubt der Host-Anwendung OpenCL-Geräte und deren Fähigkeiten zu ermitteln und Kontexte zu erstellen Runtime Manipulation von Kontexten nachdem sie erstellt wurden Trennt die unterliegende Hardware vom Betriebssystem Compiler Erstellt ausführbare Programme, welche die Kernel enthalten Apelt, Nicolas / Zöllner, Christian 9
Architektur Kernel Eine Funktion, die auf einem OpenCL-Gerät ausgeführt wird OpenCL-Kernel - in OpenCL C geschrieben - Werden zur Laufzeit übersetzt (und ausgeführt) Plattform-unabhängig Native Kernel - optional und implementierungsspezifisch Apelt, Nicolas / Zöllner, Christian 10
Platform Model (1) http://upload.wikimedia.org/wikipedia/de/thumb/9/96/platform_architecture_2009-11-08.svg/800px-platform_architecture_2009-11-08.svg.png Apelt, Nicolas / Zöllner, Christian 11
Platform Model (2) Ein Host Verteilt die Kernel zur Laufzeit auf verschiedene Geräte Ein oder mehrere OpenCL-Geräte Angeschlossen an den Host Compute Unit (CU): ein oder mehrere Recheneinheiten je Gerät - z. B.: Gerät: Multicore-CPU, CU: einzelne Cores Apelt, Nicolas / Zöllner, Christian 12
Execution Model (1) OpenCL Programme werden in 2 Teilen ausgeführt Host-Programm wird auf dem Host ausgeführt Verwaltet die Ausführung der Kernel Kernel werden auf einem oder mehreren OpenCL-Geräten ausgeführt Apelt, Nicolas / Zöllner, Christian 13
Execution Model (2) Abhängig von der bevorstehenden Aufgabe kann der Host das am besten passende Gerät auswählen Auswahl kann erfolgen auf Grund von Maximaler Anzahl Recheneinheiten Höchster Taktfrequenz Größtem Speicher Und vielen weiteren gerätespezifischen Eigenschafen - (siehe clgetdeviceinfo) Apelt, Nicolas / Zöllner, Christian 14
Execution Model (3) Apelt, Nicolas / Zöllner, Christian 15
Execution Model (4) Wenn ein Kernel ausgeführt werden soll, wird ein Index- Raum definiert Eine Instanz des Kernels wird für jeden Punkt in diesem Raum ausgeführt Diese Kernel-Instanz wird Work-Item genannt und wird durch ihren Punkt im Index-Raum identifiziert Work-Items werden zu Work-Groups zusammengefasst Apelt, Nicolas / Zöllner, Christian 16
Memory Model (1) http://upload.wikimedia.org/wikipedia/de/d/d1/opencl_memory_model.svg Apelt, Nicolas / Zöllner, Christian 17
Memory Model (2) Work-Items haben Zugriff auf vier verschiedene Speicherregionen: Global Memory: Work-Items können jedes Element lesen/schreiben Constant Memory: Teil des Global Memory, der während der Ausführung des Kernel nicht verändert wird Local Memory: Gemeinsamer Speicher einer Work-Group Private Memory: Geschützter Bereich für jeweils eine Work- Unit Apelt, Nicolas / Zöllner, Christian 18
Programming Model Das OpenCL Execution Model unterstützt: Daten-Parallele Programmier-Modelle (data parallel) - Viele Instanzen fuehren den gleichen Kernel-Code aus und bearbeiten jeweils unterschiedliche Eingabedaten Aufgaben-Parallele Programmier-Modelle (task parallel) - Verschiedene Kernel werden parallel ausgefuehrt Apelt, Nicolas / Zöllner, Christian 19
Standard ISO C99 Sample // Skalarprodukt void dot_product (const float *a, const float *b, const int n, float *c) { for (int i = 0; i < n; ++i) c[i] = a[i] * b[i]; } Apelt, Nicolas / Zöllner, Christian 20
OpenCL C Sample (1) // Skalarprodukt kernel void dot_product ( global const float4 *a, global const float4 *b, global float *c) { int gid = get_global_id(0); c[gid] = a[gid] * b[gid]; } Apelt, Nicolas / Zöllner, Christian 21
OpenCL C Sample (2) #include <stdio.h> #include <stdlib.h> #include <CL/cl.h> // OpenCL source code const char* OpenCLSource[] = { }; #define SIZE 4096 int HostVector1[SIZE] = { }; int HostVector2[SIZE] = { }; int main(int argc, char **argv) { // Create a context to run OpenCL on our CUDA-enabled NVIDIA GPU cl_context GPUContext = clcreatecontextfromtype(0, CL_DEVICE_TYPE_GPU, NULL, NULL, NULL); // Get the list of GPU devices associated with this context size_t ParmDataBytes; clgetcontextinfo(gpucontext, CL_CONTEXT_DEVICES, 0, NULL, &ParmDataBytes); cl_device_id* GPUDevices = (cl_device_id*)malloc(parmdatabytes); clgetcontextinfo(gpucontext, CL_CONTEXT_DEVICES, ParmDataBytes, GPUDevices, NULL); // Create a command-queue on the first GPU device cl_command_queue GPUCommandQueue = clcreatecommandqueue(gpucontext, GPUDevices[0], 0, NULL); // Allocate GPU memory for source vectors AND initialize from CPU memory cl_mem GPUVector1 = clcreatebuffer(gpucontext, CL_MEM_READ_ONLY CL_MEM_COPY_HOST_PTR, sizeof(int) * SIZE, HostVector1, NULL); cl_mem GPUVector2 = clcreatebuffer(gpucontext, CL_MEM_READ_ONLY CL_MEM_COPY_HOST_PTR, sizeof(int) * SIZE, HostVector2, NULL); // Allocate output memory on GPU cl_mem GPUOutputVector = clcreatebuffer(gpucontext, CL_MEM_WRITE_ONLY, sizeof(int) * SIZE, NULL, NULL); // Create OpenCL program with source code cl_program OpenCLProgram = clcreateprogramwithsource(gpucontext, 7, OpenCLSource, NULL, NULL); // Build the program (OpenCL JIT compilation) clbuildprogram(cpprogram, 0, NULL, NULL, NULL, NULL); Apelt, Nicolas / Zöllner, Christian 22
OpenCL C Sample (3) // Create a handle to the compiled OpenCL function (Kernel) cl_kernel OpenCLVectorAdd = clcreatekernel(openclprogram, "VectorAdd", NULL); // In the next step we associate the GPU memory with the Kernel arguments clsetkernelarg(openclvectoradd, 0, sizeof(cl_mem),(void*)&gpuoutputvector); clsetkernelarg(openclvectoradd, 1, sizeof(cl_mem), (void*)&gpuvector1); clsetkernelarg(openclvectoradd, 2, sizeof(cl_mem), (void*)&gpuvector2); // Launch the Kernel on the GPU size_t WorkSize[1] = {SIZE}; // one dimensional Range clenqueuendrangekernel(gpucommandqueue, OpenCLVectorAdd, 1, NULL, WorkSize, NULL, 0, NULL, NULL); // Copy the output in GPU memory back to CPU memory int HostOutputVector[SIZE]; clenqueuereadbuffer(gpucommandqueue, GPUOutputVector, CL_TRUE, 0, SIZE * sizeof(int), HostOutputVector, 0, NULL, NULL); // Cleanup free(gpudevices); clreleasekernel(openclvectoradd); clreleaseprogram(openclprogram); clreleasecommandqueue(gpucommandqueue); clreleasecontext(gpucontext); clreleasememobject(gpuvector1); clreleasememobject(gpuvector2); clreleasememobject(gpuoutputvector); } return 0; Apelt, Nicolas / Zöllner, Christian 23
Quellen http://www.khronos.org/news/press/releases/the_khronos_group_releases_opencl_1.0_specification/ http://www.khronos.org/news/press/releases/khronos_launches_heterogeneous_computing_initiativ/ http://www.khronos.org/developers/library/overview/opencl_overview.pdf http://www.khronos.org/registry/cl/specs/opencl-1.0.48.pdf http://knol.google.com/k/computing-with-nvidia-s-cuda-and-opencl# Apelt, Nicolas / Zöllner, Christian 24