4. slab着色技术
slab 着色(slab coloring)是 slab 分配器为提升 CPU 硬件缓存(L1/L2 Cache)命中率而引入的一项关键优化。其核心思想是:通过为不同 slab 中的对象起始地址添加一个可变的偏移量(即“颜色”),使得同一 slab 缓存中不同 slab 页框上的同偏移对象,不会映射到 CPU 缓存的同一组(cache set)中,从而避免缓存行冲突导致的频繁换入换出。
4.1 硬件缓存行与组相联结构
现代 CPU 的 L1/L2 缓存通常采用 组相联(set-associative) 结构。内存地址被划分为三个部分:标签(Tag)、组索引(Set Index) 和 块内偏移(Block Offset)。组索引决定了内存块被映射到缓存中的哪一个组;每个组内有若干路(way)缓存行。
| 地址字段 | 作用 | 典型位数(以 32KB L1 为例) |
|---|---|---|
| Tag | 用于区分映射到同一组的不同内存块 | 20 位 |
| Set Index | 选择缓存中的具体组 | 8 位(256 组) |
| Block Offset | 定位缓存行内的字节 | 6 位(64 字节行) |
当两个不同的物理地址具有相同的 Set Index 时,它们会竞争同一组缓存行。如果 slab 分配器不加干预,连续分配的 slab 页框往往在物理地址上对齐(例如按 4KB 页对齐),导致不同 slab 中相同偏移的对象恰好落入同一缓存组,造成严重的 缓存行颠簸(cache line thrashing)。
4.2 slab 着色技术原理
slab 着色通过在 slab 页框的起始地址上增加一个 颜色偏移量(color offset),打破物理地址对齐导致的缓存组冲突。每个 slab 缓存(kmem_cache)在创建时会计算可用的颜色数(color count),每个 slab 页框分配时选择一个颜色值,将其乘以一个基本偏移单位(通常是 L1 缓存行大小),作为该 slab 的起始偏移。
着色偏移量 color_off 的计算公式如下:
color_off = color * cache_line_size
其中 color 取值范围为 [0, color_count - 1]
每个 slab 页框的实际对象起始地址变为:
obj_start = slab_page_start + color_off
由于不同 slab 的 color_off 不同,即使它们位于不同的物理页框,相同索引的对象在缓存中的组索引也会错开,从而均匀分布到多个缓存组中。
[0, L1_CACHE_BYTES * (color_count - 1)] 范围内。
4.3 着色对缓存命中率的影响
下图展示了未着色与着色两种情况下,不同 slab 中相同偏移对象在 CPU 缓存组中的映射差异。
4.4 着色参数与性能权衡
slab 分配器在创建缓存时,会根据对象大小和硬件缓存行大小计算 颜色数(color count) 和 颜色偏移量(color offset)。以下是关键参数及其影响:
| 参数 | 含义 | 对性能的影响 |
|---|---|---|
| color_count | 可用的颜色数量 | 颜色越多,缓存分布越均匀,但会减少 slab 内可容纳的对象数 |
| color_off | 每个 slab 的实际偏移字节数 | 偏移量应为缓存行大小的整数倍,否则无法有效错开组索引 |
| slab 对象大小 | 每个固定分配单元的大小 | 对象大小越接近缓存行整数倍,着色效果越显著 |
| slab 页框数 | 每个 slab 占用的物理页数量 | 页框数越多,着色空间越大,但内存碎片风险增加 |
kmem_cache_create 中通过 calculate_slab_order 函数自动权衡这些因素。
4.5 着色在 Linux 内核中的实现路径
Linux 内核 slab 分配器(包括 SLAB 和 SLUB)实现着色的关键步骤如下:
- 计算颜色数: 根据 slab 页框大小、对象大小和缓存行大小,计算可用的颜色数量。
- 分配 slab 页框: 从伙伴系统获取物理页框后,根据当前颜色计数器选择一个颜色值。
- 设置起始偏移: 将 slab 页框的起始地址加上
color_off,作为第一个对象的放置位置。 - 对象布局: 后续对象按固定大小依次排列,直到 slab 末尾。
- 颜色轮转: 每次分配新 slab 时,颜色值循环递增,确保不同 slab 使用不同颜色。
以下是一个简化的着色分配流程示意图:
4.6 着色技术的适用场景与局限性
着色技术并非万能,其收益与具体工作负载和硬件特性密切相关。以下表格总结了适用场景与局限性:
| 场景 | 着色效果 | 说明 |
|---|---|---|
高频访问的同大小对象(如 task_struct、inode) |
显著提升 | 大量对象分布在多个 slab 中,着色可有效分散缓存压力 |
| 对象大小接近缓存行整数倍 | 显著提升 | 缓存行对齐冲突最严重,着色收益最大 |
| 对象远小于缓存行(如 8-16 字节) | 有限 | 多个对象共享同一缓存行,着色无法避免行内冲突 |
| 单 slab 缓存对象数量极少 | 有限 | 颜色数少,分散效果不明显 |
| NUMA 系统跨节点访问 | 需配合节点着色 | NUMA 着色与缓存着色叠加,需额外考虑内存延迟 |
perf stat -e cache-misses,cache-references 观察整体缓存行为。若发现特定 slab 缓存(如 kmalloc-256)miss 率异常高,可检查其对象大小是否与缓存行对齐,并考虑调整 slab 分配器的着色参数(通过 /sys/kernel/slab/<cache_name>/ 下的接口)。