内容提要
Visual Studio 2022版本17.8新增了对OpenMP折叠功能的支持,可以折叠循环以提高代码质量。折叠子句适用于循环嵌套,将它们视为一个巨大的循环,可以分割为多个线程运行。编译器生成代码并使用运行时函数计算循环边界和原始归纳变量值。非矩形循环嵌套的处理方式不同,需要额外检查边界和块执行。可以在运行时实现专门的算法以提高性能。鼓励用户尝试折叠功能并提供反馈。
延伸解读
矩形与非矩形循环折叠的差异
文章指出,OpenMP 3.1 要求折叠的循环嵌套必须是矩形,即内层循环的边界和步长不能依赖于外层循环的归纳变量。而 OpenMP 5.2 允许非矩形嵌套,例如内层循环上界依赖于外层变量。编译器对这两种情况的代码生成方式不同:矩形嵌套生成一个合并的循环,非矩形嵌套则保留原循环并添加边界检查。
运行时支持与性能考量
折叠功能的实现依赖于 LLVM OpenMP 运行时中的新增函数,如矩形嵌套的 __kmpc_process_loop_nest_rectang 和非矩形嵌套的 __kmpc_for_collapsed_init。这些函数负责计算循环边界和原始归纳变量值。文章提到,当前非矩形实现是通用的,性能与 LLVM 相当,但未来可在运行时针对特定形状(如三角形循环)实现专门算法以提升性能。
使用条件与反馈渠道
要使用折叠功能,需在 Visual Studio 2022 版本 17.8 或更高版本中启用 -openmp:llvm 开关。该功能已上游至 LLVM libomp,并包含在 LLVM 17.x 中。文章鼓励用户尝试并提供反馈,可通过 Developer Community、X (@VisualC) 或邮件 visualcpp@microsoft.com 联系团队。
Q&A
Visual Studio 2022 哪个版本开始支持 OpenMP 的 collapse 子句?
Visual Studio 2022 版本 17.8 开始支持 OpenMP 的 collapse 子句。
OpenMP 的 collapse 子句有什么作用?
collapse 子句用于将嵌套循环折叠成一个巨大的循环,从而可以将其迭代空间分割到多个线程上并行执行,类似于单个 for 循环。
OpenMP 3.1 和 5.2 标准中 collapse 子句对循环嵌套的要求有什么不同?
在 OpenMP 3.1 中,循环空间必须是矩形的,即内层循环的边界和步长不能依赖于外层循环的归纳变量。而在 OpenMP 5.2 中,内层循环的上下界可以依赖于外层循环的归纳变量,允许非矩形循环嵌套。
编译器如何为矩形循环嵌套实现 collapse 子句?
编译器会生成额外的运行时调用:使用 __kmpc_process_loop_nest_rectang 计算折叠后循环的边界,并在循环体内调用 __kmpc_calc_original_ivs_rectang 从新的归纳变量值获取原始归纳变量的值。
非矩形循环嵌套的 collapse 实现与矩形有何不同?
非矩形循环嵌套的代码生成方式不同:编译器保留所有循环,并生成额外的边界检查。运行时函数 __kmpc_for_collapsed_init 返回一个块(chunk)供执行,块的边界用原始循环归纳变量表示。运行时将整个循环空间扩大为平行四边形,再分割成块,然后映射回原始空间。
collapse 功能在哪些情况下最有用?
collapse 功能最适用于 2 层或 3 层特定形状的嵌套循环,例如上界或下界为循环嵌套不变量的三角形循环。在这些情况下,运行时可以识别并采用专门的算法来更均匀地分割整体空间,从而提升性能。