Cache Line操作和Cache相关概念介绍

1.计算机存储体系简介  

 

 存储器是分层次的,离CPU越近的存储器,速度越快,每字节的成本越高,同时容量也因此越小。寄存器速度最快,离CPU最近,成本最高,所以个数容量有限,其次是高速缓存(缓存也是分级,有L1,L2等缓存),再次是主存(普通内存),再次是本地磁盘。

 

寄存器的速度最快,可以在一个时钟周期内访问,其次是高速缓存,可以在几个时钟周期内访问,普通内存可以在几十个或几百个时钟周期内访问。

 

存储器分级,利用的是局部性原理。我们可以以经典的阅读书籍为例。我在读的书,捧在手里(寄存器),我最近频繁阅读的书,放在书桌上(缓存),随时取来读。当然书桌上只能放有限几本书。我更多的书在书架上(内存)。如果书架上没有的书,就去图书馆(磁盘)。我要读的书如果手里没有,那么去书桌上找,如果书桌上没有,去书架上找,如果书架上没有去图书馆去找。可以对应寄存器没有,则从缓存中取,缓存中没有,则从内存中取到缓存,如果内存中没有,则先从磁盘读入内存,再读入缓存,再读入寄存器。

2.计算机缓存

Cache本系列的文章重点介绍缓存cache。了解如何获取cache的参数,了解缓存的组织结构。

2.1 Cache 概述    

cache,中译名高速缓冲存储器,其作用是为了更好的利用局部性原理,减少CPU访问主存的次数。简单地说,CPU正在访问的指令和数据,其可能会被以后多次访问到,或者是该指令和数据附近的内存区域,也可能会被多次访问。因此,第一次访问这一块区域时,将其复制到cache中,以后访问该区域的指令或者数据时,就不用再从主存中取出。

 

 

    cache分成多个组,每个组分成多个行,linesize是cache的基本单位,从主存向cache迁移数据都是按照linesize为单位替换的。比如linesize为32Byte,那么迁移必须一次迁移32Byte到cache。 这个linesize比较容易理解,想想我们前面书的例子,我们从书架往书桌搬书必须以书为单位,肯定不能把书撕了以页为单位。书就是linesize。当然了现实生活中每本书页数不同,但是同个cache的linesize总是相同的。

    所谓8路组相连( 8-way set associative)的含义是指,每个组里面有8个行。


我们知道,cache的容量要远远小于主存,主存和cache肯定不是一一对应的,那么主存中的地址和cache的映射关系是怎样的呢?
    拿到一个地址,首先是映射到一个组里面去。如何映射?取内存地址的中间几位来映射。
    举例来说,data cache: 32-KB, 8-way set associative, 64-byte line size
    Cache总大小为32KB,8路组相连(每组有8个line),每个line的大小linesize为64Byte,OK,我们可以很轻易的算出一共有32K/8/64=64 个组。
    对于32位的内存地址,每个line有2^6 = 64Byte,所以地址的【0,5】区分line中的那个字节。一共有64个组。我们取内存地址中间6为来hash查找地址属于那个组。即内存地址的【6,11】位来确定属于64组的哪一个组。组确定了之后,【12,31】的内存地址与组中8个line挨个比对,如果【12,31】为与某个line一致,并且这个line为有效,那么缓存命中。
    OK,cache分成三类,
    1 直接映射高速缓存,这个简单,即每个组只有一个line,选中组之后不需要和组中的每个line比对,因为只有一个line。
    2 组相联高速缓存,这个就是我们前面介绍的cache。 S个组,每个组E个line。
  3 全相联高速缓存,这个简单,只有一个组,就是全相联。不用hash来确定组,直接挨个比对高位地址,来确定是否命中。可以想见这种方式不适合大的缓存。想想看,如果4M 的大缓存 linesize为32Byte,采用全相联的话,就意味着4*1024*1024/32 = 128K 个line挨个比较,来确定是否命中,这是多要命的事情。高速缓存立马成了低速缓存了。

 

   描述一个cache需要以下参数 :
    1 cache分级,L1 cache, L2 cache, L3 cache,级别越低,离cpu越近
    2  cache的容量
    3  cache的linesize
    4  cache 每组的行个数.
2.2 Cache 结构

    假设内存容量为M,内存地址为m位:那么寻址范围为000…00~FFF…F(m位)

    倘若把内存地址分为以下三个区间:

 

 

tag, set index, block offset三个区间有什么用呢?再来看看Cache的逻辑结构:

 

 

将此图与上图做对比,可以得出各参数如下:

B = 2^b

S = 2^s

现在来解释一下各个参数的意义:

一个cache被分为S个组,每个组有E个cacheline,而一个cacheline中,有B个存储单元,现代处理器中,这个存储单元一般是以字节(通常8个位)为单位的,也是最小的寻址单元。因此,在一个内存地址中,中间的s位决定了该单元被映射到哪一组,而最低的b位决定了该单元在cacheline中的偏移量。valid通常是一位,代表该cacheline是否是有效的(当该cacheline不存在内存映射时,当然是无效的)。tag就是内存地址的高t位,因为可能会有多个内存地址映射到同一个cacheline中,所以该位是用来校验该cacheline是否是CPU要访问的内存单元。

当tag和valid校验成功是,我们称为cache命中,这时只要将cache中的单元取出,放入CPU寄存器中即可。

当tag或valid校验失败的时候,就说明要访问的内存单元(也可能是连续的一些单元,如int占4个字节,double占8个字节)并不在cache中,这时就需要去内存中取了,这就是cache不命中的情况(cache miss)。当不命中的情况发生时,系统就会从内存中取得该单元,将其装入cache中,与此同时也放入CPU寄存器中,等待下一步处理。注意,以下这一点对理解linux cache机制非常重要:

3.计算机缓存行 ChaceLine
    高速缓存其实就是一组称之为缓存行(cache line)的固定大小的数据块,其大小是以突发读或者突发写周期的大小为基础的。

    每个高速缓存行完全是在一个突发读操作周期中进行填充或者下载的。即使处理器只存取一个字节的存储器,高速缓存控制器也启动整个存取器访问周期并请求整个数据块。缓存行第一个字节的地址总是突发周期尺寸的倍数。缓存行的起始位置总是与突发周期的开头保持一致。

当从内存中取单元到cache中时,会一次取一个cacheline大小的内存区域到cache中,然后存进相应的cacheline中。

例如:我们要取地址 (t, s, b) 内存单元,发生了cache miss,那么系统会取 (t, s, 00…000) 到 (t, s, FF…FFF)的内存单元,将其放入相应的cacheline中。


下面看看cache的映射机制:

当E=1时, 每组只有一个cacheline。那么相隔2^(s+b)个单元的2个内存单元,会被映射到同一个cacheline中。(好好想想为什么?)

当1<E<C/B时,每组有E个cacheline,不同的地址,只要中间s位相同,那么就会被映射到同一组中,同一组中被映射到哪个cacheline中是依赖于替换算法的。

当E=C/B,此时S=1,每个内存单元都能映射到任意的cacheline。带有这样cache的处理器几乎没有,因为这种映射机制需要昂贵复杂的硬件来支持。

不管哪种映射,只要发生了cache miss,那么必定会有一个cacheline大小的内存区域,被取到cache中相应的cacheline。

现代处理器,一般将cache分为2~3级,L1, L2, L3。L1一般为CPU专有,不在多个CPU中共享。L2 cache一般是多个CPU共享的,也可能装在主板上。L1 cache还可能分为instruction cache, data cache. 这样CPU能同时取指令和数据。

下面来看看现实中cache的参数,以Intel Pentium处理器为例:

  E B S C
L1 i-cache 4 32B 128 16KB
L1 d-cache 4 32B 128 16KB
L2 4 32B 1024~16384 128KB~2MB

 

补充:

Cache就是对内存的内容进行缓存的一个硬件。cache和内存的逻辑关系结构如下图所示。从左往右,从上到下逐个说明

 

首先物理内存又是通过物理地址PA(physical address)标识的,内存块用PA+SIZE表示,在读取内存的时候,CPU会将内存块load到cache中,但是并不是按照SIZE的大小load内存块,而是按照cacheline的大小load一个内存块,指定的物理内存块将被包含在这段被load的内存中(如上图黄色+蓝色部分所示,黄色代表了内存块在cacheline大小的内存块中的位置)。所以在编程的时候,尽量将结构设计为cacheline对其的,一次可以加载完成,访问下个结构体的时候,就可以直接访问另一个cacheline,而不发生冲突了。

物理地址又被分为三个部分,tag+index+offset。index就是物理地址在cache这个大数组中的位置,相当于数组索引,索引到了后,offset说明了PA所在的内存在cacheline中的偏移量。
这样看来,就会发现很可能两个物理地址中见的index很有可能发生重复,这就是cacheline冲突。这样的情况下,就要先废除cacheline上前一个的内容后重新加载新的内存才会有效。这样的冲突会大大降低内存的访问效率,所以intel有提出了一种更新的架构,如下图所示

 

在每个cacheline的下一级又多了way的概念,每个cacheline的下一级又被分为4WAY或8WAY,每个way都相当于一个cacheline。这样即使index冲突,也可以将内存内容放到不同的way中减少冲突。tag就是用来表示是那个way上的。上面的结构就是所谓的4路组相连或8路组相连的概念。

cache一致性操作-标志位clean/dirty   

        当CPU需要数据时,首先到cache内部去寻找,如果找到了,则称为命中了(cache hit),如果没有找到,则称为丢失(cache miss),这时CPU必须到内存去读取数据,并且将其保存在cache中以备后用。

       cache中需要保存数据字在主存中的地址,cache的每个入口都被相应数据的地址所标记,当CPU要完成一次读或写操作时,会在cache中寻找匹配的标记。cache中存储的地址可以是虚拟地址,也可以是物理地址,根据cache的实现而不同。由于标记在cache中本身要占用空间,所以如果以字为粒度在cache中标记主存的地址单元那就显得有些低效。解决的办法很简单,还记得cache是基于空间的局部性原理的,由于被标记的内存字的邻近单元也有可能被使用,所以会把它们也一起加载到cache中去。因此一个cache的入口处存储的并不是单一的字,而是几个连续的字,它们被称为缓存行(cache line).

       对应每个cache line,都有这样一个结构

       data bolck存放的是缓存行中所保存的就是从主存取过来的数据,tag表示的是数据块在主存中的地址(并不是完整的地址),flag bits是一些在操作过程中涉及到的标志位。

       而对于主存中的每个地址单元,我们都可以按下图的结构进行解析

         我们已经知道,一个cache line对应的是主存中连续的几个字,那么这些字的低位对于映射一个cache line是没用的,比如说在一个32位系统中,一个cache line的大小是16个字,也就是64个字节,那么其低6位在映射一个cache时是没有用的,其作用是用来指定该内存单元在对应cache line中的偏移。Cache Set则指明了该内存单元位于哪一个cache set(一个cache set 可以包含一个或多个cache line,这与cache和内存之间的映射关系有关,在后面会进行讲解),S的长度为以2为底的log(cache sets),由于一个cache set可以包含多个cache line,所以还需要高T位的tag域来指明该内存单元是位于哪个cache line中。

       下面来说说flag bits域。对于指令cache来说,flag bits位只有一位,即valid位。顾名思义,valid位指明了相应的cache line中是否装载了有效的数据。系统刚上电时,所有的cache line中的valid位都被设置为invalid.对于数据cache来说,flag bits除了有valid位外,还有一个dirty位。对于cache line来说,如果读进来的数据还未被修改,则称为clean;相反,如果数据被修改了但是还没被写回内存,这时将dirty置位表示内存和cache里的数据不同步。在SMP系统中,所有的处理器都必须协调工作保证它们所看到的内存里的内容必须是一样的,这种问题被称为缓存一致性(cache coherency).假设一个处理器的cache内对某一内存有一份clean cache copy,而这时该处理器监测到另一个处理器对相应的该内存单元发出一个写请求,那么前一个处理器将会把相应的cache line给设置为dirty,等待后者的写操作完成并进行重装载,以保证数据的同步性。显然,读操作是不会影响cache的一致性的。对于cache的实现的一些细节将在下一篇中给出。

Cache写机制

Cache 写机制分为:Write-through和Write-back

 Write-through(直写模式)

定义:在数据更新时,同时写入缓存Cache和后端存储(主存);

优点:操作简单;

缺点:因为数据修改需要同时写入存储,数据写入速度较慢。

 

对于写缺失使用no write allocate policy(见下文)的write through 处理流程

 

Write-back(回写模式)

定义:在数据更新时只写入缓存Cache,只在数据被替换出缓存时,被修改(用dirty标记)的缓存数据才会被写到后端存储(主存);

优点:数据写入速度快,因为不需要写到后端存储(主存);

缺点:一旦更新后的数据未被写入存储时出现系统掉电的情况,数据将无法找回。

 对于写缺失使用write allocate policy(见下文)的write back 的处理流程:

注意:上述两种写模式都是针对当缓存cache中存在要写的数据的地址时的分配策略,然而,当要写的数据的地址不在缓存cache中,即发生Write miss。

 

Write-miss(写失效)

write-miss(即所要写的数据的地址不在cache中)的处理方式有两种:

no write allocate policy:将要写的内容直接写回main memory;

write allocate policy:将要写的地址所在的块先从main memory(主存)调入cache中,然后写cache;

 注意:拿L1 Dcache为例,因为对一个cache line的写入一般不会刚好是一个cache line的长度(抓了trace看过,肉眼观察是8个字节的访问粒度居多),

一个cache line一般是64字节,你的一次register spilling只有几个字节,也就是说你的一次写入只更新了一个cache line的一小部分,其他部分的值必须保持和内存一致,

所以你不能在cache里随便找一个地方就写,而是要先把旧值取上来,再写入,这是write-allocate的做法。

补充:Cache基本概念

Allocation

         在CACHE中发现一个位置,并把新的cache数据存到这个位置的过程。这个处理过程可能包括evicting(驱逐)cache中已存在的数据,从而为新的数据腾出空间。

Associativity

         指每个Set(集)里包含的line frames(行帧)数。也就是cache的way(路)数。

Capacity miss容量失效

         因为cache的容量限制,导致不能包括一个程序的整个working set(工作集),从而造成cache失效。这是三个cache失效原因(容量、强制、冲突)之一。

Clean干净

         一个有效的cache行,它的内容没有被更高层内存或CPU修改写过,我们称这个cache行是“干净”的,显然相反状态是dirty(“脏”)

Coherence一致性

         如果读内存任意地址的数据返回的总是这个地址的数据最近的更新,我们说内存系统是一致的。存取指CPU和EDMA等的存取。

Compulsory miss强制失效

         有时称为首次引用失效。强制失效是一种肯定发生的一种失效,因为数据事先从没有使用过也就没有机会被cache。但有些情况也被称为强制失效,尽管它们不是第一被引用的数据,比如对非写定位的cache进行同一位置的重复写,以及对一个不被cache内存区域的存取。这是三个cache失效原因(容量、强制、冲突)之一。

Conflict miss 冲突失效

         由于Associativity的限制导致的而非容量限制造成的cache失效。

Direct-mapped cache直接映射cache

         直接映射cache映射低层内存的每个地址到cache的一个单一位置。这样低层内存的多个地址可能被映射到cache中的同一位置上。它相当于1-way set-associative cache。

Dirty脏

         对writeback回写cache,写到达多级存储结构的某一层,并更新这一层内容,但并不更新更低层的内存,这样,当一个cache行有效并包含更新后的数据,但却不更新下一更低层的内存,我们说这个cache是“脏”的,显然一个有效行的相反状态是“干净”。

DMA直接内存存取

         直接内存存取,通常一个DMA操作copy一个内存块从一个区域到另一个区域,或在外设和内存之间传输数据。对C64x DSP,DMA传输使用EDAM,这些DMA传输与程序执行是并行的。从cache一致性角度,EDMA的存取可以看成是另外一个并行处理器。

Eviction驱逐

         从cache移出一个line从而为新的数据腾出空间的过程我们成为Eviction。Eviction可以由用户发出的writeback-invalidate产生,被驱逐的line我们称为victim line。当victim line是dirty(“脏”)的时候,数据必须回写道下一级存储器中以维持内存的一致性。

Execute packet执行包

         在一个周期里并行执行的一个指令块,一个执行包可以包含1-8个指令。

Fetch packet取指包

         1个周期里存取的包含8条指令的指令块。显然一个取指包可以包含多个执行包,这样可能消耗多个周期。

First-reference miss首次引用失效

         是强制失效的一种形式。见强制失效。

Fully-associative cache全关联cache

         任何内存地址都可以被存储在cache的任何位置。这样的cache非常灵活,但通常硬件上不可实现。这种cache同直接映射cache和集关联cache形成鲜明对比,这两种cache在定位策略方面都有更多的限制,全关联cache只具有概念上的意义,当分析直接映射cache和集相关cache性能时对区分冲突失效和容量失效是有用的,全关联cache等价于这样一个集关联cache:它有line frame个way,仅有一个set。

Higher-level memory高层内存

在多级内存系统中,距离CPU较近的内存称为高层内存。在C64x系统中,最高层内存通常是L1层CACHE,这一层的内存直接同CPU相连。较高层内存通常充当较低层内存的CACHE。

Hit命中

         当请求的内存地址的数据在cache中,我们说cache命中。命中的反义词是miss(失效)。Cache的命中使cpu的阻塞时间最短,因为才cache中去数据要比从原始位置取更快。在某一层内存的“失效”可能在较低层“命中”。

Invalidate无效

         将一个有效的行标记为无效行的过程。这相当于废弃了这一行的内容,但并不回写任何更新的数据。当与writeback组合时,会先将dirty数据更新到保存有这个地址的下一层内存。这通常用于维持内存的一致性。

Least Recently Used(LRU)allocation

         对于set-associative和fully-associative cache,最近最少使用原则被用来一个set里选择一个line frame作为被驱逐的line,用来保存新的cache数据。

Line行

         是cache处理的最小单位块。显然一个cache行的尺寸要比CPU或高层内存存取的数据尺寸要大。例如尽管CPU可以进行单字节的存取,但如果发生一次读失效,则cache会将整个cache行的数据读入。

Line frame行帧

         Cache的一个位置,包含被cache的数据(一行)、一个关联的tag地址,这个行的状态信息。状态信息包括这一行是否valid(有效)、dirty(脏)、LRU状态

Line size行尺寸

         一个行的尺寸,单位字节。

Load through

         当CPU请求在第一级和第二级cache中都失效时,数据从外部内存获得,同时会存储在第一级和第二级内存中,如果一个cache,它保存数据并同时把数据送到高一层cache中,这种cache我们称之为load-through cache。相对于那种首先存储数据到低层然后第二步再把数据送到高层的cache,这种cache可以减少阻塞时间。

Long-distance access长距离存取

         CPU对不被cache内存区域数据的存取。显然,由于这种存取,不会影响cache的状态,但速度会比较慢。

Lower-level memory 低层内存

         在多级内存系统中,距离CPU较远的内存称为低层内存。在C64x系统中,最低层内存包括L2以下的外部系统内存和内存映射的外设。

LRU

         表示cache line的最近最少使用的状态信息。

Memory ordering内存访问排序

         定义在内存里以什么样的顺序看内存操作的结果,有时称为consistency(连贯性)。在分级结构的特定层的强内存访问排序表示在这一层上不可能以不同与程序序的顺序来观察内存存取的结果。松散内存访问排序允许内存分级体系结构以不同的顺序看内存操作的结果,注意强排序并不要求系统内存以程序序顺序操作,而仅仅是使操作的结果在其它请求者看来是与程序序一致的。

Miss失效

         当请求的内存地址的数据不在cache中,就是说发生了cache失效。一个cache失效,会引起阻塞请求者直到line frame被定位,数据从低层内存中获得为止。在某些情况下,比如CPU对L1D出现写失效,阻塞CPU是不必要的。Cache失效通常分为三种类型:强制失效、冲突失效和容量失效。

Miss pipelining

         服务单一的cache失效的过程需要多个流水周期。如果对连续的失效处理进行流水线操作,显然就增加对失效处理的效率,降低了阻塞周期。

Read allocate

         读失效cache是仅在发生读失效时才在cache中定位空间并把数据读入。写失效发生时不会产生数据读入到cache中,而是直接把写数据送到下一层cache中。

Set集

         Line frames的一个集合。直接映射的cache一个set包含一个line frame,N-way set-associative cache每个set包含N个line frame。Full-associative cache仅有一个set,这个唯一的set包含所有的line frames。

Set-associative cache集相关cache

         一个set-associative cache包含多个line frames用于cache低层内存。当为一个新的line数据定位一个空间时,选择哪个line frame是基于cache的定位策略的。C64x是基于LRU策略的。

Snoop侦测

         是lower-level memory(低层内存)查询higher-level memory(高层内存)以确定高层内存是否包含给定地址的数据的一种方法。Snoop的主要目的是通过允许低层内存请求高层内存进行数据更新来维持内存的一致性,snoop操作可以触发writeback(回写),或更普通的writeback-invalidate(回写并无效),触发writeback-invalidate(回写并无效)操作的snoop有时又称为snoop-invalidates。

Tag标签

         地址高位作为一个Tag存储在line中,在决定cache是否命中的时候,cache控制器会查询Tag。

Thrash

         当存取操作引起cache性能遭受严重损坏,我们说thrash cache了。Thrash的原因可以有很多:一个可能是算法在短时间存取太多的数据而很少或根本不重复使用。也就是说working set太大。这样的算法将引起非常严重的容量失效。另一种情况是算法重复存取一小组不同地址的数据,这些数据映射到cache的相同set,这样就人为造成严的冲突失效。

Touch

         对一个给定地址的存储器操作,我们称之为touch那个地址。Touch也可以指读数组元素或存储器地址的其它范围,定位它们在一个特定层cache的唯一目的。一个CPU中心环用作touch一个范围的内存是为了定位它到cache经常被引用作为一个touch环。Touching一个数组是软件控制预取数据的一种形式。

Valid有效

         当一个cache line保存从下一级内存取的数据,那么这个line frame就是有效的。无效状态发生在line frame不保存任何数据,或者说还没有被cache的数据,或先前cache的数据由于某种原因(内存一直性要求、程序要求等)被无效。有效的状态并不表示数据是否已经被修改(其实这有另外的状态来表示,即dirty或clean)。

Victim

从cache移出一个line从而为新的数据腾出空间的过程我们成为Eviction。Eviction可以由用户发出的writeback-invalidate产生,被驱逐的line我们称为victim。当victim line是dirty(“脏”)的时候,数据必须回写到下一级存储器中以维持内存的一致性。

Victim Buffer

         保存victim直到它们背回写的特殊buffer。Victim lines被移到victim buffer,这样位新的数据腾出空间。

Victim Writeback

         当victim line是dirty(“脏”)的时候,数据必须回写道下一级存储器中以维持内存的一致性。我们称这个过程为Victim Writeback

Way

         对set-associative cache,每个set包含多个line frames。每个set包含的line frames数被称为way。以set做为行,line frame作为列,那么一列的line frames集合称为一个way。例如,一个4-way set-associative cache有4个way,每个set有4个关联的line frames,结果,对于一个可cache的内存地址,在cache中有4个可能的映射位置。

Working Set

         程序或算法就是数据和指令代码的集合。在一个特定时间内被引用的这个集合我们称为working set(工作集)。通常,对于一个算法,我们需要考虑高层内存的工作集,对于整个程序我们需要考虑底层内存的工作集。

Write allocate

         对于一个write allocate cache,如果发生写失效,就会定位一个空间并将失效的数据读入到cache中。空间定位按照LRU原则。一旦数据被读入到cache中,就进行写操作,对write allocate cache,仅更新当前层的内存,写的数据并不立刻送到下一层更低的内存。

Writeback回写

         将一个有效的但为脏的cache line回写到较低层内存的过程。回写后的这个cache line我们称之为clean(“干净”)。回写后的cache line状态依然为有效。

Writeback cache回写cache

         回写cache仅修改它自己的写命中的数据,它不会立刻更新下一级较低层内存的内容。数据在将来某时被回写,如:当这个cache line被驱逐时,或当低层内存向高层内存snoop这个地址时,当然也可以直接通过程序发送writeback命令。一个写命中,会引起对应的line被标记为dirty(“脏”),也就是说数据被更新了,但还没来得及更新低层内存。

Writeback-invalidate回写后使无效

  将一个有效的但为脏的cache line回写到较低层内存并置这些cache line为无效状态的过程。对C64x,对一组cache line进行Writeback-invalidate时,仅回写那些有效且为脏的cache lines,但却置那一组中的所有cache lines为无效。

Write merging

         Write merging是组合多个独立的写操作成一个、位宽更大的写操作。显然这可以改善了内存系统的性能。例如,对C64x CPU,L1D写缓冲在某些情况下(如果它们是对同一个双字地址的写)能合并多个写。

Write-through cache

         对于一个write-through cache,能把所有的写直接旁路到较低层内存。Cache中永远不包含更新的数据,这样做的结果是cache line永远不会是dirty。C64x的cache并不使用这种cache。

 

 

参考博文https://blog.csdn.net/qq_21125183/article/details/80590934  ,https://blog.csdn.net/vanbreaker/article/details/7470830 ,https://www.cnblogs.com/lifan3a/articles/4958188.html, https://blog.csdn.net/leoufung/article/details/4880462和https://www.cnblogs.com/coderex2522/p/8057830.html

 

posted @ 2019-07-10 14:41  大海在倾听  阅读(5321)  评论(2编辑  收藏