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

我不清楚:哪个更好?我看不到使用其他版本可以更快地工作的任何东西。我在这里错过了什么吗?

我所看到的是每条指令都取决于前一条指令,这意味着我需要等待前一条指令完成才能启动之后的指令...

谢谢

4

5 回答 5

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 数组),那么展开循环会更有效,因为底层程序集是更直接地编写的。

于 2012-04-09T22:02:38.707 回答
4

循环展开用于减少跳转和分支指令的数量,这可能会使循环更快,但会增加二进制文件的大小。根据实施和平台,任何一个都可能更快。

于 2012-04-09T22:04:08.520 回答
3

好吧,这段代码是“更好”还是“更差”完全取决于AB和的实现、您期望C的值、n您使用的编译器以及运行的硬件。

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

于 2012-04-09T22:01:25.203 回答
2

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

在第一个版本中,三个函数可以同时运行,假设没有相互依赖关系。在第二个版本中,所有三个函数都可以同时使用所有三个数据集运行,假设您有足够的执行单元来执行此操作,并且没有相互依赖关系。

于 2012-04-09T22:07:03.607 回答
0

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

此外,很多时候循环展开不是很容易,如前所述,它很大程度上取决于平台、编译器等。

您还可以使用编译器选项。一个有趣的 gcc 选项是“-floop-optimize”,您可以使用“-O、-O2、-O3 和 -Os”自动获得

编辑此外,查看“-funroll-loops”编译器选项。

于 2012-04-10T07:18:23.767 回答