Cuda 并行运行多个流(而不是线程/块)

Cuda 并行运行多个流(而不是线程/块),cuda,Cuda,我有一个内核,我想从配置“1块x32线程”开始。为了提高并行性,我想启动几个流,而不是运行比“1块x 32个线程”更大的“工作包”。我想在数据来自网络的程序中使用GPU。我不想等到一个更大的“工作包”出现。 代码如下: Thread(i=0..14) { - copy data Host -> GPU [cudaMemcpyAsync(.., stream i)] - run kernel(stream i) - copy data GPU -> Host [cudaMe

我有一个内核,我想从配置“1块x32线程”开始。为了提高并行性,我想启动几个流,而不是运行比“1块x 32个线程”更大的“工作包”。我想在数据来自网络的程序中使用GPU。我不想等到一个更大的“工作包”出现。 代码如下:

Thread(i=0..14) {
  - copy data Host -> GPU [cudaMemcpyAsync(.., stream i)]
  - run kernel(stream i)
  - copy data GPU -> Host [cudaMemcpyAsync(.., stream i)]
}
真正的代码要复杂得多,但我希望保持简单(15个CPU线程使用GPU)

代码可以工作,但流不能按预期并发运行。GTX 480有15个SMs,其中每个SM有32个着色器处理器。我预计,如果启动内核15次,所有15个流都会并行运行,但事实并非如此。我使用了英伟达可视化剖析器,最多有5个流并行运行。通常只有一条流运行。演出真的很糟糕

我使用“64块x1024线程”配置获得最佳结果。如果我改为使用“32块x102threads”配置,但使用两个流,则流会一个接一个地执行,性能会下降。我正在使用Cuda Toolkit 5.5和Ubuntu 12.04

有人能解释一下为什么会这样,能给我一些背景资料吗?它应该在更新的GPU上工作得更好吗?在不想缓冲数据的时间紧迫型应用程序中,使用GPU的最佳方式是什么?也许这是不可能的,但我正在寻找技术,使我更接近一个解决方案

新闻:

我做了进一步的研究。问题是最后一个cudaMemcpyAsync(..)(GPU->主机复制)调用。如果我删除它,所有流都会并发运行。我认为幻灯片21说明了这个问题。他们说费米上有两个拷贝队列,但这只适用于特斯拉和夸德罗卡,对吗?我认为问题在于GTX 480只有一个复制队列,所有复制命令(主机->GPU和GPU->主机)都放在这个队列中。一切都是非阻塞的,第一个线程的GPU->host memcopy会阻塞其他线程的host->GPU memcopy调用。 以下是一些观察:

Thread(i=0..14) {
  - copy data Host -> GPU [cudaMemcpyAsync(.., stream i)]
  - run kernel(stream i)
}
->工作:流同时运行

Thread(i=0..14) {
  - copy data Host -> GPU [cudaMemcpyAsync(.., stream i)]
  - run kernel(stream i)
  - sleep(10)
  - copy data GPU -> Host [cudaMemcpyAsync(.., stream i)]
}
Thread(i=0..14) {
  - copy data Host -> GPU [cudaMemcpyAsync(.., stream i)]
  - run kernel(stream i)
  - cudaStreamSynchronize(stream i)
  - copy data GPU -> Host [cudaMemcpyAsync(.., stream i)]
}
->工作:流同时运行

Thread(i=0..14) {
  - copy data Host -> GPU [cudaMemcpyAsync(.., stream i)]
  - run kernel(stream i)
  - sleep(10)
  - copy data GPU -> Host [cudaMemcpyAsync(.., stream i)]
}
Thread(i=0..14) {
  - copy data Host -> GPU [cudaMemcpyAsync(.., stream i)]
  - run kernel(stream i)
  - cudaStreamSynchronize(stream i)
  - copy data GPU -> Host [cudaMemcpyAsync(.., stream i)]
}
->没用!!!可能cudaStreamSynchronize已放入复制队列

有人知道这个问题的解决方案吗。类似于阻塞内核调用的东西会很酷。如果内核已完成,则应调用最后一个cudaMemcpyAsync()(GPU->device)

Edit2: 下面举一个例子来说明我的问题: 为了保持简单,我们有两条流:

Stream1:
------------
HostToGPU1
kernel1
GPUToHost1

Stream2:
------------
HostToGPU2
kernel2
GPUToHost2
第一个流被启动。执行HostToGPU1,启动kernel1并调用GPUToHost1。GPUToHost1阻塞,因为内核1正在运行。与此同时,Stream2已启动。调用HostToGPU2时,Cuda将其放入队列中,但它无法执行,因为GPUToHost1会阻塞直到内核1完成。目前没有数据传输。Cuda只是在等待GPUToHost1。所以我的想法是在内核1完成后调用GPUToHost1。这可能是它使用sleep(..)的原因,因为在内核完成后会调用GPUToHost1。自动阻塞CPU线程的内核启动会很酷。
GPUToHost1在队列中没有阻塞(如果当时没有其他数据传输,但在我的情况下,数据传输并不耗时)。

在linux上最容易看到并发内核执行

有关良好示例和简单测试,请参阅

内核之间良好的并发性通常需要以下几点:

  • 支持并发内核的设备,如cc 2.0或更新的设备
  • 在块数和其他资源使用(寄存器、共享内存)方面足够小的内核,以便多个内核可以实际执行。资源需求较大的内核通常会连续运行。这是预期的行为
  • 正确使用流以实现并发
此外,并发内核通常意味着复制/计算重叠。为了使复制/计算重叠工作,您必须:

  • 使用具有足够复制引擎的GPU。有些GPU有一个引擎,有些有两个。如果您的GPU有一个引擎,您可以将一个复制操作(即一个方向)与内核执行重叠。如果您有2个复制引擎(您的GeForce GPU有1个),则可以将复制的两个方向与内核执行重叠
  • 对于要复制到GPU全局内存或从GPU全局内存复制到GPU全局内存或从GPU全局内存复制到GPU全局内存或从GPU全局内存复制到GPU全局内存或从GPU全局内存复制到GPU全局内存的数据,请使用固定(主机)内存
  • 正确使用流和相关api调用的必要异步版本(例如,
    cudaMemcpyAsync
根据您的观察,较小的32x1024内核不会同时执行,这可能是资源问题(块、寄存器、共享内存)防止太多的重叠。如果第一个内核中有足够的块来占用GPU执行资源,那么在第一个内核完成或基本完成之前,期望其他内核开始执行是不明智的

编辑:回答下面的问题编辑和附加评论

是的,GTX480只有一个拷贝“队列”(我在回答中明确提到了这一点,但我称之为拷贝“引擎”)。在任何给定时间,您只能运行一个cudaMemcpy…操作,因此只有一个方向(H2D或D2H)实际上可以在任何给定的时间移动数据,并且您将只看到一个cudaMemcpy…操作与任何给定的内核重叠。并且
cudaStreamSynchronize
会导致流等待,直到之前向该流发出的所有CUDA操作完成

请注意,我认为您在上一个示例中使用的
cudaStreamSynchronize
应该不是必需的