CPU Cache 机制以及 Cache miss

CPU体系结构之cache小结

1.What is cache?

Cache是用来对内存数据的缓存。

CPU要访问的数据在Cache中有缓存,称为“命中” (Hit),反之则称为“缺失” (Miss)。

CPU访问它的速度介于寄存器与内存之间(数量级的差别)。实现Cache的花费介于寄存器与内存之间。

现在 CPU 的 Cache 又被细分了几层,常见的有 L1 Cache, L2 Cache, L3 Cache,其读写延迟依次增加,实现的成本依次降低。

现代系统采用从 Register ―> L1 Cache ―> L2 Cache ―> L3 Cache ―> Memory ―> Mass storage的层次结构,是为解决性能与价格矛盾所采用的折中设计。

下图描述的就是CPU、Cache、内存、以及DMA之间的关系。程序的指令部分和数据部分一般分别存放在两片不同的cache中,对应指令缓存(I-Cache)和数据缓存(D-Cache)。

引入 Cache 的理论基础是程序局部性原理,包括时间局部性和空间局部性。即最近被CPU访问的数据,短期内CPU 还要访问(时间);被 CPU 访问的数据附近的数据,CPU 短期内还要访问(空间)。因此如果将刚刚访问过的数据缓存在Cache中,那下次访问时,可以直接从Cache中取,其速度可以得到数量级的提高。

CPU缓存(Cache Memory)位于CPU与内存之间的临时存储器,它的容量比内存小但交换速度快。在缓存中的数据是内存中的一小部分,但这一小部分是短时间内CPU即将访问的,当CPU调用大量数据时,就可避开内存直接从缓存中调用,从而加快读取速度。

在CPU中加入缓存是一种高效的解决方案,这样整个内存储器(缓存+内存)就变成了既有缓存的高速度,又有内存的大容量的存储系统了。缓存对CPU的性能影响很大,主要是因为CPU的数据交换顺序和CPU与缓存间的带宽引起的。

下图是一个典型的存储器层次结构,我们可以看到一共使用了三级缓存

2. Why should I care about cache?

从延迟上看,做一次乘法一般只要三个周期,而做一次CPU的内存访问需要167个cycle,如果需要提升程序性能,减少CPU的memory访问至关重要。因此,需要采用容量小但是更快的存储器(cache)。

3.为什么要有多级CPU Cache

随着科技发展,热点数据的体积越来越大,单纯的增加一级缓存大小的性价比已经很低了
二级缓存就是一级缓存的缓冲器:一级缓存制造成本很高因此它的容量有限,二级缓存的作用就是存储那些CPU处理时需要用到、一级缓存又无法存储的数据。
同样道理,三级缓存和内存可以看作是二级缓存的缓冲器,它们的容量递增,但单位制造成本却递减。
另外需要注意的是,L3 Cache和L1,L2 Cache有着本质的区别。,L1和L2 Cache都是每个CPU core独立拥有一个,而L3 Cache是几个Cores共享的,可以认为是一个更小但是更快的内存。

使用dmidecode命令查看cache size

4.cpu与cache 内存交互的过程

CPU接收到指令后,它会最先向CPU中的一级缓存(L1 Cache)去寻找相关的数据,然一级缓存是与CPU同频运行的,但是由于容量较小,所以不可能每次都命中。这时CPU会继续向下一级的二级缓存(L2 Cache)寻找,同样的道理,当所需要的数据在二级缓存中也没有的话,会继续转向L3 Cache、内存(主存)和硬盘.

程序运行时可以使用perf工具观察cache-miss的rate.

5.什么是cache line

Cache Line可以简单的理解为CPU Cache中的最小缓存单位。
内存和高速缓存之间或高速缓存之间的数据移动不是以单个字节或甚至word完成的。
相反,移动的最小数据单位称为缓存行,有时称为缓存块
目前主流的CPU Cache的Cache Line大小都是64Bytes。假设我们有一个512字节的一级缓存,那么按照64B的缓存单位大小来算,这个一级缓存所能存放的缓存个数就是512/64 = 8个。
查看cache line大小
cat /sys/devices/system/cpu/cpu1/cache/index0/coherency_line_size
cache line的影响:

for (int i = 0; i < N; i+=k)
    arr[i] *= 3;

注意当步长在1到16范围内,循环运行时间几乎不变。但从16开始,每次步长加倍,运行时间减半。
由于16个整型数占用64字节(一个缓存行),for循环步长在1到16之间必定接触到相同数目的缓存行:即数组中所有的缓存行。当步长为32,我们只有大约每两个缓存行接触一次,当步长为64,只有每四个接触一次。

6. cache写机制

Cache写机制分为write through和write back两种。

Write-through- Write is done synchronously both to the cache and to the backing store.
Write-back (or Write-behind) - Writing is done only to the cache. A modified cache block is written back to the store, just before it is replaced.

Write-through(直写模式)在数据更新时,同时写入缓存Cache和后端存储。此模式的优点是操作简单;缺点是因为数据修改需要同时写入存储,数据写入速度较慢。

Write-back(回写模式)在数据更新时只写入缓存Cache。只在数据被替换出缓存时,被修改的缓存数据才会被写到后端存储。此模式的优点是数据写入速度快,因为不需要写存储;缺点是一旦更新后的数据未被写入存储时出现系统掉电的情况,数据将无法找回。

7.cache 一致性

多个处理器对某个内存块同时读写,会引起冲突的问题,这也被称为Cache一致性问题。
Cache一致性问题出现的原因是在一个多处理器系统中,多个处理器核心都能够独立地执行计算机指令,从而有可能同时对某个内存块进行读写操作,并且由于我们之前提到的回写和直写的Cache策略,导致一个内存块同时可能有多个备份,有的已经写回到内存中,有的在不同的处理器核心的一级、二级Cache中。由于Cache缓存的原因,我们不知道数据写入的时序性,因而也不知道哪个备份是最新的。还有另外一个一种可能,假设有两个线程A和B共享一个变量,当线程A处理完一个数据之后,通过这个变量通知线程B,然后线程B对这个数据接着进行处理,如果两个线程运行在不同的处理器核心上,那么运行线程B的处理器就会不停地检查这个变量,而这个变量存储在本地的Cache中,因此就会发现这个值总也不会发生变化。

为了正确性,一旦一个核心更新了内存中的内容,硬件就必须要保证其他的核心能够读到更新后的数据。目前大多数硬件采用的策略或协议是MESI或基于MESI的变种:
M代表更改(modified),表示缓存中的数据已经更改,在未来的某个时刻将会写入内存;
E代表排除(exclusive),表示缓存的数据只被当前的核心所缓存;
S代表共享(shared),表示缓存的数据还被其他核心缓存;
I代表无效(invalid),表示缓存中的数据已经失效,即其他核心更改了数据。
8.cache的局部性

程序在一段时间内访问的数据通常具有局部性,比如对一维数组来说,访问了地址x上的元素,那么以后访问地址x+1、x+2上元素的可能性就比较高;现在访问的数据,在不久之后再次被访问的可能性也比较高。局部性分为“时间局部性”和“空间局部性”,时间局部性是指当前被访问的数据随后有可能访问到;空间局部性是指当前访问地址附近的地址可能随后被访问。处理器通过在内存和核心之间增加缓存以利用局部性增强程序性能,这样可以用远低于缓存的价格换取接近缓存的速度。

时间局部性:
代码1:

for (loop=0; loop<10; loop++) {
    for (i=0; i<N; i++) {
        ... = ... x[i] ...
    }
}

代码2:

for (i=0; i<N; i++) {
    for (loop=0; loop<10; loop++) {
        ... = ... x[i] ...
    }
}

代码二的性能优于代码1,x的元素现在被重复使用,因此更有可能留在缓存中。 这个
重新排列的代码在使用x[i]时显示更好的时间局部性。

空间局部性:
一个矩阵乘法的例子:
代码1:

for i=1..n
    for j=1..n
        for k=1..n
            c[i,j] += a[i,k]*b[k,j]

代码2:

for i=1..n
    for k=1..n
        for j=1..n
            c[i,j] += a[i,k]*b[k,j]

代码2的性能优于代码一的性能。
两者实现上的差异:

代码2的b[k,j]是按行访问的,所以存在良好的空间局部性,cache line被充分利用。
代码1中,b [k,j]由列访问。 由于行的存储矩阵,因此对于每个缓存行加载,只有一个元素用于遍历。

9.cache替换策略

Cache工作原理要求它尽量保存最新数据,当从主存向Cache传送一个新块,而Cache中可用位置已被占满时,就会产生Cache替换的问题。
常用的替换算法有下面三种。
(1) LFU
LFU(Least Frequently Used,最不经常使用)算法将一段时间内被访问次数最少的那个块替换出去。每块设置一个计数器,从0开始计数,每访问一次,被访块的计数器就增1。当需要替换时,将计数值最小的块换出,同时将所有块的计数器都清零。
这种算法将计数周期限定在对这些特定块两次替换之间的间隔时间内,不能严格反映近期访问情况,新调入的块很容易被替换出去。
(2)LRU
LRU(Least Recently Used,近期最少使用)算法是把CPU近期最少使用的块替换出去。这种替换方法需要随时记录Cache中各块的使用情况,以便确定哪个块是近期最少使用的块。每块也设置一个计数器,Cache每命中一次,命中块计数器清零,其他各块计数器增1。当需要替换时,将计数值最大的块换出。
LRU算法相对合理,但实现起来比较复杂,系统开销较大。这种算法保护了刚调入Cache的新数据块,具有较高的命中率。LRU算法不能肯定调出去的块近期不会再被使用,所以这种替换算法不能算作最合理、最优秀的算法。但是研究表明,采用这种算法可使Cache的命中率达到90%左右。
(3) 随机替换
最简单的替换算法是随机替换。随机替换算法完全不管Cache的情况,简单地根据一个随机数选择一块替换出去。随机替换算法在硬件上容易实现,且速度也比前两种算法快。缺点则是降低了命中率和Cache工作效率。

10.cache的映射
主存与cache的地址映射方式有全相联方式、直接方式和组相联方式三种。
直接映射
将一个主存块存储到唯一的一个Cache行。

1) 多对一的映射关系,但一个主存块只能拷贝到cache的一个特定行位置上去。
cache的行号i和主存的块号j有如下函数关系:i=j mod m(m为cache中的总行数)

优点:硬件简单,容易实现
缺点:命中率低, Cache的存储空间利用率低

2) 全相联映射
可以将一个主存块存储到任意一个Cache行。
主存的一个块直接拷贝到cache中的任意一行上

优点:命中率较高,Cache的存储空间利用率高
缺点:线路复杂,成本高,速度低

组相联映射
可以将一个主存块存储到唯一的一个Cache组中任意一个行。
将cache分成u组,每组v行,主存块存放到哪个组是固定的,至于存到该组哪一行是灵活的,即有如下函数关系:cache总行数m=u×v 组号q=j mod u

组间采用直接映射,组内为全相联
硬件较简单,速度较快,命中率较高

Cache Miss

1.不要期望编译器对你做任何优化

2.Cache(广义内存)系统代表性的包括三种级别:
(1)第一级cache (L1)位于CPU芯片上并且运算于CPU工作频率;
(2)第二级cache(L2)也位于芯片上比L1速度慢而体积大;
(3)第三级cache(L3)位于CPU外部,是速度最慢、体积最大的存储器。

3.当运算器需要从存储器中提取数据时,它首先在最高级的cache中寻找然后在次高级的cache中寻找。如果在cache中找到,则称为命中hit;反之,则称为不命中miss。

4.cache misses的种类:

(1)cold misses:不可避免。若K级cache空,则必发生cache miss,空的cache称为cold cache,这种cache misses称为compulsory misses或者cold misses。当cache已被warmed up则一般不会再发生cold misses。

(2)conflict misses:多个K+1级的blocks被映射到K级中同一个block。这一点关系到对于程序员而言能否写出cache友好代码。

(3)程序常会分阶段执行(例如循环:内层、外层),每个阶段会取cache blocks的固定几个块,这几个块所构成的集合称为working set。 当working set超过cache大小时所发生的miss称为capacity misses。

5.从cache指令上做优化:简化调用关系,减少冗余代码(即不是必须存在的的代码),减小代码量,减少不必要的调用;

6.从数据cache上做优化:即减少cache miss的次数,方法有不少,http://blog.chinaunix.net/uid-7319742-id-2059720.html 这篇文章有介绍

推荐链接:

http://bi.dataguru.cn/thread-163962-1-1.html

http://blog.chinaunix.net/uid-7319742-id-2059720.html

http://blog.csdn.net/wangjiaoyu250/article/details/9212863

http://coolshell.cn/articles/10249.html

https://blog.csdn.net/yhb1047818384/article/details/79604976

原文地址:https://www.cnblogs.com/jokerjason/p/10711022.html

时间: 2024-09-29 17:43:10

CPU Cache 机制以及 Cache miss的相关文章

cpu性能探究-Linux Cache机制

 在阅读文章前,您应该具备基本的存储器层次结构知识,至少要了解局部性原理.要详细了解cache基本原理,可以参考本书<深入理解计算机系统>中存储器体系结构一章: 带着疑问来看文章,cache对于程序员是不可见的,它完全是由硬件控制的,为什么在linux内核中还有cache.h这个头文件,定义了一些关于cache的结构? 1. cache概述 cache,中译名高速缓冲存储器,其作用是为了更好的利用局部性原理,减少CPU访问主存的次数.简单地说,CPU正在访问的指令和数据,其可能会被以后多次

受教了,memcache比较全面点的介绍,受益匪浅,适用memcached的业务场景有哪些?memcached的cache机制是怎样的?在设计应用时,可以通过Memcached缓存那些内容?

基本问题 1.memcached的基本设置 1)启动Memcache的服务器端 # /usr/local/bin/memcached -d -m 10 -u root -l 192.168.0.200 -p 12000 -c 256 -P /tmp/memcached.pid -d选项是启动一个守护进程, -m是分配给Memcache使用的内存数量,单位是MB,我这里是10MB, -u是运行Memcache的用户,我这里是root, -l是监听的服务器IP地址,如果有多个地址的话,我这里指定了服

jQuery的XX如何实现?——3.data与cache机制

往期回顾: jQuery的XX如何实现?——1.框架 jQuery的XX如何实现?——2.show与链式调用 -------------------------- 源码链接:内附实例代码 jQuery使用许久了,但是有一些API的实现实在想不通.于是抽空看了jQuery源码,现在把学习过程中发现的一些彩蛋介绍给大家(⊙0⊙). 下面将使用简化的代码来介绍,主要关注jQuery的实现思想~>_<~ 相较于第一篇(与第二篇无相关性),代码更新了:27~71 1 (function(window,

Java -- 常用缓存Cache机制的实现 -- 艺多不压身

常用缓存Cache机制的实现 缓存,就是将程序或系统经常要调用的对象存在内存中,以便其使用时可以快速调用,不必再去创建新的重复的实例. 这样做可以减少系统开销,提高系统效率. 缓存主要可分为二大类: 一.通过文件缓存,顾名思义文件缓存是指把数据存储在磁盘上,不管你是以XML格式,序列化文件DAT格式还是其它文件格式 二.内存缓存,也就是实现一个类中静态Map,对这个Map进行常规的增删查. 啦啦啦

使用GetLogicalProcessorInformation获取逻辑处理器的详细信息(NUMA节点数、物理CPU数、CPU核心数、逻辑CPU数、各级Cache)

不过必须XP SP3以上才行.所有API大全: https://msdn.microsoft.com/en-us/library/windows/desktop/aa363804(v=vs.85).aspx -------------------------------------------------------------------------------- 现在多核处理器已经很普及了,市场主流是双核处理器,还有4核.8核等高端产品.而且Intel推广了超线程技术(Hyper-Threa

cpu性能探究 :cache line 原理

 参考: 一个讲解Direct Mapped Cache非常深入浅出的文章: http://www.cs.umd.edu/class/sum2003/cmsc311/Notes/Memory/direct.html CPU cache http://en.wikipedia.org/wiki/CPU_cache http://blog.csdn.net/zqy2000zqy/article/details/1137895 ===================================

docker build 的 cache 机制

cache 机制注意事项 可以说,cache 机制很大程度上做到了镜像的复用,降低存储空间的同时,还大大缩短了构建时间.然而,不得不说的是,想要用好 cache 机制,那就必须了解利用 cache 机制时的一些注意事项. 1. ADD 命令与 COPY 命令:Dockerfile 没有发生任何改变,但是命令ADD run.sh / 中 Dockerfile 当前目录下的 run.sh 却发生了变化,从而将直接导致镜像层文件系统内容的更新,原则上不应该再使用 cache.那么,判断 ADD 命令或

Java 中常用缓存Cache机制的实现

所谓缓存,就是将程序或系统经常要调用的对象存在内存中,一遍其使用时可以快速调用,不必再去创建新的重复的实例.这样做可以减少系统开销,提高系统效率. 所谓缓存,就是将程序或系统经常要调用的对象存在内存中,一遍其使用时可以快速调用,不必再去创建新的重复的实例.这样做可以减少系统开销,提高系统效率. 缓存主要可分为二大类: 一.通过文件缓存,顾名思义文件缓存是指把数据存储在磁盘上,不管你是以XML格式,序列化文件DAT格式还是其它文件格式: 二.内存缓存,也就是实现一个类中静态Map,对这个Map进行

Java中常用缓存Cache机制的实现

缓存,就是将程序或系统经常要调用的对象存在内存中,一遍其使用时可以快速调用,不必再去创建新的重复的实例.这样做可以减少系统开销,提高系统效率. 缓存主要可分为二大类: 一.通过文件缓存,顾名思义文件缓存是指把数据存储在磁盘上,不管你是以XML格式,序列化文件DAT格式还是其它文件格式: 二.内存缓存,也就是实现一个类中静态Map,对这个Map进行常规的增删查. import java.util.*; //Description: 管理缓存 //可扩展的功能:当chche到内存溢出时必须清除掉最早