簡體   English   中英

OpenCl算法的實現結果有所不同

[英]OpenCl algorithm implementation results differs

我有個問題。 我正在嘗試學習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

我真的很困惑為什么我得到錯誤的答案。 我缺少真正明顯的東西嗎?

對不起,很長的問題。

c_mem有問題。

您可以像訪問內核中的C[gid]一樣訪問C ,但是創建的大小僅為sizeof(float) 因此,主數據無法容納到該(4字節)存儲空間中,並且您只向其中寫入4字節。 將其創建大小和寫入大小與data_size相乘就足夠了。

這就是為什么實數值為1和-1而虛數為零(sin(0))的原因。 如果運氣好的話,C溢出會產生垃圾,並給實數和虛數元素都帶來垃圾結果,這將立即顯示錯誤的來源。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM