首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >OpenCL中函数的梯度

OpenCL中函数的梯度
EN

Stack Overflow用户
提问于 2021-05-09 16:26:51
回答 1查看 81关注 0票数 0

我在玩OpenCL,我有一个问题,可以简化如下。我确信这是一个常见的问题,但我找不到许多引用或示例来说明这通常是如何实现的,例如,假设您有一个函数(用CStyle语法编写)

代码语言:javascript
复制
float function(float x1, float x2, float x3, float x4, float x5)
{
   return sin(x1) + x1*cos(x2) + x3*exp(-x3) + x4 + x5;
}

我还可以将此函数的梯度实现为

代码语言:javascript
复制
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兼容实现,如下所示:

代码语言:javascript
复制
/*
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);
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 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兼容的具有位掩蔽的版本:

代码语言:javascript
复制
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乘法/加法)而不进行任何分支。三角函数/除法比乘法/加法花费更多的时间,因此预计算项应尽可能少地使用。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67459984

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档