由于优化而重新排序代码
Code reordering due to optimization
我听说很多次优化器可能会对你的代码重新排序,我开始相信它。是否有任何可能发生这种情况的例子或典型案例,我如何避免这样的事情(例如,我希望基准不受此影响)?
有很多不同类型的"代码运动"(移动代码),它是由优化过程的许多不同部分引起的:
- 移动这些指令,因为等待内存读取完成而不在使用从内存中获取的内容在内存读取和操作之间放置至少一两条指令是浪费时间
- 将事情移出循环,因为它只需要发生一次(如果你调用
x = sin(y)
一次或 1000 次而不改变 y,x
将具有相同的值,所以在循环中这样做没有意义。所以编译器把它移了出来。 - 基于"编译器希望此代码比另一个位更频繁地命中,因此如果我们这样做,缓存命中率会更好"来移动代码 - 例如,错误处理从错误源移开,因为您不太可能收到错误[编译器通常理解常用函数,并且它们通常会导致成功]。
- 内联 - 代码从实际函数移动到调用函数中。这通常会导致其他影响,例如减少堆栈中的推送/弹出寄存器,并且参数可以保留在原处,而不必将它们移动到"正确的参数位置"。
确定我错过了上面列表中的一些案例,但这肯定是最常见的一些情况。
编译器完全有权这样做,只要它没有任何"可观察的差异"(除了运行所需的时间和使用的指令数量 - 当涉及到编译器时,这些"不计入"可观察的差异)
您几乎无法避免编译器对代码进行重新排序 - 您可以编写代码来在一定程度上确保顺序。例如,我们可以有这样的代码:
{
int sum = 0;
for(i = 0; i < large_number; i++)
sum += i;
}
现在,由于未使用sum
,编译器可以将其删除。添加一些检查打印总和的代码将确保根据编译器"使用"它。
同样:
for(i = 0; i < large_number; i++)
{
do_stuff();
}
如果编译器可以确定do_stuff
实际上并没有更改任何全局值或类似值,它将移动代码以形成以下内容:
do_stuff();
for(i = 0; i < large_number; i++)
{
}
编译器还可以删除 - 事实上几乎肯定会删除 - 现在的空循环,以便它根本不存在。[如评论中所述:如果do_stuff
实际上并没有改变自身之外的任何内容,它也可能被删除,但我想到的例子是do_stuff
产生结果的地方,但每次结果都是一样的]
(例如,如果您删除 Dhrystone 基准测试中结果的打印输出,就会发生上述情况,因为某些循环计算的值除了打印输出之外从未使用过 - 这可能导致基准测试结果超出处理器的最高理论吞吐量 10 倍左右 - 因为基准测试假设循环所需的指令实际上存在, 并说执行每次迭代需要 X 名义操作)
除了确保do_stuff
更新函数外部的某些变量或返回"已使用"的值(例如求和或其他东西)之外,没有简单的方法可以确保这种情况不会发生。
删除/省略代码的另一个示例是多次将值重复存储到同一变量中:
int x;
for(i = 0; i < large_number; i++)
x = i * i;
可以替换为:
x = (large_number-1) * (large_number-1);
有时,您可以使用volatile
来确保某些事情确实发生,但在基准测试中,这可能是有害的,因为编译器也无法优化它应该优化的代码(如果您不小心如何使用volatile
)。
如果你有一些你特别关心的特定代码,最好发布它(并使用几个最先进的编译器编译它,看看他们实际用它做什么)。
[请注意,移动代码通常绝对不是一件坏事 - 我确实希望我的编译器(无论是我自己编写的编译器,还是我正在使用的由其他人编写的编译器)通过移动代码进行优化,因为,只要它正确地这样做,它就会通过这样做产生更快/更好的代码!
大多数情况下,只有在程序的可观察效果相同的情况下才允许重新排序 - 这意味着您不应该能够分辨。
反例确实存在,例如,操作数的顺序是未指定的,优化器可以自由地重新排列事物。您无法预测这两个函数调用的顺序,例如:
int a = foo() + bar();
阅读序列点以了解做出了哪些保证。
- 二叉排序树无法编译
- 仅使用绝对值对数组进行排序,并在C++中显示实际值
- C++选择排序算法中的逻辑错误
- 使用C++程序合并排序没有得到正确的输出
- 计算排序向量的向量中唯一值的计数
- 排序算法c++
- 使用2个键的cpp-stl::优先级队列排序不正确
- 将结构向量排序为子组
- 在c++中尝试对对象数组进行排序时,出现std:bad_alloc错误
- 数组将排序排序为降序而不是升序
- 如何按姓氏排序并打印新数组
- 将一个数组的每个元素乘以另一个数组的每个元素,并对新的非常大的数组进行排序
- 合并排序 - 返回新数组,而不是将合并的数组复制到输入数组
- 如何对骰子进行排序并创建每个数组中具有相等骰子的新数组,Yahtzee,C++
- 与排序相关的算法(将每个项目替换为排序排序中的索引)
- 如何将未排序数组的排序索引放入新数组中
- "std::"具有排序/排序、存在测试和头/尾访问的数据结构?
- 创建没有重复项的新排序向量
- 使用快速排序排序不会给出排序的数组
- 读取文本行,对它们进行排序并将它们写入新的文本文件