我遇到了 OpenCL 的问题,我希望有人能提示可能是什么原因。以下是该程序的一个版本,减少了问题。我有一个大小为 4000 的输入 int 数组。在我的内核中,我正在进行扫描。显然,有很好的方法可以并行执行此操作,但要重现该问题,只有一个线程在执行整个计算。在扫描之前,输入 (result_mask) 只有值 0 或 1。
__kernel void
sel_a(__global db_tuple * input,
__global int * result_mask,
__global int * result_count,
const unsigned int max_id)
{
// update mask based on input in parallel
mem_fence(CLK_GLOBAL_MEM_FENCE);
if(gid == 0)
{
int i, c = 0;
for(i = 0; i < max_id; i++)
{
if(result_mask[i]!=0)
{
c++;
result_mask[i] = 5;
}
else
{
result_mask[i] = 5;
}
}
*result_count = c;
}
}
预期的结果将是最初具有不同于 0 的值且结果掩码中只有 5 的元素的数量。然而,事实并非如此。输出如下所示:
...
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
5 5 5 5 5 5 5 5 5 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0
0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 5 5 5 5 5 5 5 5 5 5
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
...
大约在某个地方后,我得到了这个 80 个元素的块。3200 个元素。它并不总是相同的位置,但它总是相同数量的元素 - 80。它变得更加奇怪 - 如果我将第一行更改为 if(gid == 2000) 问题就消失了。但是,在玩弄了线程 id 之后,我得出的结论是问题并没有消失,它只是移动了。使用线程 1425,我得到了一半的问题,当我得到它时,错误块位于数组的末尾。因此我假设,当我没有 0 和 1 时,该块已经“移动”得更远了。更令人兴奋的是——当我将输入大小增加到 5000 时,输出完全由 0 组成。此外,以下代码将不起作用:
if(gid == 0)
{
int i, c = 0;
for(i = 0; i < max_id; i++)
{
if(result_mask[i]!=0)
{
c++;
result_mask[i] = 5;
}
else
{
result_mask[i] = 5;
}
}
*result_count = c;
}
if(gid == 3999)
{
int i, c = 0;
for(i = 0; i < max_id; i++)
{
if(result_mask[i]!=0)
{
c++;
result_mask[i] = 5;
}
else
{
result_mask[i] = 5;
}
}
*result_count = c;
}
而只有
if(gid == 3999)
{
int i, c = 0;
for(i = 0; i < max_id; i++)
{
if(result_mask[i]!=0)
{
c++;
result_mask[i] = 5;
}
else
{
result_mask[i] = 5;
}
}
*result_count = c;
}
将起作用(同样,可能有更大的输入,它可能不起作用)。以下是该设备的一些详细信息:
Device name: GeForce 9600M GT
Device vendor: NVIDIA
Clock frequency: 1250 MHz
Max compute units: 4
Global memory size: 256 MB
Local memory size:. 16 KB
Max memory allocation size: 128 MB
Max work group size: 512
显然,我在这里错过了一些大事。我的第一个想法是这是一些内存冲突,其中 80 个元素的块被另一个“线程”覆盖。但我想得越多,它就越没有意义。
我将非常感谢任何提示!谢谢。
编辑:很抱歉回复晚了。所以我修改了代码,将其减少到最低限度以重现问题。以下是程序的c代码:
#include <stdio.h>
#include <stdlib.h>
#include <OpenCL/openCL.h>
#define INPUTSIZE (200)
typedef struct tag_openCL
{
cl_device_id device;
cl_context ctx;
cl_command_queue queue;
cl_program program;
} openCL;
int main(void)
{
int err;
openCL* cl_ctx = malloc(sizeof(openCL));
if(!cl_ctx)
exit(1);
err = clGetDeviceIDs(NULL, CL_DEVICE_TYPE_GPU, 1, &cl_ctx->device, NULL);
cl_ctx->ctx = clCreateContext(0, 1, &cl_ctx->device, clLogMessagesToStdoutAPPLE, NULL, &err);
cl_ctx->queue = clCreateCommandQueue(cl_ctx->ctx, cl_ctx->device, CL_QUEUE_PROFILING_ENABLE, &err);
printf("Successfully created context and queue for openCL device. \n");
/* Build program */
char * kernel_source = "__kernel void \
sel(__global int * input, \
__global int * result_mask, \
const unsigned int max_id) \
{ \
int gid = get_global_id(0); \
\
result_mask[gid] = input[gid] % 2 == 0; \
result_mask[gid] &= (input[gid] + 1) % 3 == 0; \
\
if(gid == 0) { \
int i; \
for(i = 0; i < max_id; i++) { \
if(result_mask[i]) { \
result_mask[i] = 5; \
} \
else { \
result_mask[i] = 5; \
} \
} \
} \
}";
cl_program prog = clCreateProgramWithSource(cl_ctx->ctx, 1, (const char**)&kernel_source, NULL, &err);
cl_ctx->program = prog;
err = clBuildProgram(cl_ctx->program, 0, NULL, NULL, NULL, NULL);
cl_kernel kernel = clCreateKernel(cl_ctx->program, "sel", &err);
/* create dummy input data */
int * input = calloc(sizeof(int), INPUTSIZE);
int k;
for(k = 0; k < INPUTSIZE; k++)
{
input[k] = abs((k % 5) - (k % 3))+ k % 2;
}
cl_mem source, intermediate;
unsigned int problem_size = INPUTSIZE;
source = clCreateBuffer(cl_ctx->ctx, CL_MEM_READ_WRITE, problem_size * sizeof(int), NULL, NULL);
clEnqueueWriteBuffer(cl_ctx->queue, source, CL_TRUE, 0, problem_size * sizeof(int), (void*) input, 0, NULL, NULL);
intermediate = clCreateBuffer(cl_ctx->ctx, CL_MEM_READ_WRITE, problem_size * sizeof(int), NULL, NULL);
int arg = 0;
clSetKernelArg(kernel, arg++, sizeof(cl_mem), &source);
clSetKernelArg(kernel, arg++, sizeof(cl_mem), &intermediate);
clSetKernelArg(kernel, arg++, sizeof(unsigned int), &problem_size);
size_t global_work_size = problem_size;
size_t local_work_size = 1;
clEnqueueNDRangeKernel(cl_ctx->queue, kernel, 1, NULL, &global_work_size, &local_work_size, 0, NULL, NULL);
clFinish(cl_ctx->queue);
// read results
int * result = calloc(sizeof(int), problem_size );
clEnqueueReadBuffer(cl_ctx->queue, intermediate, CL_TRUE, 0, problem_size * sizeof(int), result, 0, NULL, NULL);
clFinish(cl_ctx->queue);
int j;
for(j=1; j<=problem_size; j++)
{
printf("%i \t", result[j-1]);
if(j%10 ==0 && j>0)
printf("\n");
}
return EXIT_SUCCESS;
}
结果仍然是不确定的,我在输出中的随机位置得到 0 和 1。对于大小为 1 的本地工作组,它们位于数组的前半部分,大小为 2 - 在后半部分,大小为 4 的 200 个元素看起来不错,但对于一个问题大小为 400。此外,对于 1 的全局工作组大小,一切正常。也就是说,如果我使用两个内核 - 一个执行全局工作组大小为 [问题大小] 的并行计算,另一个执行全局工作组大小为 1 的并行计算,一切都很好。同样,我完全清楚这不是这样做的方法(运行这种顺序代码的内核),但是,我想知道为什么它不起作用,因为它看起来我错过了一些东西。
谢谢,瓦西尔