ARM64体系结构与编程之cache必修课(中)
为什么系统软件⼈员要深⼊了解cache?
在⼀个系统中,cache⽆处不在,对于⼀个系统编程⼈员来说,你⽆法躲藏。下图是⼀个经典的ARM64系统的架构图,由Corte-A72和Cortex-53组成了⼤⼩核架构,每个CPU核⼼都有L1 cache,每个cluster⾥共享⼀个L2 cache,另外还有Mali GPU和DMA外设。
对于系统软件⼈员,下⾯⼏个常常疑惑的问题:
1. cache的内部组织架构是怎么样的?能否画出⼀个cache的layout图?什么是t,way?
2. 直接映射,全关联和组相联之间有什么区别?优缺点是啥?
3. 重名问题是怎么发⽣的?
4. 同名问题是怎么发⽣的?
5. VIPT会不会发⽣重名问题?
6. 什么是inner shareability 和outer shareability?怎么区分?
鸭汤7. 什么是PoU?什么是PoC?
8. 什么是cache⼀致性?业界解决cache⼀致性都有哪些⽅法?
9. MESI状态转换图,我看不懂。
0. 什么cache伪共享?怎么发⽣的,如何避免?
1. DMA和cache为啥会有cache⼀致性问题?
2. ⽹卡通过DMA收数据和发数据,应该怎么操作cache?
3. 对于lf-modifying code,怎么保证data cache和指令cache的⼀致性问题?
所以,Cache这个玩意,对我们系统编程⼈员来说,⾮常重要。Cache没有理解好,或者没有完全搞透了,对系统编程影响很⼤,有时候我们在编程的时候,⼀⾏代码⼩⼩的改动可能会影响整个系统的性能,所以,我是建议系统程序员有必要把cache这玩意好好系统的学⼀学。
可开头的四字成语笨系列⽂章主要源⾃第三季《arm64体系结构与编程》视频课程,⼤概会有上下两篇:
上篇:介绍cache相关的背景知识,例如什么是cache,cache的layout结构图,cache的层级,VIPT/PIPT/VIVT,cache的重名和同名问题,cache的策略等。
中篇:主要介绍ARM特有的inner share和outer share的概念,以及神马是PoU和PoC,还有cache指令的格式。为什么会有cache⼀致性问题?arm公司对cache⼀致性问题的解决⽅案的演进。cache⼀致性问题业界常⽤的解决⽅案。
下篇:主要介绍MESI协议,怎么去看MESI协议状态图,DMA和cache之间的cache⼀致性问题,lf-modifying code导致的I-cache和D-cache的⼀致性问题,cache伪共享等问题。
inner share和outer share
Inner 和outer shareability是arm提出来的概念。很重要的⼀点,⼤家先要知道,也就是只有normal memory的内存属性的内存才能设置inner 和outer shareability,device memory是不能设置shareabilit
y的。
怎么去区分inner share还是outer share呢,arm⼿册⾥讲了,不同的SOC设计有不同的区分⽅法,不过有⼀个通⽤的规则:inner share 通常是CPU IP集成的caches,包括CPU IP集成的L1 data cache和L2 cache,⽽outer share是通过总线连接到cache,⽐如外接的L3 cache等。
正方形的面积公式是下⾯这个图,⽐较直观。这个图,虚线分成了两部分,左边都是inner share,右边都是outershare。左边表⽰是CPU IP集成的cache,上⾯的cores集成了L1和L2cache,⽽下⾯的core集成了L1 cache,那虚线框出来的都是inner share。我们再来看虚线左边,通过总线外接了L2 cache或者L3 cache,这边都是outer share。
在armv8.6⼿册⾥,在B2.7.1章⾥有⼀段话对inner share和outer share描述。
在这⾥的example B2-1⾥,举了⼀个例⼦,说在⼀个在⼀个2 cluster的系统:
1. 每⼀个cluster⾥,数据cache和unified cache都是inner shareable
2. 两个cluster之间变成了outer shareable
这个系统⾥,每⼀个cluster都是⼀个不同的inner share,但是整个系统是⼀个outer share。
在Programmer guide第11.3章⾥,也有⼀段话描述inner和outer,说的⽐较接地⽓。
PoU和PoC的区别
下⾯来介绍⼀下arm定义的两个重要的概念,⼀个是POC,PoC的英⽂全称为Point of Coherency ,⼀个是PoU,英⽂全称为Point of Unification。
神马是PoU?
PoU: 表⽰⼀个CPU中的指令cache,数据cache还有MMU,TLB等看到的是同⼀份的内存拷贝。
鲁冰逊漂流记>丰华唱片
1. PoU for a PE,是说保证PE看到的I/D cache和MMU是同⼀份拷贝。⼤多数情况下,PoU是站在单核系统的⾓度来观察的。
2. PoU for inner share,意思是说在inner share⾥⾯的所有CPU核⼼都能看到相同的⼀份拷贝。
所以,PoU有两个观察点,⼀个是poc for a PE,PE就是cpu core,另外⼀个是POU for inner share。关于pou的描述,在armv8.6⼿册的第D4.4.7章⾥有详细的描述。
其中它⾥⾯举了⼀个例⼦,说lf-modifing code在pou for inner⾥,使⽤下⾯两条简单的指令就能保证data cache和指令cache的⼀致性。如果不是pou for inner,要保证data cache和指令cache的⼀致性,需要额外的memory barrier的指令。
神马是PoC?
PoC:系统中所有的观察者例如DSP, GPU,CPU, DMA等都能看到同⼀份内存拷贝。
我们来这个图,左边那个图,就是⼀个单⼀的⼀个master,master可以是CPU,gpu,dma等有能⼒访问内存的设备。那么在单⼀的master系统⾥,这些指令cache,数据cache,TLB等都可以看成是P
OU,因为在这个单⼀的master眼⾥,他们都是同⼀份拷贝。我们来看⼀下右边这个图,这个图⾥有两个master。那么在masterA和master B眼⾥,他们要看到同⼀份拷贝的话,需要masterA和masterB 进⾏cache⼀致性,这个观察⾓度就是POC。
PoC和PoU的区别
银英语
左传的作者我们来看⼀下pou和poc的区别,最⼤的⼀个区别就是poc是系统⼀个概念,和系统配置相关,它包含了系统所有有能⼒访问内存的设备,包括cpu,gpu,dma等,这些都称为obrver,观察者。
⽽pou是个局部的概念。还有⼀点⼤家需要注意,系统配置的不同可能会影响pou的范围,我们举个例⼦,在Cortex-A53可以配置L2 cache和没有L2 cache,可能会影响PoU的范围,为什么会这样,因为我们⽀持pou有⼀个重要的观察点,就是pou for inner share,⽽inner share的划分和 这个cache是不是 CPU IP集成有关。
⽐如下⾯这个图,左边,没有集成L2 cache,那么这时候POU等于了POC,⽽且也没有其他的master。右边那个图,CORE⾥集成了L2 cache,那么core,L1 cache和L2 cache构成了POU for inner,⽽master a和master b和系统内存构成了poc。
Cache维护指令嘴干是怎么回事
接下来,我们来看cache维护。前⾯我们讲了⼀⼤通,关于cache的背景知识,接下来介绍的cache的维护,这⾥说的维护是指的⼿⼯维护,也就是软件⼲预cache的⾏为。
Armv8⾥定义的Cache的管理的操作有三种:
1. ⽆效(Invalidate)整个⾼速缓存或者某个⾼速缓存⾏。⾼速缓存上的数据会被丢弃。
2. 清除(Clean)整个⾼速缓存或者某个⾼速缓存⾏。相应的⾼速缓存⾏会被标记为脏,数据会写回到下⼀级⾼速缓存中或者主存储器
中。
3. 清零(Zero)操作。在某些情况下,对⾼速缓存进⾏清零操作起到⼀个预取和加速的功效,⽐如当程序需要使⽤⼀⼤块临时内存,在
初始化阶段对这个内存进⾏清零操作,这时⾼速缓存控制器会主动把这些零数据写⼊⾼速缓存⾏中。若程序主动使⽤⾼速缓存的清零操作,那么将⼤⼤减少系统内部总线的带宽。
对⾼速缓存的操作可以指定不同的范围。
1. 整块⾼速缓存。
2. 某个虚拟地址。
3. 特定的⾼速缓存⾏或者组和路。
另外在ARMv8架构中最多可以⽀持7级的⾼速缓存,L1~L7⾼速缓存。当对⼀个⾼速缓存⾏进⾏操作
时,我们需要知道⾼速缓存操作的范围。ARMv8架构中从处理器到所有内存的⾓度分成如下⼏个视⾓。
1. PoC(Point of Coherency,全局缓存⼀致性⾓度)
2. PoU(Point of Unification,处理器缓存⼀致性⾓度)
Cache指令格式
下⾯来讲⼀下cache的指令格式,armv8⾥提供了两条cache相关的指令,⼀条是指令cache的管理指令,就叫做IC,其实就是instruction cache的简称。另外⼀条是data cache的管理指令,叫做DCC,其实就是data cache的简称。
指令的有两部分组成,⼀个是operation,另外⼀个是xt,表⽰参数。Operation可以4个部分,分别是function,就是你要操作的功能是啥,我们前⾯讲了cache管理的三⼤功能,⼀个是invalidate,第⼆个是clean,第三个zero,第⼀个和第⼆个可以合起来,就是clean & invalidate,就是我先把cache flush回内存,然后我再⽆效它。第⼆部分是,类型type,VA表⽰要操作的地址,sw表⽰路和组,all表⽰整个cache。第三部分是point,就是我们说的管理cache的观察点,或者说范围,是POU还是POC,POU和POC的范围是不⼀样的,我们前⾯讲过。第4部分是IS,表⽰是否包括inner shareable。这⾥POU和inner share可以合在⼀起的。
最后⼀个参数,可以传递⼀些参数,⽐如你需要传递地址给这条指令。
下⾯这个表就是 armv8⾥⽀持的cache管理指令,我这⾥简单给⼤家翻译了。
⼤家可以去看armv8.6⼿册的D4.4.8章的D4-3这个表,这是最权威的。
为什么要cache⼀致性
为什么要有cache⼀致性?或者说cache⼀致性这个问题是怎么产⽣的。要了解这个问题,我们需要从
单核cpu进化到多核处理器这个过程开始说起。以arm为例,在cortex-a8其实都是单核处理器,到了cortex-a9之后,就有了多核处理器,不过准确来讲在ARM11的时候也有多核,不过那时候的多核还不是很成熟。在多核处理器⾥,每个核⼼都有⾃⼰L1 cache,多核之间可能共享⼀个L2的cache等等。以这个图为例,core0有⾃⼰的L1 cache,core1有⾃⼰的L1 cahe,然后才是内存。那么当core0 先访问⼀个地址,然后把这个地址的数据加载到它⾃⼰的cache⾥,那么这时候如果core1 也想要这个数据,它应该怎么办呢?它是应该也从内存中去读,还是去问core0 要数据呢?所以,这种情况下,就产⽣了cache⼀致性问题。