CPU中的cache结构以及cache一致性
一. 引子
在多线程环境中,经常会有一些计数操作,用来统计线上服务的一些qps、平均延时、error等。为了完成这些统计,可以实现一个多线程环境下的计数器类库,方便记录和查看用户程序中的各类数值。在实现这个计数器类库时,可以利用thread local存储来避免cache bouncing,从而提高效率。注意,这种实现方式的本质是把写时的竞争转移到了读:读得合并所有写过的线程中的数据,而不可避免地变慢了。当你读写都很频繁并得基于数值做一些逻辑判断时,你不应该用前述的实现方式。那么,cache bouncing是什么?下面详细说明一下。
二. 什么是cache bouncing?
为了以较低的成本大幅提高性能,现代CPU都有cache。cpu cache已经发展到了三级缓存结构,基本上现在买的个人电脑都是L3结构。其中L1和L2cache为每个核独有,L3则所有核共享。为了保证所有的核看到正确的内存数据,一个核在写入自己的L1 cache后,CPU会执行Cache一致性算法把对应的cacheline(一般是64字节)同步到其他核。这个过程并不很快,是微秒级的,相比之下写入L1 cache只需要若干纳秒。当很多线程在频繁修改某个字段时,这个字段所在的cacheline被不停地同步到不同的核上,就像在核间弹来弹去,这个现象就叫做cache bouncing。由于实现cache一致性往往有硬件锁,cache bouncing是一种隐式的的全局竞争。
cache bouncing使访问频繁修改的变量的开销陡增,甚至还会使访问同一个cacheline中不常修改的变量也变慢,这个现象是false sharing。按cacheline对齐能避免false sharing,但在某些情况下,我们甚至还能避免修改“必须”修改的变量。当很多线程都在累加一个计数器时,我们让每个线程累加私有的变量而不参与全局竞争,在读取时我们累加所有线程的私有变量。虽然读比之前慢多了,但由于这类计数器的读多为低频展现,慢点无所谓。而写就快多了,从微秒到纳秒,几百倍的差距。
三. cache
1. cache的意义
为什么需要CPU cache?因为CPU的频率太快了,快到主存跟不上,这样在处理器时钟周期内,CPU常常需要等待主存,浪费资源。所以cache的出现,是为了缓解CPU和内存之间速度的不匹配问题(结构:cpu -> cache -> memory)。
CPU cache有什么意义?cache的容量远远小于主存,因此出现cache miss在所难免,既然cache不能包含CPU所需要的所有数据,那么cache的存在真的有意义吗?当然是有意义的——局部性原理。
A. 时间局部性:如果某个数据被访问,那么在不久的将来它很可能被再次访问;
B. 空间局部性:如果某个数据被访问,那么与它相邻的数据很快也可能被访问;
2. cache和寄存器
存储器的三个性能指标——速度、容量和每位价格——导致了计算机组成中存储器的多级层次结构,其中主要是缓存和主存、主存和磁盘的结构。那么在主存之上,cache和寄存器之间的关系是?
举个例子,当你在思考一个问题的时候,寄存器存放的是你当前正在思考的内容,cache存放的是与该问题相关的记忆,主存则存放无论与该问题是否有关的所有记忆,所以,寄存器存放的是当前CPU执行的数据,而cache则缓存与该数据相关的部分数据,因此只要保证了cache的一致性,那么寄存器拿到的数据也必然具备一致性。
四. CPU cache结构
1. 单核CPU cache结构
在单核CPU结构中,为了缓解CPU指令流水中cycle冲突,L1分成了指令(L1P)和数据(L1D)两部分,而L2则是指令和数据共存。
2. 多核CPU cache结构
多核CPU的结构与单核相似,但是多了所有CPU共享的L3三级缓存。在多核CPU的结构中,L1和L2是CPU私有的,L3则是所有CPU核心共享的。
五. MESI(缓存一致性)
cache的写操作方式可以追溯到大学教程《计算机组成原理》一书。
A. write through(写通):每次CPU修改了cache中的内容,立即更新到内存,也就意味着每次CPU写共享数据,都会导致总线事务,因此这种方式常常会引起总线事务的竞争,高一致性,但是效率非常低;
B. write back(写回):每次CPU修改了cache中的数据,不会立即更新到内存,而是等到cache line在某一个必须或合适的时机才会更新到内存中;
无论是写通还是写回,在多线程环境下都需要处理缓存cache一致性问题。为了保证缓存一致性,处理器又提供了写失效(write invalidate)和写更新(write update)两个操作来保证cache一致性。
写失效:当一个CPU修改了数据,如果其他CPU有该数据,则通知其为无效;
写更新:当一个CPU修改了数据,如果其他CPU有该数据,则通知其跟新数据;
写更新会导致大量的更新操作,因此在MESI协议中,采取的是写失效(即MESI中的I:ivalid,如果采用的是写更新,那么就不是MESI协议了,而是MESU协议)。
2. cache line
cache line是cache与内存数据交换的最小单位,根据操作系统一般是32byte或64byte。在MESI协议中,状态可以是M、E、S、I,地址则是cache line中映射的内存地址,数据则是从内存中读取的数据。
工作方式:当CPU从cache中读取数据的时候,会比较地址是否相同,如果相同则检查cache line的状态,再决定该数据是否有效,无效则从主存中获取数据,发起一次RR(remote read);
工作效率:当CPU能够从cache中拿到有效数据的时候,消耗几个CPU cycle,如果发生cache miss,则会消耗几十上百个CPU cycle;
cache的工作原理以及在主板上的结构如下两图所示:
3. 状态介绍
MESI协议将cache line的状态分成modify、exclusive、shared、invalid,分别是修改、独占、共享和失效。
modify:当前CPU cache拥有最新数据(最新的cache line),其他CPU拥有失效数据(cache line的状态是invalid),虽然当前CPU中的数据和主存是不一致的,但是以当前CPU的数据为准;
exclusive:只有当前CPU中有数据,其他CPU中没有改数据,当前CPU的数据和主存中的数据是一致的;
shared:当前CPU和其他CPU中都有共同数据,并且和主存中的数据一致;
invalid:当前CPU中的数据失效,数据应该从主存中获取,其他CPU中可能有数据也可能无数据,当前CPU中的数据和主存被认为是不一致的;
对于invalid而言,在MESI协议中采取的是写失效(write invalidate)。
4. cache操作
MESI协议中,每个cache的控制器不仅知道自己的操作(local read和local write),通过监听也知道其他CPU中cache的操作(remote read和remote write)。对于自己本地缓存有的数据,CPU仅需要发起local操作,否则发起remote操作,从主存中读取数据,cache控制器通过总线监听,仅能够知道其他CPU发起的remote操作,但是如果local操作会导致数据不一致性,cache控制器会通知其他CPU的cache控制器修改状态。
local read(LR):读本地cache中的数据;
local write(LW):将数据写到本地cache;
remote read(RR):读取内存中的数据;
remote write(RW):将数据写通到主存;
5. 状态转换和cache操作
如上文内容所述,MESI协议中cache line数据状态有4种,引起数据状态转换的CPU cache操作也有4种,因此要理解MESI协议,就要将这16种状态转换的情况讨论清楚。
MESI协议为了保证多个CPU cache中共享数据的一致性,定义了cache line的四种状态,而CPU对cache的4种操作可能会产生不一致状态,因此cache控制器监听到本地操作和远程操作的时候,需要对地址一致的cache line状态做出一定的修改,从而保证数据在多个cache之间流转的一致性。
参考资料:
http://blog.csdn.net/reliveit/article/details/50450136
CPU中的cache结构以及cache一致性的更多相关文章
- 彻底搞懂 CPU 中的内存结构
https://www.cnblogs.com/YJK923/p/10302180.html
- [转帖]CPU Cache 机制以及 Cache miss
CPU Cache 机制以及 Cache miss https://www.cnblogs.com/jokerjason/p/10711022.html CPU体系结构之cache小结 1.What ...
- CPU Cache 机制以及 Cache miss
CPU体系结构之cache小结 1.What is cache? Cache是用来对内存数据的缓存. CPU要访问的数据在Cache中有缓存,称为“命中” (Hit),反之则称为“缺失” (Miss) ...
- 存储器结构、cache、DMA架构分析--【原创】
存储器的层次结构 高速缓冲存储器 cache 读cache操作 cache如果包含数据就直接从cache中读出来,因为cache速度要比内存快 如果没有包含的话,就从内存中找 ...
- Linux中的Buffer Cache和Page Cache echo 3 > /proc/sys/vm/drop_caches Slab内存管理机制 SLUB内存管理机制
Linux中的Buffer Cache和Page Cache echo 3 > /proc/sys/vm/drop_caches Slab内存管理机制 SLUB内存管理机制 http://w ...
- Linux系统中的Page cache和Buffer cache
Linux系统中的Page cache和Buffer cache Linux中有两个很容易混淆的概念,pagecache和buffercache,首先简单将一些Linux系统下内存的分布,使用free ...
- CPU指令重排序与MESI缓存一致性
一.重排序场景 class ResortDemo { int a = 0; boolean flag = false; public void writer() { a = 1; //1 flag = ...
- 深入理解shared pool共享池之library cache的library cache lock系列四
本文了解下等待事件library cache lock,进一步理解library cache,之前的文章请见: 深入理解shared pool共享池之library cache的library ca ...
- 深入理解shared pool共享池之library cache的library cache pin系列三
关于library cache相关的LATCH非常多,名称差不多,我相信一些人对这些概念还是有些晕,我之前也有些晕,希望此文可以对这些概念有个更为清晰的理解,本文主要学习library cache p ...
随机推荐
- Linux命令学习-cp命令
Linux中,cp命令的全称是copy,主要作用是复制文件或文件夹,类似于Windows下的复制功能. 假设当前处于wintest用户的主目录,路径为 /home/wintest ,存在文件夹test ...
- solidity智能合约如何判断地址为0或空
智能合约地址判断 在旧版本中可使用以下代码来进行比较: owner != 0x0 但如果在新版本中使用,则会提示错误信息. 那么,如何正确使用来比较地址是否为空呢. 解决方案 可以使用address( ...
- 嗨,你真的懂this吗?
this关键字是JavaScript中最复杂的机制之一,是一个特别的关键字,被自动定义在所有函数的作用域中,但是相信很多JvaScript开发者并不是非常清楚它究竟指向的是什么.听说你很懂this,是 ...
- idea下maven命令打包不同配置
1. 场景描述 不知道大家有没有遇到过?Idea集成的maven,在切换不同环境配置的时候,有时候反应很慢,还存在切换后打包还是原配置的情况. 2. 问题解决 通过在idea下执行maven命令进行切 ...
- ES6 let const 关键字
ECMAScript 和 JavaScript的关系? 前者是后者的规格,后者是前者的实现. 符合ECMAScript 规格的还有 Flash 中的AcionScript 和 TypeScript. ...
- HashMap源码分析(二):看完彻底了解HashMap
上文讲到HashMap的增加方法,现在继续 上文链接 HashMap在上一篇源码分析的文章中,如果使用put的时候如果元素数量超过threshold就会调用resize进行扩容 1.扩容机制 想要了解 ...
- C#拼装JSON数组简易方法
下面是我们想要拼接出来的JSON字符串,返回给前台 {"success":"true","msg":"","d ...
- 【题解】P2078 朋友-C++
题目传送门 这道题目就是一个模板并查集 但是!唯一不同的地方在于,这道题的编号有负数. C++的map你忘了吗!!!下表可以是任意类型. 所以把fa数组开成一个int->int的map就可以了 ...
- 【LightOJ - 1370】Bi-shoe and Phi-shoe
Bi-shoe and Phi-shoe Descriptions: 给出一些数字,对于每个数字找到一个欧拉函数值大于等于这个数的数,求找到的所有数的最小和. Input 输入以整数T(≤100)开始 ...
- 软件测试必须掌握的linux命令大全
测试工程师的四大基础技能:数据库.linux.网络协议.测试工具,不管是刚入门还是已经工作多年,这几个方向都是要掌握的.今天我们再讲一下测试工程师必须要掌握的linux命令. 测试工程师需要掌握lin ...