Я довольно новичок во встроенных и OpenCL, в настоящее время я пытаюсь разработать пример кода для выполнения на плате i.MX6q, которая поддерживает OpenCL 1.1 EP.
Я должен был начать с нуля, поэтому я последовал эти уроки, OpenCL 1.1 Справочные страницы а также этот пример OpenCL сделать мою первую реализацию / приложение OpenCL.
По сути, я хотел бы разработать «тест производительности» для запуска на плате. Он состоит из двух массивов int (входного и выходного), заполнения первого случайными значениями и вставки его в выходной массив с использованием рабочих элементов OpenCL.
Я был совершенно сбит с толку между функциями буфера clEnqueue (чтение / запись) и флагами clCreateBuffer (особенно CL_MEM_USE_HOST_PTR), поэтому я решил посмотреть и попрактиковаться с ним.
Мой код компилируется правильно и работает правильно, однако, когда я читаю значения выходного массива, они по-прежнему остаются на 0.
Вот мой код (это C ++):
void buffer_copy(char* kernelfile)
{
cl_platform_id platform_id;
cl_device_id device_id;
cl_context context;
cl_command_queue cmd_queue;
cl_program program;
// Retrieving all the OpenCL data needed
// to start the performance test
platform_id = get_platform();
device_id = get_device(platform_id);
context = get_context(platform_id, device_id);
cmd_queue = get_command_queue(context, device_id);
program = get_program(context, kernelfile);
cl_mem buffer_input, buffer_output;
size_t buffer_width = 640, buffer_height = 480;
size_t buffer_size = buffer_width * buffer_height;
cl_kernel kernel;
cl_int err = 0;
char* options = "-Werror -cl-std=CL1.1";
int data_input[buffer_size];
int data_output[buffer_size];
// Assigning random values in the data_input array and
// initializing the data_output array to zero-values
srand(time(NULL));
for (size_t index = 0; index < buffer_size; ++index)
{
data_input[index] = rand();
data_output[index] = 0;
}
// Creating OpenCL buffers
buffer_input = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, buffer_size * sizeof(int), data_input, &err);
assert(err == CL_SUCCESS);
buffer_output = clCreateBuffer(context, CL_MEM_WRITE_ONLY | CL_MEM_USE_HOST_PTR, buffer_size * sizeof(int), data_output, &err);
assert(err == CL_SUCCESS);
err = clBuildProgram(program, 1, &device_id, options, NULL, NULL);
assert(err == CL_SUCCESS);
kernel = clCreateKernel(program, "buffer_copy", &err);
assert(err == CL_SUCCESS);
clSetKernelArg(kernel, 0, sizeof(cl_mem), &buffer_input);
clSetKernelArg(kernel, 1, sizeof(cl_mem), &buffer_output);
size_t device_max_work_group_size;
size_t global_work_size, local_work_size;
size_t preferred_work_group_size_multiple;
cl_ulong global_mem_size, max_mem_alloc_size;
clGetDeviceInfo(device_id, CL_DEVICE_GLOBAL_MEM_SIZE, sizeof(cl_ulong), &global_mem_size, NULL);
clGetDeviceInfo(device_id, CL_DEVICE_MAX_MEM_ALLOC_SIZE, sizeof(cl_ulong), &max_mem_alloc_size, NULL);
clGetDeviceInfo(device_id, CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof(size_t), &device_max_work_group_size, NULL);
std::cout << "Global device memory size: " << global_mem_size << " bytes" << std::endl;
std::cout << "Device max memory allocation size: " << max_mem_alloc_size << " bytes" << std::endl;
std::cout << "Device max work group size: " << device_max_work_group_size << std::endl;
clGetKernelWorkGroupInfo(kernel, device_id, CL_KERNEL_WORK_GROUP_SIZE, sizeof(size_t), &global_work_size, NULL);
std::cout << "global_work_size value: " << global_work_size << std::endl;
clGetKernelWorkGroupInfo(kernel, device_id, CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE, sizeof(size_t), &preferred_work_group_size_multiple, NULL);
local_work_size = global_work_size / preferred_work_group_size_multiple;
std::cout << "local_work_size value: " << local_work_size << std::endl;
cl_event events[2];
err = clEnqueueNDRangeKernel(cmd_queue, kernel, 1, NULL, &global_work_size, &local_work_size, 0, 0, &events[0]);
assert (err == CL_SUCCESS);
err = clEnqueueReadBuffer(cmd_queue, buffer_output, CL_TRUE, 0, buffer_size * sizeof(int), data_output, 0, NULL, &events[1]);
assert (err == CL_SUCCESS);
err = clWaitForEvents(2, events);
assert (err == CL_SUCCESS);
for (size_t index = 0; index < buffer_size; ++index)
{
if (data_input[index] != data_output[index])
{
std::cerr << "Error, values differ (at index " << index << ")." << std::endl;
break;
}
else
{
//std::cout << "data_input[index] =\t" << data_input[index] << std::endl;
//std::cout << "data_output[index] =\t" << data_output[index] << std::endl;
}
}
cl_ulong time_start, time_end;
double total_time;
clGetEventProfilingInfo(events[0], CL_PROFILING_COMMAND_START, sizeof(time_start), &time_start, NULL);
clGetEventProfilingInfo(events[1], CL_PROFILING_COMMAND_END, sizeof(time_end), &time_end, NULL);
total_time = time_end - time_start;
std::cout << "Execution time in milliseconds: " << (total_time / 1000000.0) << " ms" << std::endl;
clReleaseKernel(kernel);
clReleaseProgram(program);
clReleaseMemObject(buffer_input);
clReleaseMemObject(buffer_output);
clReleaseCommandQueue(cmd_queue);
clReleaseContext(context);
}
А вот мое ядро OpenCL:
__kernel void buffer_copy(__global int* input, __global int* output)
{
int id = get_global_id(0);
output[id] = input[id];
}
Прямо сейчас я просто пытаюсь заставить это работать, не оптимизируя это. И я думаю, что я пропускаю хорошие моменты здесь и там, но я не могу их поймать. На мой взгляд, я путаю флаги clCreateBuffer.
Не могли бы вы, ребята, просветить меня и помочь мне в этом?
РЕДАКТИРОВАТЬ: обновленный код + новая информация!
Кажется, что значения хорошо вставлены, но только в соответствии с размером рабочей группы ядра: CL_DEVICE_MAX_WORK_GROUP_SIZE возвращает 1024, а CL_KERNEL_WORK_GROUP_SIZE также возвращает 1024 (что также странно). Таким образом, первые 1024 целых числа моего массива хорошо скопированы / вставлены, но после этого они больше не работают. Чтобы убедиться в этом, я вручную установил для global_work_group_size значение 32, снова запустил свою программу и затем правильно вставил только первые 32 целых числа. Я действительно не понимаю, что здесь происходит.
Я думаю, что смог заставить его работать как для моего ноутбука, так и для платы i.MX6q.
Вот код, который работает:
void buffer_copy(char* kernelfile)
{
cl_platform_id platform_id;
cl_device_id device_id;
cl_context context;
cl_command_queue cmd_queue;
cl_program program;
// Retrieving all the OpenCL data needed
// to start the performance test
platform_id = get_platform();
device_id = get_device(platform_id);
context = get_context(platform_id, device_id);
cmd_queue = get_command_queue(context, device_id);
program = get_program(context, kernelfile);
cl_mem buffer_input, buffer_output;
size_t buffer_width = 640, buffer_height = 480;
size_t buffer_size = buffer_width * buffer_height;
cl_kernel kernel;
cl_int err = 0;
char* options = "-Werror -cl-std=CL1.1";
int data_input[buffer_size];
int data_output[buffer_size];
// Assigning random values in the data_input array and
// initializing the data_output array to zero-values
srand(time(NULL));
for (size_t index = 0; index < buffer_size; ++index)
{
data_input[index] = rand();
data_output[index] = 0;
}
// Creating OpenCL buffers
buffer_input = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, buffer_size * sizeof(int), data_input, &err);
assert(err == CL_SUCCESS);
buffer_output = clCreateBuffer(context, CL_MEM_WRITE_ONLY | CL_MEM_USE_HOST_PTR, buffer_size * sizeof(int), data_output, &err);
assert(err == CL_SUCCESS);
err = clBuildProgram(program, 1, &device_id, options, NULL, NULL);
assert(err == CL_SUCCESS);
kernel = clCreateKernel(program, "buffer_copy", &err);
assert(err == CL_SUCCESS);
clSetKernelArg(kernel, 0, sizeof(cl_mem), &buffer_input);
clSetKernelArg(kernel, 1, sizeof(cl_mem), &buffer_output);
cl_ulong global_mem_size = 0, max_mem_alloc_size = 0;
size_t device_max_work_group_size = 0;
size_t kernel_work_group_size = 0;
size_t preferred_work_group_size_multiple = 0;
clGetDeviceInfo(device_id, CL_DEVICE_GLOBAL_MEM_SIZE, sizeof(cl_ulong), &global_mem_size, NULL);
clGetDeviceInfo(device_id, CL_DEVICE_MAX_MEM_ALLOC_SIZE, sizeof(cl_ulong), &max_mem_alloc_size, NULL);
clGetDeviceInfo(device_id, CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof(size_t), &device_max_work_group_size, NULL);
clGetKernelWorkGroupInfo(kernel, device_id, CL_KERNEL_WORK_GROUP_SIZE, sizeof(size_t), &kernel_work_group_size, NULL);
clGetKernelWorkGroupInfo(kernel, device_id, CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE, sizeof(size_t), &preferred_work_group_size_multiple, NULL);
std::cout << "CL_DEVICE_GLOBAL_MEM_SIZE : " << global_mem_size << " bytes" << std::endl;
std::cout << "CL_DEVICE_MAX_MEM_ALLOC_SIZE : " << max_mem_alloc_size << " bytes" << std::endl;
std::cout << "CL_DEVICE_MAX_WORK_GROUP_SIZE : " << device_max_work_group_size << std::endl;
std::cout << "CL_KERNEL_WORK_GROUP_SIZE : " << kernel_work_group_size << std::endl;
std::cout << "CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE : " << preferred_work_group_size_multiple << std::endl;
cl_event events[2];
err = clEnqueueNDRangeKernel(cmd_queue, kernel, 1, NULL, &buffer_size, &kernel_work_group_size, 0, NULL, &events[0]);
assert (err == CL_SUCCESS);
err = clEnqueueReadBuffer(cmd_queue, buffer_output, CL_TRUE, 0, buffer_size * sizeof(int), data_output, 1, &events[0], &events[1]);
assert (err == CL_SUCCESS);
err = clWaitForEvents(2, events);
assert (err == CL_SUCCESS);
for (size_t index = 0; index < buffer_size; ++index)
{
if (data_input[index] != data_output[index])
{
std::cerr << "Error, values differ (at index " << index << ")." << std::endl;
break;
}
}
cl_ulong time_start, time_end;
double total_time;
clGetEventProfilingInfo(events[0], CL_PROFILING_COMMAND_START, sizeof(time_start), &time_start, NULL);
clGetEventProfilingInfo(events[0], CL_PROFILING_COMMAND_END, sizeof(time_end), &time_end, NULL);
total_time = time_end - time_start;
std::cout << "clEnqueueNDRangeKernel execution time in milliseconds: " << (total_time / 1000000.0) << " ms" << std::endl;
clGetEventProfilingInfo(events[1], CL_PROFILING_COMMAND_START, sizeof(time_start), &time_start, NULL);
clGetEventProfilingInfo(events[1], CL_PROFILING_COMMAND_END, sizeof(time_end), &time_end, NULL);
total_time = time_end - time_start;
std::cout << "clEnqueueReadBuffer execution time in milliseconds: " << (total_time / 1000000.0) << " ms" << std::endl;
clReleaseKernel(kernel);
clReleaseProgram(program);
clReleaseMemObject(buffer_input);
clReleaseMemObject(buffer_output);
clReleaseCommandQueue(cmd_queue);
clReleaseContext(context);
}
Итак, как вы можете видеть, я просто копирую 640 * 480 (307200) целых чисел из одного массива в другой, используя OpenCL 1.1 EP.
Я выделил оба буфера памяти со стороны хоста и велел OpenCL использовать их через указатели хоста (то есть нет memcpy, если я прав).
Вот вывод с моего ноутбука (работает на GeForce GTX 765m):
CL_DEVICE_GLOBAL_MEM_SIZE : 2094923776 bytes
CL_DEVICE_MAX_MEM_ALLOC_SIZE : 523730944 bytes
CL_DEVICE_MAX_WORK_GROUP_SIZE : 1024
CL_KERNEL_WORK_GROUP_SIZE : 1024
CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE : 32
clEnqueueNDRangeKernel execution time in milliseconds: 0.061856 ms
clEnqueueReadBuffer execution time in milliseconds: 0.100544 ms
Вот вывод из SoM i.MX6q (работает на GPU Vivante GC2000):
CL_DEVICE_GLOBAL_MEM_SIZE : 67108864 bytes
CL_DEVICE_MAX_MEM_ALLOC_SIZE : 33554432 bytes
CL_DEVICE_MAX_WORK_GROUP_SIZE : 1024
CL_KERNEL_WORK_GROUP_SIZE : 176
CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE : 16
clEnqueueNDRangeKernel execution time in milliseconds: 4.463 ms
clEnqueueReadBuffer execution time in milliseconds: 7.199 ms
Что случилось ?
Я думаю, что я дал неправильно global_work_size а также local_work_size значения к clEnqueueNDRangeKernel функция. Тем не менее, я до сих пор не понимаю, как они работают и как их рассчитать. Я до сих пор не понимаю разницу между этими ценностями и CL_KERNEL_WORK_GROUP_SIZE и как рассчитывается размер рабочей группы ядра компилятором OpenCL. Почему CL_KERNEL_WORK_GROUP_SIZE отличается между SoM и моим ноутбуком? Я использую то же самое ядро все же.
Любые оптимизации рекомендовать?
Если у вас есть какие-либо оптимизации, чтобы рекомендовать мне, я был бы признателен! Весь этот контекст заключается в том, чтобы научиться выполнять некоторую обработку изображений и разрабатывать алгоритмы, чтобы заставить их работать с OpenCL (поскольку я не могу использовать OpenCV в этом SoM).
Других решений пока нет …