几个关于CUDA性能的问题

3

这是我第一次在这里提问,非常感谢您的帮助,还请原谅我的无知。我刚刚开始学习CUDA编程。

基本上,我有一堆点,想要计算所有成对距离。目前,我的核函数只保留一个点,并迭代地从全局内存中读取所有其他点,并进行计算。以下是我的一些困惑:

  • 我正在使用具有448个核心的Tesla M2050。但是我的当前并行版本(kernel<<<128,16,16>>>)实现了更高的并行性(大约比kernel<<<1,1,1>>>快600倍)。这可能是由于多线程或管道问题,还是它们实际上表示相同的事情?

  • 我想进一步提高性能。因此,我考虑使用共享内存来保存每个多处理块的一些输入点。但是新代码的速度与旧代码相同。可能的原因是什么?这可能与我设置太多线程有关吗?

  • 或者,是因为代码中有if语句吗?问题是,我只考虑和计算短距离,所以我有一个类似于(if dist < 200)的语句。我应该对这个语句有多担心?

非常感谢! Bin


与以往不同,Fermi拥有自动缓存。因此,你在GPU 101中学到的许多简单技巧都已经过时。 - Mikhail
消除分支的一个小技巧是这样做:count += (distance < 200); - Nathan
@Nathan:GPU 支持一种叫做“预测”的系统。它允许 GPU 基于某些条件来“预测”机器代码指令的执行。编译器使用这个系统来避免在包含少量指令的条件语句中进行分支操作。 - Roger Dahl
@misha 谢谢!你是在暗示 Fermi 的自动缓存可能已经覆盖了引入共享内存的潜在好处,这是可以理解的吗? - bin
@Nathan 非常感谢!棘手的部分是我需要使用一个数组来跟踪一堆计数器,而且只有很少一部分距离小于200。目前我正在使用全局内存(尝试了原子共享内存,但由于可能存在太多冲突,性能实际上变得更差),而你提出的方法可能会增加太多的内存访问。但我想如果每个距离的计算都必须命中内存,那么这应该会变得有益。(对于冗长的解释感到抱歉) - bin
@RogerDahl 感谢您的评论!但是,如果分支预测失败,它仍然需要返回重新执行实际指令,从而大大增加了处理时间。这是正确的吗? - bin
2个回答

4

马克·哈里斯有一份非常好的关于优化CUDA的演示文稿:在CUDA中优化并行带约简操作

算法优化
地址和算法串联变化
总共提速11.84倍!
代码优化
循环展开
总共提速2.54倍

多余的操作语句确实会造成问题,虽然这可能是你最不想优化的事情,因为你需要在实现大小假设之前了解你的代码布局!

你正在解决的问题听起来像是著名的n体问题,见使用CUDA进行快速N体模拟

如果您可以避免进行成对计算,例如元素之间太远以至于互相没有影响,则可以实现额外的性能提升。这适用于任何可以几何表达的关系,无论是成对成本还是具有弹簧的物理模拟。我的方法是将网格划分为盒子,并通过除法将每个元素放入一个盒子中,然后只在相邻盒子之间计算成对关系。这可以称为O(n*m)。


3

(1) GPU运行的线程数比核心多得多,这是因为每个核心都是流水线式的。在计算能力2.0(Fermi)架构上,操作大约需要20个周期。因此,对于每个时钟周期,核心会开始处理新的操作,返回一个操作的完成结果,并将其他(大约18个)操作向完成方向前进一步。因此,要使GPU饱和,您可能需要类似于448 * 20个线程。

(2) 这可能是因为您的值被缓存在L1和L2缓存中。

(3) 它取决于您在if条件语句中执行了多少工作。GPU必须通过所有32个线程来运行if中的代码,即使仅有一个线程满足条件。如果条件语句中的代码量与内核的其他部分相比较多,并且相对较少的线程通过该代码路径,那么您很可能会得到较低的计算吞吐量。


再次感谢您,罗杰!1和3非常清晰。但是,您能否详细说明您提到的流水线和warp之间的关系?据我理解,每个warp(例如包含32个线程的warp)“一起”执行相同的指令。它们实际上是否形成了一个流水线? - bin
所以我猜这里的缓存与Misha上面提到的是一样的,即“Fermi有一个自动缓存。因此,你在GPU 101中学到的许多简单技巧都已经过时了。”? - bin
@bin:当一个warp中的指令开始处理时,该指令(带有不同的操作数)会被发送到32个核心。然后整个warp会被暂停,直到操作在32个核心的管道中传输完成。某些类型的指令需要加载/存储和特殊功能单元。这些单元并不是每个都有32个,因此对于这些指令,warp会被多次调度,直到所有线程得到服务为止。 - Roger Dahl
@RogerDahl调度程序将选择一个Warp并发出1或2条指令(仅支持CC 2.1及以上版本的2个指令)。管道将读取寄存器和任何常量,并将指令分派到数据路径(执行单元)。如果执行单元不足32个线程,则需要多次发出指令。每种指令类型和每种架构退役前所需的周期数都不同。如果Warp未停顿(例如,数据或执行依赖性),则调度可以在下一个周期对同一Warp发出另一个指令。 - Greg Smith
@RogerDahl SM上可分配的线程数量取决于占用率和设备限制。最大值从CC <1.1的768增加到CC 3.x的2048。如果包括数学管道、LSU和TEX,则可以进行的指令数量为几百个。数据管道是流水线式的,不同数据管道的指令可以无序完成。饱和SM(更不用说GPU了)所需的线程数量取决于指令混合、指令级并行性和数据依赖性。50%的占用率是一个很好的估计值。 - Greg Smith

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接