searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

内存进阶--如何高效使用内存

2023-07-17 01:40:22
16
0

如下图,每个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缓存了同一个物理地址的值, 通过虚拟地址1cacheline1进行修改后,如果使用虚拟地址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同步问题

  1. CPU与设备(其实也可能是个异构处理器,不过在Linux运行的CPU眼里,都是设备,都是DMA)的cache同步问题

当一个内存数据在多个cpu cache上使用, 如果有写的操作, 一般通过硬件了来实现cache同步, 尽管硬件同步性能已经比较好了, 但是大量的cache同步行为还是回影响程序的性能

通常用的cache优化方法:

  1. cache预取 – prefetch等函数
  2. 避免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 */

参考资料:

宋宝华:深入理解cache对写好代码至关重要 (qq.com)

0条评论
0 / 1000
何****森
15文章数
1粉丝数
何****森
15 文章 | 1 粉丝
何****森
15文章数
1粉丝数
何****森
15 文章 | 1 粉丝
原创

内存进阶--如何高效使用内存

2023-07-17 01:40:22
16
0

如下图,每个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缓存了同一个物理地址的值, 通过虚拟地址1cacheline1进行修改后,如果使用虚拟地址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同步问题

  1. CPU与设备(其实也可能是个异构处理器,不过在Linux运行的CPU眼里,都是设备,都是DMA)的cache同步问题

当一个内存数据在多个cpu cache上使用, 如果有写的操作, 一般通过硬件了来实现cache同步, 尽管硬件同步性能已经比较好了, 但是大量的cache同步行为还是回影响程序的性能

通常用的cache优化方法:

  1. cache预取 – prefetch等函数
  2. 避免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 */

参考资料:

宋宝华:深入理解cache对写好代码至关重要 (qq.com)

文章来自个人专栏
C语言
5 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0