OpenCl算法实现结果不同

时间:2017-08-07 04:09:56

标签: c opencl

我有问题。我正在尝试学习OpenCl,所以我一直在尝试用OpenCl实现FFT算法。我正在尝试重新创建这个:

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只是一个包含复数的两个浮点实部和虚部的类,并且有一个构造函数,用Euler方程创建复数。其他一切都很直接

我可能不知道一些小的细微差别,在我的理解中,我可以在独立线程中进行循环计算,所以这样循环:

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;
}

使用这样的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];
}

但如果运行这个:

    .... // 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]);
    }

它返回:

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

我真的很困惑,为什么我会得到错误的答案。我错过了一些非常明显的东西?

很抱歉很长的问题。

1 个答案:

答案 0 :(得分:0)

c_mem有问题。

您可以访问内核中的C C[gid],但只创建了大小为sizeof(float)的{​​{1}}。因此主数据不能适合那个(4字节)存储空间,并且你只写4个字节。使用data_size将其创建大小和写入大小相乘就足够了。

这就是为什么当虚数变为零时(sin(0)),实数值变为1和-1。如果运气好的话,溢出的C会产生垃圾并给出实部和虚部的垃圾结果,这会立即显示错误的来源。