1. Cache的作用
在 存储层次结构 篇幅中,详细地说明了计算机设计师引入存储层次结的动机与目的。CPU只能操作内存和寄存器,其每一次的指令操作运算都需要经过从内存获取指令、解码、执行等过程,而内存读取速度远赶不上CPU的速度。在早期时候,内存速度非常缓慢,而价格却十分昂贵,而CPU也并没有特别快。
下图为1980~2000年间,随着CPU时钟速度的加快,CPU和内存在速度上差异日趋显著。
▲ 图片来自EXTREMETECH
下图是1980到2010间,CPU和内存在速度和性能上的差异图。
▲ 图片来自Computer Architecture
随着这差异的扩大,急需引入一种新型的快速内存来弥补两者的差距。而这种新型的高速内存便是“CPU高速缓存(CPU Cache)”。
对于CPU Cache,Wikipedia定义是:
A CPU cache is a hardware cache used by the central processing unit (CPU) of a computer to reduce the average cost (time or energy) to access data from the main memory.
即CPU cache是CPU用来降低从内存访问数据的平均成本(时间或性能)的硬件高速缓存。它位于存储层次结构体系(类似金字塔模型)中自顶向下的第二层,仅次于CPU寄存器。其容量远小于内存,但是读写访问速度接近CPU的时钟频率。
▲ 图片来自Wsfdl.com
根据程序对内存地址访问规律上的局部性(时间局部性和空间局部性)原则,CPU会将最近频繁访问使用或是临近即将使用(CPU获取内存数据以块的形式)的指令或数据存储在cache中,以减少CPU下一次获取该指令的时钟周期。当CPU在访问内存时候,会先去cache中查询所需指令或数据是否已存在,若存在,则不用去访问内存,取而代之的是直接获取并解码执行;若不在高速cache中,则继续向低一层级的内存中去获取该指令,载入高速cache,然后返回给cpu执行。
如今,无论是超低功率、低性能的CPU体系结构,还是高端、高性能的CPU,都使用了cache技术。下图显示了一个拥有二级缓存(L1、L2)的CPU命中率的状态情况,CPU的总命中率随着L2空间大小的增加而迅速上升。L2虽然速率上面稍逊于L1,但是它的空间比L1大许多,而且造价也略低,更大的cache空间提高了CPU的命中率,同时还不会对CPU芯片的尺寸设计、功耗带来其他负面的影响。
▲ 图片来自EXTREMETECH
1.1 Cache相关术语
-
缓存命中(Cache Hit)
CPU需要数据时,先去L1搜索,若L1未找到,则接着L2、L3缓存中检索,若找到了所需要的数据,则称为缓存命中。
-
缓存缺失(Cache Miss)
若CPU在高速缓存中没有找到所的数据,则CPU必须请求将其从内存或存储设备(操作系统+虚拟存储器+虚拟内存范畴)加载到缓存,这便是缓存未命中。
-
命中时间(His Rate)
访问某层存储器层次结构所需要的时间,包括了判断当前的访问是命中还是缺失所需要的时间。
-
缺失代价(Miss Penalty)
将相应的数据(内存和cache间的块称为:高速缓存线)从低级存储器复制到高层存储器所需的时间。包括访问块、数据逐层传输、将数据插入发生缺失的层和将信息块传输给请求者的时间。本条来自《计算机组成与设计 · 硬件/软件接口》
1.1.1 Cache的物理位置
前面的存储层次结构图中,描述了Cache位于CPU和内存之间,即物理位置上的第二级。那么这个Cache是在CPU芯片内部还是在主电路板上?其答案是高速Cache(L1、L2、L3或L4)通常集成在CPU芯片上或放置在与CPU有单独总线互连的单独芯片上,而不是主板中。
The L1,L2 and L3 cache is on the processor chip and is not built into the CPU.
但是一些早期的计算机(比如486)则是以外部高速cache的形式,比如该cache是一个单独芯片,通过将其插入到插槽,并嵌入到主板的上面。如今的高速cache都是集成到CPU芯片上。
L1 cache进一步分为两个部分: L1数据缓存和L1指令缓存。后者包含将被CPU核心消耗的指令,而前者用于保存将被写回主存的数据。L1缓存不仅作为指令缓存,而且还保存预解码数据和分支信息。此外,虽然L1数据缓存通常作为输出缓存,L1指令缓存的行为像一个输入缓存。当需要的指令就在获取单元旁边时,这是很有帮助的。
通常情况下,当CPU Cache大小增加时,其缓存命中时间也随着增加。这在大型游戏项目中尤其如此。
▲ 图片来自HardWareTimes
1.1.2 CPU Cache和Main Memory异同
– 相同点:
1)都是基于半导体和晶体管制造;
2) 高速缓存(CPU Cache)和主存(Main Memory)都是属于都是易失性存储器,当电源关闭时丢失其内容;
– 差异点:
1)高速缓存只保存主存中最常用的信息或程序代码的副本;
2)高速缓存通常集成在CPU芯片上。主内存(DRAM)放在主板上,并通过内存总线连接到CPU。
3)高速缓存更靠近CPU,因此读写速度比主存更快;
4)主存比高速缓存大很多倍,而且造价更便宜。通常主存为几个G,而高速缓存则为几KB或几MB;
1.2 Cache工作原理
到这里时,我们已经很清楚cache所扮演的角色。但是有两个问题需要确认和解决:
1) 如何去确定CPU需要的数据在cache中?
2) 若数据在cache中,要怎么去寻找搜索?
围绕这两个主要话题,接下来将一步步分析和讲解CPU中cache中的工作方式和原理。(备注:这部分内容将放到 揭秘CPU Cache工作原理(二)中去)。
1.3 查看设备Cache级别和大小
对于类UNIX平台设备,可以直接使用镜像包中系统自带的lscpu命令来查看当前CPU的缓存级别,以及各级缓存大小。当前手里有台服务器 Intel(R) Xeon(R) Gold设备。
[root@node164 ~]# cat /proc/cpuinfo | grep name | cut -f2 -d:| uniq -c
64Intel(R)Xeon(R) Gold 6130 CPU @ 2.10GHz
复制代码
有64颗逻辑CPU,lscpu命令默认会打印出CPU的详细信息,比如大小端、线程数、时钟周期等,这里使用grep筛选出cache信息,其他的不打印终端。
[root@node164 ~]# lscpu |grep cache
L1d cache:32K
L1i cache:32K
L2 cache:1024K
L3 cache:22528K
复制代码
根据终端信息可知,该CPU体系结构中共有3级缓存,对于L1缓存,大小为64K,其中L1d是L1 Cache for Data, 即数据缓存, L1i是L1 Cache for Instruction, 即指令缓存。L2缓存大小是1024K, L3缓存是22528K。
除了lscpu之外,还可以使用getconf来获取,比如:
[root@node164 ~]# getconf -a|grep CACHE
LEVEL1_ICACHE_SIZE 32768
LEVEL1_ICACHE_ASSOC 8
LEVEL1_ICACHE_LINESIZE 64
LEVEL1_DCACHE_SIZE 32768
LEVEL1_DCACHE_ASSOC 8
LEVEL1_DCACHE_LINESIZE 64
LEVEL2_CACHE_SIZE 1048576
LEVEL2_CACHE_ASSOC 16
LEVEL2_CACHE_LINESIZE 64
LEVEL3_CACHE_SIZE 23068672
LEVEL3_CACHE_ASSOC 11
LEVEL3_CACHE_LINESIZE 64
LEVEL4_CACHE_SIZE 0
LEVEL4_CACHE_ASSOC 0
LEVEL4_CACHE_LINESIZE 0
复制代码
其展示的信息以Byte为单位,可以看到和lscpu得到的数据信息是匹配的。
甚至还可以使用dmidecode命令来获取缓存的详细信息:
[root@node164 ~]# dmidecode -t cache
# dmidecode 3.2
Getting SMBIOS data from sysfs.
SMBIOS 3.0 present.
Handle 0x0074, DMI type 7,19 bytes
Cache Information
Socket Designation: L1-Cache
Configuration: Enabled, Not Socketed, Level 1
Operational Mode: Write Back
Location: Internal
Installed Size:1 MB
Maximum Size:1 MB
Supported SRAM Types:
Synchronous
Installed SRAM Type: Synchronous
Speed: Unknown
Error Correction Type: Single-bit ECC
System Type: Instruction
Associativity:8-way Set-associative
Handle 0x0075, DMI type 7,19 bytes
Cache Information
Socket Designation: L2-Cache
Configuration: Enabled, Not Socketed, Level 2
Operational Mode: Varies With Memory Address
Location: Internal
Installed Size:16 MB
Maximum Size:16 MB
Supported SRAM Types:
Synchronous
Installed SRAM Type: Synchronous
Speed: Unknown
Error Correction Type: Single-bit ECC
System Type: Unified
Associativity:16-way Set-associative
· · · · · · more · · · · · ·
复制代码
其中Configuration行即可对应缓存级别的大小信息,比如:
Configuration: Enabled, Not Socketed, Level 1
复制代码
表示L1缓存信息。
除了查看设备CPU体系结构的缓存信息外,还可以使用dmesg来查看内存到高速缓存间数据传输的块大小(高速缓存线,Cache Line)。从下面的终端打印信息可知该Cache Line为64字节。
[root@node164 ~]# dmesg |grep cache
[0.016037] Dentry cache hash table entries:33554432(order:16,268435456 bytes)[0.060792] Inode-cache hash table entries:16777216(order:15,134217728 bytes)[0.078032] Mount-cache hash table entries:524288(order:10,4194304 bytes)[0.078254] Mountpoint-cache hash table entries:524288(order:10,4194304 bytes)[1.481379] PCI: pci_cache_line_size set to 64 bytes
[1.783129] Dquot-cache hash table entries:512(order 0,4096 bytes)[1.992661] xhci_hcd 0000:00:14.0: cache line size of 64 is not supported
[4.079408] sd 7:0:0:0:[sdb] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
[4.079435] sd 6:0:0:0:[sda] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
[4.093800] sd 8:0:0:0:[sdc] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
[4.114642] sd 9:0:0:0:[sdd] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
[4.127546] sd 10:0:0:0:[sde] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
[4.143419] sd 11:0:0:0:[sdf] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
[4.158524] sd 12:0:0:0:[sdg] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
[4.175420] sd 13:0:0:0:[sdh] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
复制代码
另附加一系列与基于NUMA的进程和操作内存统计信息(命中和缺失)的工具,分别是:
[root@node164 CenterController]# numa
numactl numademo numastat
[root@node164 CenterController]# numastat
node0
numa_hit 1753403168
numa_miss 0
numa_foreign 0
interleave_hit 67129
local_node 1753403168
other_node 0
复制代码