1. OpenCL – Data Parallel
Language
Parallel Computing auf CPU,
GPU und Cell
Ein FG Workshop von Christopher Scherb
2. Inhalt
•
Was bedeutet Data – Parallel?
•
Was ist ein Streamprozessor?
•
Was ist OpenCL?
•
Wie programmiert man OpenCL?
•
Grenzen?
•
Beispiele
3. Was bedeutet Data –
Parallel?
• Verarbeitung von Daten zur gleichen Zeit
• Multicore CPU
• SSE bzw. SIMD
• Streamprozessor
• GPU
for (int i = 0; i < #pragma omp parallel
datasize; i++) num_threads(datasize)
{ {
data[i] = data[i] + int i = omp_get_thread_num();
1; data[i] = data[i] + 1;
} }
4. Was ist ein
Streamprozessor?
•
CO – Prozessor
•
Viele kleine „Streamprozessoren“
•
Zur Verarbeitung von Datenströmen
•
Hohe Parallelität
•
GPU?
5. 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
7. Plattform Model
•
Host = System – Prozessor (CPU)
•
Compute Device = CPU / GPU / Cell
•
Compute Unit = SIMD Einheit
•
Prozessing Element = ALU des
Compute Devices
9. 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!
10. 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
12. 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
16. 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);
17. Erstelle Kernel
•
Erstelle ausführbares Programm aus
den compilierten Daten
cl_kernel hKernel;
hKernel=clCreateKernel(hProgram, <KernelName>,0);
18. 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);
19. 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);
20. 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ück
error=clEnqueueReadBuffer(hCmdQueue,CL1,CL_TRUE,0,sizeof(cl_int
)*32,a,0,0,0);
• Free Memory
delete [] a;
clReleaseMemObject(CL1);
21. 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];
}
22. Grenzen
•
Mergesort
•
Letzter Mergevorgang nicht parallel
•
Auf Streamprozessor langsam
•
→ Flexibel das Device wechseln