【问题标题】:CUDA determining threads per block, blocks per gridCUDA 确定每个块的线程数、每个网格的块数
【发布时间】:2011-05-22 10:15:34
【问题描述】:

我是 CUDA 范式的新手。我的问题是确定每个块的线程数和每个网格的块数。有一点艺术和试验的作用吗?我发现很多例子都为这些东西选择了看似任意的数字。

我正在考虑一个问题,我可以将任意大小的矩阵传递给乘法方法。因此,C 的每个元素(如 C = A * B)将由单个线程计算。在这种情况下,您将如何确定线程/块、块/网格?

【问题讨论】:

    标签: cuda dimensions nvidia matrix-multiplication


    【解决方案1】:

    通常,您希望调整块/网格的大小以匹配您的数据并同时最大化占用率,即一次有多少线程处于活动状态。影响占用的主要因素是共享内存使用、寄存器使用和线程块大小。

    启用 CUDA 的 GPU 将其处理能力拆分为 SM(流式多处理器),SM 的数量取决于实际的卡,但为了简单起见,这里我们将重点关注单个 SM(它们的行为都相同) .每个 SM 都有有限数量的 32 位寄存器、共享内存、最大数量的活动块和最大数量的活动线程。这些数字取决于您的 GPU 的 CC(计算能力),可以在 Wikipedia 文章 http://en.wikipedia.org/wiki/CUDA 的中间找到。

    首先,您的线程块大小应该始终是 32 的倍数,因为内核在 warp 中发出指令(32 个线程)。例如,如果您有 50 个线程的块大小,GPU 仍然会向 64 个线程发出命令,而您只是在浪费它们。

    其次,在担心共享内存和寄存器之前,请尝试根据与卡的计算能力相对应的最大线程数和块数来调整块的大小。有时有多种方法可以做到这一点……例如,CC 3.0 卡每个 SM 可以有 16 个活动块和 2048 个活动线程。这意味着如果每个块有 128 个线程,则可以在 SM 中容纳 16 个块,然后再达到 2048 个线程的限制。如果您使用 256 个线程,则只能容纳 8 个线程,但您仍在使用所有可用线程并且仍将完全占用。然而,当达到 16 个块的限制时,每个块使用 64 个线程只会使用 1024 个线程,因此只有 50% 的占用率。如果共享内存和寄存器的使用不是瓶颈,这应该是您主要关心的问题(除了您的数据维度)。

    关于您的网格主题...您的网格中的块分散在 SM 上以开始,然后将剩余的块放入管道中。一旦 SM 中有足够的资源来获取块,块就会被移动到 SM 中进行处理。换句话说,当块在 SM 中完成时,新的块会被移入。您可以认为具有较小的块(128 而不是前面示例中的 256)可能会更快完成,因为特别慢的块会占用更少的资源,但是这在很大程度上取决于代码。

    关于寄存器和共享内存,请看下一个,因为它可能会限制您的占用。共享内存对于整个 SM 来说是有限的,因此请尝试以允许尽可能多的块仍然适合 SM 的数量使用它。寄存器使用也是如此。同样,这些数字取决于计算能力,可以在维基百科页面上找到。祝你好运!

    【讨论】:

    • 为什么我们不能使用设备能力计算块/线程?假设我的设备具有 2.1 功能(GT 520),所以它有 48 个 SM,每个 8 个块和每个块 1024 个线程。我无法理解,如果我的问题很愚蠢,请见谅。
    • 有没有办法使用一些 CUDA API 以编程方式确定每个块的最大线程数?
    • cudaDeviceProp prop; cudaGetDeviceProperties( &prop, 0); docs.nvidia.com/cuda/cuda-runtime-api/structcudaDeviceProp.html
    • @underpickled 您说“网格中的块分散在 SM 上开始”。这是程序员必须做的事情,还是 GPU 负责将块分散到 SM 上?如果是后者:如果我在 32 个块上运行内核,同时使用具有 32 个 SM 的硬件,是否可以保证每个 SM 接收一个块?
    • @Silicomancer 你没有保修,GPU 自己做。多个内核可以同时运行(使用不同的流),因此当时并非所有 SM 都可用。笔记本 GPU 可能会停用一些用于电源管理的 SM。使用小型内联 PTX 程序集,您可以读取内核正在运行的 SM id (%smid),还可以找出 SM 中的 warp id (%warpid)。 SM 在内部被划分为 4 个 SMSP 单元,每个 Warp 在不同的单元上运行。
    【解决方案2】:

    https://docs.nvidia.com/cuda/cuda-occupancy-calculator/index.html

    CUDA 占用率计算器允许您通过给定的 CUDA 内核计算 GPU 的多处理器占用率。多处理器占用率是 GPU 的多处理器上支持的活动扭曲与最大扭曲数的比率。设备上的每个多处理器都有一组 N 个寄存器可供 CUDA 程序线程使用。这些寄存器是在多处理器上执行的线程块之间分配的共享资源。 CUDA 编译器尝试最小化寄存器使用以最大化机器中可以同时处于活动状态的线程块的数量。如果程序尝试启动内核,而每个线程使用的寄存器乘以线程块大小大于 N,则启动将失败...

    【讨论】:

    【解决方案3】:

    除了极少数例外,您应该在每个块中使用恒定数量的线程。然后每个网格的块数由问题大小决定,例如矩阵乘法中的矩阵维数。

    选择每个块的线程数非常复杂。大多数 CUDA 算法都承认有很大范围的可能性,并且选择基于使内核运行最有效的因素。由于线程调度硬件的工作方式,它几乎总是 32 的倍数,并且至少是 64。第一次尝试的好选择是 128 或 256。

    【讨论】:

    • 您能否更好地解释您对每个块的恒定线程的推理? (甚至链接相关文章)。非常感谢,
    【解决方案4】:

    您还需要考虑共享内存,因为同一块中的线程可以访问相同的共享内存。如果您正在设计需要大量共享内存的东西,那么每个块更多的线程可能是有利的。

    例如,在上下文切换方面,32 的任何倍数都一样。因此对于 1D 情况,启动 1 个具有 64 个线程的块或 2 个具有 32 个线程的块每个对全局内存访问没有影响。但是,如果手头的问题自然分解为 1 个长度为 64 的向量,那么第一个选项会比第二个更好(内存开销更少,每个线程都可以访问相同的共享内存)。

    【讨论】:

      【解决方案5】:

      没有灵丹妙药。每个块的最佳线程数很大程度上取决于被并行化的特定应用程序的特性。 CUDA's design guide 建议当卸载到 GPU 的函数有多个障碍时,每个块使用少量线程,但是,有实验表明,对于某些应用程序,每个块的少量线程会增加同步的开销,从而带来更大的开销.相比之下,每个块的线程数较多可能会减少同步量并提高整体性能。

      关于每个块的线程数对 CUDA 内核的影响的深入讨论(对于 StackOverflow 来说太长了),请查看this journal article,它显示了 NPB 中每个块的线程数的不同配置的测试(NAS Parallel Benchmarks)套件,一组 CFD(计算流体动力学)应用程序。

      【讨论】:

        猜你喜欢
        • 2016-01-19
        • 2011-08-28
        • 2020-10-15
        • 1970-01-01
        • 2012-08-02
        • 2015-12-27
        • 2018-07-31
        相关资源
        最近更新 更多