博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
关于JAVA并发编程你需要知道的——硬件篇
阅读量:5068 次
发布时间:2019-06-12

本文共 1717 字,大约阅读时间需要 5 分钟。

无论程序语言如何千变万化,他们都深深地根植于目前的计算机体系结构。

左图是intel CPU的三级高速缓存设计,由于高速缓存对程序员基本不可见,因此可以抽象为右图。

1092610-20170327025628498-2081564793.png

缓存的设计

首先还是先谈谈左图。

  1. L1-cache分为两部分,i-cache存储指令(只读),d-cache存储数据(可读可写)
  2. CPU只能和寄存器以及L1-cache进行直接交互,数据不能隔层传递,只能一层一层往上读,一层一层往下写
  3. 访问L1需要至少4个时钟周期,L2需要至少10个,L3需要至少30个。即便是速度最快的L1,也低于运算单元的执行速度,何况存在缓存未命中的情况,因此在L1和运算单元之间加上了Writebuffer和Readbuffer(合称Memory Ordering Buffer,MOB),数据准备好的时候再完成相关指令,这就是CPU指令乱序——顺序执行乱序完成。乱序完成的结果放入到Writebuffer中,按照原有的执行顺序,刷到缓存中
  4. 缓存由多个缓存行组成。每个缓存行结构如图所示(以64位机器为例)
    1092610-20170327031709279-385561315.png
  5. CPU读取缓存的时候找到对应的缓存行,如果前面的有效位为零,就从下一级缓存加载到这一级缓存

相关的问题

明白缓存的设计之后,再看右图来分析其中的问题

  1. 缓存导致的内存可见性:已知线程A运行在core 0上,线程B运行在core 1上,两者都对同一个内存地址进行读取,这个内存地址的内容会被加载到cache,然后CPU读取,这时候线程A对内容进行了修改,但是线程B却可能一直从本核心的cache读取,无法感知到该地址的内容已被修改。
  2. 多核导致的自增操作原子性:自增操作分为三步:从内存读取变量到寄存器;寄存器中的值加1;写回到内存。已知线程A运行在core 0上,线程B运行在core 1上,两者都对变量执行加一操作。A执行完一二两步时,B执行完第一步,A将加一后的值写入到内存,B执行完二三两步也将加一后的值写入到内存,结果变量只加了一,而不是加二
  3. MOB导致的cache可见性:a=1.0; a=a/2; a=a-1.0;按照正常的逻辑,a最后的结果为-0.5;但是因为除法的执行时钟周期大于减法,第三句执行时,a/2的结果存放在writebuffer中还没写入到缓存,a-1.0中a的值已经从缓存中加载到readbuffer,也就是a-1.0=1.0-1.0=0 (高级语言不会出现这个问题,因为编译器已经做了处理,前面的伪代码仅表示逻辑)

相关的实现

为了解决这些问题,CPU提供了一些指令,其中比如lock和cmpxchg。

  • lock 汇编前缀,在Intel奔腾系列之前,这个指令前缀能够锁定总线,禁止其他CPU核心操作内存,执行完后边的指令后释放总线,在这个过程中其他CPU核心会监听总线,发现某个内存地址内容被修改,就会将本核心下的对应cache行有效位置0。因为锁总线会禁止所有内存操作,降低效率,因此在奔腾之后,这个指令前缀不锁总线而是锁定相关的cache行,对某个地址修改后直接让相关cache失效。这样解决了问题一
  • cmpxchg 将寄存器a中的值与内存中比较如果一样,将寄存器c中的值和内存中的值交换,如果不一样就设置异常位并将内存中的值读取到寄存器a。代入到问题二,从内存读取值到寄存器a,加一后保存到寄存器c,然后执行cmpxchg,执行完后如果有异常,就重新加一,再尝试写回,直到成功。这样解决了问题二
  • cmpxchg和lock 在执行完以后会将writebuffer刷到cache并清空readbuffer,这样解决了问题三。另外X86_64引入了内存屏障指令 lfence、sfence、mfence。lfence前面的读取操作完成,也就是readbuffer中的内容全部被cpu读取后,才能执行lfence之后的读取操作;sfence前面的写入操作完成,也就是writebuffer全部刷到缓存中,才能执行sfence之后的写入操作;mfence之前的读写操作全部完成,才能进行mfence之后的操作。

转载于:https://www.cnblogs.com/hanzai/p/6624246.html

你可能感兴趣的文章
oracle用户锁定
查看>>
(转)盒子概念和DiV布局
查看>>
Android快速实现二维码扫描--Zxing
查看>>
获取元素
查看>>
nginx+lighttpd+memcache+mysql配置与调试
查看>>
ubuntu12.04 启动apache2 对.htaccess 的支持
查看>>
proxy写监听方法,实现响应式
查看>>
前端工具----iconfont
查看>>
Azure Site Recovery 通过一键式流程将虚拟机故障转移至 Azure虚拟机
查看>>
Hello China操作系统STM32移植指南(一)
查看>>
cocos2dx CCEditBox
查看>>
VC++2012编程演练数据结构《8》回溯法解决迷宫问题
查看>>
第一阶段冲刺06
查看>>
WIN下修改host文件并立即生效
查看>>
十个免费的 Web 压力测试工具
查看>>
ckeditor 粘贴后去除html标签
查看>>
面试题
查看>>
51Nod:活动安排问题之二(贪心)
查看>>
EOS生产区块:解析插件producer_plugin
查看>>
数据库框架的log4j日志配置
查看>>