Performance OpenCL矩阵乘法应该更快吗?

Performance OpenCL矩阵乘法应该更快吗?,performance,opencl,matrix-multiplication,pyopencl,Performance,Opencl,Matrix Multiplication,Pyopencl,我正在尝试学习如何使GPU优化OpenCL内核,我举了一个在本地内存中使用方块的矩阵乘法的例子。然而与numpy.dot() 我发现在的研究中,他们的加速率>200x(>1000gflops)。 我不知道我做错了什么,或者只是因为我的GPU(nvidia GTX 275)。或者是因为一些pyOpenCl开销。但我也测量了将结果从GPU复制到RAM需要多长时间,它只占矩阵乘法时间的10% #define BLOCK_SIZE 22 __kernel void matrixMul( _

我正在尝试学习如何使GPU优化OpenCL内核,我举了一个在本地内存中使用方块的矩阵乘法的例子。然而与numpy.dot()

我发现在的研究中,他们的加速率>200x(>1000gflops)。 我不知道我做错了什么,或者只是因为我的GPU(nvidia GTX 275)。或者是因为一些pyOpenCl开销。但我也测量了将结果从GPU复制到RAM需要多长时间,它只占矩阵乘法时间的10%

#define BLOCK_SIZE 22 
__kernel void matrixMul(
      __global float* Cij, 
      __global float* Aik, 
      __global float* Bkj, 
      __const int ni, 
      __const int nj,
      __const int nk
){
//   WARRNING : interchange of  i  and  j  dimension  lower the performance >2x on my nV GT275 GPU    
int gj = get_global_id(0);    int gi = get_global_id(1); 
int bj = get_group_id(0);     int bi = get_group_id(1);  // Block index
int tj = get_local_id(0);     int ti = get_local_id(1);  // Thread index
int oj = bi*BLOCK_SIZE;       int oi = bj*BLOCK_SIZE; 
float Csub =0; 
__local float As   [BLOCK_SIZE][BLOCK_SIZE];
__local float Bs   [BLOCK_SIZE][BLOCK_SIZE];
for (int ok = 0; ok < nk; ok += BLOCK_SIZE )   {
    As[ti][tj] = Aik[ nk*(gi   ) + tj + ok ];   // A[i][k]
    Bs[ti][tj] = Bkj[ nj*(ti+ok) + gj ];        // B[k][j]
    barrier(CLK_LOCAL_MEM_FENCE);
    for (int k = 0; k < BLOCK_SIZE; ++k) Csub += As[ti][k] * Bs[k][tj];
    barrier(CLK_LOCAL_MEM_FENCE);
}
Cij[ nj * ( gi ) + gj ] = Csub;
注:“Gflops”数为N^3/次,它确实包括将结果从GPU复制到主内存所需的时间,但该时间仅为总时间的百分之几,尤其是当N>1000时

也许更多的是以秒为单位的时间:

N =  220 numpy 0.003 [s] GPU 0.001 [s] load 0.001 [s] speedUp 5.000 
N =  330 numpy 0.008 [s] GPU 0.001 [s] load 0.001 [s] speedUp 7.683 
N =  440 numpy 0.017 [s] GPU 0.002 [s] load 0.001 [s] speedUp 7.565 
N =  550 numpy 0.043 [s] GPU 0.004 [s] load 0.001 [s] speedUp 11.957 
N =  660 numpy 0.055 [s] GPU 0.006 [s] load 0.002 [s] speedUp 9.298 
N =  770 numpy 0.100 [s] GPU 0.009 [s] load 0.003 [s] speedUp 10.638 
N =  880 numpy 0.125 [s] GPU 0.010 [s] load 0.000 [s] speedUp 12.097 
N =  990 numpy 0.195 [s] GPU 0.015 [s] load 0.000 [s] speedUp 12.581 
N = 1100 numpy 0.250 [s] GPU 0.031 [s] load 0.000 [s] speedUp 8.065 
N = 1210 numpy 0.328 [s] GPU 0.031 [s] load 0.000 [s] speedUp 10.581 
N = 1320 numpy 0.422 [s] GPU 0.047 [s] load 0.000 [s] speedUp 8.979
我在想也许可以通过使用
异步\u工作\u组\u复制或甚至读取\u imageui以将块复制到本地内存但是我不明白为什么我在使用基本相同的代码时会有如此大的差异,因为人们说他们有200倍的加速比????

甚至不看你的代码,让我对你的基准做一些评论。让我们忽略numpy,比较Intel CPU与Nvidia和AMD GPU的最大SP浮点/秒和DP浮点/秒

4 GHz的Intel 2600K可以实现4 GHz*(8 AVX)*(2 ILP)*(4核)=256 SP千兆次/秒。DP为一半:128 DP GFLOPs/s。几周后上市的Haswell将使这两个数字翻倍。“英特尔MKL”库在GEMM中的效率超过80%。我自己的GEMM代码在我的i7-2700上占70%,因此您向numpy提供的5gflops/s的报价很小,不公平

我不知道GTX 275能做什么,但我猜它的速度远远超过50千兆次/秒

您参考的文章比较了AMD7970。它们获得848(90%效率)DP GFlops/s和2646(70%效率)SP GFlops/s。这接近CPU性能的10倍,而不是200倍

编辑: 你对触发器的计算是错误的,应该是2.0*n^3。这仍然是近似值,但它是渐近正确的。让我解释一下

考虑一个3D点产品。它是x1*x2+y1*y2+z1*z2。这是3次乘法和2次加法。所以一个N维点积是N次乘法和(N-1)次加法。矩阵积相当于nxn点积,即n*n*n乘法和n*n*(n-1)加法。这大约是2.0*n^3次失败。所以你应该把你的Gflops/s数字翻一番

编辑: 您可能需要考虑内核时间。我使用OpenCL已经有一段时间了,但是使用C++绑定我做了类似的事情

queue = cl::CommandQueue(context, devices[device], CL_QUEUE_PROFILING_ENABLE|CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE, &err);
//other code...run kernel

time_end = clevent.getProfilingInfo<CL_PROFILING_COMMAND_END>();  
time_start = clevent.getProfilingInfo<CL_PROFILING_COMMAND_START>();
queue=cl::CommandQueue(上下文、设备[device]、cl_queue_PROFILING_ENABLE | cl_queue_OUT_OF u ORDER _EXEC_MODE_ENABLE,&err);
//其他代码…运行内核
time_end=clevent.getProfilingInfo();
time_start=clevent.getProfilingInfo();

一个好的GPU矩阵乘法不仅使用本地内存,它还将A、B和/或C块存储在寄存器中(这会导致寄存器使用率更高,占用率更低,但最终速度会更快)。这是因为GPU的寄存器比本地内存多(NVIDIA为128-256KB,而NVIDIA为48KB),并且寄存器提供ALU所能处理的带宽。

您好,谢谢,但我在ot中的问题是关于如何获得专业前沿性能sgemm/dgemm。我还想学习如何制作非常有效的OpenCL内核,我之所以选择矩阵乘法,是因为它是全局内存瓶颈的一个非常常见的例子。所以我想要一些一般的答案,当它比应该的慢几倍时,我做错了什么。我没有顶级CPU(我没有2.66 GHz的Core 2 Quad Q9450),也没有顶级GPU,也没有高度优化的MKL/LAPACK安装(只是Python中的默认numpy(x,y))。但这不是我问题的重点,我明白。但我想先修正你的比较。你引用的论文是你定义的最前沿的,你正在与它相去甚远的东西进行比较。但无论如何,我发现你的Gflops/s计算有错误。它应该是2.0*n^3。根据维基百科和其他网站的数据,GTX 275能够达到大约1000千兆次/秒(我假设SP)。您将获得大约100千兆次/秒的流量。这是10%的效率。我不知道你的效率低下在哪里。到目前为止,我是在CPU上优化的,不是GPU。谢谢。确切地说,50%的效率对我来说是可以的,但是10%似乎有点不对劲,我刚刚从nVidia SDK尝试了矩阵乘法,它的性能与我的matrixMul版本完全相同(实际上代码几乎完全相同)。。。所以我想这可能是因为pyOpenCL(要么编译器没有很好地优化,要么python warper开销太大)pyOpenCL只是将内核发送给编译它们的OpenCL驱动程序,因此pyOpenCL不应该影响内核的运行方式。这可能会影响启动开销。这就是为什么我认为您可能希望找到内核时间。我不知道如何使用pyOpenCL来实现这一点。我已经一年多没有使用pyOpenCL了。
queue = cl::CommandQueue(context, devices[device], CL_QUEUE_PROFILING_ENABLE|CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE, &err);
//other code...run kernel

time_end = clevent.getProfilingInfo<CL_PROFILING_COMMAND_END>();  
time_start = clevent.getProfilingInfo<CL_PROFILING_COMMAND_START>();