OpenCL FFT实现 - 无意义的输出数据 - 推测正确的算法

时间:2013-06-16 21:12:45

标签: c++ scipy opencl gpu fft

我正在创建一个FFT信号互相关模块,(利用循环卷积定理等)。我想确认以下方案将确保FFT蝶形计算的特定递归级别在下一级别开始之前完成,并且包含数据的缓冲区完全写入/完成。因此,循环相关/卷积涉及FFT,矢量方内积,然后是IFFT。

由于这种方案,我没有内核按位反转索引顺序对数据进行排序。正向FFT内核产生位反转FFT,在内积之后,IFFT只使用该结果来计算自然顺序解决方案。

我应该提到我有多个GPU。

无论如何,这里是每个FFT / IFFT的伪代码表示,(访问/操作算法是等价的,除了共轭旋转因子,标准化内核后来出现:

    for numDevices:
        data -> buffers
        buffers -> kernel arguments

    for fftRecursionLevels:
        for numDevices:
            recursionLevel -> kernel arguments
            deviceCommandQueue -> enqueueNDRangeKernel
            deviceCommandQueue ->  flush()

        for numDevices:
            deviceCommandQueue -> finish()

(编辑:方法是Radix-2 DIT,如果不清楚,抱歉。)

我可以逃脱吗?据我所知,finish()是一个阻塞函数,并且直到每个内核在其全局范围内完成计算时才会完成循环,(此处为fftSize / 2,请参阅有关Radix-2蝶形运算的任何文献),以及,对于奖励积分,由于flush()而某些内核正在执行,而我正在将剩余的内核排入队列。

总的来说,我正在使用openCL / c ++为这个特定软件提供一些时髦/垃圾结果。 我已经在python中实现了完整的数据管道,(如果你愿意,算法是“拓扑等价的”,显然没有主机< - >设备缓冲区/指令或设备端操作w / python方法),并模拟内核应该如何运行,当我使用scipy.fftpack模块时它会产生IDENTICAL结果,并且只对信号数据向量进行操作。

我猜有些照片会有所帮助。这正是两个计划中正在发生的事情。

1)生成高斯向量 2)零填充高斯矢量到下一个下一个最大功率2长度 3)正向FFT,产生自然顺序(w)结果 4)情节

这是我的内核的python模拟,与使用scipy.fftpack.fft(vector)进行比较:

http://i.imgur.com/pGcYTrL.png

他们是一样的。现在将其与以下任何一个进行比较:

http://i.imgur.com/pbiYGpR.png

(忽略x轴上的凹痕,它们都是自然顺序FFT结果)

它们都是相同类型的起始数据,(从0到N的guassian,以N / 2为中心,在这种情况下为零填充到2N)。它们应该看起来像图像中的绿/蓝线,但它们不是。从我盯着第二个程序的主机/设备代码多久以来我的眼睛已经上釉了,我没有看到任何拼写错误或不正确的算法。我非常怀疑设备方面发生了一些我不知道的事情,因此我在这里发帖。显然,算法看起来像它的操作正确,(红色/红色的一般形状大约是蓝色/绿色的形状,无论起始数据。我已经在不同的起始集上运行算法,它始终看起来像蓝色/绿色但是有那种荒谬的噪音/错误),但有些不对劲。

所以我转向互联网。提前谢谢。

编辑:下面的一张海报表示很难评论至少看不到设备端代码,因为有关于内存防护的问题,所以我发布了下面的内核代码。

//fftCorr.cl
//
//OpenCL Kernels/Methods for FFT Cross Correlation of Signals
//
//Copyright (C) 2013  Steve Novakov
//   
//This file is part of OCLSIGPACK.
//
//OCLSIGPACK is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.
//
//OCLSIGPACK is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with OCLSIGPACK.  If not, see <http://www.gnu.org/licenses/>.
//


#define PIE 3.14159265359f


void DFT2(float2 * a,float2 * b, float2 *w){
    float2 tmp; 
    float2 bmul = ( (*w).x*((*b).x) - (*w).y*((*b).y),  (*w).x*((*b).y) + (*w).y*((*b).x) );
    tmp = (*a) - bmul; 
    (*a) += bmul; 
    (*b) = tmp; 
}
//
//
// Spin Factor Calc
// 
// Computes spin/twiddle factor for particular bit reversed index.
//
//

float2 spinFact(unsigned int N, unsigned int k)
{
    float phi = -2.0 * PIE * (float) k / (float)  N;
    // \bar{w}^offset_(groupDist)   

    float spinRe, spinIm;
    spinIm = sincos( phi, &spinRe); 

    return (float2) (spinRe, spinIm);   
}


float2 spinFactR(unsigned int N, unsigned int k)
{
    float phi = 2.0 * PIE * (float) k / (float)  N;
    // w^offset_(groupDist) 

    float spinRe, spinIm;
    spinIm = sincos( phi, &spinRe); 

    return (float2) (spinRe, spinIm);   
}


//
// Bit-Reversed Index Reversal, (that sounds confusing)
//
unsigned int BRIR( unsigned int index, unsigned int fftDepth)
{
    unsigned int rev = index;   

    rev = (((rev & 0xaaaaaaaa) >> 1 ) | ((rev & 0x55555555) << 1 ));
    rev = (((rev & 0xcccccccc) >> 2 ) | ((rev & 0x33333333) << 2 ));
    rev = (((rev & 0xf0f0f0f0) >> 4 ) | ((rev & 0x0f0f0f0f) << 4 ));
    rev = (((rev & 0xff00ff00) >> 8 ) | ((rev & 0x00ff00ff) << 8 ));
    rev = (((rev & 0xffff0000) >> 16) | ((rev & 0x0000ffff) << 16));

    rev >>= (32-fftDepth);  

    return rev;
}


//
//
// Index Bit Reversal Kernel, if Necessary/for Testing.
//
// Maybe I should figure out an in-place swap algorithm later.
//
//
__kernel void bitRevKernel(     __global float2 * fftSetX,
                                __global float2 * fftSetY,
                                __global float2 * fftRevX,
                                __global float2 * fftRevY, 
                                unsigned int fftDepth
                            )
{
    unsigned int glID = get_global_id(0);

    unsigned int revID = BRIR(glID, fftDepth);

    fftRevX[revID] = fftSetX[glID];
    fftRevY[revID] = fftSetY[glID];

}


//
//
// FFT Radix-2 Butterfly Operation Kernel
//
// This is an IN-PLACE algorithm. It calculates both bit-reversed indeces and spin factors in the same thread and
// updates the original set of data with the "butterfly" results.
// 
// recursionLevel is the level of recursion of the butterfly operation 
// # of threads is half the vector size N/2, (glID is between 0 and this value, non inclusive)
//
// Assumes natural order data input. Produces bit-reversed order FFT output.
//
//
__kernel void fftForwKernel(    __global float2 * fftSetX,
                                __global float2 * fftSetY,
                                unsigned int recursionLevel,
                                unsigned int totalDepth
                            )
{

    unsigned int glID = get_global_id(0);

    unsigned int gapSize = 1 << (recursionLevel - 1);
    unsigned int groupSize = 1 << recursionLevel;
    unsigned int base = (glID >> (recursionLevel - 1)) * groupSize;
    unsigned int offset = glID & (gapSize - 1 );

    unsigned int bitRevIdA = (unsigned int) base + offset;
    unsigned int bitRevIdB = (unsigned int) bitRevIdA + gapSize;

    unsigned int actualIdA = BRIR(bitRevIdA, totalDepth);
    unsigned int actualIdB = BRIR(bitRevIdB, totalDepth);


    float2 tempXA = fftSetX[actualIdA];
    float2 tempXB = fftSetX[actualIdB];

    float2 tempYA = fftSetY[actualIdA];
    float2 tempYB = fftSetY[actualIdB];                 

    float2 spinF = spinFact(groupSize, offset);

    // size 2 DFT   
    DFT2(&tempXA, &tempXB, &spinF); 
    DFT2(&tempYA, &tempYB, &spinF);


    fftSetX[actualIdA] = tempXA;
    fftSetX[actualIdB] = tempXB;

    fftSetY[actualIdA] = tempYA;
    fftSetY[actualIdB] = tempYB;    

}

对于图片中提供的数据。我按照帖子开头的描述运行“fftForwKernel”,然后运行“bitRevKernel”

1 个答案:

答案 0 :(得分:1)

因此,如果没有代码通知任何内容,并且在假设代码确实“相同”的情况下运行,我倾向于说假设使用的算法在Python和Python之间看起来确实相同OpenCL 这可能是一个同步问题。如果不是全局同步问题(在内核调用之间正确分割工作),则每个内核调用都会丢失全局内存范围或每个本地组的本地内存范围。

你是如何组织电话的?提供的伪代码在分割递归FFT的确切方面看起来很模糊。我猜你正在为一个......做正确的事......好吧,我甚至不确定你是在做DIT还是DIF,或者是FFT算法可用的各种各样的数据流。据我所知,可能是你在没有正确记忆它们的情况下做蝴蝶,或者你可能如此严格地同步你的FFT步骤,蝴蝶是整个不同的内核调用的一部分,而不是递归的FFT步骤,我的建议完全是null并且无效。

(我会把这个写入评论,但我没有这样做的声誉,所以我很抱歉这是作为'答案'发布的)