CPU乱序执行是什么?
我们理解的程序是顺序执行的,其实是指在单个独立的进程中表现得像顺序运行的,而多处理器多线程共享内存系统是十分复杂的,需要约束这些复杂系统的行为。 我们将程序线程共享的单块不可分割的内存称为原子内存(atomic memory),该内存上的动作是可序列化的一系列Load 和Store操作。
通常,对单处理器(uniprocessor,单核)而言,指令间的数据依赖关系(可以是无依赖关系)能保持不变的话,就可以乱序(reordering/out-of-order execution)执行; 但对于共享内存系统(multiprocessor,多处理器)并行程序依赖 Loads和Stores到不同地址的相对顺序,需要进行约束。
例程
- 数据依赖约束
//无依赖关系,可乱序执行
X = 1;
Y = 1;
- 内存模式
//Total Store Order (TSO)内存模型,非原子内存,广泛于SPARC架构
//唯一允许的重排序:a later Load bypass an eralier Store
INIT X = 0
INIT Y = 0
INIT a = 1
INIT b = 1
/* Thread A */
X = 1;
a = Y;
/* Thread B */
Y = 1;
b = X;
/* Result */
//乱序导致的结果
//正常情况任意线程启动是先赋值X和Y,再赋值a b,那么a和b必定有一个是1
a = 0, b = 0
//正常预期结果
a = 1, b = 0
a = 0, b = 1
a = 1, b = 1
参考
Memory Model = Instruction Reordering + Store Atomicity
总结
程序中的乱序执行在高并发的应用场景中会出现不可预知的结果,现代编译器如GCC和MSVC已经对这种情况做了处理,因此我们只需要简单了解乱序执行的原因即可。
PS: 测试环境MSVC和GCC,使用C++11开启优化并未出现乱序执行,目前猜测为新版本编译器做了处理。