欢迎在任何平台上使用任何(C / C ++)编译器的示例。
答案 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]
。此外,除非编译器知道src
和dest
不重叠,否则它是无效转换,即编译器不允许进行转换。使用restrict
关键字(C99及更高版本)可以让您告诉编译器它们不重叠,以便可以进行这种(非常有价值的)优化。
同样的事情一直出现在不仅仅是复制的数组上 - 例如矢量/矩阵运算,声音/图像样本数据的转换等等。