简体   繁体   中英

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. 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. 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:

__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.

You access to C like C[gid] in kernel but you created it with size of sizeof(float) only. So main data can not fit in that(4 byte) memory space and you are writing only 4 byte to it. Multiplying both its creation size and write size with data_size should be enough.

Thats why real values get 1 and -1 while imaginaries get zero (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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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