混合隐式和显式 CUDA 流

时间:2021-02-04 07:01:55

标签: c++ cuda

我有一些 CUDA 8.0 代码(编辑:我继承的,不是我写的),基本上是这样的:

cudaMemcpy(devInputData, ..., cudaMemcpyHostToDevice);
kernelThings<<<GRIDS, BLOCKS, 0, myStream>>>(devInputData);
cudaDeviceSynchronize();

cudaMemcpy() 在没有流的情况下同步工作,因此据我所知,此代码是正确的。

如果我使用 CUDA_API_PER_THREAD_DEFAULT_STREAM 编译,这段代码还安全吗?我认为不,cudaMemcpy() 现在异步发生,因此内核有可能在 cudaMemcpy() 完成之前启动。但是,查看 Nsight 分析器,我发现没有重叠 - 从文字上我看到:

[Memcpy HtoD]
                  [kernelThings]

两个函数之间有 16 微秒的间隔。此行为在应用程序中多次重复。

然而,我接下来删除了 cudaDeviceSynchronize(),重新运行 Nsight,发现它们现在重叠了:

[Memcpy HtoD]
         [kernelThings]

内核现在在 cudaMemcpy 完成前 10 微秒启动。

显然,正确的解决方法是使用带有 cudaMemcpyAsync() 的流:

cudaMemcpyAsync(devInputData, ..., cudaMemcpyHostToDevice, myStream);

但是,我的问题是为什么我在使用 cudaDeviceSynchronize() 时没有看到进程重叠?一个简单的答案是,我可能没有那么幸运,使用不同版本的 CUDA 或 GPU?

1 个答案:

答案 0 :(得分:-1)

不要依赖隐式/默认流和操作及其同步行为的拐杖。对于您的第一个“Hello world”级别的程序 - 这可能很方便,但正如您自己所注意到的,您必须成为 API 律师或通灵者才能猜测在某些复杂场景中究竟会发生什么。

只需确保您在内核中使用的每个缓冲区是:

  1. 由同一命令队列上的先前操作填充或


  2. 2.1 如果它是一个输入缓冲区 - 有一个事件对,确保在内核在另一个流上启动之前发生任何影响缓冲区的事情,并且

    2.2 如果它是一个输出缓冲区 - 有一个事件对,确保在输出缓冲区在其他地方使用之前执行内核