refill 取 slab
per-cpu 缓存耗尽
从 node partial list 取
一次可能要取多个
node partial list
n->partial
n->list_lock 保护
slab->slab_list 串起来
摘挂都要改指针
本地 pc->slabs
refill 的临时目标
批量 slab 先搬过来
锁内完成搬移
之后逐个取 freelist
取与还都走链表
一取一还,两条路径
都值得批量化
旧:逐个摘挂
get_partial_node_bulk 循环
remove_partial + list_add
每个 slab 一删一加
锁临界区
n->list_lock 自旋锁
搬得越多持锁越久
指针 churn 纯浪费
其他 CPU 只能等
链表指针抖动
slab_list->prev/next
节点链表与本地链表
同一批反复改写
成本落在临界区
不是算法错
是搬移方式太粗
常态被当成特例
will-it-scale mmap 次数
1: 277,345,324
2: 335,238,023
3: 175,717,884 | ≥4: 88,862,337
只取 1 个
约 2.77 亿次
逐个搬还能接受
但不是主流
不能代表常态
取 2 个及以上
约 6.0 亿次
连续段才是常态
逐个搬就浪费
批量搬才有收益
常态当特例
优化空间明确
按段搬移
按连续 run 搬移
遍历时记录连续匹配
first / last 标出 run
一次 list_bulk_move_tail
旧:N 次摘挂
N 个 slab
N 次 remove_partial
N 次 list_add
N 次改指针
新:1 次搬段
run 内部链接不动
只重接首尾边界
list_bulk_move_tail()
锁内 churn 大减
少改指针
缩短持锁时间
把批量落到实处
get_partial_node_bulk()
持 n->list_lock
扫描 n->partial
first / last 记录 run
遇到不匹配
先冲掉已有 run
list_bulk_move_tail
first = NULL
继续扫描
结束或命中上限
if (first) 再冲一次
整段挪到 pc->slabs
状态已逐个清除
解锁返回 total_free > 0
run 可以多段
每段一次搬移
不匹配处自然分段
__refill_objects_node()
pc.slabs 还剩 slab
要挂回 n->partial
旧实现逐个 add_partial
旧:逐个归还
list_for_each_entry_safe
list_del + add_partial
每个 slab 一删一加
同样在锁内
新:整段 splice
先 set_node_partial_state
更新 nr_partial / flag
list_splice_tail
一次挂回整段
一取一还
两条路径都批量
锁内操作更短
状态 helper
nr_partial 计数
node_partial 标志
封装置位与清除
set_node_partial_state()
slab_set_node_partial()
n->nr_partial++
__add_partial 调用
early 分配路径无锁
clear_node_partial_state()
slab_clear_node_partial()
n->nr_partial--
remove_partial 调用
shrink 路径也复用
没有 __remove_partial
移除没有无锁用例
封装减少重复
will-it-scale mmap
作者实测
硬件配置未给出
提升 2% ~ 5%
收益来源
锁内指针改写减少
持锁时间缩短
锁内步骤更少
批量取是常态
边界
硬件配置未给出
只测 mmap
不外推其他 benchmark
数字来自作者实测
按段搬移
批量取是常态
2% ~ 5% 来自实测
空格
播放/暂停
←
→
逐动作
R
重播
⚠
1x