小cache与大内存的映射详细解析

        更多精彩内容,或者需要更详细的资料,欢迎关注微信公众号:云计算coding之美,留言,感谢。

        众所周知,电脑的设计与制造可以说是信息时代,整个人类智慧的集大成者,包括众多技术,如计算,存储,网络等等,个人理解它更像是传统的制造技术与新型高技术的结合体,我也希望,通过开设“体系结构那些事儿”专题,能让我们更好认识到电脑里各个冷冰冰的器件之间的联系及存在意义。

        从个人的理解上来说,我们的个人电脑的功能上就是云计算的一个缩影,云计算中的功能块几乎我们的个人电脑中都有,区别在于规模的大小。我们的个人pc机器最重要的几个功能,计算,存储,互联(网络),而这些功能也同样涵盖了Asure,AWS,阿里云,腾讯云等云厂商们所提供给大家的服务内容。而云计算的本质就是堆服务器。

        最近我们在做一些与DPDK相关的开发工作,提高云计算系统的数据快速处理、匹配的能力,所面对的环境具有高度数据密集型特征,尤其是TLB方面做的一些优化对性能提升比较明显,这就今天的主角,cache。

什么是cache?

        cache,译为:高速缓存(SRAM)。早前的cpu其l2 cache放在主板或与cpu同一块电路板上,成为片外cache。得益于工艺的进步,目前大多数cpu的l2 cache是集成于cpu内部的,称为片内cache。而目前l3 缓存我理解是片外cache,属于cpu封装集成,并不在core内。如图1

图片

图1 三级缓存结构

cache的作用?

        cache是为了解决内存读写速度与cpu计算能力不匹配的问题,提高cpu计算资源的利用率。目前主流的设计是三级缓存结构,如图1所示。

不同级cache的区别?

      1、L1 Cache 容量最小,到L3依次增大。以我自己的电脑为例,L1 Cache有32KB/core),L2 Cache有512KB/core,L3 Cache有64MB。

      2、L1缓存中可存放数据和指令,L2和L3缓存中只有数据。当前大多数的架构中L1缓存的指令与数据是分开的,成为分立缓存,早期也有不区分的架构方案,成为统一缓存,这也是缓存方案的一大改进。

      3、L1、L2缓存与L3缓存有本质的区别,从图1中也可以看出来,L1、L2。缓存是每个core独占的,L3缓存是所有核共享的,L3缓存可以认为是高速缓存L1L2与低速内存memory之间的缓冲区,最早是由AMD提出的,通过增加L3高速缓存的命中率以抵消io芯片独立所带来的延迟。这也是Cache一个非常重要的改进,多级缓存,之后intel等厂商纷纷出现了L3缓存,目前已知的个别芯片做到了L4缓存。

      4、L1 Cache的延迟最低,访问时间为几个数时钟周期(4),L2 Cache的访问时间为十几个时钟周期(10),L3 Cache的访问时间为几十个时钟周期(30),内存memory约为上百个时钟周期。

如何增加Cache命中?

        Cache存在的理论依据有两点,1、一个存储位置如果当前被引用,那么该位置附近的存储内容会在不久的将来被程序引用(空间局部性)。2、一个存储位置如果当前被引用,在不久将来,该存储位置会被程序多次引用(时间局部性)。

        因此,我们在写程序时,一方面,变量的定义要尽量符合时间局部性与空间局部性,如:同一个变量在程序的局部多次使用,联系紧密的变量的定义位置相近,likely unlikely  三段表达式等增加使用。虽然现在的编译器已经非常智能地帮忙优化,但是良好的编程习惯还是非常重要。

Cache映射方法?

        到这里,聪明的小伙伴都发现,Cache是小的,尤其是与CPU更近的L1缓存,只有区区几KB~几十KB,而主存通常是几GB~几百GB,大约差了6~7个数量级,所以,内存中的内容不可能完全搬入Cache中,由大到小的过程,就需要涉及到一个概念:映射。Cache的空间十分有限,究竟memory中的数据应该搬迁至cache的哪些位置,是由我们的映射方法决定的。至于哪些内容可以搬迁至Cache中,如果冲突后Cache该怎么取舍,其基本的原理是提前获取CPU需要的数据,减少Cache miss的次数多而引发的缓存抖动,至于具体细节我们以后专门给大家做一篇内容进行讲解,现在先看Cache映射方法。

        通常Cache映射方法有3个,直接映射,全相联映射,组相联映射。三种方法各有优缺点,我们一一来看:

直接映射:

直接映射方法在三种映射方法中最简单,如图2所示,

图片

图2 直接映射

直接映射中,Memory中的任意一块地址空间,只能映射到唯一的Cache空间中,该方法的优点:硬件设计简单,地址转化速度快,避免了置换算法。但同时,其缺点也非常明显,试想一下,当图2中,程序同时需要Memory 0x00与0x08中的数据,此时,两块Memory中的数据需要在Cache的0x00空间中不断置换,影响了程序执行效率。因此,直接映射方法由于Memory中每一块地址空间都只能映射到固定的Cache空间中,因此,Cache容易发生冲突,且存在Cache利用率低的情况,一般该方法更适用于Cache空间比较大的情况。

全相联映射:

     针对直接映射Cache miss高的缺点,全相联映射应运而生,相比于直接映射中,一个Memory地址空间只能映射到固定Cache空间中,全相联映射中,Memory中的地址空间可以映射到任意Cache空间,其原理如图3所示:

图3 全相联映射

全相联映射方法中,Memory中的所有地址空间都可以映射到Cache的任意位置,该方法的优点:同等条件下,最大限度降低了Cache miss 的可能性,但是,其缺点也是比较明显,Memory映射到Cache中时,需遍历Cache空间,直到找到空闲空间,硬件设计复杂,开销高,同样在检查cache是否miss时,依然需要遍历所有的Cache空间,且该方案引入了置换算法,即:当Cache空间被映射满时,此时若新的数据需要映射到Cache中,新数据需要与现有Cache中某个Cache line中的数据发生置换,将原有数据取出,将新数据写入,置换算法一般选取长时间不用,或使用频率最低的数据为置换数据。全相联映射方法在映射和查找过程中需要遍历整个Cache,为了提高性能,现在大多数硬件平台都使用并发查找的方式,虽然一定程度上提升了效率,但也增加了设计的复杂度。该方法更适用于Cache空间较小的情况。

组相联映射:

     前面提到的直接映射,全相联映射方法其实更像是两个极端,硬件设计一个简单,一个复杂;Cache miss率一个高,一个低;一个适用于大Cache空间,一个适用于小Cache空间。组组联映射没有太多新鲜的东西,本质上是直接映射与全相联映射两个极端的中庸之道。

     组组联映射方法首先将Cache划分为s个组(Set),每个组中包含n个Cache line,一个Memory只能映射到一个固定的组(Set)中,在组内,该Memory可以映射到组内任意Cache line空间。如图4所示,Cache共分为4组,每组包含两个Cache line。例如:Set 0包含0x00和0x01两个地址空间,Memory中的0x00地址空间能映射到固定的Set 0中的任意Cache line中。

图片

图4 组组联映射

从上述原理可知,组组联映射时,针对某一个Memory地址应先确定组(Set)编号,再确定Cache line地址,确定组编号使用的是直接映射方法,确定Cache line地址使用的是全相联映射方法,组组联映射方法同时具备了两种方法的优点和缺点,但是通过分组的方式,将两者的缺点的影响面控制在了一个较小的范围,而且较为灵活,在实际的表现中效果更好,因此该方法目前应用最为广泛。

判断Cache命中:

     根据上述中,数据从Memory到Cache的搬运规则我们清楚,Cache相对于Memory是非常小的,一个Cache line中的数据可以来自多个Memory空间,自然就引出了一个问题,那就是如何确定当前Cache line中的数据为我们需要的数据。

Cache line 的结构:

判断Cache line中的数据是否为程序当前所需的数据,首先需要了解Cache line的数据结构。简单来讲,Cache line 由两部分组成,一部分是索引信息(Tag Cache),另一部分是数据信息(Data Cache),而其中的索引信息就是系统判断Cache是否命中的依据,判断命中,则根据索引信息中的某些索引值,从数据信息部分获取相应的目标值,返回给CPU。在不同的映射方法下,Cache line的数据信息部分的基本类似的,除了存放对应页的数据外,还有如Dirty等标记位,而索引信息部分则会根据映射方法的不同而划分相应的索引信息位。这里我们假设在64位计算机中,每个Cache line的Tag Cache有64bit。(现在实际使用的是48bit)。

直接映射缓存结构:

     根据前面所述,直接映射方法一个Memory单元映射到固定的Cache line中,因此,需要有set index值才能寻找到对应的Cache line;每个Cache line对应的字节数一般有多个,因此需要设定offset值来确定程序所需要的字节是哪一个。最后,文章前面提到一个Cache line中存放的数据可能来自于一个Memory空间集合中的任何一个,因此为了判断Cache line中当前存放的数据是否是程序所需的数据,还需要对目标Cache line中存放的Tag信息进行比对,如果比对信息符合,则证明该信息是目标信息;但是此时并不意味着Cache line中的目标信息是正确的,还需要对标识为进行检查,通常Tag信息中的标志位有Valid等,表示是否有效,Data信息中的标志位有Dirty位,表示当前信息是否是过期的脏数据等等。在系统中的构成及查找过程如图5所示。

图5 直接映射缓存结构

如图5所示,当Cache的set个数是64,则需要idx有6bit来表示(2^6=64),每个Cache line中包含的byte数为64,offset同样也需要6bit,在当前的体系结构中,低位部分表示offset信息,中间位表示index信息,高位表示tag信息。

全相联映射缓存结构:

     回顾全相联映射方法,Memory中的数据可能会被映射到Cache中的任意Cache line中,所以,不存在set组概念,可以理解为整个Cache是一个大组,因此缓存结构不再需要idx字段,仅需要tag字段和offset字段。

图片

图6 全相联映射缓存结构

如图6所示,当一个Cache line为64byte时,offset需要6bit表示,tag的bit数由实际操作系统参数决定。在全相联映射中,目标tag值与Cache中每个tag值进行比对,当寻找到相等的tag值时,表示缓存命中,则需要根据offset值,去除相应的byte。

组组联映射缓存结构:

     组组联映射缓存结构是目前使用最为广泛的结构,因为也存在set,所以,需要idx字段,与直接映射相比较,组组联映射的一个set中含有多个Cache line,在总Cache size一致的情况下,set数目比较少,相应的,idx数目会降低。因此,组组联映射结构包含tag,idx,offset等字段。

图片

图7 组组联映射缓存结构

     如图7所示,首先根据Memory Address中的idx字段,寻找对应的set n,之后,将目标tag信息与Cache的set n的所有Cache line中的tag信息进行比对,如果出现匹配的tag,且Tag部分和Data部分的标志为都符合预期,则认为Cache命中,最后,利用offset字段,取出命中Cache line中对应的byte。

64位服务器的Cache情况

     我们结合以上理论,通过某台具体服务器系统实际的参数来更好理解,首先,我们的服务器大多是选择组组联映射方式,首先需要确定的是服务器的cache大小,set数目,cache line大小三个信息,以我们的linux服务器为例,进入目录 /sys/devices/system/cpu/目录,(该目录下有多个cpu+num的目录,这是对应某个cpu,服务器有多少cpu,就有多少该文件),选择其中一个cpu的文件,例如:/sys/devices/system/cpu/cpu0/cache/。(此时,文件夹下有多个文件,一般是4个,分别是index0,index1,index2,index3,为什么会出现四个文件夹,一会儿我们会说到,这四个文件夹中的文件名称完全一样,其作用如表1中所示)。当看到ways_of_associativity,则说明该系统使用的是组组联映射,在本文参考的系统中,index0和index1的size=32KB,coherency_line_size=64B,number_of_sets=64,ways_of_associativity=8,

即:size= associativity* number_of_sets* coherency_line_size

以此确定,Memory Address的offset 6bit,index 6bit,64位系统中,Memory Address使用了48bit,所以,系统中的原理如图8所示。

     在cpu+index文件夹下总共有4个index文件,实际上index0 和 index1都是L1cache,分别是存放数据和指令的,可以通过type文件查看,index2和index3通常表示L2缓存和L3缓存,可以通过level查看,在我们实验系统中,L1缓存和L2缓存均为cpu独占,因此,shared_cpu 文件中仅有一个cpu值,没有其他cpu,而L3缓存位共享缓存,可以查看到shared_cpu文件中记录了共享该缓存的所有cpu。现在不论intel还是amd的x86处理器,大多数与实验中的处理器表现相一致。

图片

图8 64位真实系统cache结构

表1:系统Cache文件夹中各文件信息含义

文件

表示内容

coherency_line_size

Size of each line usually representing the minimum amount of data that gets transferred from memory

翻译:cache line 大小

level

Represents the hierarchy in the multi-level cache

翻译:L几的Cache

number_of_sets

Total number of sets, a set is a collection of cache lines sharing the same index

翻译:cache set的数量

physical_line_partition

Number of physical cache lines sharing the same cachetag

翻译:多个cache line共享一个tag

size

Total size of the cache

翻译:cache总大小

type

Type of the cache-data,inst or unified

翻译:cache种类分为

ways_of_associativity

Number of ways in which a particular memory block can the placed in the cache

翻译:几路组相联

shared_cpu_list

表示共享该缓存的cpu有哪些,一般L1和L2都是独占的,L3才会出现其他核共享

shared_cpu_map

     学习体系结构有助于一个程序开发者更好,更好认识承载我们程序的计算机系统,比如当我们了解到了register cache memory的原理,程序设计时就能更好的考虑到变量局部性,有助于我们设计出更优秀的程序,体系结构更过精彩内容,请关注微信公众号:云计算coding之美,带你一起学习更多更有趣的知识。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值