关于OOO,我们假设我只有一个进程(有一个线程)运行此代码:
void foo() {
if (x == 0) {
return;
}
y->data = 5;
}
现在,我们假设我知道 y
仅在x
不为零时才有效。
从硬件的角度来看,CPU可以在阅读y->data = 5
之前执行x
吗?
它可能导致CPU访问NULL /垃圾指针并崩溃。
如果没有,这是什么原因? 因为if / while / for / goto是控制语句,CPU在看到控制语句时不会提前获取指令吗?
我记得,OOO应该对执行其指令的一个线程100%透明。
答案 0 :(得分:2)
取决于你如何看待它。
从用户的角度来看,程序的行为必须“好像”它是按顺序运行的 换句话说,顺序运行和使用OOE运行之间没有明显的区别。 (除了表演)
从CPU的角度来看,是的,它实际上可以绕过if语句并执行y->data = 5;
。但这是因为branch prediction而不是OOE。
在现代处理器上,线程可能会错误地预测分支:
if (x == 0) {
return;
}
并且实际上尝试执行y->data = 5;
...
如果发生这种情况并且y
是一个错误的指针,它将获得硬件异常,但由于执行仍处于推测模式,因此该异常被保留。
一旦线程意识到它错误预测了分支,它就会抛弃分支之外的所有内容(包括异常)。
所以最后,没有什么可担心的。即使处理器试图做某事它也不能,它也不会影响顺序行为。
换句话说,如果一个现代处理器造成一个不是你的错误的混乱,它将会自行清理。
当你有多个线程时,事情变得更加丑陋,但这超出了这个问题的范围。
答案 1 :(得分:0)
答案是:这取决于。
C标准描述了中的抽象机器,其中优化问题无关紧要。除了访问volatile
对象之外,如果语句不改变程序的可观察行为,则可以自由地重新排序语句。 C11提出了该程序的可观察行为的定义:
(C11,5.1.2.3p6)“符合要求的实施的最低要求是:
- 严格按照摘要规则评估对易失性对象的访问 机。
- 程序终止时,写入文件的所有数据应与结果相同 根据抽象语义执行程序会产生。
- 交互设备的输入和输出动态应按照规定进行 7.21.3。这些要求的目的是无缓冲或行缓冲输出 尽快出现,以确保提示消息实际出现在之前 等待输入的程序。
这是程序“
的可观察行为
C11也有这一段(已经在C99中出现):
(C11,5.1.2.3p10)“或者,实现可以在每个翻译单元内执行各种优化,例如 只有在跨翻译单元边界进行函数调用时,实际语义才会与抽象语义一致。 [...]“
因此,在您的示例中,它实际上取决于y
的声明方式(即,它的链接是什么?)以及它在程序中的使用方式。
重新排序实际上是由编译器在现实生活中使用,如果它认为它不会影响程序的可观察行为,则优化代码的某些部分。