cache coherency & atomic & cpu-reorder

最近看了一些cache一致性以及cpu乱序执行的文章,结合之前看的原子操作写下个人总结。

首先这几个概念都是software编程时比较难理解的,其中cache一致性和cpu的乱序执行按理说应该做到对software透明,换句话说sw programer应该不用care这些硬件相关的知识才对。可实际上如果完全不懂这两个概念:software可能会遇到一些非常 “诡异”的bug。

cache一致性是引入cache后带来的问题,cache一致性是必须要满足的,我的理解是cache一致性用来保证多个cpu对memory的正确访问,广义的说,在一般情况下,当多个master share 某一资源的时候都需要某种协议来保证并发访问的一致性,比方说某个总线上有两个master,当两个master同时写同一个总线地址的时候,需要总线来保证访问的不冲突,例如先请求的master会锁住总线,防止两个master同时写数据等等。对于cache,它是cpu观察/操作memory的 “窗口”,在SMP系统中,每个核有自己的cache,当多个CPU同时访问某个变量的时候,这个变量所在的memory在不同的核中都有对应的cache来缓存,也就是说多个cache对应同一个内存变量,cache一致性就是保证在多个核访问各自的cache时,它们 “看到" 的值是一致的。

看起来这个是对software透明的呀,问题不是这么简单,结合CPU的乱序执行,就有可能有各式各样的问题。

先说下CPU的乱序执行吧,这里先不考虑编译阶段的乱序(后面再说),在执行阶段,简单来说如果前一条指令完成的时间较长(比如cache未命中),CPU往往不会等它完成了再去执行下一条指令,而是将后面的能执行的指令先”发射“出去。这里问题来了,CPU不能保证先执行的指令(第一条指令)最终先于第二条指令执行完成。在多核下,这一点会引起一些逻辑问题,具体例子可以看 http://blog.csdn.net/gjq_1988/article/details/79093305 这篇文章。

总之,如果要保证逻辑正确,那么要做到:对于本CPU以及其它任一外部的CPU,它们所观察到的指令生效顺序要符合编译后指令的本来顺序(program order), 你可以乱序发射(如前所述),但你要保证最终指令commit的顺序不乱,中间怎么乱都可以。

可惜了,很多CPU架构为了速度,是不能保证这一点的,其实也能理解,毕竟只有少数写的不规范的代码会在这种情况下遇到问题,大部分代码即使commit的顺序乱了也不会发生问题,但却可以大大提高CPU的执行效率。如果为了少数的情况就强制所有指令按序执行,按序提交,那么损失的性能还是很多的。

所以software在某些情况下就要使用barrier了...

最后说一下原子性的问题,这是一个逻辑上的概念而已,物理上没有真正的原子操作,毕竟光速还是有限的。 但是在software编程的时候我们在逻辑上是有原子操作要求的,比如a++;这个语句,我们可能理所当然认为(要求)这句话是一次性完成的,事实上CPU要先读取a,再自增,最后写回a。所以如果我们没有注意到这个过程,就很容易由于自己的逻辑与硬件实际行为不符而导致在software programer眼中程序结果错误(对CPU来说其实是没有错的)。所以当多个CPU之间同时共享某个资源的时候,是没有原子性这种要求的,原子性只是software为了自己的逻辑提出的要求,那就由software自己使用自己的机制(如spin lock)来保证,CPU/硬件是不需要管的(虽然有些CPU内部提供了原子操作指令,那只是为了software方便而已,我们只要知道物理上任何操作都不可能是原子的)。

你可能感兴趣的:(linux技术)