В чем разница между созданием объекта буфера с clCreateBuffer + CL_MEM_COPY_HOST_PTR и clCreateBuffer + clEnqueueWriteBuffer? - PullRequest
17 голосов
/ 30 сентября 2010

Я видел обе версии в уроках, но не смог выяснить, в чем их преимущества и недостатки. Какой из них правильный?

cl_mem input = clCreateBuffer(context,CL_MEM_READ_ONLY,sizeof(float) * DATA_SIZE, NULL, NULL);
clEnqueueWriteBuffer(command_queue, input, CL_TRUE, 0, sizeof(float) * DATA_SIZE, inputdata, 0, NULL, NULL);

против

cl_mem input = clCreateBuffer(context,CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, ,sizeof(float) * DATA_SIZE, inputdata, NULL);

Спасибо.

[Update]

Я добавил CL_MEM_COPY_HOST_PTR, во второй пример, чтобы исправить его.

Ответы [ 5 ]

9 голосов
/ 02 августа 2012

Во время моей работы с OpenCL я обнаружил очень важное различие между

cl_mem CT = clCreateImage3DContext, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR , Volume_format, X, Y, Z, rowPitch, slicePitch, sourceData, &error);

и

cl_mem CT = clCreateImage3D(Context, CL_MEM_READ_ONLY , Volume_format, X, Y, Z, 0, 0, 0, &error);
error = clEnqueueWriteImage(CommandQue, CT, CL_TRUE, origin, region, rowPitch, slicePitch, sourceData, 0, 0, 0);

. При первом подходе OpenCL скопирует указатель хоста не напрямую в GPU.Сначала он выделит второй временный буфер на хосте, который может вызвать проблемы, если вы загружаете большие вещи, такие как CT, в GPU.На короткое время необходимая память в два раза больше размера КТ.Также данные не копируются во время этой функции.Он копируется во время установки аргумента в функцию ядра, которая использует объект трехмерного изображения.

Второй подход позволяет напрямую копировать данные в графический процессор.Нет дополнительных выделений, сделанных OpenCL.Я думаю, что это, вероятно, то же самое для обычных объектов буфера.

6 голосов
/ 01 октября 2010

Я предполагаю, что входные данные не равны NULL.

В этом случае второй подход вообще не должен работать, поскольку в спецификациях сказано, что clCreateBuffer возвращает NULL и ошибку, если:

CL_INVALID_HOST_PTR, если host_ptr имеет значение NULL и CL_MEM_USE_HOST_PTR или CL_MEM_COPY_HOST_PTR установлены во флаги или если host_ptr не равен NULL, но CL_MEM_COPY_HOST_PTR или CL_MEM_USE_HOST_P не установлены в флаги

так что вы имеете в виду либо

clCreateBuffer(context,CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,sizeof(float) * DATA_SIZE, inputdata, NULL);

или

clCreateBuffer(context,CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR,sizeof(float) * DATA_SIZE, inputdata, NULL);

Первый должен быть более или менее таким же, как первый подход, который вы показали, тогда как второй на самом деле не будет копировать данные, а вместо этого будет использовать предоставленную ячейку памяти для хранения в буфере (части кэширования или все в память устройства). Какой из этих двух вариантов лучше, зависит от сценария использования.

Лично я предпочитаю использовать двухэтапный подход: сначала выделить буфер, а затем заполнить его writeToBuffer, так как мне легче увидеть, что происходит (конечно, один шаг может быть быстрее (или нет), это просто думаю))

3 голосов
/ 24 мая 2012

Приятным аспектом первого подхода является то, что clEnqueueWriteBuffer позволяет вам назначить событие для копии буфера. Итак, предположим, что вы хотите измерить время, необходимое для копирования данных в графический процессор, с помощью параметров GPU_Profiling, вы сможете сделать это с первым подходом, но не со вторым.

Второй подход более компактен, проще для чтения и требует меньше строк кода.

2 голосов
/ 12 февраля 2011

Одно существенное отличие, с которым я столкнулся:

cl_mem input = clCreateBuffer(context,CL_MEM_READ_ONLY,sizeof(float) * DATA_SIZE, NULL, NULL); clEnqueueWriteBuffer(command_queue, input, CL_TRUE, 0, sizeof(float) * DATA_SIZE, inputdata, 0, NULL, NULL);

Этот первый набор команд создаст пустой буфер и поместит команду в вашу очередь команд для заполнения буфера.

cl_mem input = clCreateBuffer(context,CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, ,sizeof(float) * DATA_SIZE, inputdata, NULL)

Эта вторая команда создаст буфер и немедленно его заполнит. Обратите внимание, что в этом списке аргументов нет очереди команд, поэтому она использует содержимое входных данных, как и сейчас.

Если вы уже выполняли код CL, и ваш указатель источника зависит от предыдущей команды в завершении очереди команд (например, чтение в очередь из предыдущего буфера вывода), вы определенно хотите использовать 1-й метод. Если вы попытаетесь создать и заполнить буфер одной командой, вы получите условие гонки, при котором содержимое буфера не будет должным образом ожидать завершения предыдущего чтения буфера.

1 голос
/ 30 сентября 2010

Ну, главное различие между этими двумя заключается в том, что первый выделяет память на устройстве, а затем копирует данные в эту память. Второй только выделяет.

Или вы имели в виду clCreateBuffer(context,CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,sizeof(float) * DATA_SIZE, inputdata, NULL);?

...