2017-08-07 109 views
0

我有個問題。我正在嘗試學習OpenCl,所以我一直試圖用OpenCl來實現FFT算法。我試圖重新創建:OpenCl算法執行結果不同

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僅僅是持有複數兩個浮體實部和虛部並具有與歐拉方程建立複數一個構造函數的類。和其他一切是相當簡單

我可能不知道一些小的細微差別,在我的理解,我可以做的週期計算在獨立線程,以便循環是這樣的:

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

它[R eturns這:

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 

我真的很困惑,爲什麼我得到錯誤的答案。我錯過了一些非常明顯的東西?

對不起,長期以來的問題。

回答

0

c_mem有問題。

您在內核中訪問C,如C[gid],但是僅在sizeof(float)的範圍內創建了它。所以主數據不能適應那個(4字節)的存儲空間,而你只寫了4個字節。將其創建大小和寫入大小與data_size相乘應該足夠了。

這就是爲什麼實數值得到1和-1而虛數零(sin(0))。如果你有幸,溢出的C會給垃圾並給出真實和虛構的元素垃圾結果,這會立即顯示錯誤的根源。

+0

Omg ......我覺得不好意思看不到這個...謝謝。 –