对程序执行顺序重新排序

定义

编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段

  • 编译器和处理器不会改变存在数据依赖关系的两个操作的执行顺序。

as-if-serial

不管怎么重排序(编译器和处理器为了提高并发度),(单线程)程序的执行结果不能被改变

  • 编译器、runtime和处理器都必须遵守as-if-serial语义

    • 不存在数据依赖,重排序
    • 存在数据依赖,不重排序
  • as-if-serial语义把单线程程序保护了起来

程序顺序规则

JMM仅仅要求前一个操作(执行结果)对后一个操作可见,且前一个操作按顺序排在第二个操作之前。

在计算机中,然间技术和硬件技术有一个共同的目标:在不改变程序执行结果的前提下,尽可能提高并行度

对多线程的影响

当代码中存在控制依赖性时,会影响指令序列执行的并行度。为此,编译器和处理器会采用猜测(Speculation)执行来克服控制相关性对并行度的影响。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ReorderExample {
int a = 0;
boolean flag = false;
// A线程执行
public void writer() {
a = 1;
flag = true;
}
// B线程执行
public void reader() {
if (flag) {
int i = a * a;
}
}
}

以处理器的猜测执行为例,执行线程B的处理器可以提前读取并计算a*a,然后把结果临时保存到一个名为**重排序缓冲(Reorder Buffer,ROB)的硬件缓存中。当flag为真时,就把该计算结果写入变量i中。

总结

在单线程程序中,对存在控制依赖的操作重排序,不会改变执行结果(这也是as-if-serial语义允许在存在控制依赖的操作做重排序的原因),但在多线程程序中,对存在控制依赖的操作重排序,可能会改变程序的执行结果