OpenCL 奇怪的行为
OpenCL strange behavior
美好的一天,
我想我已尽一切努力找出问题所在,但我做不到。我有以下主机代码:
cl_mem cl_distances = clCreateBuffer(context, CL_MEM_READ_WRITE, 2 * sizeof(cl_uint), NULL, NULL);
clSetKernelArg(kernel, 0, sizeof(cl_mem), &cl_distances);
cl_event event;
clEnqueueNDRangeKernel(command_queue, kernel, 1, NULL, &global_workers, &local_workers, 0, NULL, &event);
clWaitForEvents(1, &event);
对于设备:
__kernel void walk(__global uint *distance_results)
{
uint global_size = get_global_size(0);
uint local_size = get_local_size(0);
uint global_id = get_global_id(0);
uint group_id = get_group_id(0);
uint local_id = get_local_id(0);
for (uint step = 0; step < 500; step++) {
if (local_id == 0) {
distance_results[group_id] = 0;
}
barrier(CLK_LOCAL_MEM_FENCE);
for (uint n = global_id; n < 1000; n += global_size) {
if (local_id == 0) {
atomic_add(&distance_results[group_id], 1);
}
}
barrier(CLK_GLOBAL_MEM_FENCE);
if (global_id == 0) {
for (uint i = 0; i < (global_size / local_size); i++) {
printf("step: %d; group: %d; data: %d\n", step, i, distance_results[i]);
}
}
barrier(CLK_GLOBAL_MEM_FENCE);
}
}
因此,在每个 "step" 处,我只需将每组的距离 [group_id] 加一个 1 1000 次。然后我只是从 global_id == 1 的线程中读取结果。
在每一步我都应该有以下文本:
step: 59; group: 0; data: 500
step: 59; group: 1; data: 500
但实际上有很多字符串数据错误:
step: 4; group: 0; data: 500
step: 4; group: 1; data: 210
step: 5; group: 0; data: 500
step: 5; group: 1; data: 214
如果我将 global_workers 设置为 1,将 local_workers 设置为 1,那么一切正常。但是,如果我将 global_workers 设置为 2,将 local_workers 设置为 1,那么我就会出现这种奇怪的行为。
您知道为什么会发生这种情况吗?
这里发生了一些事情,但我认为核心问题来自对 OpenCL 的一个非常普遍的误解。此调用:
barrier(CLK_GLOBAL_MEM_FENCE);
这不是全球性障碍。它是具有全局内存栅栏的局部屏障。换句话说,它仍然只在单个工作组中的工作项之间同步,而不是在其他工作组中的工作项之间。
打印结果的代码中的循环将只有工作组 0 的正确值,因为它在工作组 0 中只有 运行。如果您真的希望此代码工作,循环打印结果必须在单独的 NDRange 中,并在 NDRange 之间进行适当的同步。
内存栅栏只控制将哪些类型的内存写入提交到内存。在这种情况下,您需要对两者进行全局隔离,因为您试图隔离全局内存写入,而不是本地内存写入。
美好的一天,
我想我已尽一切努力找出问题所在,但我做不到。我有以下主机代码:
cl_mem cl_distances = clCreateBuffer(context, CL_MEM_READ_WRITE, 2 * sizeof(cl_uint), NULL, NULL);
clSetKernelArg(kernel, 0, sizeof(cl_mem), &cl_distances);
cl_event event;
clEnqueueNDRangeKernel(command_queue, kernel, 1, NULL, &global_workers, &local_workers, 0, NULL, &event);
clWaitForEvents(1, &event);
对于设备:
__kernel void walk(__global uint *distance_results)
{
uint global_size = get_global_size(0);
uint local_size = get_local_size(0);
uint global_id = get_global_id(0);
uint group_id = get_group_id(0);
uint local_id = get_local_id(0);
for (uint step = 0; step < 500; step++) {
if (local_id == 0) {
distance_results[group_id] = 0;
}
barrier(CLK_LOCAL_MEM_FENCE);
for (uint n = global_id; n < 1000; n += global_size) {
if (local_id == 0) {
atomic_add(&distance_results[group_id], 1);
}
}
barrier(CLK_GLOBAL_MEM_FENCE);
if (global_id == 0) {
for (uint i = 0; i < (global_size / local_size); i++) {
printf("step: %d; group: %d; data: %d\n", step, i, distance_results[i]);
}
}
barrier(CLK_GLOBAL_MEM_FENCE);
}
}
因此,在每个 "step" 处,我只需将每组的距离 [group_id] 加一个 1 1000 次。然后我只是从 global_id == 1 的线程中读取结果。 在每一步我都应该有以下文本:
step: 59; group: 0; data: 500
step: 59; group: 1; data: 500
但实际上有很多字符串数据错误:
step: 4; group: 0; data: 500
step: 4; group: 1; data: 210
step: 5; group: 0; data: 500
step: 5; group: 1; data: 214
如果我将 global_workers 设置为 1,将 local_workers 设置为 1,那么一切正常。但是,如果我将 global_workers 设置为 2,将 local_workers 设置为 1,那么我就会出现这种奇怪的行为。
您知道为什么会发生这种情况吗?
这里发生了一些事情,但我认为核心问题来自对 OpenCL 的一个非常普遍的误解。此调用:
barrier(CLK_GLOBAL_MEM_FENCE);
这不是全球性障碍。它是具有全局内存栅栏的局部屏障。换句话说,它仍然只在单个工作组中的工作项之间同步,而不是在其他工作组中的工作项之间。
打印结果的代码中的循环将只有工作组 0 的正确值,因为它在工作组 0 中只有 运行。如果您真的希望此代码工作,循环打印结果必须在单独的 NDRange 中,并在 NDRange 之间进行适当的同步。
内存栅栏只控制将哪些类型的内存写入提交到内存。在这种情况下,您需要对两者进行全局隔离,因为您试图隔离全局内存写入,而不是本地内存写入。