这一文,让我们分析一下,《浅谈 Cache》 一文中的奇怪现象,事实上如今来看也并不奇怪了。

        在什么情况下 r1 和 r2 都为 0 呢?

        细致看代码,你会发现,两个线程分别被执行在不同的 CPU 核上,而且在线程開始的阶段还使用了一个随机数,是为了让两个线程能尽量同一时候执行。

        如果 CPU0 执行:

        x = 1;

        r1 = y;

        CPU1 执行:

        y = 1;

        r2 = x;

        假设以下的情况发生:

        x 在 CPU1 的 Cache 中。 y 在 CPU0 的 Cache 中。

        1. CPU0 运行 x = 1, cache miss, 发送 "read invalidate" 消息,并把 x 的值 1 存入 Store Buffer, 開始运行 r1 = y, Cache 命中,r1 为 0;

        2. CPU1 运行 y = 1,cache miss, 发送 "read invalidate" 消息,并把 y 的值 1 存入 Store Buffer, 開始运行 r2 = x,Cache 命中,r2 为 0;

        3. CPU0 和 CPU1 各自收到了对方的消息,并作出回应,x 和 y 的值均应用到 Cache 中,且都为 1;

        主函数收到信号,比較 r1 和 r2 的值,奇迹发生了。

        假设你知道我讲的这些细节,就会发现,事实上这并非奇怪了。那么假设解决问题呢?

        事实上答案就非常easy了,要么让两个线程执行在同一个核心上,要么在两个语句之间加上内存屏障,经验证,问题攻克了。

        题外篇:

        在不同 CPU 架构下,对内存的乱序訪问事实上是不同的,一般的内存乱序分为下面四种:

        LoadStore, LoadLoad, StoreStore, StoreLoad。而且 X86 下仅仅会出现 StoreLoad 乱序,也就是上面的样例,我的 CPU 是 X86 的,所以出现了这样的情况,由此可知,事实上 X86 内存乱序訪问的还不算太厉害。

        简单解释一下,x = 1,为 Store, 读取 y 的过程为 Load,所以 Load 指令在 X86 下同意在 Store 还未更新到 Cache 中之前被运行。

        走出一个误区,内存乱序訪问与 CPU 乱序运行(Out of Order,即 OOO)不同。

早期的处理器为有序处理器(In-order processors),有序处理器处理指令通常有下面几步:

        1. 指令获取

2. 假设指令的输入操作对象(input operands)可用(比如已经在寄存器中了),则将此指令分发到适当的功能单元中。假设一个或者多个操作对象不可用(一般是因为须要从内存中获取),则处理器会等待直到它们可用;

3. 指令被适当的功能单元运行

4. 功能单元将结果写回寄存器堆(Register file,一个 CPU 中的一组寄存器)

 

        相比之下,乱序处理器(Out-of-order processors)处理指令通常有下面几步:

        1. 指令获取

        2. 指令被分发到指令队列

        3. 指令在指令队列中等待,直到输入操作对象可用(一旦输入操作对象可用,指令就能够离开队列,即便更早的指令未被运行)

        4. 指令被分配到适当的功能单元并运行

        5. 运行结果被放入队列(而不马上写入寄存器堆)

        仅仅有全部更早请求运行的指令的运行结果被写入寄存器堆后,指令运行的结果才被写入寄存器堆(运行结果重排序,让运行看起来是有序的)

        从上面的运行过程能够看出,乱序运行相比有序运行能够避免等待不可用的操作对象(有序运行的第二步)从而提高了效率。现代的机器上,处理器运行的速度比内存快非常多,有序处理器花在等待可用数据的时间里已经能够处理大量指令了。

        如今思考一下乱序处理器处理指令的过程,我们能得到几个结论:

        对于单个 CPU 指令获取是有序的(通过队列实现)

        对于单个 CPU 指令运行结果也是有序返回寄存器堆的(通过队列实现)

        由此可知,在单 CPU 上,不考虑编译器优化导致乱序的前提下,多线程运行不存在内存乱序訪问的问题

        CPU 尽管是乱序运行,可是是顺序流出结果,在我们看来,乱序运行对我们来讲是透明的,我们看到的结果和指令顺序是一样的。

最新文章

  1. 使用jekins自动构建部署java maven项目(jdk1.7+tomcat7.0+jenkins2.19.3)
  2. 终端下使用cocopods
  3. 零基础十分钟学会用git在coding.net上传(pull)和push
  4. python常用函数
  5. iOS开发-【C语言】三目运算符
  6. 微信公众平台SDK Python
  7. quaternion*Vector3的新理解
  8. 腾讯微博模拟登陆+数据抓取(java实现)
  9. 你用哪种工具进行iOS app自动化功能测试?
  10. 类似百度文库pdf2swf+flexpaper解决pdf在线阅读的效果
  11. Cooley-Tukey算法 (蝶形算法)
  12. [2014-02-23]Asp.net Mvc分布式Session存储方案
  13. 再好好聊聊 HTTP 里的 Cookie | 实用 HTTP
  14. 阿里云物联网套件(iot)设备间通信(M2M)在web端的实践
  15. axure—日期函数
  16. 【Qt】信号和槽对值传递参数和引用传递参数的总结
  17. Redis server went away的解决方案
  18. [原]Jenkins(四)---Jenkins添加密钥对
  19. vm虚拟机黑屏解决办法
  20. python3-----多进程、多线程、多协程

热门文章

  1. Intervals(差分约束系统)
  2. codevs1060 搞笑世界杯(概率dp)
  3. C# 导出word 表格代码
  4. netty百万连接跟踪记录
  5. lua 10进制转换成其它进制table表示
  6. Android开发笔记(12)——ListView & Adapter
  7. 手动触发dom节点事件代码
  8. 对学Oracle数据库初学者的开场篇
  9. QT显示框架嵌入Vs控制台工程
  10. redis客户端连接到服务器的步骤