admin管理员组文章数量:1598842
CPU三级缓存技术解析
cpu存取数据
cpu存取数据大致可以认为是下图的流程(此处图比较简单)
cpu拿到需要的内存地址,之后这个地址会被mmu转换成真正的物理地址,接下来会去查接下来查L1 cache,L1 cache不命中查L2 cache,L2 cache不命中查L3 cache,L3 cache不能命中查内存。
其实现在查到内存还算完,现在有了虚拟内存,内存其实也是一层cache,是磁盘的cache,也就是说查内存也有可能不会命中,因为内存中的数据可能被虚拟内存系统放到磁盘中了,如果内存也不能命中就要查磁盘。
为什么需要cache
程序局部性原理
如果访问内存中的一个数据A,那么很有可能接下来再次访问到,同时还很有可能访问与数据A相邻的数据B,这分别叫做时间局部性和空间局部性。
cpu cache 有多快
根据摩尔定律,CPU 的访问速度每 18 个月就会翻 倍,相当于每年增⻓ 60% 左右,内存的速度当然也会不断增⻓,但是增⻓的速度远小于 CPU,平均每年 只增⻓ 7% 左右。于是,CPU 与内存的访问性能的差距不断拉大。
为了弥补 CPU 与内存两者之间的性能差异,就在 CPU 内部引入了 CPU Cache,也称高速缓存。
CPU Cache 通常分为大小不等的三级缓存,分别是 L1 Cache、L2 Cache 和 L3 Cache。其中L3是多个核心共享的。
程序执行时,会先将内存中的数据加载到共享的 L3 Cache 中,再加载到每个核心独有的 L2 Cache,最后 进入到最快的 L1 Cache,之后才会被 CPU 读取。之间的层级关系,如下图。
越靠近 CPU 核心的缓存其访问速度越快
cpu cache 读取过程
CPU Cache 的数据是从内存中读取过来的,以一小块一小块读取数据的,而不是按照单个数组元素来
读取数据的,在 CPU Cache 中的,这样一小块一小块的数据,称为 Cache Line(缓存块)。
可以在linux机器下执行一下命令查询L1cache的大小,单位是字节
#查看cache line 大小
cat /sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size
#查看各级缓存大小 inde0-3分别是 L1数据缓存 L1指令缓存 L2数据缓存 L3数据缓存
cat /sys/devices/system/cpu/cpu0/cache/index0/size
比如,有一个 int array[100] 的数组,当载入 array[0] 时,由于这个数组元素的大小在内存只占 4 字 节,不足 64 字节,CPU 就会顺序加载数组元素到 array[15] ,意味着 array[0]~array[15] 数组元素都会 被缓存在 CPU Cache 中了,因此当下次访问这些数组元素时,会直接从 CPU Cache 读取,而不用再从内 存中读取,大大提高了 CPU 读取数据的性能。
如何写出让cpu跑的更快的代码
其实,这个问题定义为如何提高cpu缓存利用率更好
大家可以看下如下代码哪个执行效率更高
func main() {
n := 100
x := 0
arr := createArray(n)
//var arr [][]int
t := time.Now().UnixNano()
for i := 0; i < n; i++ {
for j := 0; j < n; j++ {
x = arr[i][j]
}
}
t1 := time.Now().UnixNano()
for i := 0; i < n; i++ {
for j := 0; j < n; j++ {
x = arr[j][i]
}
}
fmt.Println(x)
}
//创建二维数组
func createArray(n int) [][]int {
var arr [][]int
for i := 0; i < n; i++ {
var tmp []int
for j := 0; j < n; j++ {
tmp = append(tmp, i+j)
}
arr = append(arr, tmp)
}
return arr
}
/**
经过测试,形式一 array[i][j] 执行时间比形式二 array[j][i] 快好几倍。
之所以有这么大的差距,是因为二维数组 array 所占用的内存是连续的,比如⻓度 N 的指是 2 的 话,那么内存中的数组元素的布局顺序是这样的:
array[0][0] array[0][1] array[1][0] array[1][1]
形式一用 array[i][j] 访问数组元素的顺序,正是和内存中数组元素存放的顺序一致。当 CPU 访问 array[0][0] 时,由于该数据不在 Cache 中,
于是会「顺序」把跟随其后的 3 个元素从内存中加载到 CPU Cache,这样当 CPU 访问后面的 3 个数组元素时,就能在 CPU Cache 中成功地找到数据,
这意味着缓存命中率很高,缓存命中的数据不需要访问内存,这便大大提高了代码的性能。
而如果用形式二的 array[j][i] 来访问,则访问的顺序就是:
array[0][0] array[1][0] array[0][1] array[1][1]
可以看到,访问的方式跳跃式的,而不是顺序的,那么如果 N 的数值很大,那么操作 array[j][i] 时,是 没办法把 array[j+1][i] 也读入到
CPU Cache 中的,既然 array[j+1][i] 没有读取到 CPU Cache,那么就 需要从内存读取该数据元素了。很明显,这种不连续性、跳跃式访问数据元素
的方式,可能不能充分利用 到了 CPU Cache 的特性,从而代码的性能不高。那访问 array[0][0] 元素时,CPU 具体会一次从内存中加载多少元素到
CPU Cache 呢?这个问题,在前 面也提到过,这跟 CPU Cache Line 有关,表示 CPU Cache 一次性能加载数据的大小,可以在 Linux 里通过
coherency_line_size 配置查看大小,通常是 64 个字节。
*/
cpu cache的结构
CPU Cache 是由很多个 Cache Line 组成的,CPU Line 是 CPU 从内存读取数据的基本单位,而 CPU Line 是由各种标志(Tag)+ 数据块(Data Block)组成
cpu cache数据的写入
事实上,数据不止有读取还有写入,如果数据写入cache之后,内存和cache的数据就不同了,需要把cache同步到内存中。
问题的关键就在于在什么时机去把数据写到内存?一般来讲有以下两种策略
写直达
保持内存与 Cache 一致性最简单的方式是,把数据同时写入内存和 Cache 中,这种方法称为写直达 (Write Through)。
在这个方法里,写入前会先判断数据是否已经在 CPU Cache 里面了:
如果数据已经在 Cache 里面,先将数据更新到 Cache 里面,再写入到内存里面; 如果数据没有在 Cache 里面,就直接把数据更新到内存里面。
写直达法很直观,也很简单,但是问题明显,无论数据在不在 Cache 里面,每次写操作都会写回到内存, 这样写操作将会花费大量的时间,无疑性能会受到很大的影响。
写回
由于写直达的机制会有性能问题,所以产生了写回(Write Back)的方法
在写回机制中,当发生写操作时,新的数据仅仅被写入 Cache Block 里,只有当修改过的 Cache Block 「被替换」时才需要写到内存中,减少了数据写回内存的频率,这样便可以提高系统的性能。
- 如果当发生写操作时,数据已经在 CPU Cache 里的话,则把数据更新到 CPU Cache 里,同时标记 CPU Cache 里的这个 Cache Block 为脏(Dirty)的,这个脏的标记代表这个时候, CPU Cache 里面的这个 Cache Block 的数据和内存是不一致的,这种情况是不用把数据写到内存里的;
- 如果当发生写操作时,数据所对应的 Cache Block 里存放的是「别的内存地址的数据」的话,就要检 查这个 Cache Block 里的数据有没有被标记为脏的,如果是脏的话,就要把这个 Cache Block 里的数据写回到内存,然后再把当前要写入的数据,写入到这个 Cache Block 里,同时标记为脏的;如果 Cache Block 里面的数据没有被标记为脏,则就直接将数据写入到这个 Cache Block 里,然后再把这个 Cache Block 标记为脏的就好了。
可以发现写回这个方法,在把数据写入到 Cache 的时候,只有在缓存不命中,同时数据对应的 Cache 中 的 Cache Block 为脏标记的情况下,才会将数据写到内存中,而在缓存命中的情况下,则在写入后 Cache 后,只需把该数据对应的 Cache Block 标记为脏即可,而不用写到内存里。
这样的好处是,如果大量的操作都能够命中缓存,那么大部分时间里 CPU 都不需要读写内存,自然性 能相比写直达会高很多。
缓存一致性问题
现在的CPU都是多核的,由于L1/L2cache是各个核心独有的,那么会带来多核心的缓存一致性问题,如果不能保证缓存一致性问题就会造成错误的结果
那缓存一致性的问题具体是怎么发生的呢?以一个含有两个核心的 CP
版权声明:本文标题:CPU三级缓存技术解析 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://www.elefans.com/dianzi/1728314412a1153390.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论