鉴于代码:
for (int i = 0; i < n; ++i)
{
A(i) ;
B(i) ;
C(i) ;
}
优化版本:
for (int i = 0; i < (n - 2); i+=3)
{
A(i)
A(i+1)
A(i+2)
B(i)
B(i+1)
B(i+2)
C(i)
C(i+1)
C(i+2)
}
我不清楚:哪个更好?我看不到任何使用其他版本的速度更快的东西。我在这里错过了什么吗?
我所看到的是每条指令都取决于之前的指令,这意味着 我需要等待上一条指令完成才能在...之后启动一条指令。
谢谢
答案 0 :(得分:9)
在语言的高级视图中,您不会看到优化。速度增强来自编译器对你所拥有的功能。
在第一种情况下,它类似于:
LOCATION_FLAG;
DO_SOMETHING;
TEST FOR LOOP COMPLETION;//Jumps to LOCATION_FLAG if false
在第二个中它就像:
LOCATION_FLAG;
DO_SOMETHING;
DO_SOMETHING;
DO_SOMETHING;
TEST FOR LOOP COMPLETION;//Jumps to LOCATION_FLAG if false
在后一种情况下,您可以看到测试和跳转的开销仅为每3个指令。在第一个中,每1个指令1个;所以它经常发生。
因此,如果你有不变量,你可以依赖(mod 3的数组,使用你的例子),那么解开循环会更有效,因为底层程序集更直接地编写。
答案 1 :(得分:4)
循环展开用于减少跳跃次数和数量。分支指令可能会使循环更快但会增加二进制的大小。根据实施和平台,可能更快。
答案 2 :(得分:3)
那么,这段代码是“更好”还是“更差”完全取决于A
,B
和C
的实现,您期望n
的值,您正在使用哪个编译器以及您正在运行的硬件。
通常,循环展开的好处是减少了执行循环的开销(即,增加i
并将其与n
进行比较)。在这种情况下,可以减少3倍。
答案 3 :(得分:2)
只要函数A(),B()和C()不修改相同的数据集,第二个版本就会提供更多的并行化选项。
在第一个版本中,假设没有相互依赖性,三个函数可以同时运行。在第二个版本中,所有三个函数可以同时运行所有三个数据集,假设您有足够的执行单元来执行此操作,并且没有相互依赖性。
答案 4 :(得分:0)
一般来说,尝试“发明”优化并不是一个好主意,除非你有确凿的证据表明你会获得增加,因为很多时候你可能会最终引入降级。通常,获得此类证据的最佳方式是使用良好的分析器。我将使用分析器测试此代码的两个版本以查看差异。
此外,很多时候循环展开不是非常有用,如前所述,它在很大程度上取决于平台,编译器等。
您还可以使用编译器选项。一个有趣的gcc选项是“-floop-optimize”,你可以自动获得“-O,-O2,-O3和-Os”
编辑此外,请查看“-funroll-loops”编译器选项。