4. CPU域拓扑结构:NUMA、多核、超线程下的域层级与标志位

CPU域(sched_domain)是内核调度器感知物理拓扑的核心数据结构。在NUMA、多核、超线程共存的现代处理器上,域层级直接决定了负载均衡的粒度、迁移代价和调度决策路径。理解域层级与标志位的对应关系,是调优组调度场景下CPU域迁移参数的前提。

4.1 域层级与物理拓扑的映射关系

Linux内核将CPU拓扑抽象为树形层级,从底向上依次为:SMT(同步多线程)→ MC(多核)→ DIE(芯片)→ NUMA(非一致性内存访问)。每个层级对应一个调度域,域内包含一组CPU,域间通过父指针串联。

拓扑层级 域类型 包含关系 典型跨度 迁移代价
超线程 SD_SMT 同一物理核内的逻辑CPU 2-4个CPU 极低(共享L1/L2缓存)
多核 SD_MC 同一芯片内的物理核 4-16个CPU 低(共享L3缓存)
芯片 SD_DIE 同一物理封装内的所有核 8-32个CPU 中(跨L3但同内存控制器)
NUMA节点 SD_NUMA 跨内存节点的CPU集合 16-256个CPU 高(远程内存访问延迟)
关键认知:域层级越高,CPU之间的物理距离越远,缓存共享越少,迁移代价越大。组调度场景下,跨NUMA节点的任务迁移可能引入显著的延迟抖动,必须谨慎配置迁移阈值。

4.2 域标志位与调度行为控制

每个调度域通过一组标志位(sched_domain_flags)控制负载均衡的行为。标志位在域初始化时由内核根据拓扑特征自动设置,但部分标志位可通过内核参数或sysfs接口调整。

标志位 含义 影响范围 组调度相关
SD_BALANCE_NEWIDLE CPU空闲时从其他域拉取任务 所有域层级 影响空闲CPU的组任务迁移
SD_BALANCE_EXEC 任务exec()时重新选择CPU MC/DIE/NUMA 组任务fork/exec时的初始放置
SD_BALANCE_FORK 任务fork()时选择目标CPU MC/DIE/NUMA 组内子任务的分布策略
SD_WAKE_AFFINE 唤醒时优先选择上次运行的CPU SMT/MC 组任务唤醒局部性
SD_SHARE_PKG_RESOURCES 域内CPU共享封装级资源 SMT/MC 缓存亲和性判断
SD_ASYM_PACKING 非对称封装(如大小核架构) MC/DIE 组任务优先放置到大核
SD_SERIALIZE 域间负载均衡串行化 NUMA 防止多域并发迁移冲突
调优陷阱:在组调度场景下,SD_WAKE_AFFINE 如果被错误关闭,会导致组内任务频繁跨核唤醒,破坏缓存亲和性,造成吞吐量下降10%-30%。建议在SMT和MC层级始终保留该标志位。

4.3 域层级结构的内联SVG示意图

下图展示了一个典型的4-NUMA节点、每节点16核、每核2超线程的CPU域层级结构。每个矩形代表一个调度域,箭头表示父子关系,颜色区分拓扑层级。

CPU域层级结构(4 NUMA节点 × 16核 × 2 SMT) NUMA域 0 (SD_NUMA) CPU 0-31 | 本地内存节点0 DIE域 0-0 CPU 0-15 MC域 0 CPU 0-7 SMT SMT MC域 1 CPU 8-15 SMT SMT DIE域 0-1 CPU 16-31 MC域 2 CPU 16-23 SMT SMT MC域 3 CPU 24-31 SMT SMT NUMA域 1 (SD_NUMA) CPU 32-63 | 本地内存节点1 DIE域 1-0 CPU 32-47 MC域 4 SMT SMT MC域 5 SMT SMT DIE域 1-1 CPU 48-63 MC域 6 SMT SMT MC域 7 SMT SMT 图例与说明 NUMA域:跨内存节点,迁移代价高,标志位 SD_NUMA DIE域:同封装内,共享L3缓存,标志位 SD_DIE MC域:多核域,共享L2/L3,标志位 SD_MC SMT域:超线程域,共享L1/L2,标志位 SD_SMT 箭头方向:子域 → 父域(负载均衡向上遍历路径) 注:实际系统中域层级数量取决于硬件拓扑深度。ARM大小核架构可能额外增加 SD_ASYM_PACKING 域。 组调度场景下,跨NUMA域的迁移需通过 cpuset 或 sched_migrate_cost_ns 参数控制。

4.4 域层级与组调度迁移路径

在组调度(CFS组调度或RT组调度)场景下,任务迁移遵循域层级的自底向上遍历路径:

  1. 触发条件:当前CPU负载超过阈值,或CPU空闲(newidle_balance)。
  2. 遍历顺序:从当前CPU所在的最低级域(SMT)开始,逐级向上检查父域。
  3. 迁移决策:在每个域层级,检查域内CPU的负载差异,若超过 sched_domain->imbalance_pct 则触发迁移。
  4. 迁移范围:默认只迁移到同域内的其他CPU;跨域迁移需要父域标志位允许。
组调度迁移路径示例:一个属于组A的任务在CPU 0(SMT域)上运行,当CPU 0过载时,调度器首先检查同SMT域的CPU 1;若仍不平衡,则上溯到MC域检查CPU 2-7;若仍不平衡,继续上溯到DIE域检查CPU 8-15;最后到NUMA域检查CPU 16-31。每次上溯都对应一次迁移代价的跃升。

4.5 查看当前系统的域拓扑

通过 /proc/schedstat 和 /sys/kernel/debug/sched/domains 可以查看运行时的域层级和标志位配置:

# 查看所有CPU的域层级
cat /proc/schedstat | grep -A 20 "domain"

# 查看特定CPU的域标志位(以CPU 0为例)
cat /sys/kernel/debug/sched/domains/cpu0/domain*/flags

# 输出示例(4个域层级)
# domain0 (SMT): flags=0x00001027
#   SD_BALANCE_NEWIDLE | SD_BALANCE_EXEC | SD_BALANCE_FORK |
#   SD_WAKE_AFFINE | SD_SHARE_PKG_RESOURCES
# domain1 (MC):  flags=0x0000102f
#   SD_BALANCE_NEWIDLE | SD_BALANCE_EXEC | SD_BALANCE_FORK |
#   SD_WAKE_AFFINE | SD_SHARE_PKG_RESOURCES | SD_LOAD_BALANCE
# domain2 (DIE): flags=0x0000102b
#   SD_BALANCE_NEWIDLE | SD_BALANCE_EXEC | SD_BALANCE_FORK |
#   SD_LOAD_BALANCE
# domain3 (NUMA): flags=0x00001029
#   SD_BALANCE_NEWIDLE | SD_BALANCE_EXEC | SD_BALANCE_FORK |
#   SD_SERIALIZE
调优检查点:在组调度场景下,重点关注 SD_BALANCE_NEWIDLE 和 SD_WAKE_AFFINE 是否在SMT/MC域中同时存在。如果 SD_WAKE_AFFINE 缺失,组内任务可能被分散到不同核,导致缓存命中率下降。

4.6 域层级与迁移代价的量化关系

不同域层级的迁移代价差异显著,直接影响组调度场景下的吞吐量和延迟。下表给出了典型服务器(Intel Xeon 4-NUMA)的实测参考值:

迁移类型 域层级 平均延迟(μs) 缓存影响 组调度建议
同SMT内迁移 SMT 0.5 - 1.0 L1/L2命中率不变 允许,无副作用
同MC内跨核迁移 MC 2.0 - 5.0 L1/L2失效,L3命中 允许,但需关注组内局部性
同DIE内跨MC迁移 DIE 8.0 - 15.0 L3部分失效 谨慎,建议增大 imbalance_pct
跨NUMA节点迁移 NUMA 30.0 - 80.0 全部缓存失效 + 远程内存访问 强烈建议避免,使用 cpuset 绑定

4.7 域标志位与组调度参数的交互

域标志位与组调度参数(如 sched_migrate_cost_ns、sched_nr_migrate)之间存在协同关系。理解这种交互是调优的关键:

  • SD_BALANCE_NEWIDLE + sched_migrate_cost_ns:当空闲CPU触发负载均衡时,sched_migrate_cost_ns 决定了是否值得从远程域拉取组任务。如果迁移代价高于阈值,空闲CPU将保持空闲。
  • SD_WAKE_AFFINE + sched_min_granularity_ns:唤醒时优先选择上次运行的CPU,但如果组任务的调度粒度(sched_min_granularity_ns)过小,频繁唤醒可能抵消亲和性收益。
  • SD_SERIALIZE + sched_nr_migrate:在NUMA域中,串行化标志位防止多个CPU同时进行跨域迁移。如果组任务数量大,sched_nr_migrate 限制每次迁移的任务数,避免单次迁移开销过高。
调优警告:在组调度场景下,如果同时设置了 SD_BALANCE_NEWIDLE 和较低的 sched_migrate_cost_ns,空闲CPU可能频繁从远程NUMA域拉取组任务,导致组内任务分散,破坏缓存亲和性。建议在NUMA域中增大 sched_migrate_cost_ns 或通过 cpuset 限制组任务的CPU范围。

4.8 域层级与cpuset的协同

cpuset(控制组cgroup的cpuset子系统)允许用户显式指定组任务可运行的CPU集合。当cpuset与域层级结合时,调度器会在cpuset限定的CPU集合内构建子域拓扑:

# 创建一个cpuset,将组任务限制在NUMA节点0的CPU 0-15
mkdir /sys/fs/cgroup/cpuset/mygroup
echo 0-15 > /sys/fs/cgroup/cpuset/mygroup/cpuset.cpus
echo 0 > /sys/fs/cgroup/cpuset/mygroup/cpuset.mems

# 此时调度器为mygroup构建的域层级只包含CPU 0-15
# 域层级变为:SMT(0-1) → MC(0-7) → DIE(0-15)
# NUMA域被裁剪,因为所有CPU都在同一NUMA节点内
最佳实践:对于延迟敏感的组任务(如数据库查询、实时音频处理),使用cpuset将任务绑定到单个NUMA节点内的CPU集合,并确保 SD_WAKE_AFFINE 和 SD_BALANCE_NEWIDLE 在SMT/MC域中启用。对于吞吐量敏感的任务(如批处理、视频编码),可以放宽到DIE域,但避免跨NUMA节点。

4.9 域层级标志位的动态调整

部分域标志位可以通过内核参数在运行时调整,无需重新编译内核:

内核参数 影响标志位 默认值 组调度调优建议
sched_balance_newidle SD_BALANCE_NEWIDLE 1(启用) 组任务密集时建议保持启用,避免CPU空转
sched_balance_exec SD_BALANCE_EXEC 1(启用) 组任务fork/exec时建议启用,实现初始负载分布
sched_wake_affine SD_WAKE_AFFINE 1(启用) 组任务建议保持启用,提升缓存亲和性
sched_migrate_cost_ns 影响跨域迁移决策 500000(500μs) NUMA域建议增大到2000000(2ms)
动态调整示例:通过 sysctl 在运行时关闭NUMA域的唤醒亲和性,减少跨节点迁移:
sysctl -w kernel.sched_wake_affine=0
注意:此操作会全局影响所有域层级,建议在组调度场景下通过cpuset隔离而非全局关闭。

4.10 域层级与组调度性能的关联分析

域层级配置不当对组调度性能的影响可以通过以下指标量化:

  • 跨域迁移率:单位时间内组任务跨域迁移的次数。高迁移率通常意味着缓存亲和性差。
  • 平均迁移代价:每次迁移的平均延迟,由迁移跨越的域层级决定。
  • 组任务吞吐量:组内所有任务的总完成量,受迁移率和迁移代价共同影响。
  • 尾延迟:组任务中最长完成时间,跨NUMA迁移可能导致尾延迟显著增加。

通过 perf 工具可以采集域迁移事件:

# 采集跨NUMA域迁移事件
perf stat -e sched:sched_migrate_task -a -- sleep 10

# 结合cgroup过滤,只观察特定组的迁移
perf stat -e sched:sched_migrate_task --cgroup=/mygroup -a -- sleep 10
调优闭环:观察跨域迁移率 → 调整域标志位或cpuset绑定 → 重新测量吞吐量和延迟 → 确认优化效果。在组调度场景下,目标是将跨NUMA迁移率降低到总迁移率的5%以下。