我正在尝试通过编写基本代码来学习CUDA,这有望使我能够更好地将现有的C ++代码转换为CUDA(用于研究)。
我需要做一些复杂的数字操作,所以我写了这个非常基本的代码,用一个实数来乘以一个复数的数组 在GPU内核中。
#include <complex>
#include <iostream>
#include <cmath>
#include "cuda.h"
#include "math.h"
#include "cuComplex.h"
#define n 5
using namespace std;
#define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); }
inline void gpuAssert(cudaError_t code, char *file, int line, bool abort=true)
{
if (code != cudaSuccess)
{
fprintf(stderr,"GPUassert: %s %s %d\n", cudaGetErrorString(code), file, line);
if (abort) exit(code);
}
}
__global__ void func( double *s, cuDoubleComplex *j, cuDoubleComplex *calc ) {
int tid = blockIdx.x;
calc[tid] = cuCmul(j[tid], make_cuDoubleComplex(*s, 0));
}
int main( void ) {
cuDoubleComplex calc[n+1], *dev_j, *dev_calc;
double *dev_s, s[n+1] = { 2.0, 2.0, 2.0, 2.0, 2.0 };
//complex<double> j[n+1]
cuDoubleComplex j[n+1];
for (int i = 1; i <= n; i++) {
j[i] = make_cuDoubleComplex(0, 5);
cout << "\nJ cout = " << cuCreal(j[i]) << ", " << cuCimag(j[i]);
}
// allocate the memory on the GPU
cudaMalloc( (void**)&dev_s, (n+1) * sizeof(double) );
cudaMalloc( (void**)&dev_j, (n+1) * sizeof(double) );
cudaMalloc( (void**)&dev_calc, (n+1) * sizeof(double) );
cudaMemcpy( dev_s, s, (n+1) * sizeof(double), cudaMemcpyHostToDevice );
cudaMemcpy( dev_j, j, (n+1) * sizeof(double), cudaMemcpyHostToDevice );
func<<<n,1>>>( dev_s, dev_j, dev_calc );
//kernel<<<1,1>>>(a_d);
gpuErrchk( cudaPeekAtLastError() );
gpuErrchk( cudaMemcpy(calc, dev_calc, (n+1) * sizeof(double), cudaMemcpyDeviceToHost) );
//cudaMemcpy( calc, dev_calc, (n+1) * sizeof(double), cudaMemcpyDeviceToHost );
for (int i = 1; i <= n; i++) {
cout << "\nCALC cout = " << cuCreal(calc[i]) << ", " << cuCimag(calc[i]);
}
return 0;
}
最后的答案是错误的,我还确定了其他一些我没有得到预期值的地方。
1)我期望在下面的代码行之后为'j'的所有元素提供一个复数的双数组(0,5i)。但是,我得到全0。那是为什么?
j[i] = make_cuDoubleComplex(0, 5);
2)为什么我不能用cout打印我的数组?下面显示的代码行给出以下错误:无操作符“&lt;&lt;”匹配这些操作数。如何在不使用printf的情况下解决这个问题?
cout << "\nJ = " << j[i];
3)GPU函数'func'应该给出一个(0,10i)数组作为最终答案,给出如下随机值:
CALC = -1.#QNAN0
CALC = -1.#QNAN0
CALC = -9255963134931783100000000...000.. etc
CALC = -9255963134931783100000000...000.. etc
4)对于我的实际研究,复数数组'j'将以复数(double)而不是cuDoubleComplex的格式给出。我可以使用函数'func'对'j'数组的复数(double)进行类似的操作吗?如果没有,我的选择是什么?
我想我已经很好地解释了自己,但随时可以提出任何后续问题。 C ++和CUDA的新手很好:D
答案 0 :(得分:1)
编写CUDA代码时,特别是当你正在学习或遇到困难时(事情没有按照你期望的方式工作),你应该对所有CUDA API调用和内核调用进行cuda error checking。
我认为你的代码中确实没有任何CUDA功能错误(干得好!)但值得指出。
您的大部分问题都是因为您没有正确打印出类型cuDoubleComplex
。您的printf语句指定了float
格式参数(%f
),但您没有传递float
值(您传递cuDoubleComplex
值)。这不起作用,printf
在你这样做时会表现得很奇怪,而不会给出任何错误指示。
相反,尝试这样的事情:
printf("\nJ = %f, %f", cuCreal(j[i]), cuCimag(j[i]));
这些函数(cuCreal
和cuCimag
)返回cuComplex
个数字的实部和虚部,并将它们作为适当的类型返回float
或{{1在这种情况下,从double
到double
的隐式强制转换对于您正在执行的操作是可以的,并且可以由float
处理(尽管它不是很好的编程实践 - 而是为printf
值使用正确的printf
格式说明符。
如果你对两个printf语句进行了更改,我认为你会得到预期的结果 - 至少我在运行你的代码时做了。如果你仍然得到垃圾,那么你的CUDA GPU可能无法正常工作,这就是我提到的CUDA错误检查将帮助你发现问题所在。
关于double
的问题,答案大致相当于我对cout
发生的事情的解释。 printf
无法理解类型cout
,因此会抛出错误。如果您想在不使用cuDoubleComplex
的情况下进行修复,请使用转换函数将printf
转换为各个实部和虚部,由cuDoubleComplex
或float
表示在上面的double
声明中指出。
关于您的上一个问题,将printf
数据转换为complex
类型并不困难。根据{{1}}中的实用程序编写一个转换函数来实现它。有一些后门方法,但它们不是很好的编程习惯。
编辑:针对后续问题,当前发布的代码中还有两个错误。
cuDoubleComplex
和cuComplex.h
属于dev_j
类型,但您对这些数量进行了dev_calc
和cuDoubleComplex
,就像它们的大小{{1}一样}。在以下代码中,我将这些cudaMalloc
条目更改为cudaMemcpy
。以下是对我有用的代码修改:
double