clBuildProgram выдает исключение AccessViolationException при построении этого конкретного ядра
Это часть какого-то параллельного ядра редукции/экстремума. Я сократил его до минимального кода, который все еще получает clBuildProgram
сбой (обратите внимание, что он действительно падает, а не просто возвращает код ошибки):
EDIT : похоже, что это также происходит, когда local_value
объявляется global
вместо local
.
EDIT2 / SOLUTION : проблема заключалась в том, что существовал бесконечный цикл. Я должен был написать remaining_items >>= 1
вместо remaining_items >> 1
. Как уже было сказано в ответах, компилятор nvidia кажется не очень надежным, когда речь заходит об ошибках компиляции/оптимизации.
kernel void testkernel(local float *local_value)
{
size_t thread_id = get_local_id(0);
int remaining_items = 1024;
while (remaining_items > 1)
{
// throw away the right half of the threads
remaining_items >> 1; // <-- SPOTTED THE BUG
if (thread_id > remaining_items)
{
return;
}
// look for a greater value in the right half of the memory space
int right_index = thread_id + remaining_items;
float right_value = local_value[right_index];
if (right_value > local_value[thread_id])
{
local_value[thread_id] = right_value;
}
barrier(CLK_GLOBAL_MEM_FENCE);
}
}
Удаление строк return;
и / или local_value[thread_id] = right_value;
приводит к успешному завершению программы clbuild.
Есть ли у кого-нибудь идея, что могло бы быть проблема?
Возможно ли, что локальная (она же общая) память автоматически принимается за (WORK_GROUP_SIZE) * siezof(its_base_type)
? Это объясняет, почему он работает, когда строки, о которых я упоминал выше, удаляются.
Минимальный код хоста (совместимый с C99) для воспроизведения:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#ifdef __APPLE__
#include <OpenCL/opencl.h>
#else
#include <CL/cl.h>
#endif
#define RETURN_THROW(expression) do { cl_int ret = expression; if (ret) { printf(#expression " FAILED: %dn" , ret); exit(1); } } while (0)
#define REF_THROW(expression) do { cl_int ret; expression; if (ret) { printf(#expression " FAILED: %dn" , ret); exit(1); } } while (0)
int main(int argc, char **argv)
{
// Load the kernel source code into the array source_str
FILE *fp;
fp = fopen("testkernel.cl", "rb");
if (!fp)
{
fprintf(stderr, "Failed to load kernel.n");
exit(1);
}
fseek(fp, 0, SEEK_END);
int filesize = ftell(fp);
rewind(fp);
char *source_str = (char*)calloc(filesize, sizeof(char));
size_t bytes_read = fread(source_str, 1, filesize, fp);
source_str[bytes_read] = 0;
fclose(fp);
// Get platform information
cl_uint num_platforms;
RETURN_THROW(clGetPlatformIDs(0, NULL, &num_platforms));
cl_platform_id *platform_ids = (cl_platform_id *)calloc(num_platforms, sizeof(cl_platform_id));
RETURN_THROW(clGetPlatformIDs(num_platforms, platform_ids, NULL));
cl_device_id selected_device_id = NULL;
printf("available platforms:n");
for (cl_uint i = 0; i < num_platforms; i++)
{
char platform_name[50];
RETURN_THROW(clGetPlatformInfo(platform_ids[i], CL_PLATFORM_NAME, 50, platform_name, NULL));
printf("%sn", platform_name);
// get devices for this platform
cl_uint num_devices;
RETURN_THROW(clGetDeviceIDs(platform_ids[i], CL_DEVICE_TYPE_GPU, 0, NULL, &num_devices));
cl_device_id *device_ids = (cl_device_id *)calloc(num_devices, sizeof(cl_device_id));
RETURN_THROW(clGetDeviceIDs(platform_ids[i], CL_DEVICE_TYPE_GPU, num_devices, device_ids, NULL));
// select first nvidia device
if (strstr(platform_name, "NVIDIA")) // ADAPT THIS ACCORDINGLY
{
selected_device_id = device_ids[0];
}
}
if (selected_device_id == NULL)
{
printf("No NVIDIA device foundn");
exit(1);
}
// Create an OpenCL context
cl_context context;
REF_THROW(context = clCreateContext(NULL, 1, &selected_device_id, NULL, NULL, &ret));
// Create a program from the kernel source
cl_program program;
REF_THROW(program = clCreateProgramWithSource(context, 1, (const char **)&source_str, NULL, &ret));
// Build the program
cl_int ret = clBuildProgram(program, 1, &selected_device_id, NULL, NULL, NULL);
if (ret)
{
printf("BUILD ERRORn");
// build error - get build log and display it
size_t build_log_size;
ret = clGetProgramBuildInfo(program, selected_device_id, CL_PROGRAM_BUILD_LOG, 0, NULL, &build_log_size);
char *build_log = new char[build_log_size];
ret = clGetProgramBuildInfo(program, selected_device_id, CL_PROGRAM_BUILD_LOG, build_log_size, build_log, NULL);
printf("%sn", build_log);
exit(1);
}
printf("build finished successfullyn");
return 0;
}
1 ответ:
По моему опыту компилятор nvidia не очень надежен, когда дело доходит до обработки ошибок сборки, так что у вас, вероятно, где-то есть ошибка компиляции.
Я думаю, что ваша проблема-это действительноreturn
, или, точнее, его комбинация сbarrier
. Согласно спецификации opencl о барьерах:Все рабочие элементы в рабочей группе, выполняющей ядро на процессоре необходимо выполнить эту функцию, прежде чем любой из них будет разрешено продолжить казнь за барьером. Эта функция должна встречаются все рабочие элементы в рабочей группе, выполняющей ядро.
Если барьер находится внутри условного оператора, то все рабочие элементы должны войти в onditional, если какой-либо рабочий элемент входит в условный оператор и выполняет барьер.
Если Баррер находится внутри цикла, все рабочие элементы необходимо выполнить барьер для каждой итерации цикла, прежде чем любой из них будет разрешили продолжить экзекуцию за барьером.
Поэтому я думаю, что ваша проблема в том, что вероятно, что многие потоки вернутся, прежде чем добраться до барьера, что сделает этот код недействительным. Может быть, вам стоит попробовать что-то вроде этого:
kernel void testkernel(local float *local_value) { size_t thread_id = get_local_id(0); int remaining_items = 1024; while (remaining_items > 1) { remaining_items >>= 1;// throw away the right half of the threads if (thread_id <= remaining_items) { // look for a greater value in the right half of the memory space int right_index = thread_id + remaining_items; float right_value = local_value[right_index]; if (right_value > local_value[thread_id]) local_value[thread_id] = right_value; } barrier(CLK_GLOBAL_MEM_FENCE); } }
Кроме того, как отмечалось в комментариях, он должен быть
remaining_items>>=1
вместоremaining_items>>1
, чтобы избежать создания бесконечного цикла.