我正在尝试通过编写基本代码来学习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)的复杂 double 数组。但是,我得到全0。这是为什么?
j[i] = make_cuDoubleComplex(0, 5);

2)为什么我不能使用cout打印阵列?下面显示的代码行给出以下错误:没有运算符“<
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'将以complex(double)而不是cuDoubleComplex的格式给出。我可以使用函数'func'对复数(double)的'j'数组进行类似的操作吗?如果没有,我有什么选择?

我想我已经很好地解释了自己,但是随时可以提出任何后续问题。
C++和CUDA的新手,所以不错:D

最佳答案

编写CUDA代码时,尤其是在学习或遇到困难(事情无法按预期方式运行)时,应始终对所有CUDA API调用和内核调用执行cuda error checking

我认为您的代码中实际上没有任何CUDA功能错误(很好!),但值得指出。

您的大多数问题是由于您没有正确打印出cuDoubleComplex类型而引起的。您的printf语句指定了float格式参数(%f),但没有传递float值(正在传递cuDoubleComplex值)。那是行不通的,这样做时printf的行为会很奇怪,而不会给出任何错误指示。

而是尝试这样的事情:

printf("\nJ = %f, %f", cuCreal(j[i]), cuCimag(j[i]));

这些函数(cuCrealcuCimag)返回cuComplex数字的实部和虚部,并以适当的类型floatdouble形式返回它们,在这种情况下,从doublefloat的隐式强制转换对于您正在做的事情和可以通过printf处理(尽管这不是很好的编程习惯-而是使用正确的printf格式说明符作为double值)。

如果您对两个printf语句都进行了更改,那么我认为您将获得预期的结果-至少在我运行代码时做到了。如果您仍然遇到垃圾,则您的CUDA GPU可能无法正常工作,在这里我提到的执行CUDA错误检查将帮助您发现问题所在。

关于有关cout的问题,答案大致上与我对printf发生的情况的解释相同。 cout无法识别cuDoubleComplex类型,因此引发错误。如果要在不使用printf的情况下进行修复,请使用上面在cuDoubleComplex语句中指示的转换函数,将float转换为其单独的实部和虚部,由doubleprintf表示。

关于最后一个问题,将complex数据转换为cuDoubleComplex类型应该不难。编写一个转换函数以根据cuComplex.h中的实用程序来执行此操作。有很多后门方法,但是它们不是好的编程习惯。

编辑:为了回答后续问题,当前发布的代码中还有两个错误。
  • dev_jdev_calc的类型为cuDoubleComplex,但是您正在对这些数量执行cudaMalloccudaMemcpy,就好像它们的大小为double一样。在以下代码中,我将这些sizeof(double)条目更改为sizeof(cuDoubleComplex)
  • 对于C和C++,您的索引通常有点奇怪。通常索引从零开始。您遇到了索引问题,其中最后一个元素未得到正确的计算。我将所有索引更改为基于零的索引。

  • 这是对您的代码的修改,适用于我:
    //#include <complex>  // not necessary for this code
    #include <iostream>
    #include <cmath>
    //#include "cuda.h"  // not necessary when compiling with nvcc
    #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] = { 2.0, 2.0, 2.0, 2.0, 2.0 };
        //complex<double> j[n+1]
        cuDoubleComplex j[n];
    
        for (int i = 0; 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) * sizeof(double) );
        cudaMalloc( (void**)&dev_j, (n) * sizeof(cuDoubleComplex) );
        cudaMalloc( (void**)&dev_calc, (n) * sizeof(cuDoubleComplex) );
    
        cudaMemcpy( dev_s, s, (n) * sizeof(double), cudaMemcpyHostToDevice );
        cudaMemcpy( dev_j, j, (n) * sizeof(cuDoubleComplex), cudaMemcpyHostToDevice );
    
        func<<<n,1>>>( dev_s, dev_j, dev_calc );
        //kernel<<<1,1>>>(a_d);
        gpuErrchk( cudaPeekAtLastError() );
        gpuErrchk( cudaMemcpy(calc, dev_calc, (n) * sizeof(cuDoubleComplex), cudaMemcpyDeviceToHost) );
    
        //cudaMemcpy( calc, dev_calc, (n+1) * sizeof(double), cudaMemcpyDeviceToHost );
    
        for (int i = 0; i < n; i++) {
            cout << "\nCALC cout = " << cuCreal(calc[i]) << ", " << cuCimag(calc[i]);
        }
    
        return 0;
    }
    

    关于c++ - CUDA-简单复数乘法,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/17476978/

    10-14 08:35