2013-07-21 2 views
5

Итак, я немного поиграл с OpenCL и проверил скорость передачи данных между хостом и устройством. Я использовал Intel OpenCL SDK и работал на Intel i5 Processor со встроенной графикой. Затем я обнаружил clEnqueueMapBuffer вместо clEnqueueWriteBuffer, который оказался быстрее почти в 10 раз при использовании возлагали памяти так:CL_MEM_ALLOC_HOST_PTR медленнее 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); 

Где ab и ret 128 бит выровнен ИНТ массивы. время вышло около 22,026186 мс, по сравнению с 198,604528 мс с использованием clEnqueueWriteBuffer Однако, когда я изменил мой код

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

время увеличивается до 91,350065 мс

Что может быть проблемой? Или это проблема вообще?

EDIT: Это, как я инициализировать массивы во втором коде:

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

И теперь, когда я проверяю, map_a и map_b сделать содержат правильные элементы в конце программы, но map_c содержит все 0. Я сделал это:

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

и мое ядро ​​просто

__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

во втором коде вы можете показать, как вы инициализируете k_a, k_b и k_c с данными a, b и ret и где clFinish. Если 2 кода делают разные вещи, вам будет сложно помочь –

+0

Извините, код тот же, что я просто не копировал все случайно.Во втором коде я не инициализирую k_c с ret, потому что думал, что могу просто прочитать данные с map_c. – selena731

+0

После сопоставления и использования вы должны либо отменить, либо выполнить clWrite/Read из сопоставленного объекта, чтобы обеспечить согласованность памяти. – DarkZeros

ответ

1

Я понимаю, что CL_MEM_ALLOC_HOST_PTR выделяет, но не копирует. Получает ли второй блок кода какие-либо данные на устройстве?

Кроме того, clCreateBuffer при использовании с CL_MEM_USE_HOST_PTR и CL_MEM_COPY_HOST_PTR не должен требовать clEnqueueWrite, так как буфер создается с памятью, на которую указывает void * host_ptr.

Использование «прижат» памяти OpenCL должен быть процесс, как:

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: Последним, что вы можете сделать, чтобы ускорить программу, чтобы не сделать памяти для чтения блокировки/записи с помощью CL_FALSE вместо CL_TRUE. Просто убедитесь, что вы вызываете clFinish(), прежде чем данные будут скопированы обратно на хост, чтобы очередь команд была опустошена и все команды обработаны.

Источник: OpenCL In Action

+0

Sry, но я не могу согласиться с этим ответом. Поскольку отображение памяти и выполнение memcpy() действительно копируют данные параллельно с использованием DMA, и это должно быть быстрее. Однако вы не можете использовать его в ядре без unmap(). Так как ядро ​​может использовать неполную копию буфера. Как правило, это может привести к искусственной скорости, что совсем не реально, но неполной копией памяти. – DarkZeros

+0

Не отвечает ли этот ответ на сопоставление (например, транзакции PCIe для записи на одну из сторон) с фиксацией (т. Е. Блокировка страниц, так что вам никогда не нужно будет разрешать физическую страницу или свопировать)? – einpoklum

0

С правильной комбинации флагов, вы должны быть в состоянии достигнуть «нулевой копии» (т.е. очень быстро) отображение/Unmap на Intel Integrated Graphics, так как нет необходимости в «CPU в GPU ", так как они оба используют одну и ту же память (это значит, что означает« Интегрированный »). Прочтите раздел Intel OpenCL Optimization Guide по памяти.