2013-07-21 15 views
5

Tak więc bawię się teraz z OpenCL i testuję prędkości transferu pamięci między hostem a urządzeniem. Użyłem Intel OpenCL SDK i działa na Intel i5 Processor ze zintegrowaną grafiką. Następnie odkryto clEnqueueMapBuffer zamiast clEnqueueWriteBuffer który okazał się szybciej, prawie 10 razy przy użyciu przypięte pamięci tak:CL_MEM_ALLOC_HOST_PTR wolniej niż 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); 

przypadku ab i ret są dopasowane 128 bit int tablice. Czas wyszedł do około 22,026186 ms, w porównaniu do 198,604528 ms korzystających clEnqueueWriteBuffer Jednak, gdy zmieniłem kod do

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

czas zwiększa się 91,350065 ms

Co może być problemem? Czy to w ogóle problem?

EDIT: ten sposób zainicjować tablice w drugiej Kod:

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

a teraz, że mogę sprawdzić, map_a i map_b zrobić zawierać odpowiednie elementy na końcu programu, ale map_c zawiera wszystkie 0. Zrobiłem to:

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

i moje jądro jest tylko

__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

w drugim kodzie możesz pokazać, jak zainicjować k_a, k_b i k_c z danymi a, b i ret i gdzie jest clFinish. Jeśli 2 kody robią różne rzeczy, trudno będzie ci pomóc. –

+0

Niestety, kod jest taki sam, po prostu nie skopiowałem wszystkiego przypadkowo.W drugim kodzie nie inicjalizuję k_c z ret, ponieważ pomyślałem, że mogłem po prostu odczytać dane z map_c. – selena731

+0

Po mapowaniu i używaniu, musisz albo usunąć mapę, albo wykonać clWrite/Read z odwzorowanego obiektu w celu zapewnienia spójności pamięci. – DarkZeros

Odpowiedz

1

Rozumiem, który przydziela CL_MEM_ALLOC_HOST_PTR ale nie kopiuje. Czy drugi blok kodu faktycznie dostaje jakieś dane do urządzenia?

Ponadto, clCreateBuffer w przypadku użycia z CL_MEM_USE_HOST_PTR i CL_MEM_COPY_HOST_PTR nie powinien wymagać clEnqueueWrite, ponieważ bufor jest tworzony z pamięcią wskazywaną przez void * host_ptr.

Używanie „przypięty” pamięć w OpenCL powinno być procesem jak:

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: I ostatnia rzecz, jaką można zrobić, aby przyspieszyć program jest nie sprawiają, że pamięć odczytu/zapisu blokowanie za pomocą CL_FALSE zamiast CL_TRUE. Po prostu upewnij się, że wywołałeś clFinish() zanim dane zostaną skopiowane z powrotem do hosta, dzięki czemu kolejka poleceń zostanie opróżniona i wszystkie polecenia zostaną przetworzone.

Źródło: OpenCL In Action

+0

Sry, ale nie mogę zgodzić się z tą odpowiedzią. Od mapowania pamięci i robienia memcpy() rzeczywiście kopiuje dane równolegle za pomocą DMA i powinno być szybciej. Jednak nie można go używać w jądrze bez unmap(). Ponieważ jądro może wykorzystywać niekompletną kopię bufora. Zazwyczaj może to prowadzić do sztucznego przyśpieszenia, które nie jest w rzeczywistości zwiększone, ale niepełna kopia pamięci. – DarkZeros

+0

Czy ta odpowiedź nie myl odwzorowania (tj. Transakcji PCIe przy zapisie na jedną ze stron) z przypinaniem (tj. Blokowaniem strony, aby nigdy nie trzeba było rozwiązywać fizycznej strony lub wymiany)? – einpoklum

0

Przy odpowiednim połączeniu flag, powinieneś być w stanie osiągnąć „zero kopię” (czyli bardzo szybko) map/unmap na zintegrowana grafika Intela, ponieważ nie ma potrzeby na „CPU do GPU "kopiują, ponieważ obie używają tej samej pamięci (co oznacza" Zintegrowane "). Przeczytaj sekcję o pamięci w sekcji Intel OpenCL Optimization Guide.

Powiązane problemy