0/1 已展开

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-ID20260803074026.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_startedktime_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:风险与注意点

  1. 跨 cacheline 边界period_timer 原先在 cacheline 1 内部起始,
    现在它的起点从 cacheline 0 的尾部 56 字节偏移挪到了这里——仍在
    cacheline 0/1 边界上,但跨过边界的方式改变了。同样的,slack_timer
    throttled_cfs_rq 的相对位置都跟着平移 8 字节。作者在 commit
    message 中明确说明:period_timer 已经不是首访 cacheline、
    并且也并非热路径,影响可忽略。
  2. 没有功能改动:纯布局调整,不会引入新的运行时分支或行为差异。
  3. 可维护性要求:调度器里所有访问这三个 u8 的地方都是通过
    cfs_b->idle / period_active / slack_started 这种结构体引用
    而非硬编码偏移,所以字段重排不会破坏既有调用方。如果有谁直接
    通过偏移访问(极少见),需要随 patch 重新校验。
  4. 可借鉴点:社区中还有若干类似的小空洞(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,无功能改动。