涉及编译器重新排序的优化示例

时间:2014-12-23 05:58:58

标签: c++ c compiler-optimization

C& C&只要as-if规则成立,C ++编译器就可以重新排序操作。编译器执行此类重新排序的示例是什么,通过这样做可以获得潜在的性能提升?

欢迎在任何平台上使用任何(C / C ++)编译器的示例。

3 个答案:

答案 0 :(得分:10)

假设您正在执行以下操作:

int i=0,j=0;
i++;
i++;
i++;
j++;
j++;
j++;

忽略目前三个增量可能被编译器优化为一个+=3的情况,如果将操作重新排序为

,则最终会有更高的处理器 - 管道吞吐量
i++;
j++;
i++;
j++;
i++;
j++;

由于j++在前一种情况下不必等待i++的结果,因此大多数指令都对前一条指令有数据依赖性。在更复杂的计算中,在没有减少要执行的指令数量的简单方法的情况下,编译器仍然可以查看数据依赖性并重新排序指令,以便根据早期指令的结果的指令是尽可能远离它。

此类优化的另一个例子是当您处理pure functions时。再看一个简单的例子,假设你有一个纯函数f(int x),你在循环中求和。

int tot = 0;
int x;//something known only at runtime
for(int i = 0; i < 100; i++)
  tot += f(x);

由于f是一个纯函数,编译器可以根据需要重新排序对它的调用。特别是,它可以将此循环转换为

int tot = 0;
int x;//something known only at runtime
int fval = f(x);
for(int i = 0; i < 100; i++)
  tot += fval;

答案 1 :(得分:4)

我确信有很多例子可以让重新排序操作产生更快的性能。一个明显的例子是尽可能早地重新排序负载,因为这些通常比其他CPU操作慢得多。通过在获取内存时执行其他不相关的工作,CPU可以节省整体时间。

也就是说,给出类似的东西:

expensive_calculation();
x = load();
do_something(x);

我们可以像这样重新排序:

x = load();
expensive_calculation();
do_something(x);

因此,当我们等待负载完成时,我们基本上可以免费expensive_calculation()

答案 2 :(得分:4)

假设您有一个循环:

for (i=0; i<n; i++) dest[i] = src[i];

思考memcpy。您可能希望编译器能够对其进行向量化,即一次加载8或16个字节,然后一次存储8或16个字节。进行转换是一种重新排序,因为它会导致在src[1]存储之前读取dest[0]。此外,除非编译器知道srcdest不重叠,否则它是无效转换,即编译器不允许进行转换。使用restrict关键字(C99及更高版本)可以让您告诉编译器它们不重叠,以便可以进行这种(非常有价值的)优化。

同样的事情一直出现在不仅仅是复制的数组上 - 例如矢量/矩阵运算,声音/图像样本数据的转换等等。