【问题标题】:CUDA performance improves when running more threads than there are cores运行的线程数多于内核数时,CUDA 性能会提高
【发布时间】:2012-11-25 18:23:35
【问题描述】:

为什么当我每个块运行超过 32 个线程时性能会提高?

我的显卡有 480 个 CUDA 核心 (15 MS * 32 SP)。

【问题讨论】:

    标签: c++ cuda opencl


    【解决方案1】:

    每个 SM 有 1-4 个 warp 调度器(特斯拉 = 1,费米 = 2,开普勒 = 4)。每个warp调度器负责执行分配给SM的warp子集。每个 warp 调度程序都维护一个合格的 warp 列表。如果扭曲可以在下一个周期发出指令,则它是合格的。如果 warp 因数据依赖关系而停止,等待获取和指令,或者下一条指令的执行单元正忙,则 warp 不符合条件。在每个循环中,每个 warp 调度程序都会从​​符合条件的 warp 列表中选择一个 warp 并发出 1 或 2 条指令。

    每个 SM 的活动 warp 越多,每个 warp 调度程序必须在每个周期中选择的 warp 数量就越大。在大多数情况下,当每个 SM 有足够的活动 warp 以使每个循环调度程序有 1 个合格的 warp 时,可以实现最佳性能。超过此点的占用率不会提高性能,并且可能会降低性能。

    主动扭曲的典型目标是 SM 最大扭曲的 50-66%。发射配置支持的扭曲与最大扭曲的比率称为理论占用率。每个周期的活动经线与每个周期的最大经线的运行时比率是已达到的占用率。对于 GTX480(CC 2.0 设备),设计内核时的一个良好起点是 50-66% 的理论占用率。 CC 2.0 SM 最多可以有 48 个经线。 50% 的占用率意味着每个 SM 有 24 个经纱或 768 个线程。

    Nsight Visual Studio 版中的 CUDA Profiling Activity 可以显示理论占用率、实现的占用率、每个 SM 的活动 warp、每个 SM 合格的 warp 以及停顿原因。

    CUDA Visual Profiler、nvprof 和命令行分析器可以显示理论占用率、活动扭曲和实现的占用率。

    注意:CUDA 核心数只能用于比较类似架构的卡、计算理论 FLOPS 以及可能比较架构之间的差异。设计算法时不要使用计数。

    【讨论】:

    • 谢谢你,格雷格。非常有用的信息。为什么 50-66% 是一个好的目标?
    • @RogerDahl:为了达到高占用率,您的内核必须使用很少的寄存器(并且本地内存不多)。这意味着基本上只有最简单的内核才能达到 100%,这使得在大多数情况下瞄准这一目标是不切实际的。此外,达到 100% 而不是 50% 的性能优势并不是很大(即使 50% 的占用率也会隐藏大多数延迟)。因此,以 50% 为目标可以让您实际做事,同时仍能获得大部分性能
    • 正如@Grizzly 回答的那样,占用是扭曲和其他资源之间的权衡。 50-66% 往往是你有足够的活动 warp 的地方,这样每个调度程序每个周期至少有 1 个 warp 是合格的。如果内核是数学密集型的并且有很多 ILP 占用可以减少并且仍然覆盖延迟。如果内核受内存限制,则通常需要增加占用率。
    【解决方案2】:

    欢迎来到 Stack Overflow。原因是 CUDA 内核是流水线的。在 Fermi 上,管道大约有 20 个时钟长。这意味着要使 GPU 饱和,每个内核可能需要多达 20 个线程。

    【讨论】:

    • 此外,在 SM 单元上拥有比运行中的内核更多的线程允许 GPU 隐藏内存访问延迟。如果您运行的线程数与内核数完全相同,那么当一个线程访问全局内存时,它必须等待数百个时钟周期才能真正接收到数据。同时,如果没有多余的线程,该 SM 单元将处于空闲状态。如果有额外的线程,SM 单元将暂停等待内存访问的线程并切换到另一组有更多工作要做的线程。这有效地隐藏了访问全局内存的开销。
    • Brendan 的回答是正确的,但它确实与管道深度没有太大关系。它使用每个内核的多个线程来隐藏内存访问的延迟,而不是管道深度。如果您的代码不需要访问内存,您可以以 100% 的效率运行流水线 CPU 内核(甚至是 CUDA 内核),每个内核只有 1 个线程。管道深度很重要的地方在于需要刷新管道(例如分支)的指令的执行延迟,但每个内核有多个线程通常并不一定有助于解决这个问题。
    • @reirab:请记住,管道中存在与管道深度相对应的延迟。使用深流水线,很难找到足够的指令级并行性 (ILP) 来使内核饱和。因此,如果没有线程级并行 (TLP),您会遇到停顿,因为所有剩余的指令都需要仍在管道中的东西作为输入。
    【解决方案3】:

    主要原因是 CUDA 的内存延迟隐藏模型。大多数现代 CPU 使用缓存来隐藏主内存的延迟。这导致很大比例的芯片资源被用于缓存。大多数台式机和服务器处理器在芯片上都有几兆字节的缓存,这实际上占了芯片空间的大部分。为了封装更多具有相同能耗和散热特性的内核,基于 CUDA 的芯片将其芯片空间投入到大量 CUDA 内核(大多数只是浮点 ALU)上。因为缓存非常少,而是依靠让更多线程准备好运行,而其他线程正在等待内存访问返回以隐藏延迟。这使内核在一些线程等待内存访问时可以高效地工作。每个 SM 的 warp 越多,它们中的一个在任何给定时间运行的机会就越大。

    CUDA 还具有零成本线程切换,以帮助这种内存延迟隐藏方案。由于需要将要切换的线程的所有寄存器值存储到堆栈上,然后为该线程加载所有寄存器值,因此普通 CPU 从执行一个线程切换到下一个线程会产生很大的开销正在切换到。 CUDA SM 仅有大量的寄存器,因此每个线程都有自己的一组物理寄存器,在线程的生命周期中分配给它。由于不需要存储和加载寄存器值,每个 SM 可以在一个时钟周期从一个 warp 执行线程,并在下一个时钟周期从另一个 warp 执行线程。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-10
      相关资源
      最近更新 更多