我在玩OpenCL,我有一个问题,可以简化如下。我确信这是一个常见的问题,但我找不到许多引用或示例来说明这通常是如何实现的,例如,假设您有一个函数(用CStyle语法编写)
float function(float x1, float x2, float x3, float x4, float x5)
{
return sin(x1) + x1*cos(x2) + x3*exp(-x3) + x4 + x5;
}我还可以将此函数的梯度实现为
void functionGradient(float x1, float x2, float x3, float x4, float x5, float gradient[])
{
gradient[0] = cos(x1) + cos(x2);
gradient[1] = -sin(x2);
gradient[2] = exp(-x3) - x3*exp(-x3);
gradient[3] = 1.0f;
gradient[4] = 1.0f;
}现在我正在考虑实现一个OpenCL C内核函数,它可以做同样的事情,因为我想加快速度。我想要做的唯一方法是给每个工作单元分配梯度的一个组件,但是接下来我需要在代码中放置一组if语句,以确定哪个工作单元正在计算哪个组件,这在一般情况下是不太好的,因为发散。
因此,这是一个问题,这类问题一般如何处理?例如,我知道GPU上的渐变下降实现,例如,请参阅机器学习和反向传播。因此,我想知道通常如何避免代码中的差异。
对建议的跟进
我正在考虑一个可能的SIMD兼容实现,如下所示:
/*
Pseudo OpenCL-C code
here weight is a 5x5 array containing weights in {0,1} masking the relevant
computation
*/
__kernel void functionGradient(float x1, float x2, float x3, float x4, float x5, __global float* weight,__global* float gradient)
{
size_t threadId = get_global_id(0);
gradient[threadId] =
weight[5*threadId]*(cos(x1) + cos(x2)) +
weight[5*threadId + 1]*(-sin(x2)) +
weight[5*threadId + 2]*(exp(-x3) - x3*exp(x3)) +
weight[5*threadId + 3] + weight[5*threadId + 4];
barrier(CLK_GLOBAL_MEM_FENCE);
}发布于 2021-05-09 20:11:03
如果梯度函数只有5个组件,那么以一个线程处理一个组件的方式并行它是没有意义的。正如您所提到的,如果每个组件的数学结构不同(多个指令-多个数据,MIMD),GPU并行化就无法工作。
但是,如果您需要在100 k不同的坐标下计算5维梯度,那么每个线程将为每个坐标完成所有5个组件,并行化将有效地工作。
在反向传播示例中,有一个具有数千维的梯度函数。在这种情况下,您确实将梯度函数本身并行化,以便一个线程计算梯度的一个组件。但是,在这种情况下,所有梯度分量都具有相同的数学结构(全局内存中不同的加权因子),因此不需要分支。每个梯度分量是相同的方程,具有不同的数字(单指令多个数据,SIMD)。GPU被设计为只处理SIMD;这也是为什么与CPU相比,GPU具有如此高的能效(~30 3TFLOPs@300 W)(它可以处理MIMD,~2-3 3TFLOPs@150 W)。
最后,请注意反向传播/神经网络是专门设计为SIMD的。并不是每一个新的算法都能以这种方式并行化。
回到你的5维梯度例子:有方法使它不分支SIMD兼容。具体而言,位掩蔽:您将计算两个余弦(对于组件1表示正弦通过余弦)和一个指数,并把所有的项加起来,在每个前面加上一个因子。你不需要的条件,乘以0。最后,这些因素是组件ID的函数。然而,正如上面提到的,只有当您有数千到数百万个维度时,这才有意义。
编辑:这里是与SIMD兼容的具有位掩蔽的版本:
kernel void functionGradient(const global float x1, const global float x2, const global float x3, const global float x4, const global float x5, global float* gradient) {
const float gid = get_global_id(0);
const float cosx1 = cos(x1);
const float cosx2 = cos((gid!=1)*x2+(gid==1)*3.1415927f);
const float expmx3 = exp(-x3);
gradient[gid] = (gid==0)*cosx1 + (gid<=1)*cosx2 + (gid==2)*(expmx3-x3*expmx3) + (gid>=3);
}请注意,没有额外的全局/本地内存访问,所有(相互排斥的)加权因子都是全局ID的函数。每个线程计算完全相同的东西(2 cos,1 exp和fes乘法/加法)而不进行任何分支。三角函数/除法比乘法/加法花费更多的时间,因此预计算项应尽可能少地使用。
https://stackoverflow.com/questions/67459984
复制相似问题