这个OpenCL代码可以优化吗?

这个OpenCL代码可以优化吗?,opencl,gpgpu,pyopencl,Opencl,Gpgpu,Pyopencl,我正在为一个特殊的矩阵函数编写一段OpencL代码:对于aDx1vectorv,两个DxD矩阵a和B和一个常量c,返回1xDvectorr其中r[I]=c*sum\u/j(v[j]*a[I][j]/B][I]/code> 下面是到目前为止我所看到的,但它运行得异常缓慢。不求和的版本返回DxD矩阵的速度大约快十倍。如果这有什么区别的话,它是从PyOpenCL调用的 有什么做错了吗?它能被优化吗 #define D 1000 ... __kernel void element_mult(

我正在为一个特殊的矩阵函数编写一段OpencL代码:对于a
Dx1
vector
v
,两个
DxD
矩阵
a
B
和一个常量
c
,返回
1xD
vector
r
其中
r[I]=c*sum\u/j(v[j]*a[I][j]/B][I]/code>

下面是到目前为止我所看到的,但它运行得异常缓慢。不求和的版本返回
DxD
矩阵的速度大约快十倍。如果这有什么区别的话,它是从PyOpenCL调用的

有什么做错了吗?它能被优化吗

#define D 1000
...

   __kernel void element_mult(
      __global float *result,
      __global const float *vector,
      __global const float *matrix,
      __global const float *matrix2,
        const float factor)
      {
         int y = get_global_id(1);
         float sum = 0;
         for(int k = 0; k < D; k++)
         {
            sum += vector[k] * matrix[(y*D) + k]
            * matrix2[(y*D) + k ];
         }
         result[y] = sum * factor;
      }
#定义D 1000
...
__核空元素(
__全球浮动*结果,
__全局常量浮点*向量,
__全局常量浮点*矩阵,
__全局常量浮点*矩阵2,
常量浮动系数)
{
int y=获取全局id(1);
浮点数和=0;
对于(int k=0;k
干杯

优化#1:使向量"局部化

我在这方面的第一次传球在表现上有了不错的提高。我注意到每个向量[k]总共被读取D次,所以我将其复制到a__本地。这是可能的,因为D足够小,允许这样做。上面的内核在5870和6970 GPU上的ALU:fetch比率都非常糟糕,都是0.08。即使速度较慢的GPU仍在等待内存访问

   #define D 1000
    __kernel void element_mult(
    __global float *result,
    __global const float *vector,
    __global const float *matrix,
    __global const float *matrix2,
    const float factor)
    {
        int y = get_global_id(0);
        float sum = 0;

        __local float vectCopy[D];
        int ls = get_local_size(0);
        int lid = get_local_id(0);
        for(int i=0;i<D;i+=ls){
            vectCopy[i+lid] = vector[i+lid];
        }
        mem_fence(CLK_LOCAL_MEM_FENCE);

        for(int k = 0; k < D; k++)
        {
            sum += vectCopy[k] * matrix[(y*D) + k] * matrix2[(y*D) + k ];
        }
        result[y] = sum * factor;
    }
#定义D 1000
__核空元素(
__全球浮动*结果,
__全局常量浮点*向量,
__全局常量浮点*矩阵,
__全局常量浮点*矩阵2,
常量浮动系数)
{
int y=获取全局id(0);
浮点数和=0;
__本地浮点向量副本[D];
int ls=获取本地大小(0);
int lid=get_local_id(0);
对于(int i=0;i1034和1261-->861)在同一卡上。低端GPU现在由ALU绑定,而不是fetch。(大于4:1的比率)

优化#2:使用整个工作组计算每个结果[y]

您必须这样做,id D要大得多(100k+)。其想法是通过使用工作组一次计算结果的单个元素来获得最佳内存访问模式。我定义了ls(本地大小)这里是64,因为它适用于我的硬件以及大多数供应商。除非更改该定义,否则从主机端使用的工作组大小必须是64。需要将其定义为将sum[ls]存储创建为_local,并且我不喜欢将可变大小的_local变量传递到我的内核中

结果:5870 ALU:fetch=0.59:1,平均值=708.6970 ALU:fetch=0.72,平均值=590。根据APP profiler,这大约是原始列表的两倍

#define D 1000
#define ls 64
__kernel void element_mult(
__global float *result,
__global const float *vector,
__global const float *matrix,
__global const float *matrix2,
const float factor)
{
    __local float vectCopy[D];
    int lid = get_local_id(0);
    for(int i=0;i<D;i+=ls){
        vectCopy[i+lid] = vector[i+lid];
    }
    mem_fence(CLK_LOCAL_MEM_FENCE);

    int ng = get_num_groups(0);
    int gid = get_group_id(0);
    int y, k;
    __local float sum[ls];
    for(y = gid; y < D; y+=ng){
        for(k = lid; k < D; k+=ls)
        {
            sum[lid] += vectCopy[k] * matrix[(y*D) + k] * matrix2[(y*D) + k ];
        }
        if(lid==0){
            result[y] = sum[0];
            for(k=1;k<ls;k++){
                result[y] += sum[k];
            }
            result[y] *= factor;
        }
        mem_fence(CLK_LOCAL_MEM_FENCE);
    }
}
#定义D 1000
#定义LS64
__核空元素(
__全球浮动*结果,
__全局常量浮点*向量,
__全局常量浮点*矩阵,
__全局常量浮点*矩阵2,
常量浮动系数)
{
__本地浮点向量副本[D];
int lid=get_local_id(0);

对于(int i=0;i)您确信yD的计算是由编译器从k循环中提取出来的,并且公共子表达式(yD)是+k在每次迭代中只计算一次?你是在NVIDIA GPU上运行的吗?@Talonmes,我不能确定。计算不是在我的计算机上本地完成的,基本上它只需要OpenCL。@trolle3000:你的代码使用的内存访问模式对NVIDIA GPU的性能非常不利,如果这恰好是目标的话t、 这个计算实际上是两个矩阵与一个向量的哈达玛积的点积?@talonmies,这是正确的。你能详细说明一下内存访问模式吗?