__kernel void Test(__global uchar* A, __global uchar* B, int W)
{
int i = get_global_id(0);
uchar c = 0;
for (int di=-1; di<2; ++di)
// +W%W is too loop the indeces around the array
c += A[(i+di+W)%W];
// Make sure all the values are read, before nullifying A
// The values I pass to work_group_barrier are overkill - having one of them should've been enough
work_group_barrier(CLK_LOCAL_MEM_FENCE | CLK_GLOBAL_MEM_FENCE);
// Without this nullification all the values of B are 3, as expected
A[i] = 0;
B[i] = c;
}
A
并且B
是大小为 的缓冲区W
。
在执行之前A
用所有字节填充 1,而B
用所有零填充。我想A
在被覆盖之前读入所有线程中的局部变量,在这种情况下,只是零。这不是work_group_barrier
为了什么吗?
但是当W
超过〜5000时 - 在内核执行一些操作后我开始看到2
字节B
。什么时候W
是 10000000 - 我看到数百个2
,每次都有不同的数量。
此外,有趣的是,如果我放在B[i] = c;
障碍之前 - 只有最后一个值B
is 2
,其他一切都3
符合预期。但是放在前面A[i] = 0
没有效果。
对于这个测试,在 CPU 端,我cl_command_queue
为每个新命令创建了一个新命令,并在继续之前等待最后一个命令的事件。
我还验证了在启动内核缓冲区之前,它们处于我期望的状态。
所以 CPU 端代码不对我所看到的负责。
这是一个简化的例子,我实际上是在制作一个生命游戏。当前值的下一个状态是根据上一步中附近值的状态确定的。
所以我需要读取并保存以前的值,然后编写新的值。使用双缓冲区设置是一种选择,这就是我之前的做法,但后来我想起work_group_barrier
并决定清理代码,但发现了这个问题。
我认为问题在于线程被分成组,每个组仅在前一个组完成后执行。但是我希望2
在这些组的边缘有值,但是当'sW=1000000
的索引是:2
13499 13750 14249 21249 262249 306250 315999 365249 377750 542250 617749 699499 717749 804249 846000 868499 898249 901500 907750 910250 934249 967500 987250 987999 999999
17749 21250 47499 48000 48249 82499 146250 202749 213499 270500 339000 382749 402999 524750 539249 600499 671749 690250 739500 800499 826999 899249 931249 961499 999999
12000 53750 58750 126249 138750 282250 299500 408999 449750 453749 539249 576749 581999 609000 621000 682249 715500 724250 736500 830249 889999 896999 900999 966999 972499 988749 990499 998000 999250 999999
(每个新行都是我的程序的新执行,所以它是 3 个单独的示例)
我看不到任何关于损坏值的统一位置,所以这可能不是问题。
我有一个NVidia GeForce GTX 750 Ti
,如果这可能会有所帮助。
这可能是 NVidia 错误还是我只是误解了障碍的工作原理?