08 memory order与下层的联系

与下层的联系

分层协议模型

c++11 memory order编译器,硬件形成的结构是一个分层协议。

// c++ 11 6 种 memory order
// 编译器
// cpu硬件

就是类似一套协议。仅告诉编译器硬件想要达到什么样的效果。编译器在接收到这个请求之后,每种编译器都可以有自己的选择 只要满足6种内存序的需求即可。

打个比方,有的编译器作者可能比较懒,在实现上层C++11要求的6种内存序要求时,全部直接都使用std::memory_order_seq_cst的方式来生成指令。只是性能差一点。也能够满足要求。

缓存同步与atomic

此外,编译器还需要根据不同的cpu生成不同的二进制代码。比如,如果从缓存同步的角度来讲,当release_thread写入原子变量之后,那么原子变量的内容应该可以被acquire_thread看到。这两个线程如果运行在不同的cpu,而刚好这两个cpu又有不同的cache。那么就需要在汇编发现对这个变量进行写入之后,插入cache同步指令。

但是,对于x86硬件而言,硬件是保证了cache的一致性的。也就是说,当写入内存之后,无论是否是原子变量,普通变量都可以保证强一致性。不需要软件层面来做硬件的同步。所以编译器在生成x86上的代码的时候,与生成其他的cpu上的代码的时候,可能还需要中间插入不同的指令来满足上层语言的6种内存序的要求。

进一步的讨论是,为什么x86即然不需要做cache同步,那么为什么还需要atomic变量?需要明白的是。cache同步,本来就与atomic是两个不一样的要求。atomic本身是要求C/C++的语句一定要在一个指令周期完成。而缓存同步是要求内存与CACHE的一致性。

现在讨论原子性的要求,比如普通的变量,n++,有可能在操作的时候,会被翻译成几句话。

load n内存 -> cache
cache-> eax寄存器
eax += 1
eax -> cache
cache -> n内存地址

如果翻译成这么几条指令,那么万一在执行中间某条指令的时候,cpu被切换到别的线程执行同样的指令序列。那么内存中n的值就不可预测。

所以atomic的本意就是,当执行n++的时候,一定要在一个指令内完成。即使达不到真正在一个指令内完成,也要达到同样的效果。这就是上层语言对编译器提出要求。编译器进而向底层硬件提出要求。

总结

可以发现,c++11的内存序是一种语言上的抽象。并不直接是底层硬件行为的描述。这种抽象是反过来的,是向编译器和硬件提出要求,要求一定要达成这样的软件需求。从而减轻了c++11软件编程人员对底层硬件细节的了解。

所以对于上层编程人员而言,其实并不需要去详细了解底层的实现。当然,有了解可以更好的理解内存序是如何实现的。比如对于内存壁障而言,可能只需要了解,内存壁障前后的代码不能翻越这个墙壁就可以了。

至于底层硬件啊,是否要同步cache,是否要锁总线,是否要如何特殊地操作内存。那是编译器和硬件负责具体的实施。