Почему я не могу правильно скопировать данные в структуре в буфер openCL cl_mem?
Хорошо, я выделил это на очень специфическую проблему.
У меня сложилось впечатление, что вы можете передавать OpenCL данные любого типа в буфер массива; целые числа, символы, ваши собственные пользовательские структуры, если они были всего лишь данными и не содержали указателей на кучу объектов, которые GPU не сможет извлечь.
Теперь я попробовал это, и я думаю, что это работает для большого массива целых, но не для моего массива структур. В частности,
cl_mem log_buffer = clCreateBuffer(context, CL_MEM_READ_WRITE,
num_elements * sizeof(int), NULL, NULL);
int* error_codes_in = (int*)malloc(num_elements * sizeof(int));
for (i = 0; i < num_elements; i++) {
error_codes_in[i] = i;
}
error = clEnqueueWriteBuffer(command_queue, log_buffer, CL_TRUE,
0, num_elements * sizeof(int), error_codes_in, 0, NULL, NULL);
это прекрасно работает, и я получаю массив чисел на GPU и могу успешно ими управлять, параллельно.
Тем не менее, когда я использую свою собственную структуру:
typedef struct {
float position[2];
float velocity[2];
float radius;
float resultant_force[2];
} ocl_element_2d_t;
(также определяется в ядре, как)
const char* kernel_string =
"typedef struct { float position[2]; float velocity[2]; float radius; float resultant_force[2]; } ocl_element_2d_t;"...
и я использую тот же / очень похожий код для записи в GPU-версию моего массива struct:
cl_mem gpu_buffer = clCreateBuffer(context, CL_MEM_READ_WRITE,
num_elements * sizeof(ocl_element_2d_t), NULL, NULL);
error = clEnqueueWriteBuffer(command_queue, (cl_mem)gpu_buffer, CL_TRUE,
0, num_elements * sizeof(ocl_element_2d_t), host_buffer, 0, NULL, NULL);
Я получаю пустые значения в графическом процессоре, а иногда и мусор (три или четыре значения в 350) для всех значений с плавающей точкой внутри структуры. Оба возвращаемых значения CL_SUCCESS
,
Любые предложения относительно того, где я иду не так? Я думал только о том, что компилятор GPU создает структуру в памяти с различными пробелами, и поскольку метод copy игнорирует внутреннюю структуру элементов и просто копирует непрерывный блок RAM, вы в конечном итоге получаете несоответствия и возможные элементы, находящиеся вне фазы. Возможно ли, что моя ОС является 64-разрядной (OS X Lion) на i7 (четырехъядерном процессоре), а мой графический процессор работает на 32-разрядной версии, и в этом проблема? Это ATI Radeon HD 5750, который не имеет поддержки двойной точности и претендует на использование 128-битной шины (что может иметь или не иметь значение, я не знаю точно, что означает этот материал).
Есть ли правильный способ сделать это? Я собираюсь пройти весь FORTRAN и иметь 7 разных массивов, каждый со своим собственным аргументом ядра, для разных свойств в структуре?
2 ответа
Все благодарности @0A0D за подозрение в моих выборочных примерах кода. Проблема была в том, что я не смог правильно инициализировать структуры.
Я оправдываюсь просто тем, что я привык работать со структурными указателями, а не со структурами, и поэтому писать
ocl_element_2d_t element = host_buffer[i];
element.position[0] = 1.2;
element.position[1] = 5.7;
был стандартный способ добавить свойства к объекту. После быстрого ознакомления со структурами я наткнулся на очень простое руководство по C, http://www.asic-world.com/scripting/structs_c.html которое указывало, что
struct_instance = other_struct_instance;
выполняет глубокую копию, а не справочную копию.
Таким образом, когда я тестировал вывод из локальной переменной структуры, ожидаемое мной значение было там, но все же еще далеко от массива host_buffer
,
Здесь, вероятно, есть два урока:
- Убедитесь, что вы публикуете весь соответствующий код, когда задаете вопрос Stackru, включая всю инициализацию, чтобы можно было рассмотреть все возможные проблемы.
- При использовании библиотеки, особенно такой сложной, как OpenCL, не думайте, что ее разработчики допустят глупые ошибки - они почти наверняка ваши собственные!
Я не уверен, как ваши компиляторы выравнивают вашу структуру 'float', но используя gcc вы можете попробовать:
#pragma pack(1)
чтобы выровнять его без пробелов.
Чтобы отменить эту упаковку используйте:
#pragma pack()
Также вы можете попытаться просто переставить участников, вот так:
typedef struct {
float position[2];
float velocity[2];
float resultant_force[2];
float radius;
} ocl_element_2d_t;