____cacheline_aligned_in_smp用于linux中的结构

为什么在Linux中,许多结构使用____cacheline_aligned_in_smpmacros? 这是否有助于在访问结构时提高性能? 如果是,那么如何?

任何缓存(dcache或icache)中的每个缓存行都是64字节(x86)体系结构。 需要高速缓存对齐以避免高速缓存行的虚假分页。 如果在全局变量之间共享高速缓存行(在内核中发生更多)如果其中一个全局变量由其高速缓存中的一个处理器改变,那么它将该高速缓存行标记为脏。 在剩下的CPU cahce行它变成陈旧的条目。 哪些需要刷新并从内存中重新获取。 这可能会导致缓存行未命中。 这需要更多的CPU周期。 这降低了系统的性能。 记住这是golabl变量。 大多数内核数据结构使用这个来避免缓存行遗漏。

____cacheline_aligned指示编译器在对应于L1高速缓存线的开始的地址处实例化结构或变量,以用于特定的架构,即,使得它是L1高速缓存线对齐的。 ____cacheline_aligned_in_smp类似,但实际上只有当内核以SMP配置编译时(即使用选项CONFIG_SMP ),才能实现L1缓存行对齐。 这些在文件include / linux / cache.h中定义

这些定义对于没有通过某个分配器动态分配的变量(和数据结构)是有用的,而是全局的,编译器分配的变量(类似的效果可以通过动态内存分配器在特定的对齐处分配内存来完成)。

高速缓存行对齐变量的原因是在SMP系统中通过硬件高速缓存一致性机制来管理这些变量的高速缓存到高速缓存的传输,以便在移动其他变量时不会隐含地发生它们的移动。 这是针对性能严重的代码,其中一个预期争用多个cpus(核心)访问变量。 在这种情况下,人们试图避免的常见问题是错误的分享。

从高速缓存行开始的一个变量的内存是这个目的的一半。 一个也需要“包装它” 应该一起移动的变量。 一个例子是一个变量数组,其中数组的每个元素只能被一个cpu(核心)访问:

 struct my_data { long int a; int b; } ____cacheline_aligned_in_smp cpu_data[ NR_CPUS ]; 

这种定义将要求编译器(在内核的SMP配置中)每个cpu的结构将从缓存线边界开始。 编译器会隐式地在每个cpu的结构之后分配额外的空间,以便下一个cpu的结构也将从高速缓存线边界开始。

另一种方法是用虚拟的缓存行大小来填充数据结构,未使用的字节:

 struct my_data { long int a; int b; char dummy[L1_CACHE_BYTES]; } cpu_data[ NR_CPUS ]; 

在这种情况下,只有虚拟的,未使用的数据会被无意的移动,而每个CPU实际访问的数据只会从缓存移动到内存,反之亦然,这是由于缓存容量不足造成的。

Linux以与TLB非常类似的方式管理CPU Cache。 像TLB缓存一样,CPU缓存利用了这样一个事实,即程序往往表现出一个参考的地方。 为了避免为每个引用从主内存中获取数据,CPU将会缓存CPU缓存中非常少量的数据。 通常有两个级别,称为1级和2级CPU高速缓存。 二级CPU高速缓存比L1高速缓存更大但速度更慢,但Linux只关心Level 1或L1高速缓存。

CPU高速缓存被组织成行。 每行通常非常小,通常为32个字节,每行对齐到它的边界大小。 换句话说,32字节的高速缓存行将在32字节地址上对齐。 在Linux中,行的大小是由每个体系结构定义的L1_CACHE_BYTES

地址映射到缓存行的方式在不同体系结构之间有所不同,但映射归入三个标题,即直接映射关联映射集合关联映射 。 直接映射是最简单的方法,每个内存块只映射到一个可能的缓存行。 通过关联映射,任何内存块都可映射到任何缓存行。 集合关联映射是一种混合方法,其中任何内存块都可映射到任何行,但只能在可用行的子集内。

不管映射方案如何,它们每个都有一个共同点,那些靠近在一起并且与缓存大小对齐的地址可能使用不同的行。 因此,Linux使用简单的技巧来尝试和最大化缓存使用率

  • 经常访问的结构字段在结构的起始处,以增加仅需要一行来寻址公共字段的机会;
  • 结构中不相关的项目应该尽量至少缓存大小字节,以避免CPU之间的错误共享;
  • 一般高速缓存中的对象(如mm_struct高速缓存)与L1 CPU高速缓存对齐,以避免错误共享。

如果CPU引用不在高速缓存中的地址,则会发生高速缓存未命中,并从主内存中获取数据。 缓存未命中的成本相当高,因为对缓存的引用通常可以在小于10ns内执行,其中对主存储器的引用通常将花费在100ns与200ns之间。 基本的目标是尽可能多的缓存命中和尽可能少的缓存未命中。

正如一些体系结构不会自动管理它们的TLB一样,有些不会自动管理它们的CPU高速缓存。 钩子被放置在虚拟到物理映射改变的位置,例如在页面表更新期间。 CPU高速缓存刷新应该总是首先发生,因为有些CPU要求虚拟地址从高速缓存刷新时存在虚拟到物理的映射。

更多信息在这里