循环展开有利的条件以及收益率下降的点?

Posted

技术标签:

【中文标题】循环展开有利的条件以及收益率下降的点?【英文标题】:Conditions for loop unrolling to be beneficial and the point at which margins of return decrease? 【发布时间】:2013-06-21 20:48:27 【问题描述】:

请有人首先解释一下展开循环(在 C/C++ 中)在什么时候成为有用的优化?

其次,与第一个问题有关,什么时候不应该进一步展开展开?是否应该始终将展开操作拆分为 2 的幂的批次?或者它是否与您的 CPU 可以执行多少计算有关?一个比率是缓存行大小的乘数?等等

例如,如果我有一个从 0 到 99 的 for 循环会更好/我如何确定哪个(除了反复试验)- 是否有科学方法:

0 到 49,每个循环有两个“操作” 0 到 24,每个循环有四个“操作” 0 到 19 和每个循环五个“操作” 0 到 9 和每个循环十个“操作”

【问题讨论】:

有问题吗?如果是,请分析、更改并重复,直到满意为止。否则离开它。 通常编译器可以为您执行此操作。这是一个微观优化,是否有用将取决于具体情况。因此,基准测试。 (如果您可以将循环展开与特定于体系结构的矢量化结合起来,那就另当别论了。这通常会大大提高性能,并被许多高性能库使用。但同样只能是通过基准测试验证) 基本上,如果您继续展开循环开销(在单步执行、条件检查和分支指令上花费的时间)相对于实际完成的工作来说很小的点,那么您只是在浪费资源一次(展开)迭代中的循环体。没有理由更喜欢两个或特定除法的幂,但是如果循环具有固定的迭代次数,最好确保展开的步骤数均匀地除以迭代次数,这样您就不需要特殊的修复代码为尾巴。 @jxh:这是在课堂上听到的或在博客中回响的。也许会,也许不会。您必须检查汇编代码才能找到答案。 “应该”和“做”之间有很大的区别。 【参考方案1】:

如果不提及Duff's Device,我不能让有关循环展开的问题长时间无人回答。此实现与经典版本略有不同,但仍然有效。

假设您正在对一块内存应用掩码:

while (n-- > 0) 
    *ptr++ &= mask;

然后,可以这样展开:

switch (n % 4) do 
case 0: *ptr++ &= mask;
case 3: *ptr++ &= mask;
case 2: *ptr++ &= mask;
case 1: *ptr++ &= mask;
 while ((n -= 4) > 0);

如果迭代跳转的成本占循环迭代内完成的工作成本的很大一部分,则循环展开很有用。一个好的优化编译器可以在足够的优化级别为您解决这个问题。如果您的编译器无法执行此操作,您只需自行展开。

如 cmets 中所述,一旦跳转的计算成本与展开的循环体的计算相比不再显着,则无需展开。极端情况下,循环展开可能会导致指令缓存抖动,从而损害性能(类似于过度使用函数内联)。

【讨论】:

以上是关于循环展开有利的条件以及收益率下降的点?的主要内容,如果未能解决你的问题,请参考以下文章

循环展开对内存绑定数据的影响

如何在 C++ 中展开嵌套的 for 循环?

循环展开和优化

CUDA 内核:循环次数增加 10% 时性能下降 10 倍

为啥 clang 无法展开循环(即 gcc 展开)?

展开循环有效,for循环无效[重复]