【GPU 算子工程】Reduction 与 Scan:warp shuffle、block 级与 grid 级归约

💡 原文中文,约4300字,阅读约需11分钟。
📝

内容提要

归约是将数组压缩为标量的基本操作,优化归约的关键在于消除分支发散和bank conflict。通过比较三种版本,顺序寻址和warp shuffle显著提高性能。单遍归约受访存限制,优化需针对真实瓶颈。跨block合并可用两趟kernel或原子加。scan比归约复杂,建议使用CUB库。

🎯

关键要点

  • 归约是将数组压缩为标量的基本操作,优化归约的关键在于消除分支发散和bank conflict。

  • 归约的并行结构采用树形结构,串行求和是O(n),而并行化的标准结构是二叉树归约。

  • 版本一使用交错寻址和取模,存在分支发散和bank conflict,性能最差。

  • 版本二采用顺序寻址,消除了发散和冲突,性能显著提高。

  • 版本三使用warp shuffle,省去了最后几步的同步,进一步提升性能。

  • 实测结果显示,顺序寻址和warp shuffle相较于交错寻址性能分别提高了1.72倍和3.4倍。

  • 单遍归约受访存限制,归约树的优化在此情况下并不明显,优化应针对真实瓶颈。

  • 跨block合并可以通过两趟kernel或原子加实现,前者适合需要可复现结果的场景,后者则省去一趟kernel但结果不确定。

  • Scan(前缀和)比归约复杂,建议使用CUB库进行实现。

🔎

延伸解读

归约优化的关键

归约操作的性能优化主要依赖于消除分支发散和bank conflict。文章中提到的三种版本中,顺序寻址和warp shuffle显著提高了性能,分别提升了1.72倍和3.4倍。这表明在设计并行算法时,选择合适的索引方式至关重要,直接影响到计算效率。

访存限制的影响

在单遍归约的情况下,访存限制成为了性能瓶颈,导致不同版本的归约性能几乎相同。文章强调,优化应针对真实瓶颈,特别是在处理大数组时,访存的效率比归约树的优化更为重要。这提醒开发者在优化时要关注整体数据流和访存策略。

跨block合并的选择

在进行跨block合并时,开发者可以选择两趟kernel或原子加。两趟kernel适合需要可复现结果的场景,而原子加则省去了一趟kernel,但可能导致结果不确定。选择合适的方法需根据具体应用场景的需求进行权衡。

延伸问答

什么是归约操作,它的主要用途是什么?

归约是将数组压缩为标量的基本操作,主要用于求和、求最大值和求范数等计算。

如何优化归约操作以提高性能?

优化归约的关键在于消除分支发散和bank conflict,采用顺序寻址和warp shuffle可以显著提高性能。

不同版本的归约实现性能差异有多大?

实测结果显示,顺序寻址和warp shuffle相较于交错寻址性能分别提高了1.72倍和3.4倍。

单遍归约的性能瓶颈是什么?

单遍归约受访存限制,归约树的优化在此情况下并不明显,优化应针对真实瓶颈。

跨block合并归约结果的常见方法有哪些?

跨block合并可以通过两趟kernel或原子加实现,前者适合需要可复现结果的场景,后者则省去一趟kernel但结果不确定。

Scan操作与归约操作有什么区别?

Scan比归约复杂,因为每个输出都依赖前面所有输入,建议使用CUB库进行实现。

🏷️

标签

➡️

继续阅读