2012-04-09 147 views
5

给予代码:循环展开和优化

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) 
} 

东西是我不明白:这是更好?使用其他版本看不到任何更快的工作。我在这里错过了什么吗?

所有我看到的是,每一个指令根据之前的指令,这意味着 我需要等待前一指令将在以开始一前一后完成...

感谢

+1

哪种语言? – Bytemain 2012-04-09 22:00:44

+0

维基百科有一篇很好的文章,介绍循环展开后的想法,以了解它的价值:http://en.wikipedia.org/wiki/Loop_unwinding – 2012-04-09 22:02:00

+0

一般而言,这些并不等同。应该是A(i);双); C(I); A(I + 1); B(I + 1);等等。 – gnasher729 2014-06-10 21:43:15

回答

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个指令1;所以它经常发生很多。因此,如果你有不变式可以依赖(使用你的例子中的一个mod 3的数组),那么展开循环会更高效,因为底层组件的编写更直接。

3

那么,这个代码是“更好”还是“更糟糕”完全取决于ABC的实现,您期望的值为n,您正在使用哪种编译器以及正在运行哪个硬件。

通常,循环展开的好处是可以减少循环的开销(即增加i并将其与n进行比较)。在这种情况下,可以减少3倍。

4

循环展开用于减少分支指令的跳转次数,这可能会使循环更快,但会增加二进制文件的大小。取决于实施和平台,要么可能会更快。

2

只要函数A(),B()和C()不修改相同的数据集,第二个版本就提供了更多的并行化选项。

在第一个版本中,三个函数可以同时运行,假设没有相互依赖关系。在第二个版本中,假设你有足够的执行单元来做这样一次又一次,所有三个函数可以同时运行所有三个数据集,没有相互依赖关系。

0

一般来说,尝试“发明”优化并不是一个好主意,除非您有确凿证据表明您会获得增加,因为很多时候您最终可能会引入降级。通常,获得这种证据的最佳方式是使用一个好的分析器。我会用一个分析器来测试这个代码的两个版本,以查看其差异。

而且,多次循环展开心不是很可移植,如前面提到的,它极大地取决于平台,编译器等

您可以使用编译器选项还播放。一个有趣的gcc的选项是 “-floop-优化”,你有 “-O,-O2,-O3和-Os”

编辑另外自动获取,看看 “-funroll-循环” 编译选项。

+0

另外,看看这个相当简洁但令人惊叹的循环展开示例:[Duff's device](http://en.wikipedia.org/wiki/Duff%27s_device) – Brady 2012-04-10 07:33:53