sched discussion
[PATCH RESEND v2] sched: adjust the layout of the cfs_bandwith structure to save memory
LLM 分析
CFS Bandwidth 结构体布局微调:节省 8 字节内存
系列概况
| 字段 | 内容 |
|---|---|
| 标题 | [PATCH RESEND v2] sched: adjust the layout of the cfs_bandwith structure to save memory |
| 作者 | Hongling Zeng zenghongling@kylinos.cn |
| 版本 | v2 RESEND |
| 规模 | 1 个文件,3 处新增、3 处删除(diffstat 为 6 +++---) |
| 修改文件 | kernel/sched/sched.h |
| 代码统计 | 仅重排 struct cfs_bandwidth 中三个 u8 字段的位置 |
| Message-ID | 20260803074026.57701-1-zenghongling@kylinos.cn |
| 完整性 | 完整:含 From、Reviewed-by、Sign-off、v2 变更说明、pahole Before/After 输出、diffstat |
补丁目的
通过 pahole 工具观察 struct cfs_bandwidth 中存在的 padding holes,
把三个 1 字节的 u8 idle / period_active / slack_started 标志字段挪到
raw_spinlock_t lock 后面,与 lock 共用前 8 字节,让 8 字节对齐自然吃
掉空洞,把结构体大小从 240 字节压到 232 字节,节约 8 字节内存。
这个改动只调整字段顺序,没有任何功能行为变化。
旧流程的问题
u8 idle / period_active / slack_started 原先是紧跟 s64 hierarchical_quota
之后,受 s64(8 字节对齐)影响,紧接着的三个 u8 后会留下 5 字节空洞;
而结构体最前面的 raw_spinlock_t lock(4 字节)之后又留出 4 字节空洞。
两处空洞合计 9 字节额外 padding。
新流程
把三个 u8 移到最顶部,与 lock 共用一个 8 字节槽(lock 占 4 字节,
三个 u8 占 3 字节,再补 1 字节 padding),从而:
- 把原来 lock 后的 4 字节空洞消化掉;
- 把原来三个 u8 后的 5 字节空洞也消化掉;
- 只在
u8 slack_started与ktime_t period之间留下 1 字节空洞(ktime_t需要 8 字节对齐)。
整体:holes 总和 13 字节 → 5 字节;结构体 240 字节 → 232 字节。
关键实现
/* 调整后的字段顺序(节选) */
struct cfs_bandwidth {
raw_spinlock_t lock; /* 0 4 */
u8 idle; /* 4 1 */
u8 period_active; /* 5 1 */
u8 slack_started; /* 6 1 */
/* hole: 1 byte (对齐到 ktime_t) */
ktime_t period; /* 8 8 */
u64 quota; /* 16 8 */
u64 runtime; /* 24 8 */
u64 burst; /* 32 8 */
u64 runtime_snap; /* 40 8 */
s64 hierarchical_quota; /* 48 8 */
struct hrtimer period_timer; /* 56 64 -- 跨界到下一个 cacheline */
/* ... slack_timer / throttled_cfs_rq / 计数与时间字段 ... */
};
具体 diff:
@@ -444,6 +444,9 @@ static inline u64 default_bw_period_us(void)
+ u8 idle;
+ u8 period_active;
+ u8 slack_started;
@@ -451,9 +454,6 @@ struct cfs_bandwidth {
- u8 idle;
- u8 period_active;
- u8 slack_started;
类比
整理行李箱:原来三个小物件(u8)紧跟在一个大物件(s64)之后,
大物件右侧需要空间对齐,三个小物件之间反而留出了一大片空位。
现在把小物件们挪到箱子的最前面,跟锁扣(lock)共用那块本来就有
富余的小隔层,箱子立刻变紧实了。原本浪费的空间被吃掉,但代价
是大件 period_timer 拐了个小弯,才会多碰到 1 个 cacheline(像
行李箱拉杆拉过隔板时多挂一档)。
Highlight:风险与注意点
- 跨 cacheline 边界:
period_timer原先在 cacheline 1 内部起始,
现在它的起点从 cacheline 0 的尾部 56 字节偏移挪到了这里——仍在
cacheline 0/1 边界上,但跨过边界的方式改变了。同样的,slack_timer、
throttled_cfs_rq的相对位置都跟着平移 8 字节。作者在 commit
message 中明确说明:period_timer已经不是首访 cacheline、
并且也并非热路径,影响可忽略。 - 没有功能改动:纯布局调整,不会引入新的运行时分支或行为差异。
- 可维护性要求:调度器里所有访问这三个 u8 的地方都是通过
cfs_b->idle / period_active / slack_started这种结构体引用
而非硬编码偏移,所以字段重排不会破坏既有调用方。如果有谁直接
通过偏移访问(极少见),需要随 patch 重新校验。 - 可借鉴点:社区中还有若干类似的小空洞(
struct rq、
struct task_struct等)有待类似的字段重排清理,patch 虽小
但思路通用,可作为同类型 cleanup 的范本。
版本变化
- v1 → v2(RESEND):仅修改 commit message,没有任何代码改动。
这是因为 v1 的邮件可能在作者侧被退信或未触达上游,所以 v2 以
RESEND 形式重发。
一句话总结
把 struct cfs_bandwidth 里三个 u8 标志挪到最顶部与 lock 相邻,
把结构体从 240 字节压到 232 字节,仅让 period_timer 多触达一个
cacheline,无功能改动。