程序序(Program Order)
对单线程程序而言,代码会一行行顺序执行,就像我们编写的程序的顺序那样。比如:
a = 1; b = 2;
会先执行`a = 1`,再执行`b = 2`,从程序角度看到的代码行依次执行叫程序序,我们在此基础上构建软件,以此作为讨论的基础。
内存序(Memory Order)
与程序序相对应叫内存序,是指从某个角度观察到的对于内存的读和写所真正发生的顺序。
内存操作顺序并不唯一,在一个包含core0和core1的CPU中,core0和core1有着各自的内存操作顺序,这两个内存操作顺序不一定相同。
从包含多个Core的CPU的视角看到的全局内存操作顺序(Global Memory Order)跟单core视角看到的内存操作顺序亦不同,而这种不同,对于有些程序逻辑而言,是不可接受的,例如:
程序序要求`a = 1`在`b = 2`之前执行,但内存操作顺序可能并非如此,对a赋值1并不确保发生在对b赋值2之前。
- 如果编译器认为对b赋值没有依赖对a赋值,那它完全可以在编译期为了性能调整编译后的汇编指令顺序
- 即使编译器不做调整,到了执行期,也有可能对b的赋值先于对a赋值执行
虽然对一个Core而言,如上所述,这个Core观察到的内存操作顺序不一定符合程序序,但内存操作序和程序序必定产生相同的结果,无论在单Core上对a、b的赋值哪个先发生,结果上都是a被赋值为1、b被赋值为2,如果单核上,乱序执行会影响结果,那编译器的指令重排和CPU乱序执行便不会发生,硬件会提供这项保证。
但多核系统,硬件不提供这样的保证,多线程程序中,每个线程所工作的Core观察到的不同内存操作序,以及这些顺序与全局内存序的差异,常常导致多线程同步失败,所以,需要有同步机制确保内存序与程序序的一致,内存屏障(Memory Barrier)的引入,就是为了解决这个问题,它让不同的Core之间,以及Core与全局内存序达成一致。
乱序执行(Out-of-order Execution)
乱序执行会引起内存顺序跟程序顺序不同,乱序执行的原因是多方面的,比如编译器指令重排、超标量指令流水线、预测执行、Cache-Miss等。内存操作顺序无法精确匹配程序顺序,这有可能带来混乱,既然有副作用,那为什么还需要乱序执行呢?
答案是为了性能。
我们先看看没有乱序执行之前,早期的有序处理器(In-order Processors)是怎么处理指令的?