我听过很多次,优化器可能会重新排序我开始相信它的代码.是否有任何可能发生这种情况的例子或典型案例,我怎样才能避免这样的事情(例如我希望基准不受此影响)?
有许多不同类型的"代码运动"(移动代码),它是由优化过程的许多不同部分引起的:
x = sin(y)不改变y的情况下调用一次或1000次,x将具有相同的值,因此在循环中执行此操作没有意义.因此编译器将其移出.我确定我错过了上面列表中的一些案例,但这肯定是最常见的一些案例.
编译器完全有权这样做,只要它没有任何"可观察的差异"(除了运行所花费的时间和使用的指令数量 - 在可观察到的差异中"不计算"当谈到编译器)
你可以做很少的事情来避免编译器重新排序你的代码 - 你可以编写一些代码来确保订单的某种程度.例如,我们可以使用以下代码:
{
int sum = 0;
for(i = 0; i < large_number; i++)
sum += i;
}
Run Code Online (Sandbox Code Playgroud)
现在,由于sum未使用,编译器可以删除它.添加一些检查打印总和的代码将确保根据编译器"使用"它.
同样:
for(i = 0; i < large_number; i++)
{
do_stuff();
}
Run Code Online (Sandbox Code Playgroud)
如果编译器可以弄清楚do_stuff实际上并没有改变任何全局值或类似的东西,它会移动代码来形成这个:
do_stuff();
for(i = 0; i < large_number; i++)
{
}
Run Code Online (Sandbox Code Playgroud)
编译器也可以删除 - 事实上几乎肯定会 - 现在是空循环,以便它根本不存在.[正如评论中所提到的:如果do_stuff实际上并没有改变任何外部的东西,它也可能被移除,但我想到的例子是do_stuff产生结果的地方,但结果每次都是相同的]
(例如,如果您在Dhrystone基准测试中删除结果的打印输出,则会出现上述情况,因为某些循环会计算除打印输出之外从未使用过的值 - 这可能会导致基准测试结果超出处理器的最高理论吞吐量大约10倍左右 - 因为基准测试假设循环所需的指令实际上存在,并说它需要X个标称操作来执行每次迭代)
除了确保do_stuff更新函数外部的某个变量或返回"已使用"的值(例如总结或其他内容)之外,没有简单的方法可以确保不会发生这种情况.
删除/省略代码的另一个示例是将值重复存储到同一个变量的位置:
int x;
for(i = 0; i < large_number; i++)
x = i * i;
Run Code Online (Sandbox Code Playgroud)
可以替换为:
x = (large_number-1) * (large_number-1);
Run Code Online (Sandbox Code Playgroud)
有时,您可以使用volatile以确保真正发生的事情,但在基准测试中,这可能是有害的,因为编译器也无法优化它应该优化的代码(如果您不小心使用它volatile).
如果您有一些特别关注的SPECIFIC代码,最好发布它(并使用几个最先进的编译器编译它,看看它们实际上用它做了什么).
[请注意,移动代码通常不是一件坏事 - 我确实希望我的编译器(无论是我自己编写的编译器,还是我正在使用的编译器是由其他人编写的)来进行优化移动代码,因为,只要它正确地运行,它将通过这样做产生更快/更好的代码!]
| 归档时间: |
|
| 查看次数: |
459 次 |
| 最近记录: |