为什么每个块运行超过 32 个线程时性能会提高?
我的显卡有 480 个 CUDA 核心 (15 MS * 32 SP)。
每个 SM 有 1-4 个 warp 调度器(Tesla = 1,Fermi = 2,Kepler = 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 的活动经线、每个 SM 的合格经线和停顿原因。
CUDA Visual Profiler、nvprof 和命令行分析器可以显示理论占用率、活动扭曲和实现的占用率。
注意:CUDA 核心数只能用于比较类似架构的卡、计算理论 FLOPS 以及可能比较架构之间的差异。设计算法时不要使用计数。
欢迎来到堆栈溢出。原因是 CUDA 内核是流水线的。在 Fermi 上,管道大约有 20 个时钟长。这意味着要使 GPU 饱和,每个内核可能需要多达 20 个线程。
主要原因是 CUDA 的内存延迟隐藏模型。大多数现代 CPU 使用缓存来隐藏主内存的延迟。这导致很大比例的芯片资源被用于缓存。大多数台式机和服务器处理器在芯片上都有几兆字节的缓存,这实际上占了芯片空间的大部分。为了封装更多具有相同能耗和散热特性的内核,基于 CUDA 的芯片将其芯片空间投入到大量 CUDA 内核(大多数只是浮点 ALU)上。因为缓存非常少,而是依靠让更多线程准备好运行,而其他线程正在等待内存访问返回以隐藏延迟。这使内核在一些线程等待内存访问时可以高效地工作。
CUDA 还具有零成本线程切换,以帮助这种内存延迟隐藏方案。由于需要将要切换的线程的所有寄存器值存储到堆栈上,然后为该线程加载所有寄存器值,因此普通 CPU 从执行一个线程切换到下一个线程会产生很大的开销正在切换到。CUDA SM 只有成吨的寄存器,所以每个线程都有自己的一组物理寄存器分配给它通过线程的生命周期。由于不需要存储和加载寄存器值,每个 SM 可以在一个时钟周期从一个 warp 执行线程,并在下一个时钟周期从另一个 warp 执行线程。