____cacheline_aligned_in_smp用于Linux内核中的结构

时间:2014-09-20 10:51:03

标签: linux-kernel smp

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

3 个答案:

答案 0 :(得分:5)

____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实际访问的数据只会从缓存移动到内存,反之亦然。

答案 1 :(得分:2)

任何缓存(dcache或icache)中的每个缓存行都是64字节(x86)架构。需要缓存对齐以避免错误共享缓存行。如果缓存行在全局变量之间共享(在内核中发生更多)如果其中一个全局变量由其缓存中的一个处理器更改,则它将该缓存行标记为脏。在剩余的CPU缓存行中,它变为过时的条目,需要刷新并从内存中重新获取。这可能会导致缓存行未命中,这需要更多的CPU周期。这降低了系统的性能。请记住,这是针对全局变量的。大多数内核数据结构使用它来避免缓存行未命中。

答案 2 :(得分:1)

Linux以与TLB非常相似的方式管理CPU Cache。像TLB缓存一样,CPU缓存利用了程序倾向于展示引用位置的事实。为避免必须从每个引用的主内存中获取数据,CPU将在CPU缓存中缓存非常少量的数据。通常,有两个级别称为1级和2级CPU缓存。 2级CPU缓存比L1缓存更大但更慢,但Linux只关注1级或L1级缓存。

CPU缓存按行组织。每条线通常非常小,通常为32个字节,每条线与其边界大小对齐。换句话说,32字节的高速缓存行将在32字节地址上对齐。对于Linux,行的大小为L1_CACHE_BYTES,由每个体系结构定义。

地址映射到缓存行的方式因架构而异,但映射分为三个标题, 直接映射 关联映射 设置关联映射 。直接映射是最简单的方法,其中每个存储器块仅映射到一个可能的高速缓存行。使用关联映射,任何内存块都可以映射到任何缓存行。集合关联映射是一种混合方法,其中任何内存块都可以映射到任何行,但只能映射到可用行的子集中。

无论映射方案如何,它们都有一个共同点,即靠近并与高速缓存大小对齐的地址可能使用不同的行。因此Linux采用简单的技巧来尝试并最大化缓存使用

  • 经常访问的结构字段位于开头 结构,以增加只需要一行的机会 解决公共领域;
  • 结构中不相关的项目应该尝试 至少要将高速缓存大小字节分开以避免之间的错误共享 CPU的;
  • 一般缓存中的对象,例如mm_struct缓存,是 与L1 CPU缓存对齐以避免错误共享。

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

正如某些架构不会自动管理其TLB一样,有些架构不会自动管理其CPU缓存。钩子放置在虚拟到物理映射发生变化的位置,例如在页表更新期间。 CPU缓存刷新应始终首先进行,因为当从缓存中刷新虚拟地址时,某些CPU需要存在虚拟到物理映射。

更多信息here