Cuda 并行运行多个流(而不是线程/块)
我有一个内核,我想从配置“1块x32线程”开始。为了提高并行性,我想启动几个流,而不是运行比“1块x 32个线程”更大的“工作包”。我想在数据来自网络的程序中使用GPU。我不想等到一个更大的“工作包”出现。 代码如下: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
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
cudaStreamSynchronize
会导致流等待,直到之前向该流发出的所有CUDA操作完成
请注意,我认为您在上一个示例中使用的cudaStreamSynchronize
应该不是必需的