尽管 CudaSuccess,CUDA cudaMemCpy 似乎没有复制

CUDA cudaMemCpy doesn't appear to copy despite CudaSuccess

我刚刚开始使用 CUDA,这是我的第一个项目。我搜索了这个问题,虽然我注意到其他人也有类似的问题,但 none 的建议似乎与我的具体问题相关或对我的情况有所帮助。

作为练习,我正在尝试使用 CUDA 编写 n 体模拟。在这个阶段,我对我的具体实现是否有效不感兴趣,我只是在寻找有用的东西,我可以在以后改进它。我还需要稍后更新代码,一旦它工作,就可以处理我的 SLI 配置。

以下是流程的简要概述:

  1. 创建 X 和 Y 位置、速度、加速度向量。
  2. 在 GPU 上创建相同的向量并跨
  3. 复制值
  4. 在循环中:(i) 计算迭代加速度,(ii) 将加速度应用于速度和位置,以及 (iii) 将位置复制回主机以供显示。

(显示还未实现,稍后再做)

暂时不用担心加速度计算函数,这里是更新函数:

__global__ void apply_acc(double* pos_x, double* pos_y, double* vel_x, double* vel_y, double* acc_x, double* acc_y, int N)
{
    int i = threadIdx.x;

    if (i < N);
    {
        vel_x[i] += acc_x[i];
        vel_y[i] += acc_y[i];

        pos_x[i] += vel_x[i];
        pos_y[i] += vel_y[i];
    }
}

下面是主要方法中的一些代码:

cudaError t;

t = cudaMalloc(&d_pos_x, N * sizeof(double));
t = cudaMalloc(&d_pos_y, N * sizeof(double));
t = cudaMalloc(&d_vel_x, N * sizeof(double));
t = cudaMalloc(&d_vel_y, N * sizeof(double));
t = cudaMalloc(&d_acc_x, N * sizeof(double));
t = cudaMalloc(&d_acc_y, N * sizeof(double));

t = cudaMemcpy(d_pos_x, pos_x, N * sizeof(double), cudaMemcpyHostToDevice);
t = cudaMemcpy(d_pos_y, pos_y, N * sizeof(double), cudaMemcpyHostToDevice);
t = cudaMemcpy(d_vel_x, vel_x, N * sizeof(double), cudaMemcpyHostToDevice);
t = cudaMemcpy(d_vel_y, vel_y, N * sizeof(double), cudaMemcpyHostToDevice);
t = cudaMemcpy(d_acc_x, acc_x, N * sizeof(double), cudaMemcpyHostToDevice);
t = cudaMemcpy(d_acc_y, acc_y, N * sizeof(double), cudaMemcpyHostToDevice);

while (true)
{
    calc_acc<<<1, N>>>(d_pos_x, d_pos_y, d_vel_x, d_vel_y, d_acc_x, d_acc_y, N);
    apply_acc<<<1, N>>>(d_pos_x, d_pos_y, d_vel_x, d_vel_y, d_acc_x, d_acc_y, N);

    t = cudaMemcpy(pos_x, d_pos_x, N * sizeof(double), cudaMemcpyDeviceToHost);
    t = cudaMemcpy(pos_y, d_pos_y, N * sizeof(double), cudaMemcpyDeviceToHost);

    std::cout << pos_x[0] << std::endl;
}

每个循环,cout 写入相同的值,无论最初创建位置数组时设置的随机值是什么。如果我将 apply_acc 中的代码更改为:

__global__ void apply_acc(double* pos_x, double* pos_y, double* vel_x, double* vel_y, double* acc_x, double* acc_y, int N)
{
    int i = threadIdx.x;

    if (i < N);
    {
        pos_x[i] += 1.0;
        pos_y[i] += 1.0;
    }
}

然后它仍然给出相同的值,所以要么 apply_acc 没有被调用,要么 cudaMemcpy 没有复制数据。

所有 cudaMalloccudaMemcpy 调用 return cudaScuccess.

Here 的一个 PasteBin link 到完整的代码。它应该很容易理解,因为各种数组有很多重复。

就像我说的,我以前从未编写过 CUDA 代码,我是根据来自 NVidia 的 #2 CUDA 示例视频编写的,其中有人编写了并行数组加法代码。我不确定它是否有任何区别,但我使用的是带有最新 NVidia 驱动程序和 CUDA 7.0 RC 的 2x GTX970,并且我选择在安装 CUDA 时不安装捆绑的驱动程序,因为它们比我拥有的驱动程序旧。

这行不通:

const int N = 100000;
...
calc_acc<<<1, N>>>(...);
apply_acc<<<1, N>>>(...);

内核启动配置 (<<<...>>>) 的第二个参数是每个块参数的线程数。它被限制为 512 或 1024,具体取决于您的编译方式。这些内核将不会启动,并且需要使用正确的 CUDA error checking 来捕获由此产生的错误类型。简单地查看后续 CUDA API 函数的 return 值不会表明存在此类错误(这就是为什么您随后会看到 cudaSuccess)。

关于概念本身,建议大家多了解一下CUDA thread and block hierarchy。要启动大量线程,您需要使用内核启动配置的 both 参数(即前两个参数都不应为 1)。从性能的角度来看,这通常也是可取的。