【问题标题】:CUDA - Simple complex number multiplicationCUDA - 简单的复数乘法
【发布时间】:2013-07-04 19:20:26
【问题描述】:

我正在尝试通过编写基本代码来学习 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 打印我的数组?下面显示的代码行给出了以下错误:没有运算符“

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'对'j'复杂(双)数组进行类似的操作吗?如果没有,我有什么选择?

我认为我已经很好地解释了自己,但请随时提出任何后续问题。 对 C++ 和 CUDA 不熟悉,所以很好:D

【问题讨论】:

  • 当您尝试将 单个复数乘以 GPU 内核中的实数时发生了什么?

标签: c++ cuda


【解决方案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])); 

这些函数(cuCrealcuCimag)返回 cuComplex 数字的实部和虚部,并将它们作为适当的类型返回,floatdouble,在这种情况下是隐式转换从doublefloat 对你正在做的事情来说是可以的,并且可以由printf 处理(虽然这不是一个很好的编程习惯——而是使用正确的printf 格式说明符作为double 值) .

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

关于你对cout的问题,答案大致相当于我对printf的解释。 cout 不理解 cuDoubleComplex 类型,因此会引发错误。如果您想在不使用printf 的情况下修复它,请使用我在上面printf 语句中指出的转换函数,将cuDoubleComplex 转换为其单独的实部和虚部,由floatdouble 表示.

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

编辑:针对后续问题,当前发布的代码中还有两个错误。

  1. dev_jdev_calccuDoubleComplex 类型,但您在这些数量上执行 cudaMalloccudaMemcpy 就好像它们的大小是 double。在以下代码中,我将那些 sizeof(double) 条目更改为 sizeof(cuDoubleComplex)
  2. 总体而言,对于 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;
}

【讨论】:

  • cuComplex.h 中复杂类型的数据布局与主机上 C/C++/Fortran 代码中内置复杂类型的数据布局兼容,因此无需显式转换。基本上,复杂类型是实部后跟虚部的结构,两者都是 IEEE-754 单精度或双精度数。其他软件环境可能会以类似 SOA 的方式存储复杂的数据,其中实部和虚部被分成不同的部分;在这些情况下,需要重新洗牌。
  • 我把 printf 改成: cout
  • 您的 GPU 设置可能有问题。你能运行其他 GPU 代码吗?使用显示您输入的错误检查的新代码编辑您的原始问题,我会看看。就像我说的那样,只需更改 printf 语句,我就能让您的代码正常工作。
  • 我现在已经更新了代码。我的 CALC cout 值为:-5.69507e+303、-5.68301e+303 -5.69507e+303、-5.68301e+303 -9.25596e+061、-9.25596e+061 -9.25596e+061、-9.25596e+061 -9.25596e+061, -9.25596e+061 是的,我已经成功运行了 CUDA 示例代码以及 CUDA by Example 书中的代码
  • 这看起来很可疑:cudaMalloc( (void**)&amp;dev_calc, (n+1) * sizeof(double) );。改用sizeof(dev_calc[0]),这样大小计算总是自洽的,不管dev_cal 有什么类型。同样的错误也出现在cudaMemcpy() 的调用中。
猜你喜欢
  • 2016-05-22
  • 2014-05-25
  • 2012-02-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-06
  • 1970-01-01
相关资源
最近更新 更多