【问题标题】:Asynchronous streams in CUDA do not yield increase in performanceCUDA 中的异步流不会提高性能
【发布时间】:2013-03-05 03:24:05
【问题描述】:

我正在尝试通过使用多个流来加速以下 CUDA 代码。

#define N (4096 * 4096)
#define blockDimX  16
#define blockDimY  16

float domain1 [N];
float domain2 [N];

__global__ updateDomain1_kernel(const int dimX, const int dimY) {
    // update mechanism here for domain1
    // ...
}

__global__ updateDomain2_kernel(const int dimX, const int dimY) {
    // update mechanism here for domain2, which is nearly the same
    // ...
}

__global__ addDomainsTogether_kernel(float* domainOut, 
                                     const int dimX, 
                                     const int dimY) 
{
    // add domain1 and domain2 together and fill domainOut
}

void updateDomains(float* domainOut) {
    dim3 blocks((dimX + blockDimX - 1) / blockDimX , (dimY + blockDimY- 1) / blockDimY);
    dim3 threads(blockDimX, blockDimY);

    updateDomain1_kernel<<<blocks, threads>>> (dimX, dimY);
    updateDomain2_kernel<<<blocks, threads>>> (dimX, dimY);
    addDomainsTogether_kernel<<<block, threads>>> (domainOut_gpu, dimX, dimY);
    cudaMemcpy(domainOut, domainOut_gpu, N * sizeof(float), cudaMemcpyDeviceToHost);
}

精确的实现并不重要;重要的是更新各自的域是两个完全独立的操作,之后都在第三个内核调用中使用。因此,我认为尝试通过将每个更新内核放在自己的流中来加速它是一个好主意,我想同时运行它。所以我把它改成了下面这样:

void updateDomains(float* domainOut) {
    dim3 blocks((dimX + blockDimX - 1) / blockDimX , (dimY + blockDimY- 1) / blockDimY);
    dim3 threads(blockDimX, blockDimY);

    cudaStream_t stream0, stream1;
    cudaStreamCreate(&stream0);
    cudaStreamCreate(&stream1);

    updateDomain1_kernel<<<blocks, threads, 0, stream0>>> (dimX, dimY);
    updateDomain2_kernel<<<blocks, threads, 0, stream1>>> (dimX, dimY);
    cudaDeviceSynchronize();

    addDomainsTogether_kernel<<<block, threads>>> (domainOut_gpu, dimX, dimY);
    cudaMemcpy(domainOut, domainOut_gpu, N * sizeof(float), cudaMemcpyDeviceToHost);

    cudaStreamDestroy(stream0);
    cudaStreamDestroy(stream1);
}

我推测发现了性能速度的差异,但绝对没有明显的差异。因此,考虑到编译器可能是第一次通过自动调度更新调用而变得聪明,我认为以下应该会降低性能:

void updateDomains(float* domainOut) {
    dim3 blocks((dimX + blockDimX - 1) / blockDimX , (dimY + blockDimY- 1) / blockDimY);
    dim3 threads(blockDimX, blockDimY);

    cudaStream_t stream0;
    cudaStreamCreate(&stream0);

    updateDomain1_kernel<<<blocks, threads, 0, stream0>>> (dimX, dimY);
    updateDomain2_kernel<<<blocks, threads, 0, stream0>>> (dimX, dimY);

    addDomainsTogether_kernel<<<block, threads0, stream0>>> (domainOut_gpu, dimX, dimY);
    cudaMemcpy(domainOut, domainOut_gpu, N * sizeof(float), cudaMemcpyDeviceToHost);

    cudaStreamDestroy(stream0);
}

但是,性能速度几乎没有任何差异。如果有的话,最后一个似乎最快。这让我觉得我不了解 CUDA 流。有人能告诉我如何加速这段代码吗?

【问题讨论】:

  • 您的计算能力如何?为了让流正常工作,有很多规则需要遵守,我建议你阅读this
  • 我看过这个文档,但是主要讲的是数据分区,这让我有点困惑。顺便说一句,我正在使用计算能力 2.0。

标签: c cuda


【解决方案1】:

如果您没有已经使用了所有可用的内核,增加的并行性只会增加您的计算吞吐量。如果您已经拥有足够的并行度,那么它只会增加同步开销。

【讨论】:

  • 但是我怎么知道我是否有足够的并行度呢? IE。我怎么知道第一个例子就足够了?这是否意味着第二个实现可能会在另一台计算机(具有更好的 GPU)上产生更高的性能,还是代码中的某些问题?
  • 这是一个很好的答案。添加一些细节,这些内核的网格是 (4096/16)*(4096/16) = 65536 个线程块。单个内核启动中的这么多线程块将完全填满机器,阻止后续内核启动的任何线程块执行,直到第一次内核启动的几乎所有线程块都被耗尽。此问题与其他几个问题重复,例如 this one
  • 嗯,我还有一些疑问。如果你说的是对的,我希望对于较小的N,GPU 还没有满,流可以并行执行。但是无论我把域做得多么小,这都不会发生:我分析了 NSight 中的性能,无论N 的大小如何,两个流总是连续运行。还有其他方法可以确保它们并行运行吗?
  • 对于一个明显的并发内核场景,每个内核只能有“几个块”,这也取决于您在哪个 GPU 上运行。您是否尝试过小 N(例如 32*32)?这样的内核可能运行得如此之快,以至于无论如何都很难观察到重叠。如果你真的想观察并发内核执行,windows 会干扰时间轴排序,而在 linux 下,nsight EE 可能会在视觉分析器中序列化。另请阅读the requirements。这可能无法在 cmets 中涵盖。
猜你喜欢
  • 2019-08-06
  • 1970-01-01
  • 1970-01-01
  • 2021-08-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多