背景
swap 子系统为每个 swap slot 维护一份 swap count(有多少处引用了这个 swap entry),用来判断何时能真正释放。旧实现用一张静态的 swap_map(per-slot 数组)存计数,引用计数过高(超 SWAP_CONT_MAX)时还要靠 COUNT_CONTINUED 页接管。
┌──────────────────────────────┐
│ 交换映射表是逐槽的静态数组 │
└──────────────┬───────────────┘
▼
┌──────────────────────────────┐
│ 交换表也得维护一份,双重更新 │ 高引用计数还需连续页机制接管
└──────────────┬───────────────┘
▼
┌──────────────────────────────┐
│ 静态元数据随设备大小线性增长 │ 1TB 交换设备占约 256MB
└──────────────────────────────┘
swap table phase I 引入了 per-cluster 的 swap table 后,slot 的状态已在表里维护,但 swap count 仍由 swap_map 单独记账。于是 swap table 和 swap_map 做着重复的 per-slot 更新,而 swap_map 这张静态数组随设备大小线性增长,挂一个 1TB 的 swap 设备就要占约 256MB。
问题
- swap count 由静态 swap_map 逐槽记账
- swap table 也维护状态,两套机制双重更新
- 高引用计数还需连续页机制(COUNT_CONTINUED)
- 静态元数据随设备大小线性增长(1TB 占 256MB)
方案
核心是让 swap table 直接承担 swap count 的记账,把 swap_map 整张表连同 SWP_CONTINUED/COUNT_CONTINUED 机制一并移除。
旧:双重计数,静态数组
┌──────────────────────────────┐
│ 交换映射表逐槽计数 │
└──────────────┬───────────────┘
▼
┌──────────────────────────────┐
│ 交换表也得维护一份,双重更新 │
└──────────────────────────────┘
新:交换表直接跟踪计数
┌──────────────────────────────────────┐
│ 计数存进交换表高位,超限由扩展表接管 │
└──────────────────┬───────────────────┘
▼
┌──────────────────────────────────────┐
│ 移除静态映射表与连续页机制 │ 基于簇,稀疏性好;双重更新消除
└──────────────────────────────────────┘
swap table 的每条目高位能存到 SWP_TB_COUNT_MAX-1 个计数,足以覆盖绝大多数场景。当某 slot 的计数真的超出这个上限,就给该 cluster 分配一个 extend_table(cluster 大小的 unsigned int 数组),把计数卸载过去,直到计数降回再收回。swap table 与 extend_table 都基于 cluster,稀疏性与性能都不错。计数搬进 swap table,它就成了唯一的 per-slot 账本,原本散落各处、各自为 swap_map 编织的逻辑随之收敛:条目要腾出高位存计数,workingset shadow 编码便预留出这些位;坏槽位不再单独记账,直接在表中标记;swapon 与设备启停等路径里的锁和参数,也因无须双重更新而精简。计数切换落定后,扫描边界与影子清理等既有逻辑顺势简化。
收益
作者在多组场景测试(封面信数据)。
内存(挂载 1TB brd swap 设备,free -m):
| 指标 | Before | After | 变化 |
|---|---|---|---|
| used | 1,051 MB | 795 MB | 省 256 MB(约 30%) |
内核构建系统时间(全局压力,32 次均值):
| 配置 | Before | After | 变化 |
|---|---|---|---|
| ZSWAP+NVME,x86_64 VM 5G,-j48,defconfig | 1038.97s | 1013.75s | -2.4% |
| ZRAM,ARM64 VM 1.5G,-j12,tinyconfig | 67.75s | 66.65s | -1.6% |
Redis/Valkey(ZRAM,ARM64 VM 1.5G,全局压力,64 次均值):
| 场景 | Before (RPS) | After (RPS) | 变化 |
|---|---|---|---|
| 无持久化 | 472,705.71 | 481,197.93 | +1.8% |
| BGSAVE | 369,451.68 | 374,922.32 | +1.5% |
封面信结论:性能全面更好("performance is better in all cases, and memory usage is much lower")。内存省约 30%(1TB 省 256MB);内核构建系统时间降 1.6%~2.4%;Redis/Valkey RPS 提升 1.5%~1.8%。后续阶段还会把 swap cgroup 数组合进 swap table,省另外约 60% 静态元数据。