我正在尝试模拟类型
的循环方程式s i (t + 1)= f [Σ j W ij s j (t) + v i * input(t)]
在OpenCL中,其中f(。)是一些非线性函数(在下面的代码中它只是一个具有阈值th的阶梯函数)而s(t)是一些外部输入。当然,我为每个x i 实现了一个worker。在每个时间步骤中,每个工人计算上面等式的结果,然后将该结果与所有其他工人共享。因此,所有工人都必须在同一个工作组中。
我目前的OpenCL内核看起来像这样
__kernel void part1(__global int* s, __global float* W, __global float* Th, __global float* V, __global float* input, int N, int T)
{
unsigned int i = get_global_id(0);
float value = 0;
float v = V[i];
float th = Th[i];
for(int t = 0; t < T; t++){
value = v*input[t];
for(int j = 0; j < N; j++){
value = value + W[i*N + j]*s[j];
}
barrier(CLK_GLOBAL_MEM_FENCE);
if (value >= th){
s[i] = 1;
} else {
s[i] = 0;
}
barrier(CLK_GLOBAL_MEM_FENCE);
}
}
不幸的是,这段代码实际上比同等的C实现慢三倍。此外,我预计工人数量的变化不会产生巨大的差异(因为新工人坐在与其他人并行运行的新线程上),但实际上处理时间随工人数量线性增加。瓶颈似乎是第一道屏障后的书写操作。消除此操作(但保留屏障)可将处理时间缩短25倍,并消除线性依赖性。
我是OpenCL的新手,我很感激为加快这段代码提供帮助!
提前多多感谢! Blue2script
答案 0 :(得分:0)
正如我在评论中所说,访问全局内存很慢。通常,硬件通过在同一计算单元上运行多个线程子组来隐藏延迟。我所指的子组是NVIDIA术语中的调用warp和AMD中的wavefronts。通常,工作组由几个子组组成。
因此,同时一个子组等待从全局存储器接收数据,另一个子组已经拥有所有必需的资源。当正在运行的一个因为需要从/向全局存储器读/写数据而停止时,另一个可以开始运行,等等。
但是,在您的情况下,由于存在障碍,所有子组中的所有工作人员都必须在其他人写入内存之前才能继续计算(屏障位于工作组级别)。因此,延迟会直接击中你:)。
现在,改进实现的一种方法是使用本地内存,这次是本地内存级别的屏障(标志为CLK_LOCAL_MEM_FENCE)。我刚才解释的原理仍然适用,但访问本地内存要快得多。
据我了解你的代码(很可能我没有得到所有的细微之处),你的数组有N个元素,我想你也有N个工作者。因此,您创建了一个包含N个元素的本地数组,并执行类似的操作:
kernel void part1(global int* s, global float* W, global float* Th, global float* V, global float* input, local int* local_s, int N, int T)
{
unsigned int i = get_global_id(0);
unsigned int local_i = get_local_id(0);
float value = 0;
float v = V[i];
float th = Th[i];
//fetch from global to local and sync before computing
local_s[local_i] = s[i];
barrier(CLK_LOCAL_MEM_FENCE);
for(int t = 0; t < T; t++){
value = v*input[t];
for(int j = 0; j < N; j++){
value = value + W[i*N + j]*local_s[j];
}
barrier(CLK_LOCAL_MEM_FENCE);
if (value >= th){
local_s[i] = 1;
} else {
local_s[i] = 0;
}
barrier(CLK_LOCAL_MEM_FENCE);
}
//If necessary write some stuff to global (maybe the last s computed?)
}
现在我要警告你:
请注意,我删除了领先__,因为它们不是必需的,在我看来更容易阅读。
编辑:关于您对CLK_LOCAL_MEM_FENCE与CLK_GLOBAL_MEM_FENCE的评论。始终在工作组级别应用障碍,因此工作组中的所有工作人员都必须达到该障碍。作为参数给出的标志指的是内存访问。当标志为CLK_GLOBAL_MEM_FENCE时,这意味着在任何工作人员可以继续运行下一个语句之前,每个工作人员必须完成关于全局存储器的每个读/写操作。这与CLK_LOCAL_MEM_FENCE标志完全相同,但对于本地存储器。