【问题标题】:An example of an optimization that involves compiler reordering涉及编译器重新排序的优化示例
【发布时间】:2014-12-23 05:58:58
【问题描述】:

只要as-if 规则成立,C 和 C++ 编译器就可以重新排序操作。编译器执行这种重新排序的例子是什么?这样做有什么潜在的性能增益?

欢迎提供涉及任何平台上的任何 (C/C++) 编译器的示例。

【问题讨论】:

标签: c++ c compiler-optimization


【解决方案1】:

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

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;

【讨论】:

  • 您的第一个示例指出重新排序有助于管道吞吐量。 Tho,我认为这种优化也可以通过 CPU 重新排序来完成。如果是这样,为什么编译器会费心重新排序?提前致谢。
【解决方案2】:

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

也就是说,给定这样的事情:

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

我们可以这样重新排序:

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

因此,在我们等待加载完成时,我们基本上可以免费执行expensive_calculation()

【讨论】:

    【解决方案3】:

    假设你有一个像这样的循环:

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

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

    同样的事情总是出现在对数组的操作中,而不仅仅是复制——比如向量/矩阵操作、声音/图像样本数据的转换等。

    【讨论】:

      猜你喜欢
      • 2011-04-12
      • 2012-03-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-08
      • 1970-01-01
      相关资源
      最近更新 更多