sched discussion
[PATCH RESEND v2] sched: adjust the layout of the cfs_bandwith structure to save memory
LLM 分析
CFS Bandwidth:紧凑结构体布局以节省内存
系列概况
- 标题: [PATCH RESEND v2] sched: adjust the layout of the cfs_bandwith structure to save memory
- 作者: zenghongling zenghongling@kylinos.cn
- 版本: v2(RESEND,单 patch)
- 规模: 1 个文件,3 处插入、3 处删除
- 修改文件: kernel/sched/sched.h(第 444–457 行附近
struct cfs_bandwidth) - Message-ID: 20260320072028.41409-1-zenghongling@kylinos.cn
- 完整性: 完整 —— diff 块、Before/After 的 pahole 输出、Reviewed-by、Signed-off-by、Changes in v2 都齐全
补丁目的
利用 pahole 把 struct cfs_bandwidth 里散落的 3 个 u8 状态位(idle、period_active、slack_started)紧贴到 raw_spinlock_t lock 后面,借助这些字段后面的 8 字节对齐 hole 顺带把它们"吃掉"。
最终效果:
- 结构体总大小从 240 字节降到 232 字节
- hole 字节数从 13 降到 5
- 最后一个 cacheline 利用率从 48/64 提升到 40/64
唯一权衡:period_timer(64 字节)现在从 offset 56 开始,正好骑在 64 字节 cacheline 边界上,访问它可能多触发一次 cacheline load。作者自称这条路径本就涉及多个 cacheline 且非热路径,所以可接受。
旧流程的问题
原结构体里同时存在两类"塞不满"的缝隙:
lock(4 字节)后面留 4 字节 hole,才能对齐到 8 字节的period。- 三个
u8状态位加一块只占 3 字节,再后面u64 throttled_time需要 8 字节对齐,中间留下 5 字节 hole。 int nr_burst之后还有一处 4 字节 hole,给后面对齐的u64 throttled_time。
pahole 报告:sum holes = 13 字节、padding = 8 字节、4 个 cacheline、最后一个 cacheline 只用 48/64 字节。
新流程
Before (240 B) After (232 B)
+----------------------+ +----------------------+
| lock [0..3] | | lock [0..3] |
| hole 4..7 (4B) | | idle [4] |
| period [8..15]| | period_active [5] |
| ... | | slack_started [6] |
| hier_quota [48..55]| | hole 7 (1B) |
| idle [56] | | period [8..15] |
| period_active [57] | | ... |
| slack_started [58] | | period_timer[56..119]|<-- cross cacheline @64
| hole 59..63 (5B) | | slack_timer [120..183]|
| period_timer[64..127]| | throttled_cfs_rq |
| slack_timer [128..191]| | [184..199] |
| throttled_cfs_rq | | nr_* [200..211]|
| [192..207] | | hole (4B) |
| nr_* [208..219]| | throttled_time[216..223]
| hole (4B) | | burst_time [224..231]
| throttled_time[224..231] +----------------------+
| burst_time [232..239] CL4 used: 40 / 64 B
+----------------------+
CL4 used: 48 / 64 B
关键实现
改动只在 include 段,没有运行时新增逻辑。语义、并发模型、throttle 行为都保持不变。
struct cfs_bandwidth {
#ifdef CONFIG_CFS_BANDWIDTH
raw_spinlock_t lock;
u8 idle; /* moved up: 紧跟 lock */
u8 period_active; /* moved up */
u8 slack_started; /* moved up */
ktime_t period;
u64 quota;
u64 runtime;
u64 runtime_snap;
s64 hierarchical_quota;
struct hrtimer period_timer; /* offset 56, crosses cacheline */
struct hrtimer slack_timer;
struct list_head throttled_cfs_rq;
int nr_periods;
int nr_throttled;
int nr_burst;
u64 throttled_time;
u64 burst_time;
};
唯一变化:把 idle; period_active; slack_started 三行从 hierarchical_quota 之后搬到了 lock 之后,并删去原位置。
类比
把 struct 想成一个 4 格的行李箱,每格 64 字节。原本 4 个"小件"(1 个 lock + 3 个 u8)分散在第 1 格和第 2 格开头,每段后面又紧跟一件"必须 8 字节整齐摆的大家具(u64 / hrtimer)",于是每段都留下一截塞不满的缝隙。补丁相当于把这 4 个小件拢成一摞塞进第 1 格开头,第一道缝隙完全消失,整体少用了 8 字节。
唯一代价是:period_timer 这件 64 字节的大家具现在正好骑在第 1 格和第 2 格的格挡上,需要时偶尔要跨格伸手够一次。但这条路径本来就在多格里翻东西,多翻一次也没关系。
Highlight:风险与注意点
- 跨 cacheline 的微性能:访问
period_timer现在可能比之前多一次 cacheline load。建议用perf stat -e cache-misses在 throttle 密集触发场景(如hackbench --pipe高负载)里做对照。 - 正确性:字段重排不改变并发语义。pahole 报告里
forced holes从 1 降到 0,说明此处没有强加 alignment 要求,packing 是干净的。 - v2 只是修文案:
Changes in v2: - fix the commit message.表示 v2 没补跑 perf 数据,也没有新测试,仅 commit message 措辞改动。 - 下游 offset 依赖:
cfs_bandwidth没有 EXPORT_SYMBOL,但 BTF / vmlinux 里的字段 offset 会变;任何 CO-RE BPF 程序若使用offsetof读这些字段,需重新生成 vmlinux.h。 - Reviewed-by 分量:
Reviewed-by: Ben Segall(Google CFS bandwidth 维护者之一)显著降低正确性顾虑,但他未对"跨 cacheline 是否真的无影响"做正面背书。
版本变化
- v2(RESEND):diff 与 v1 完全一致,仅 commit message 措辞修复;phole 输出作为直观证据保留在邮件正文里。
- v1 → v2 没有功能或性能改动,仅提交说明改写。
一句话总结
把 3 个 u8 状态位塞到 lock 后面,借自然对齐 hole 吃掉冗余 padding,让 cfs_bandwidth 从 240 字节瘦到 232 字节,代价是 period_timer 跨 cacheline,但 throttle 本就非热路径。