Chinaunix

标题: 行列分解 2D FFT OpenCL 并行化 [打印本页]

作者: zhujiang73    时间: 2011-01-13 12:37
标题: 行列分解 2D FFT OpenCL 并行化
本帖最后由 zhujiang73 于 2011-01-13 12:42 编辑

搞了大约两周,其间大部分时间用在补习有关理论上了,上学时学的高数忘了不少了。

先把这个算法:

是这样的?  google  到这个:
zhujiang73 发表于 2010-12-29 10:42



改一下:
  1.         for(int i = 0; i < mLen / 2; i++)
  2.         {
  3.                 float dAngle = -2 * FPI * i / mLen;
  4.                 W[i].real = (float)cos(dAngle);
  5.                 W[i].image = (float)sin(dAngle);
  6.         }

  7.         reverse(a,b,mLen,M);
复制代码
  1. void reverse(int *a, int *b, int len, int M)
  2. {
  3.     int i,j;

  4.     for(i=0; i<M; i++)
  5.     {
  6.         a[i] = 0;
  7.     }

  8.     b[0] = 0;
  9.     for(i=1; i<len; i++)
  10.     {
  11.         j = 0;
  12.         while(a[j] != 0)
  13.         {
  14.             a[j] = 0;
  15.             j++;
  16.         }

  17.         a[j] = 1;
  18.         b[i] = 0;
  19.         for(j=0; j<M; j++)
  20.         {
  21.             b[i] = b[i]+a[j]*(int)pow(2,M-1-j);
  22.         }
  23.     }
  24. }
复制代码
  1. void fft(zcomplex *A, zcomplex *W, int fft_nLen, int fft_M)
  2. {
  3.         int p,dist;
  4.         zcomplex C,B;

  5.         for(int lev=1; lev<=fft_M; lev++)
  6.         {
  7.                 dist = 1<<(lev-1);
  8.                 for(int t=0; t<dist; t++)
  9.                 {
  10.                         p = t*(1<<(fft_M-lev));

  11.                         for(int i=t; i<fft_nLen; i+=1<<lev)
  12.                         {
  13.                                 B = Mul(A[i+dist],W[p]);
  14.                                 C = Add(A[i],B);
  15.                                 A[i+dist] = Sub(A[i],B);
  16.                                 A[i].real = C.real;
  17.                                 A[i].image = C.image;
  18.                         }

  19.                 }
  20.         }
  21. }
复制代码
然后再用 OpenCL 并行化,同时计算多行的 FFT,计算结果转置,再算一遍再转置回来就行了:
  1. __kernel void fft4_nx( __global float4 *A,  __global const float2 *W,  const int nlen, const int fft_M)
  2. {
  3.         float4  f4_b;

  4.         int i;
  5.         int lev,dist,p,t;
  6.         
  7.         float2  w_l;
  8.         float4  f4_tmp;
  9.         float4  f4_tmp01;
  10.         float4  f4_tmp02;
  11.         
  12.         float4   a4,b4;
  13.         
  14.         int  y = get_global_id(0);

  15.         for(lev=1; lev<=fft_M; lev++)
  16.         {
  17.                 dist = 1<<(lev-1);
  18.                 for(t=0; t<dist; t++)
  19.                 {
  20.                         p = t*(1<<(fft_M-lev));

  21.                         for(i=t; i<nlen; i+=1<<lev)
  22.                         {
  23.                                 w_l = W[p];
  24.                                 b4 = A[y*nlen+i+dist];
  25.                                 f4_b.x = b4.x*w_l.x - b4.y*w_l.y;
  26.                                 f4_b.y = b4.x*w_l.y + b4.y*w_l.x;

  27.                                 f4_b.z = b4.z*W[p].x - b4.w*W[p].y;
  28.                                 f4_b.w = b4.z*W[p].y + b4.w*W[p].x;
  29.                                 
  30.                                 a4 = A[y*nlen+i];
  31.                                 
  32.                                 A[y*nlen+i+dist] = a4 - f4_b;
  33.                                 
  34.                                 A[y*nlen+i] = a4 + f4_b;
  35.                         }

  36.                 }
  37.         }
  38. }
复制代码

作者: zhujiang73    时间: 2011-01-13 13:01
回复 1# zhujiang73


    可惜我的入门级 ATI4570 显卡不太给力,用了 GPU 的程序的计算速度和 T6670 CPU 上对应算法单线程程序速度差不多,估计如果是中高端显卡用了 GPU 的程序速度应该快很多。
作者: erlangs    时间: 2011-01-13 13:27
虽然不懂OpenCL,但这绝对是个有趣的东西
作者: erlangs    时间: 2011-01-13 13:34
回复  zhujiang73


    可惜我的入门级 ATI4570 显卡不太给力,用了 GPU 的程序的计算速度和 T6670 CP ...
zhujiang73 发表于 2011-01-13 13:01



   
怎么得知这块运算的瓶颈?
作者: zhujiang73    时间: 2011-01-13 13:56
怎么得知这块运算的瓶颈?
erlangs 发表于 2011-01-13 13:34



    FFT 的瓶颈就是那一大堆的复数运算,把多个 1D FFT 运算并行应该是可以加速的。
作者: zhujiang73    时间: 2011-01-13 14:01
虽然不懂OpenCL,但这绝对是个有趣的东西
erlangs 发表于 2011-01-13 13:27



    OpenCL 是显卡的第二职业,不打游戏时还可以算数学题。
作者: ecjtubaowp    时间: 2011-01-13 15:18
高深!
作者: wsw1wsw2    时间: 2011-01-14 09:22
任何显卡都能openCL?
作者: zhujiang73    时间: 2011-01-14 12:03
回复 1# zhujiang73


    这个 kernel 其实不太好,global 数据访问太多了,但是它比较简单,在我的 ATI4570 上能跑,AMD OpenCL SDK 里的 FFT 例子算法应该更好,但是我的显卡不支持,估计是需要 ATI5* 系列以上的显卡。{:3_201:}
作者: zhujiang73    时间: 2011-01-14 12:13
任何显卡都能openCL?
wsw1wsw2 发表于 2011-01-14 09:22



    AMD 系的显卡,推荐 ATI5* 以上的。

    NV  系的我没注意,不过 NV 搞通用计算比较早,支持得应该不错。
作者: zhujiang73    时间: 2011-01-15 15:02
本帖最后由 zhujiang73 于 2011-01-15 15:32 编辑
回复  zhujiang73


    这个 kernel 其实不太好,global 数据访问太多了,但是它比较简单,在我的 ATI ...
zhujiang73 发表于 2011-01-14 12:03




    改进一下 kernel ,开一个 __private 数组当缓存,看起来有点笨,但是真的可以加速。  {:3_189:}

    建议有  ATI5* 以上显卡的程序员试试 AMD Math Libraries ,资料上说在中高端显卡上用 OpenCL 可以轻松使并行程序提速几十倍,如果是 N 卡 NV 也有对应的库。
  1. __kernel void fft4_nx( __global const float4 *src, __global float4 *dst, __global const float2 *w, __global const int *b, const int nlen, const int m)
  2. {
  3.         __private float4  aa[2048];
  4.         
  5.         float2  w_l;
  6.         
  7.         float4  a4,b4;
  8.         
  9.         int     p,dist;
  10.         
  11.         int  y = get_global_id(0);
  12.         
  13.         for (int j=0; j<nlen; j++)
  14.         {
  15.                 aa[j] = src[y*nlen+b[j]];
  16.         }

  17.         for(int lev=1; lev<=m; lev++)
  18.         {
  19.                 dist = 1<<(lev-1);
  20.                 for(int t=0; t<dist; t++)
  21.                 {
  22.                         p = t*(1<<(m-lev));

  23.                         for(int i=t; i<nlen; i+=1<<lev)
  24.                         {
  25.                                 w_l = w[p];
  26.                                 b4 = aa[i+dist];
  27.                                 
  28.                                 a4.x = b4.x*w_l.x - b4.y*w_l.y;
  29.                                 a4.y = b4.x*w_l.y + b4.y*w_l.x;

  30.                                 a4.z = b4.z*w_l.x - b4.w*w_l.y;
  31.                                 a4.w = b4.z*w_l.y + b4.w*w_l.x;
  32.                                 
  33.                                 aa[i+dist] = aa[i] - a4;
  34.                                 
  35.                                 aa[i] = aa[i] + a4;
  36.                         }

  37.                 }
  38.         }
  39.         
  40.         for (int j=0; j<nlen; j++)
  41.         {
  42.                 dst[y*nlen+j] = aa[j];
  43.         }

  44. }
复制代码

作者: mercuryknight    时间: 2011-08-02 13:27
CPU代码简单的翻译成OpenCL kernel执行效率不会提高太多,甚至可能会下降,即便高端GPU也不一定能比同档CPU强。如果是NVIDIA架构,global memory联合访问和local memory这两块优化好了,性能提高个四五倍不是问题。AMD的GPU不太清楚,但是也应该有类似的手段。FFT这东西成熟的典范太多,想在性能上有什么突破很难。而且好的算法互相都是有借鉴的,架构也比较复杂。就像Apple的OpenCL FFT,理念上极大的借鉴了FFTW。楼主如果有兴趣,可以拿这两个库对比一下看看性能差别
作者: zhujiang73    时间: 2011-08-02 13:50
CPU代码简单的翻译成OpenCL kernel执行效率不会提高太多,甚至可能会下降,即便高端GPU也不一定能比同档CPU ...
mercuryknight 发表于 2011-08-02 13:27



    我那个老式的 ATI4570 GPU 只能做最简单的运算,以后的 GPU 应该和 CPU 共享同一个内存空间,这样就方便多了。
作者: mercuryknight    时间: 2011-08-04 01:30
本帖最后由 mercuryknight 于 2011-08-04 01:56 编辑

回复 13# zhujiang73


CPU和GPU共享內存空间最大的问题不在于技术,而在于成本。技术上现在Intel和AMD都能做到,下一步从软件上把GPU编程的复杂度简化到跟CPU编程相似也不难做到。但是要GPU跟CPU一样去访问低速的系统内存,其优势就大大削弱了。反之要把系统内存整个都换成高端显卡上的高速GDDR,成本又会成倍上升。所以在可预见的未来几年,真正高性能科学计算领域,还得是GPU归GPU,CPU归CPU。短期内只有这种架构才是性能和成本之间的最佳平衡点。两者共享内存这种架构主要还是应对中低端家用电脑,去加速一些对性能不太敏感的日常应用。

另外你说AMD SDK里的FFT例子执行不了,可能是其他原因,并不是因为你GPU的问题。只要驱动支持OpenCL,理论上所有的OpenCL程序就应该都能跑。尤其这种官方自家出品的库,对自己硬件平台应该很了解,硬件参数也都可以在runtime获取,然后动态修改kernel,不至于因为某个硬件特征就彻底挂掉了。OpenCL总共才出了1.0,1.1两个版本,据我所知还没有把特定应用限制在特定平台上的情况出现,浮点精度问题除外,不过目前的OpenCL FFT默认设置应该都是单精度的,就算因为不支持双精度而出错,也会给出明确错误信息或警告。况且FFT算法用到的都是很常规的运算,就是加减乘除,sin,cos而已,没有理由某个GPU会运行不了。如果从程序代码本身角度考虑,有可能会发现原因。就像当初Apple的OpenCL FFT在我的Mac上跑的好好的,移植到linux下就各种问题。从PC的linux上移植到超级计算机的linux上又是各种问题。别看说的好听各种标准,有些时候还是需要自己动手去改的。毕竟OpenCL还在婴儿期,各路神仙还都是各自为政,将来标准逐渐成熟就好了。
作者: zhujiang73    时间: 2011-08-05 10:02
回复  zhujiang73


CPU和GPU共享內存空间最大的问题不在于技术,而在于成本。技术上现在Intel和AMD都能 ...
mercuryknight 发表于 2011-08-04 01:30



    "只要驱动支持OpenCL,理论上所有的OpenCL程序就应该都能跑。尤其这种官方自家出品的库,对自己硬件平台应该很了解,硬件参数也都可以在runtime获取,然后动态修改kernel,不至于因为某个硬件特征就彻底挂掉了。"


    ati的4* 系列没有 OpenCL 的正式支持,看错误提示好像是什么处理单元的数目不够。




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2