背景

SLUB 分配器在 per-cpu 缓存的对象耗尽时,从节点的 partial slab 链表(node partial list)上取部分空闲的 slab 来 refill。批量取(get_partial_node_bulk)时,每个选中的 slab 都从节点链表摘下、挂到本地的 pc->slabs 链表,用的是一对 remove_partiallist_add。这些操作全程持有节点的 n->list_lock 自旋锁。

┌────────────────────────────────────┐
│   从节点 partial 链表批量取 slab   │
└─────────────────┬──────────────────┘
                  ▼
┌────────────────────────────────────┐
│       逐个搬移,每个一删一加       │  每次都改写链表指针,全程持有节点自旋锁
└─────────────────┬──────────────────┘
                  ▼
┌────────────────────────────────────┐  实测一次取多个连续 slab 极其频繁,逐个搬
│ 连续取多个相邻 slab 时反复抖动指针 │  是浪费
└────────────────────────────────────┘

问题在于,取 slab 时常常一口气摘下好几个相邻 slab,它们在节点链表里本就挨着。但旧实现逐个处理:每个 slab 单独摘下、单独挂回,等于在持锁期间把同一批链表指针反复改写好多遍。这种 list 指针的 churn 纯属浪费,而且它发生在锁临界区里,直接拉长了持锁时间。

作者用 will-it-scale 的 mmap 压测统计了"一次连续取几个 slab"的分布:

连续 slab 数 出现次数
1 277,345,324
2 335,238,023
3 175,717,884
≥4 88,862,337

一次取多个连续 slab 的频次(2、3、≥4 合计约 6 亿)甚至超过只取一个的(约 2.77 亿)。也就是说,"批量取"不是偶发而是常态;逐个搬移等于把常态当特例处理。

问题

  • 节点 partial 链表取 slab 时逐个摘挂
  • 每个 slab 一删一加,锁内反复改写链表指针
  • 一次取多个连续 slab 极其频繁,逐个搬是浪费
  • 指针抖动拉长节点自旋锁的持锁时间

方案

核心是让"批量取"真正按批量搬。

get_partial_node_bulk 遍历节点链表时,追踪连续匹配的 slab run,遇到 run 就用 list_bulk_move_tail 把整段一并挪到本地链表尾,而非逐个摘挂。1 次批量搬移只改写 run 两端的指针,run 内部的指针原样不动,锁临界区里的指针 churn 大幅减少。

改动前:逐个搬移,N 次指针改写
┌──────────────────────────────────┐
│ 每个 slab 单独删除并加回本地链表 │
└────────────────┬─────────────────┘
                 ▼
┌──────────────────────────────────┐
│     持锁期间反复抖动链表指针     │
└──────────────────────────────────┘

改动后:连续 run 单次搬移
┌──────────────────────────┐
│ 追踪连续匹配 slab 的整段 │
└────────────┬─────────────┘
             ▼
┌──────────────────────────┐  锁临界区内指针改写次数大减;重新挂回节点链表也照此
│ 用批量搬移整段挪到链表尾 │  批量
└──────────────────────────┘

同样的批量优化也用到把剩余 partial slab 重新挂回节点链表的路径(__refill_objects_node):归还时同样按段批量搬,而非逐个 add。这 2 条路径一取一还,都从"逐个改写"改成"按段搬移"。

铺垫是把 partial slab 计数的增减、标志的置清封装成 helper,减少重复,为批量操作扫清调用点。

收益

作者用 will-it-scale 的 mmap benchmark 实测(具体硬件配置未给出):

benchmark 提升
will-it-scale mmap 2%~5%

提升来自批量搬移减少了节点自旋锁临界区内的链表指针改写次数,缩短持锁时间;前述频率统计印证"一次取多个连续 slab"是常态,故按段搬移的收益稳定可复现。