2013-07-21 13 views
5

Also habe ich jetzt ein bisschen mit OpenCL herumgespielt und die Geschwindigkeit des Speichertransfers zwischen Host und Gerät getestet. Ich verwendete Intel OpenCL SDK und lief auf dem Intel i5 Processor mit integrierter Grafik. entdeckte ich dann clEnqueueMapBuffer statt clEnqueueWriteBuffer die fast 10-mal schneller durch entpuppte, wenn wie so gepinnt Speicher mit:CL_MEM_ALLOC_HOST_PTR langsamer als CL_MEM_USE_HOST_PTR

int amt = 16*1024*1024; 
... 
k_a = clCreateBuffer(context,CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, sizeof(int)*amt, a, NULL); 
k_b = clCreateBuffer(context,CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, sizeof(int)*amt, b, NULL); 
k_c = clCreateBuffer(context,CL_MEM_WRITE_ONLY | CL_MEM_USE_HOST_PTR, sizeof(int)*amt, ret, NULL); 

int* map_a = (int*) clEnqueueMapBuffer(c_q, k_a, CL_TRUE, CL_MAP_READ, 0, sizeof(int)*amt, 0, NULL, NULL, &error); 
int* map_b = (int*) clEnqueueMapBuffer(c_q, k_b, CL_TRUE, CL_MAP_READ, 0, sizeof(int)*amt, 0, NULL, NULL, &error); 
int* map_c = (int*) clEnqueueMapBuffer(c_q, k_c, CL_TRUE, CL_MAP_WRITE, 0, sizeof(int)*amt, 0, NULL, NULL, &error); 
clFinish(c_q); 

Wo ab und ret sind 128 Bit ausgerichtet int Arrays. Es kam die Zeit, um über 22,026186 ms, im Vergleich zu 198,604528 ms mit clEnqueueWriteBuffer Allerdings, wenn ich meinen Code

k_a = clCreateBuffer(context,CL_MEM_READ_ONLY | CL_MEM_ALLOC_HOST_PTR, sizeof(int)*amt, NULL, NULL); 
k_b = clCreateBuffer(context,CL_MEM_READ_ONLY | CL_MEM_ALLOC_HOST_PTR, sizeof(int)*amt, NULL, NULL); 
k_c = clCreateBuffer(context,CL_MEM_WRITE_ONLY | CL_MEM_ALLOC_HOST_PTR, sizeof(int)*amt, NULL, NULL); 

int* map_a = (int*)clEnqueueMapBuffer(c_q, k_a, CL_TRUE, CL_MAP_READ, 0, sizeof(int)*amt, 0, NULL, NULL, &error); 
int* map_b = (int*)clEnqueueMapBuffer(c_q, k_b, CL_TRUE, CL_MAP_READ, 0, sizeof(int)*amt, 0, NULL, NULL, &error); 
int* map_c = (int*)clEnqueueMapBuffer(c_q, k_c, CL_TRUE, CL_MAP_WRITE, 0, sizeof(int)*amt, 0, NULL, NULL, &error); 

/** initiate map_a and map_b **/ 

die Zeit erhöht sich auf 91,350065 ms

geändert

Was könnte sei das Problem? Oder ist es überhaupt ein Problem?

EDIT: Dies ist, wie ich die Arrays in dem zweiten Code initialisiert werden:

for (int i = 0; i < amt; i++) 
{ 
    map_a[i] = i; 
    map_b[i] = i; 
} 

Und jetzt, wo ich überprüfen, map_a und map_b tun die richtigen Elemente am Ende des Programms enthalten, aber map_c enthält alle Nullen. Ich tat dies:

clEnqueueUnmapMemObject(c_q, k_a, map_a, 0, NULL, NULL); 
clEnqueueUnmapMemObject(c_q, k_b, map_b, 0, NULL, NULL); 
clEnqueueUnmapMemObject(c_q, k_c, map_c, 0, NULL, NULL); 

und mein Kern ist nur

__kernel void test(__global int* a, __global int* b, __global int* c) 
{ 
    int i = get_global_id(0); 
    c[i] = a[i] + b[i]; 
} 
+0

im zweiten Code können Sie zeigen, wie initialisieren Sie k_a, k_b und k_c mit a, b und ret Daten und wo ist die ClFinish. Wenn die 2 Codes verschiedene Dinge tun, wäre es schwierig, Ihnen zu helfen. –

+0

Entschuldigung, der Code ist der gleiche Ich habe einfach nicht alles versehentlich kopiert.Im zweiten Code initialisiere ich k_c nicht mit ret, weil ich dachte, ich könnte einfach die Daten von map_c lesen. – selena731

+0

Nach dem Mapping und der Verwendung müssen Sie entweder die Zuordnung aufheben oder ein clWrite/Read vom zugeordneten Objekt ausführen, um die Konsistenz des Speichers zu gewährleisten. – DarkZeros

Antwort

1

mein Verständnis, dass CL_MEM_ALLOC_HOST_PTR zuordnet ist aber nicht kopiert. Bekommt der 2. Codeblock tatsächlich irgendwelche Daten auf das Gerät?

Außerdem sollte CLCREATEBuffer bei Verwendung mit CL_MEM_USE_HOST_PTR und CL_MEM_COPY_HOST_PTR kein clEnqueueWrite erfordern, da der Puffer mit dem Speicher erstellt wird, auf den void * host_ptr zeigt.

„gepinnt“ Speicher in OpenCL Verwendung sollte ein Prozess sein, wie:

int amt = 16*1024*1024; 
    int Array[] = new int[amt]; 
    int Error = 0; 

    //Note, since we are using NULL for the data pointer, we HAVE to use CL_MEM_ALLOC_HOST_PTR 
    //This allocates memory on the devices 
    cl_mem B1 = clCreateBuffer(context, CL_MEM_READ_WRITE | CL_MEM_ALLOC_HOST_PTR, sizeof(int)*amt, NULL, &Error); 

    //Map the Device memory to host memory, aka pinning it 
    int *host_ptr = clEnqueueMapBuffer(queue, B1, CL_TRUE, CL_MAP_READ | CL_MAP_WRITE, 0, sizeof(int)*amt, 0, NULL, NULL, &Error); 

    //Copy from host memory to pinned host memory which copies to the card automatically` 
    memcpy(host_ptr, Array, sizeof(int)*amt); 

    //Call your kernel and everything else and memcpy back the pinned back to host when 
    //you are done 

Edit: Eine letzte Sache, die Sie um das Programm zu beschleunigen tun können, ist nicht der Speicher-Lese/Schreib-Sperr zu machen durch CL_FALSE mit anstelle von CL_TRUE. Stellen Sie sicher, dass Sie clFinish() aufrufen, bevor Daten zurück auf den Host kopiert werden, sodass die Befehlswarteschlange geleert wird und alle Befehle verarbeitet werden.

Quelle: OpenCL In Action

+0

Sry, aber ich kann dieser Antwort nicht zustimmen. Da das Mapping des Speichers und das Ausführen eines memcpy() die Daten tatsächlich mit DMA parallel kopieren, sollte es schneller sein. Sie können es jedoch nicht ohne unmap() im Kernel verwenden. Da der Kernel möglicherweise eine nicht vollständige Kopie des Puffers verwendet. In der Regel kann dies zu einer künstlichen Beschleunigung führen, die überhaupt nicht aufgetaucht ist, sondern eine unvollständige Speicherkopie. – DarkZeros

+0

Verwirrt diese Antwort das Mapping (d. H. PCIe-Transaktionen beim Schreiben auf eine der Seiten) nicht mit Pinning (d. H. Seitensperrung, so dass Sie niemals physische Seite oder Swap-In auflösen müssen)? – einpoklum

0

Mit der richtigen Kombination von Fahnen, sollten Sie in der Lage sein, zu erreichen „Null-Kopie“ (dh sehr schnell) Karte/unmap auf Intel Integrated Graphics, da gibt es keine Notwendigkeit für eine „CPU GPU "kopieren, da sie beide den gleichen Speicher verwenden (das ist was" Integriert "bedeutet). Lesen Sie den Abschnitt Intel OpenCL Optimization Guide im Speicher.