Performance OpenCL中模拟回归方程的最快方法

Performance OpenCL中模拟回归方程的最快方法,performance,recursion,opencl,Performance,Recursion,Opencl,我试图模拟这种类型的循环方程 si(t+1)=f[∑j Wijsj(t)+vi*输入(t)] 在OpenCL中,其中f(.)是一些非线性函数(在下面的代码中,它只是一个具有阈值th的阶跃函数),s(t)是一些外部输入。自然,我为每一席都实施了一名工人。在每个时间步骤中,每个工作人员计算上述等式的结果,然后将此结果与所有其他工作人员共享。因此,所有工人必须在同一个工作组中 我当前的OpenCL内核如下所示 __kernel void part1(__global int* s, __global

我试图模拟这种类型的循环方程

si(t+1)=f[∑j Wijsj(t)+vi*输入(t)]

在OpenCL中,其中f(.)是一些非线性函数(在下面的代码中,它只是一个具有阈值th的阶跃函数),s(t)是一些外部输入。自然,我为每一席都实施了一名工人。在每个时间步骤中,每个工作人员计算上述等式的结果,然后将此结果与所有其他工作人员共享。因此,所有工人必须在同一个工作组中

我当前的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);
        }
    }
\uuuu内核无效部分1(\uuu全局整型*s、\uu全局浮点*W、\uu全局浮点*Th、\uu全局浮点*V、\uu全局浮点*input、int N、int T)
{
无符号整数i=获取全局id(0);
浮点数=0;
浮点数v=v[i];
浮点数th=th[i];
for(int t=0;t=th){
s[i]=1;
}否则{
s[i]=0;
}
屏障(CLK_GLOBAL_MEM_围栏);
}
}
不幸的是,这段代码实际上比同等的C语言实现慢三倍。此外,我还认为工作人员数量的变化不会产生巨大的差异(因为新工作人员坐在与其他工作人员并行运行的新线程上),但实际上,处理时间会随着工作人员数量的增加而线性增加。瓶颈似乎是第一道障碍后的书写操作。消除此操作(但保留屏障)可将处理时间缩短25倍,并消除线性相关性

我是OpenCL的新手,如果能帮助我加快代码的速度,我将不胜感激

提前多谢!
Blue2script

正如我在评论中所说的,访问全局内存很慢。通常,硬件通过在同一计算单元上运行多个子线程来隐藏延迟。我指的是在英伟达LINO和AMD的波前中的呼叫扭曲。通常一个工作组由几个子组组成

因此,与此同时,一个子组等待从全局内存接收数据,另一个子组已经拥有所有必要的资源,可以运行。当运行的一个由于需要从全局内存读/写数据而停止时,另一个可以开始运行,以此类推

但是,在您的情况下,由于障碍的存在,所有子组中的所有工作人员在能够继续计算之前,都必须将其他工作人员写入内存(障碍位于工作组级别)。因此,延迟正好击中了你的脸:)

现在,改进实现的一种方法是使用本地内存,这一次是在本地内存级别设置一个屏障(带有标志CLK_local_MEM_FENCE)。我刚才解释的原理仍然适用,但访问本地内存要快得多

据我所知,您的代码(很可能我没有掌握所有的细节),您的s数组有N个元素,我猜您也有N个worker。因此,您创建一个由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?)
    }
内核无效部分1(全局整数*s,全局浮点*W,全局浮点*Th,全局浮点*V,全局浮点*input,局部整数*local_s,int N,int T)
{
无符号整数i=获取全局id(0);
unsigned int local_i=get_local_id(0);
浮点数=0;
浮点数v=v[i];
浮点数th=th[i];
//从全局提取到本地,并在计算前同步
本地_s[local_i]=s[i];
屏障(CLK_本地_MEM_围栏);
for(int t=0;t=th){
局部_s[i]=1;
}否则{
局部_s[i]=0;
}
屏障(CLK_本地_MEM_围栏);
}
//如有必要,向global写入一些内容(可能是最后计算的s?)
}
现在我必须警告你:

  • 我可能完全误解了你的需要:)
  • 我刚在键入此答案时编辑了您的代码,所以很可能存在打字错误之类的问题
  • 即使使用本地内存,有这么多障碍仍然可能使opencl版本比C版本慢
请注意,我删除了前导,因为它们不是必需的,而且在我看来更容易阅读


编辑:关于您对CLK\U本地成员围栏与CLK\U全球成员围栏的评论。屏障始终应用于工作组级别,因此工作组中的所有员工都必须达到该屏障。作为参数给出的标志表示内存访问。当标志为CLK_GLOBAL_MEM_FENCE时,意味着在任何工作程序可以继续运行下一个语句之前,必须由每个工作程序完成有关全局内存的每个读/写操作。这与CLK_LOCAL_MEM_FENCE标志完全相同,但适用于本地内存。

您能再解释一下j号代表什么吗?在你写的公式中+si(t),但在代码中你有*s[j]。这件衣服如何搭配?我很想知道辅助工x如何需要辅助工y计算的s值。移除两个屏障之间的操作时观察到的加速可以通过全局内存中的读写速度非常慢来解释。亲爱的队长:显然,感谢您的评论!事实上,我没有正确地将等式与代码对齐。x是s。我纠正了这个。但是,这种减速是否仅仅是由于对全局内存的写入访问?即使我在CPU上运行代码,我也会遇到这种减速。不是吗