简体   繁体   English

OpenCl算法的实现结果有所不同

[英]OpenCl algorithm implementation results differs

I have a problem. 我有个问题。 I am trying to learn OpenCl, so I've been trying to implement FFT algorithm with OpenCl. 我正在尝试学习OpenCl,所以我一直在尝试使用OpenCl实现FFT算法。 I was trying recreate this: 我试图重新创建这个:

void FFT (cmplx* data, int dataSize){
    if(dataSize == 1){
        return;
    }else{
        cmplx* even = (cmplx*)malloc(dataSize/2*sizeof(cmplx));
        cmplx* odd = (cmplx*)malloc(dataSize/2*sizeof(cmplx));
        for (int i = 0;i<dataSize;i+=2){
            even[i/2] = data[i];
            odd[i/2] = data[i+1];
        }

        FFT(even,dataSize/2);
        FFT(odd,dataSize/2);

        for (int i = 0;i<dataSize;i++){
            cmplx C = cmplx(-2*M_PI/dataSize*i);
            data[i].real = even[i].real + C.real*odd[i].real - C.imag*odd[i].imag;
            data[i].imag = even[i].imag + C.real*odd[i].imag + C.imag*odd[i].real;
        }
    }
}

cmplx is just a class that holds two floats real and imaginary parts of complex number and has a constructor that creates complex number with Euler equation. cmplx只是一个类,其中包含两个浮点数的复数实数和虚数部分,并且具有一个构造函数,该构造函数使用Euler方程创建复数。 and everything else is quite straight forward 其他一切都非常简单

I probably do not know some small nuance, in my understanding I can do cycle calculation in independent threads so cycle like this : 我可能不了解一些细微差别,以我的理解,我可以在独立线程中进行循环计算,因此循环如下所示:

for (int i = 0;i<dataSize;i++){
    cmplx C = cmplx(-2*M_PI/dataSize*i);
    data[i].real = even[i].real + C.real*odd[i].real - C.imag*odd[i].imag;
    data[i].imag = even[i].imag + C.real*odd[i].imag + C.imag*odd[i].real;
}

With OpenCl code like this: 使用这样的OpenCl代码:

__kernel void FFTComplexSum(__global float *evenReal,__global float *evenImag,
                        __global float *oddReal,__global float *oddImag,
                        __global float *real,__global float *imag,
                        __global float *C){
    int gid = get_global_id(0);
    real[gid] = evenReal[gid] + cos(C[gid])*oddReal[gid] - sin(C[gid])*oddImag[gid];
    imag[gid] = evenImag[gid] + cos(C[gid])*oddImag[gid] + sin(C[gid])*oddReal[gid];
}

But if run this: 但是如果运行这个:

    .... // instantiating stuff like platform, device_id, kernel, program...  
    size_t buffer_size;
    cl_mem evenReal_mem, evenImag_mem, oddReal_mem, oddImag_mem, real_mem, imag_mem, c_mem;

    float evenReal[dataSize];
    float evenImag[dataSize];
    float tReal[dataSize];
    float tImag[dataSize];
    float oddReal[dataSize];
    float oddImag[dataSize];
    float C[dataSize];

    for (int i = 0;i<dataSize;i+=2){
        evenReal[i/2] = real[i];
        evenImag[i/2] = imag[i];
        oddReal[i/2] = real[i+1];
        oddImag[i/2] = imag[i+1];
        C[i] = -2*M_PI/dataSize*i;
        C[i+1] = -2*M_PI/dataSize*(i+1);
    }

    doubleArray(evenReal,dataSize); // doubleArray function just makes array to loop
    doubleArray(evenImag,dataSize);

    doubleArray(oddReal,dataSize);
    doubleArray(oddImag,dataSize);

    buffer_size = sizeof(float) * dataSize;

    evenReal_mem = clCreateBuffer(context, CL_MEM_READ_ONLY, buffer_size, NULL, NULL);
    err = clEnqueueWriteBuffer(cmd_queue, evenReal_mem, CL_TRUE, 0, buffer_size,(void*)evenReal, 0, NULL, NULL);
    assert(err == CL_SUCCESS); // Fail check

    evenImag_mem = clCreateBuffer(context, CL_MEM_READ_ONLY, buffer_size, NULL, NULL);
    err = clEnqueueWriteBuffer(cmd_queue, evenImag_mem, CL_TRUE, 0, buffer_size,(void*)evenImag, 0, NULL, NULL);
    assert(err == CL_SUCCESS); // Fail check

    oddReal_mem = clCreateBuffer(context, CL_MEM_READ_ONLY, buffer_size, NULL, NULL);
    err = clEnqueueWriteBuffer(cmd_queue, oddReal_mem, CL_TRUE, 0, buffer_size,(void*)oddReal, 0, NULL, NULL);
    assert(err == CL_SUCCESS); // Fail check

    oddImag_mem = clCreateBuffer(context, CL_MEM_READ_ONLY, buffer_size, NULL, NULL);
    err = clEnqueueWriteBuffer(cmd_queue, oddImag_mem, CL_TRUE, 0, buffer_size,(void*)oddImag, 0, NULL, NULL);
    assert(err == CL_SUCCESS); // Fail check

    real_mem = clCreateBuffer(context, CL_MEM_WRITE_ONLY, buffer_size, NULL, NULL);
    err = clEnqueueWriteBuffer(cmd_queue, real_mem, CL_TRUE, 0, buffer_size,(void*)real, 0, NULL, NULL);
    assert(err == CL_SUCCESS); // Fail check

    imag_mem = clCreateBuffer(context, CL_MEM_WRITE_ONLY, buffer_size, NULL, NULL);
    err = clEnqueueWriteBuffer(cmd_queue, imag_mem, CL_TRUE, 0, buffer_size,(void*)imag, 0, NULL, NULL);
    assert(err == CL_SUCCESS); // Fail check

    c_mem = clCreateBuffer(context, CL_MEM_READ_ONLY, sizeof(float), NULL, NULL);
    err = clEnqueueWriteBuffer(cmd_queue, c_mem, CL_TRUE, 0, sizeof(float),(void*)C, 0, NULL, NULL);
    assert(err == CL_SUCCESS); // Fail check

    clFinish(cmd_queue);

    err  = clSetKernelArg(kernel[0],  0, sizeof(cl_mem), &evenReal_mem);
    err  = clSetKernelArg(kernel[0],  1, sizeof(cl_mem), &evenImag_mem);
    err  = clSetKernelArg(kernel[0],  2, sizeof(cl_mem), &oddReal_mem);
    err  = clSetKernelArg(kernel[0],  3, sizeof(cl_mem), &oddImag_mem);
    err  = clSetKernelArg(kernel[0],  4, sizeof(cl_mem), &real_mem);
    err  = clSetKernelArg(kernel[0],  5, sizeof(cl_mem), &imag_mem);
    err  = clSetKernelArg(kernel[0],  6, sizeof(cl_mem), &c_mem);
    assert(err == CL_SUCCESS); // Fail check

    size_t global_work_size = dataSize;
    err = clEnqueueNDRangeKernel(cmd_queue, kernel[0], 1, NULL, &global_work_size, NULL, 0, NULL, NULL);
    assert(err == CL_SUCCESS);
    clFinish(cmd_queue);

    printf("test data:\n");

    for (int i = 0;i<dataSize;i++){
        float r,I;
        r = evenReal[i] + cos(C[i])*oddReal[i] - sin(C[i])*oddImag[i];
        I = evenImag[i] + cos(C[i])*oddImag[i] + sin(C[i])*oddReal[i];
        printf("%f + %f\n",r,I);
    }

    err = clEnqueueReadBuffer(cmd_queue, real_mem, CL_TRUE, 0, buffer_size, tReal, 0, NULL, NULL);
    assert(err == CL_SUCCESS);
    clFinish(cmd_queue);
    err = clEnqueueReadBuffer(cmd_queue, imag_mem, CL_TRUE, 0, buffer_size, tImag, 0, NULL, NULL);
    assert(err == CL_SUCCESS);
    clFinish(cmd_queue);

    clReleaseMemObject(evenReal_mem);
    clReleaseMemObject(evenImag_mem);
    clReleaseMemObject(oddReal_mem);
    clReleaseMemObject(oddImag_mem);
    clReleaseMemObject(real_mem);
    clReleaseMemObject(imag_mem);
    clReleaseMemObject(c_mem);

    prinf("data:");

    for (int i = 0;i<dataSize;i++){
        printf("%f + %f\n",tReal[i],tImag[i]);
    }

It returns this: 它返回以下内容:

test data:
1.000000 + 0.000000
0.000000 + 1.000000
-1.000000 + 0.000000
-0.000000 + -1.000000
data:
1.000000 + 0.000000
-1.000000 + 0.000000
1.000000 + 0.000000
-1.000000 + 0.000000

Am I am really confused why I get wrong answers. 我真的很困惑为什么我得到错误的答案。 I am missing something really obvious? 我缺少真正明显的东西吗?

Sorry for long question. 对不起,很长的问题。

c_mem has the problem. c_mem有问题。

You access to C like C[gid] in kernel but you created it with size of sizeof(float) only. 您可以像访问内核中的C[gid]一样访问C ,但是创建的大小仅为sizeof(float) So main data can not fit in that(4 byte) memory space and you are writing only 4 byte to it. 因此,主数据无法容纳到该(4字节)存储空间中,并且您只向其中写入4字节。 Multiplying both its creation size and write size with data_size should be enough. 将其创建大小和写入大小与data_size相乘就足够了。

Thats why real values get 1 and -1 while imaginaries get zero (sin(0)). 这就是为什么实数值为1和-1而虚数为零(sin(0))的原因。 If you had luck, overflowing C would give garbage and give both real and imaginary elements garbage result which would show the source of error immediately. 如果运气好的话,C溢出会产生垃圾,并给实数和虚数元素都带来垃圾结果,这将立即显示错误的来源。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM