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.