2013-07-21 26 views
5

Quindi ho giocato un po 'con OpenCL per un po' e sto testando le velocità di trasferimento della memoria tra host e dispositivo. Stavo usando Intel OpenCL SDK e in esecuzione su Intel i5 Processor con grafica integrata. Ho poi scoperto clEnqueueMapBuffer invece di clEnqueueWriteBuffer che si è rivelato essere più veloce di quasi 10 volte quando si usa la memoria riposto in questo modo:CL_MEM_ALLOC_HOST_PTR più lento di 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); 

Dove ab e ret sono 128 array di int bit allineato. Il tempo è venuto fuori per circa 22,026,186 mila ms, rispetto al 198,604,528 mila ms utilizzando clEnqueueWriteBuffer Tuttavia, quando ho cambiato il mio codice per

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 **/ 

il tempo aumenta di 91,350,065 mila ms

Cosa potrebbe essere il problema? O è un problema?

EDIT: Ecco come ho inizializzare le matrici del secondo codice:

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

E ora che posso controllare, map_a e map_b fare contengono gli elementi a destra alla fine del programma, ma map_c contiene tutti gli 0. Ho fatto questo:

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); 

e il mio kernel è solo

__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

nel secondo codice è possibile mostrare come si inizializza k_a, k_b e k_c con a, b e rit i dati e dove si trova il clFinish. Se i 2 codici fanno cose diverse, sarebbe difficile aiutarti –

+0

Spiacente, il codice è lo stesso, ma non ho copiato tutto casualmente.Nel secondo codice, non inizializzo k_c con ret perché pensavo che potessi semplicemente leggere i dati da map_c. – selena731

+0

Dopo la mappatura e l'utilizzo, è necessario rimuovere la mappatura o eseguire un clWrite/Read dall'oggetto mappato per garantire la coerenza della memoria. – DarkZeros

risposta

1

La mia comprensione è che alloca CL_MEM_ALLOC_HOST_PTR ma non copia. Il secondo blocco di codice ottiene effettivamente dati sul dispositivo?

Inoltre, clCreateBuffer se utilizzato con CL_MEM_USE_HOST_PTR e CL_MEM_COPY_HOST_PTR non dovrebbe richiedere clEnqueueWrite, poiché il buffer viene creato con la memoria indicata da void * host_ptr.

Utilizzando "bloccato" la memoria in OpenCL dovrebbe essere un processo simile:

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: Un'ultima cosa si può fare per accelerare il programma è quello di non fare la memoria di lettura blocco/scrittura utilizzando CL_FALSE invece di CL_TRUE. Assicurati di chiamare clFinish() prima che i dati vengano copiati sull'host in modo che la coda dei comandi venga svuotata e tutti i comandi vengano elaborati.

Fonte: OpenCL In Action

+0

Sry ma non posso essere d'accordo con questa risposta. Poiché mappare la memoria e fare un memcpy(), in effetti, copia i dati in parallelo usando DMA e dovrebbe essere più veloce. Tuttavia, non è possibile utilizzarlo nel kernel senza unmap(). Poiché il kernel potrebbe utilizzare una copia non completa del buffer. In genere questo può portare ad accelerare artificialmente, che non è affatto una vera e propria copia, ma una copia di memoria incompleta. – DarkZeros

+0

Questa risposta non confonde la mappatura (ad esempio transazioni PCIe in scrittura su uno dei lati) con il pinning (ovvero blocco della pagina, in modo che non sia mai necessario risolvere la pagina fisica o lo swap-in)? – einpoklum

0

Con la giusta combinazione di bandiere, si dovrebbe essere in grado di raggiungere "copia zero" (cioè molto veloce) map/unmap sulla grafica integrata Intel poiché non v'è alcuna necessità di un "CPU GPU "copia poiché entrambi usano la stessa memoria (questo è ciò che significa" Integrato "). Leggi la sezione Intel OpenCL Optimization Guide sulla memoria.