如下图,每个cpu都有自己可以使用的L1, L2缓存(cachce),多个核可能会共享L3 cache, 通过缓存cpu可以更快的寻址和执行指令
cache是通过cacheline进行寻址的,
cache_line结构,一个cache line大小64B, 包括tag(用于计算内存地址释放命中缓存), flag(标志缓存,比如缓存是否失效,写dirty等)
+-------------------------------------------+
| tag | data block(cache line) | flag |
+-------------------------------------------+
内存地址分成3部分来查询是否命中了缓存
+-------------------------------------------+
| tag | index | offset |
+-------------------------------------------+
index 用于计算在哪个set, 即同一个set的index都是一样的, tag不一样(即一个set下有多路,每路的tag不一样)
offset指定在cache line的偏移, 64B的cacheline需要6位来表示偏移
查看cacheline大小:
cat /sys/devices/system/cpu/cpu1/cache/index0/coherency_line_size
查看缓存级别
cat /sys/devices/system/cpu/cpu1/cache/index0/level
查看缓存set
cat /sys/devices/system/cpu/cpu1/cache/index0/number_of_sets
查看缓存way
cat /sys/devices/system/cpu/cpu1/cache/index0/ways_of_associativity
way * set * 64 = cache总大小
如下64B*64set*8way=32K
获取cache值可能有这么些组合:
VIVT、VIPT、PIPT。(V虚拟地址, P 物理地址, I为index, T为tag)
VIVT的硬件实现开销最低,但是软件维护成本高;PIPT的硬件实现开销最高,但是软件维护成本最低;VIPT介于二者之间,但是有些硬件是VIPT,但是behave as PIPT,这样对软件而言,维护成本与PIPT一样。
VIVT的硬件实现效率很高,不需要经过MMU就可以去查cache了。不过,对软件来说,这是个灾难。因为VIVT有严重的歧义和别名问题。
歧义:一个虚拟地址先后指向两个(或者多个)物理地址
别名:两个(或者多个)虚拟地址同时指向一个物理地址
两个不同的虚拟地址回命中不同的cachline, 两个cachline缓存了同一个物理地址的值, 通过虚拟地址1对cacheline1进行修改后,如果使用虚拟地址2对cacheline2访问,访问的是旧的值, 出现了不一致的情况,软件必须写完虚拟地址1后,对虚拟地址1对应的cache执行clean,对虚拟地址2对应的cache执行invalidate(实现很困难, 容易疏漏)。
PIPT不存在这类问题,虚拟地址最终通过MMU转换成唯一的物理地址, 进行对cache的访问。
VIPT呢?当index+offset的位小于等于内存页位时候VI=PI, 这样cpu访问的内存地址映射内存不会有别名,因为假设一页是4K,那么地址的低12位虚拟地址和物理地址是完全一样的, VIPT相当于PIPT了
Cache的一致性有这么几个层面
1.一个CPU的icache和dcache的同步问题
2.多个CPU各自的cache同步问题
- CPU与设备(其实也可能是个异构处理器,不过在Linux运行的CPU眼里,都是设备,都是DMA)的cache同步问题
当一个内存数据在多个cpu cache上使用, 如果有写的操作, 一般通过硬件了来实现cache同步, 尽管硬件同步性能已经比较好了, 但是大量的cache同步行为还是回影响程序的性能
通常用的cache优化方法:
- cache预取 – prefetch等函数
- 避免false sharing – 使用cacheline对齐
- 常访问的数据保证在cacheline的开头,如使用padding[cacheline_size - sizeof(int)];保证前面的int数据在独立的cacheline中, 对后面的数据读写不影响前面的数据的cache命中效率
- 使用____cacheline_aligned标识数据要独占cacheline, 比如下面的数据定义
// /linux/include/net/xdp.h structxdp_rxq_info { structnet_device *dev; u32 queue_index; u32 reg_state; structxdp_mem_infomem; } ____cacheline_aligned; /* perf critical, avoid false-sharing */ |
参考资料: