这是我的第一篇文章。我会尽量保持简短,因为我珍惜你的时间。这个社区对我来说太不可思议了。
我正在学习OpenCL,并希望从以下算法中提取一点并行性。我只会向您展示我正在处理的部分,我也尽可能地简化了部分。
1)输入:两个长度为(n)的1D数组:A,B和n的值。也值C [0],D [0]。
2)输出:两个长度为(n)的1D阵列:C,D。
C[i] = function1(C[i-1])
D[i] = function2(C[i-1],D[i-1])
所以这些是递归定义,但C&对于给定的i值,D可以并行完成(它们显然更复杂,以便有意义)。一个天真的想法是为以下内核创建两个工作项:
__kernel void test (__global float* A, __global float* B, __global float* C,
__global float* D, int n, float C0, float D0) {
int i, j=get_global_id(0);
if (j==0) {
C[0] = C0;
for (i=1;i<=n-1;i++) {
C[i] = function1(C[i-1]);
[WAIT FOR W.I. 1 TO FINISH CALCULATING D[i]];
}
return;
}
else {
D[0] = D0;
for (i=1;i<=n-1;i++) {
D[i] = function2(C[i-1],D[i-1]);
[WAIT FOR W.I. 0 TO FINISH CALCULATING C[i]];
}
return;
}
}
理想情况下,两个工作项(数字0,1)中的每一个都会进行一次初始比较,然后输入各自的循环,同步每次迭代。现在给出GPU的SIMD实现,我认为这不起作用(工作项将等待所有内核代码),但是是否可以将这种类型的工作分配给两个CPU内核并使其按预期工作?在这种情况下,障碍是什么?
答案 0 :(得分:1)
这可以在opencl中实现,但是就像其他答案所说的那样,你最多只能限制为2个线程。
应该使用包含两个工作项的单个工作组调用我的函数版本。
__kernel void test (__global float* A, __global float* B, __global float* C, __global float* D, int n, float C0, float D0)
{
int i;
int gid = get_global_id(0);
local float prevC;
local float prevD;
if (gid == 0) {
C[0] = prevC = C0;
D[0] = prevD = D0;
}
barrier(CLK_LOCAL_MEM_FENCE);
for (i=1;i<=n-1;i++) {
if(gid == 0){
C[i] = function1(prevC);
}else if (gid == 1){
D[i] = function2(prevC, prevD);
}
barrier(CLK_LOCAL_MEM_FENCE);
prevC = C[i];
prevD = D[i];
}
}
这应该在任何opencl硬件上运行。如果您不关心保存所有C和D值,只需在两个浮点数而不是整个列表中返回prevC和prevD。由于为所有读取和写入中间值而坚持较低的高速缓存级别(即本地存储器),这也会使其快得多。本地内存增强也应该适用于所有opencl硬件。
那么有必要在GPU上运行吗?不是为了并行性。你被困在2个线程中。但是如果你不需要返回C和D的所有值,你可能会看到显着的加速,因为GPU的内存要快得多。
所有这些都假定function1和function2并不过分复杂。如果是这样,只需坚持使用CPU - 可能还有其他多处理技术,如OpenMP。
答案 1 :(得分:0)
你的情况下的依赖是完全线性/递归的(我需要i-1)。甚至不像其他问题(减少,总和,排序等)的对数。因此,这个问题不适合SIMD设备。
你能做的最好就是在CPU中采用双线程方法。线程1将&#34;生成&#34;线程2的数据(C值)。
一种非常天真的方法,例如:
Thread 1:
for(){
ProcessC(i);
atomic_inc(counter); //This function should unlock
}
Thread 2:
for(){
atomic_dec(counter); //This function should lock
ProcessD(i);
}
例如,可以使用计数信号量来实现atomic_inc
和atomic_dec
。