Download - OpenCL Grundlagen
OpenCL – Data Parallel Language
Parallel Computing auf CPU, GPU und Cell
Ein FG Workshop von Christopher Scherb
Inhalt
• Was bedeutet Data – Parallel?• Was ist ein Streamprozessor?• Was ist OpenCL?• Wie programmiert man OpenCL?• Grenzen?• Beispiele
Was bedeutet Data – Parallel?
• Verarbeitung von Daten zur gleichen Zeit
• Multicore CPU
• SSE bzw. SIMD
• Streamprozessor
• GPU
for (int i = 0; i < datasize; i++){ data[i] = data[i] + 1;}
#pragma omp parallel num_threads(datasize){ int i = omp_get_thread_num(); data[i] = data[i] + 1;}
Was ist ein Streamprozessor?
• CO – Prozessor• Viele kleine „Streamprozessoren“ • Zur Verarbeitung von Datenströmen• Hohe Parallelität • GPU?
Was ist OpenCL?
• Data/Task Parallel Language• Für CPUs, GPUs, Cell und DSPs• Von Apple entwickelt• Verwaltet von der Khronos – Group• Programmiert wird in OpenCL C
Plattform Model
Plattform Model
• Host = System – Prozessor (CPU)• Compute Device = CPU / GPU / Cell• Compute Unit = SIMD Einheit• Prozessing Element = ALU des
Compute Devices
Speichermodel
Speichermodel
• Host Memory = System Memory• Global Memory = für alle Compute Units• Local Memory = für eine SIMD Einheit• Private Memory = für eine ALU
• Nur Local Memory kann synchronisiert werden!
Ausführmodel
• Verarbeite jedes Element „einzeln“– Verteile auf verschiedene Compute Units
– SIMD wird nicht genutzt
• Fasse bestimmte Anzahl der Elemente zu Blöcken zusammen
– SIMD
– Verteile mehrere Blöcke über die Compute Units
– Synchronisierung
Ausführmodel
Wie programmiert man OpenCL?
• Host (System) initialisiert das OpenCL Device• Host erstellt und compiliert ein OpenCL
Programm• Host erstellt Kernel (= ausführbarer Code)• Host schreibt Daten auf das Device• Device verarbeitet Daten• Host liest Daten zurück
Initialiesiere OpenCL Device
• Wähle eine Platform (= SDK)• Lese Context Einstellungen aus Platform• Erstelle Context
• Initialisiere Platform:
cl_uint numPlatforms;cl_platform_id platform = NULL;clGetPlatformIDs(0, NULL,
&numPlatforms);
Platform auswählen
if (0 < numPlatforms){cl_platform_id* platforms = new cl_platform_id[numPlatforms];clGetPlatformIDs(numPlatforms, platforms, NULL);for (unsigned i = 0; i < numPlatforms; ++i){
char pbuf[100];
clGetPlatformInfo(platforms[i],CL_PLATFORM_VENDOR,sizeof(pbuf),pbuf,NULL);
platform = platforms[i];if (!strcmp(pbuf, "Advanced Micro Devices, Inc."))break;
}delete[] platforms;
}
cl_context_properties cps[3] ={CL_CONTEXT_PLATFORM,(cl_context_properties)platform,0};cl_context_properties* cprops =(platform==NULL) ? NULL : cps;
Context erstellen
cl_context hContext;
hContext=clCreateContextFromType(cprops,CL_DEVICE_TYPE_GPU,0,0, &error);
errcheck(error,"clCreateContextFromType");
Errorcheck Funktion (Sollte nach jeden Verweis auf error aufgerufen werden):
void errcheck(int error, std::string error_code){if(error != CL_SUCCESS){
std::cout<<"Error: "<<error_code<<"("<<error<<")"<<std::endl;
exit(-1);}
}
Lese Programm ein
• Lese Programm von Datei oder String• Compiliere Programm
std::ifstream file(<filename>);std::string prog(std::istreambuf_iterator<char>(file),(std::istreambuf_iterator<char>()));const char *sProgramSource=prog.c_str();
hProgram = clCreateProgramWithSource(hContext,1,(const char **)&sProgramSource,0,&error);
error = clBuildProgram(hProgram,0,0,0,0,0);
Erstelle Kernel
• Erstelle ausführbares Programm aus den compilierten Daten
cl_kernel hKernel;hKernel=clCreateKernel(hProgram, <KernelName>,0);
Initialisiere Daten
• Daten müssen gebuffert werden
• Minimiere Transfer zwischen Host und Device!
• Setze Kernel Argumente
cl_int *a=new cl_int[32];cl_mem CL1;CL1=clCreateBuffer(hContext,CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR,
sizeof(cl_int)*32,a, &error);error = clSetKernelArg(hKernel,0,sizeof(cl_mem),(void *)&CL1);
Erstelle CommandQueue
• Umgebung zum Ausführen von Kerneln
• Wichtig für Synchronisierung zwischen verschiedenen Kerneln
• Nutze gemeinsame Daten in verschiedenen Kerneln
hCmdQueue=clCreateCommandQueue(hContext,aDevices[0],0,0);
Führe die Queue aus
• In Order oder Qut of Order
error = clEnqueueNDRangeKernel(hCmdQueue, hKernel,1,0,<globalworksize>, <localworksize>,0,0,0,0);
• Lese Daten zurückerror=clEnqueueReadBuffer(hCmdQueue,CL1,CL_TRUE,0,sizeof(cl_int)*32,a,0,0,0);
• Free Memory
delete [] a;clReleaseMemObject(CL1);
Wie sieht ein Programm aus?
• Vectoraddition
• OpenCL C
__kernel void vecadd(__global int* a, __global int* b, __global int* c){
int i = get_global_id(0);c[i] = a[i] + b[i];
}
Grenzen
• Mergesort• Letzter Mergevorgang nicht parallel• Auf Streamprozessor langsam• → Flexibel das Device wechseln
Demos
• Vectoraddition• Mergesort• Mandelbrot
Optimierungen● Maxiemiere Local Worksize● Code Vektorisierung (floatn)● Übertrage Vektoren statt Skalaren● Nutze Rechenleistung durch MIMD● OpenGL(D3D)/OpenCL Interaktion
(Speicherreduzierung)