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 ...
随机推荐
- Docker学习第二天-容器
Docker 容器 容器是 Docker 又一核心概念. 简单的说,容器是独立运行的一个或一组应用,以及它们的运行态环境.对应的,虚拟机可以理解为模拟运行的一整套操作系统(提供了运行态环境和其他系统环 ...
- Linux下程序下载
每个开发板/PC机都有硬盘(ROM,read only memory,只读存储器)和运行内存(RAM,random access memory,随机存取存储器).其中Nand/Nor flash相当于 ...
- 将 unsiged char 转换成对应的十六进制字符用以显示出来如 unsiged char a[]={0x00,0x01,0x30,0x38}转化为“00013038”
int CEnCryptionAndDeCryptionDlg::Byte2Hex(const unsigned char* input,unsigned long inLen, unsigned c ...
- mysql -h139.129.205.80 -p test_db_dzpk < db.dump
mysqldump -h139.129.205.80 -uroot -p db_a > db_dzpk.dump mysql -h139.129.205.80 -p test_db< db ...
- 洛谷P2472 [SCOI2007]蜥蜴 题解
题目链接: https://www.luogu.org/problemnew/show/P2472 分析: 这道题用最大流解决. 首先构建模型. 一根柱子可以跳入和跳出,于是拆成两个点:入点和出点. ...
- 洛谷P4304 [TJOI2013]攻击装置 题解
题目链接: https://www.luogu.org/problemnew/show/P4304 分析: 最大独立集 最大独立集=总点数-最大匹配数 独立集:点集,图中选一堆点,这堆点两两之间没有连 ...
- redis 发布与订阅原理分析
前言:用了redis也有一段时间了,但是发布与订阅的使用频率也不高,趁着这次空闲,深究下redis的发布与订阅模式. 一.订阅频道和信息发布 功能说明:Redis 的 SUBSCRIBE 命令可以让客 ...
- 个人永久性免费-Excel催化剂功能第53波-无比期待的合并工作薄功能
合并工作薄.工作表功能,几乎每一款Excel插件都提供,而且系列衍生功能甚至有多达10多个.今天Excel催化剂重拾武器,在现有众多插件没提供到位的部分场景中,给予支持和补充,做到人有我优,人无我有的 ...
- FFT中的一个常见小问题(递推式)
FFT中的一个常见小问题这里不细说FFT的内容,详细内容看这些就足以了解大概了小学生都能看懂的FFT!!!FFT详解补充——FFT中的二进制翻转问题主要是对学习过程中一个容易困扰的小问题进行解释,以便 ...
- C语言入门5-键盘的输入和屏幕输出
C程序中的键盘输入和屏幕输出都是通过 调用输入/输出函数 实现的. 一.数据的格式化 屏幕输出 函数printf()的一般格式 (有两种) (1)第一种: printf(格式控制字符串): ...