背景

在双路服务器上讨论性能问题时,很多人会把几类现象混在一起:

  1. 时间片与调度优先级
  2. SMT(超线程)带来的资源共享
  3. 同一 socket 内部 LLC / 内存带宽共享
  4. 不同 socket 之间的 NUMA 差异

为了把这些问题拆开,我在一台 Dell T430 双路服务器RockyLinux 8.10 环境上做了一组微基准实验,重点观察以下四种放置方式:

  • A:单线程基线
  • B:同一物理核的两个 SMT 线程
  • C:同一 socket 的不同物理核
  • D:不同 socket

这组实验的一个重要前提是:任务都通过 taskset 绑到了固定 CPU 上
因此,本文观察到的主要不是“两个任务在同一个逻辑 CPU 上轮流吃时间片”,而是 CPU 拓扑与硬件资源共享 对性能的影响。


实验目标

本文主要回答两个问题:

1. CPU 型 workload 在不同 CPU 拓扑放置下,性能如何变化?

也就是:

  • SMT sibling 会不会让单线程变慢?
  • 同 socket 不同物理核,能否接近线性扩展?
  • 不同 socket,是否还能继续提升?

2. 内存型 workload 在不同放置方式下,差异有多大?

尤其想验证:

  • 同核 SMT 是否会严重恶化内存流任务
  • 同 socket 内是否会受内存带宽约束
  • 跨 socket 是否能接近双倍带宽

实验方法

1. 两类 workload

本文使用两个简单微基准:

CPU 型 workload

一个纯计算循环,固定执行 2000000000 次迭代,输出:

  • elapsed
  • iter_per_sec
  • final

其中 final 用来确认每轮完成的是相同计算,避免被编译器错误优化掉。

内存型 workload

顺序遍历大数组,固定处理 100000000 个元素,输出:

  • elapsed
  • elem_per_sec
  • final_sum

其中 final_sum 用于确认每次读写结果一致。


2. 四种放置方式

实验分为四组:

A:单线程基线

只运行一个 benchmark,绑定在 CPU 2 上。

B:同一物理核,不同 SMT 线程

两个相同 benchmark 分别绑定到同一物理核的两个 SMT sibling 上:

  • A -> CPU 2
  • B -> CPU 34

C:同一 socket,不同物理核

两个 benchmark 绑定到同一 socket 的不同物理核:

  • A -> CPU 2
  • B -> CPU 4

D:不同 socket

两个 benchmark 分别绑定到不同 socket:

  • A -> CPU 2
  • B -> CPU 3

注:以上 CPU 编号以本机实验时的实际拓扑关系为准。


3. 数据处理方式

每组实验均执行 5 次,使用 中位数 作为代表值。
这样可以降低单次 warm-up、后台中断、进程启动不同步等抖动带来的影响。


原始结果概览

CPU 型 workload 原始日志特征

单线程基线大致稳定在 24.97s ~ 25.00s,第一轮略慢。
SMT 同核双跑时,两边都稳定在 27.1s ~ 27.4s
同 socket 不同核时,两边大多在 25.93s ~ 26.04s
不同 socket 时,则基本回到基线附近。

内存型 workload 原始日志特征

单线程基线稳定在 0.176s ~ 0.177s
SMT 同核时,两边都拉长到 0.364s ~ 0.368s
同 socket 不同核时,大多落在 0.279s ~ 0.289s
不同 socket 时,绝大多数又回到 0.177s ~ 0.178s 附近。


结果汇总

一、CPU 型 workload

中位数统计

场景 CPU 放置 中位 elapsed (s) 约当吞吐 (M iter/s) 相对单线程
A 单线程基线 24.986 80.05 1.00x
B-A 同核 SMT / CPU2 27.310 73.23 0.91x
B-B 同核 SMT / CPU34 27.187 73.56 0.91x
C-A 同 socket / CPU2 25.938 77.11 0.96x
C-B 同 socket / CPU4 25.937 77.11 0.96x
D-A 不同 socket / CPU2 24.864 80.44 1.01x
D-B 不同 socket / CPU3 25.050 79.84 1.00x

总吞吐对比(相对单线程)

  • B:约 1.83x
  • C:约 1.93x
  • D:约 2.00x

CPU workload 解读

1. 同核 SMT:单线程变慢,但总吞吐仍然增加

CPU 型 benchmark 在 SMT sibling 上运行时,单线程耗时大约增加了 9% 左右
但是两条线程加起来的总吞吐仍然可以达到单线程的 1.83 倍

这说明:

  • SMT 对这类计算型任务仍然有价值
  • 但它不能等价于“再多一个完整物理核”
  • 对单任务时延敏感的 workload,应尽量避免与重任务共享 SMT sibling

2. 同 socket 不同核:已经接近理想扩展

把两个任务放到同一 socket 的不同物理核后,单线程耗时只比基线差 约 4%,总吞吐达到了 1.93 倍

这说明:

  • 对 CPU 型 workload 而言
  • 同 socket 内部资源共享影响较小
  • 主要瓶颈并不在内存带宽

3. 不同 socket:基本线性扩展

跨 socket 后,两边几乎回到单线程基线,总吞吐约为 2.00x

这说明:

  • 这个 workload 的主要瓶颈是 core 计算资源
  • 跨 socket 可以获得非常理想的扩展效果
  • NUMA 对该 workload 的负面影响很小

二、内存型 workload

中位数统计

场景 CPU 放置 中位 elapsed (s) 约当吞吐 (M elem/s) 相对单线程
A 单线程基线 0.176733 565.82 1.00x
B-A 同核 SMT / CPU2 0.366864 272.58 0.48x
B-B 同核 SMT / CPU34 0.364267 274.52 0.49x
C-A 同 socket / CPU2 0.280340 356.71 0.63x
C-B 同 socket / CPU4 0.279861 357.32 0.63x
D-A 不同 socket / CPU2 0.176934 565.18 1.00x
D-B 不同 socket / CPU3 0.178281 560.91 0.99x

总吞吐对比(相对单线程)

  • B:约 0.97x
  • C:约 1.26x
  • D:约 1.99x

内存 workload 解读

1. SMT 同核:最差,甚至总吞吐不如单线程

这是整组实验中最值得注意的现象。

在同一物理核的两个 SMT 线程上同时运行 memory workload 时:

  • 每个线程吞吐几乎只剩下单线程的 48% ~ 49%
  • 两个线程加起来的总吞吐仅为单线程的 0.97x

也就是说:

两个内存流任务同时塞到同一个物理核的两个 SMT 线程上,合起来居然还不如只跑一个线程。

这说明 memory workload 在该场景下主要受限于:

  • load/store 资源
  • L1/L2
  • outstanding miss 能力
  • cache / prefetch 行为
  • 同核访存路径共享

2. 同 socket 不同核:比 SMT 好很多,但仍明显受限

把两个 memory workload 分配到同一 socket 的不同物理核后:

  • 单线程吞吐下降到基线的 63% 左右
  • 总吞吐约提升到单线程的 1.26x

这说明:

  • 这里的瓶颈已经不在 core 内部
  • 而是在 socket 级别共享的资源上
  • 最可能的就是内存控制器、内存通道、LLC / uncore 带宽

3. 不同 socket:接近双倍带宽

跨 socket 后,两条线程的单线程吞吐都几乎回到了基线,总吞吐达到 1.99x

这说明在这台双路服务器上:

  • 两个 socket 的内存带宽可以近似独立叠加
  • 对内存型 workload,NUMA 感知放置非常重要
  • 正确的跨 socket 放置,可以显著提升带宽型任务的整体效率

关键结论

基于这组实验,可以得到以下结论。

结论一:CPU 型 workload

排序大致是:

不同 socket ≈ 同 socket 不同物理核 > 同核 SMT

含义是:

  • SMT 会带来中等程度的单线程损失
  • 但总吞吐仍然比单线程高
  • 如果追求单任务性能,应优先避开 SMT sibling
  • 如果追求总体吞吐,同 socket 不同核通常已经很好
  • 跨 socket 对 CPU 型 workload 能进一步逼近理想扩展

结论二:内存型 workload

排序大致是:

不同 socket » 同 socket 不同物理核 » 同核 SMT

含义是:

  • 同核 SMT 对 memory workload 极不友好
  • 同 socket 不同核会明显受共享带宽制约
  • 不同 socket 几乎可以拿到双倍带宽
  • NUMA 感知放置对内存型任务远比对 CPU 型任务更关键

工程启示

1. CPU 密集型任务如何放?

如果任务主要吃算力:

  • 优先放到不同物理核
  • 对低延迟任务,尽量避开 SMT sibling
  • 对批处理型吞吐任务,SMT 仍然可用,但收益不会等价于再多一个完整 core

2. 内存带宽型任务如何放?

如果任务主要吃内存带宽:

  • 避免两个重任务共享同一个物理核的 SMT sibling
  • 尽量不要把多个重度流式任务塞满同一个 socket
  • 最好跨 socket 放置,并结合本地内存分配策略

3. 性能问题别只盯着“CPU 利用率”

同样是“两个线程都在跑”:

  • 在 CPU 型 workload 中,SMT 可能还能带来不错的总吞吐
  • 在 memory 型 workload 中,SMT 甚至可能让总吞吐变差

所以实际分析时,必须结合:

  • workload 类型
  • CPU 拓扑
  • NUMA / 内存带宽
  • 线程绑核策略

不能只看 top、mpstat 或简单的 %CPU


这组实验没有涉及什么

需要特别说明的是,这组实验并不直接涉及“时间片轮转”问题

原因是:

  • 本文所有任务都绑到了不同逻辑 CPU 上
  • 它们不是在同一个逻辑 CPU 上轮流执行
  • 因此这里看到的主效应是硬件资源共享,而不是 CFS 在单 CPU runqueue 上的切换分配

如果要进一步研究“时间片”和“调度优先级”,更合适的实验应该是:

  • 把两个 CPU 型任务都绑到 同一个逻辑 CPU
  • 再比较:
    • nice 0 vs nice 0
    • nice 0 vs nice 10
    • SCHED_OTHER vs SCHED_BATCH
    • SCHED_OTHER vs SCHED_IDLE

那样看到的才更接近“纯调度策略”对执行份额的影响。


实验中的几个注意点

1. 第一轮样本可能不是稳态

日志里能看到某些组的第一轮结果偏离后续几轮,这通常与以下因素有关:

  • 频率爬升
  • cache / predictor warm-up
  • 进程启动不同步
  • 中断或后台任务打扰

所以本文统一使用 中位数 而不是单次最优值。

2. 双进程启动时可能存在轻微不同步

例如 memory workload 在某些组里第一轮明显偏快,这很可能说明两个进程的“测量区间”没有完全重叠。
正式做更严谨的 benchmark 时,可以考虑加入 barrier,使多个进程在同一时刻进入正式测量阶段。

3. 不同 workload 对 SMT/NUMA 的敏感度完全不同

这组实验最有价值的地方就在于:

  • CPU 型 workload 的结果很难直接外推到内存型 workload
  • 内存型 workload 的最优放置策略,往往完全不同

小结

这组在 Dell T430 双路服务器上的实验,给出了一个非常清晰的图景:

  • CPU 型 workload
    SMT 会让单线程变慢,但总体吞吐仍有收益;同 socket 不同核已经很好,跨 socket 基本线性扩展。

  • 内存型 workload
    SMT 同核最差;同 socket 不同核受带宽制约明显;跨 socket 几乎可以获得双倍总吞吐。

对于日常的 EDA、HPC、批处理或高性能服务部署来说,这个结论很实用:

放置策略必须与 workload 类型匹配。
同样一台双路服务器,CPU 密集型任务和内存带宽型任务,最优绑核方式可能完全不同。


附:本文使用的典型命令示例

单线程基线

1
2
taskset -c 2 ./smt_bench CPU 2000000000
taskset -c 2 ./mem_bench CPU 100000000

同核 SMT

1
2
3
4
5
taskset -c 2  ./smt_bench A 2000000000 &
taskset -c 34 ./smt_bench B 2000000000 &

taskset -c 2  ./mem_bench A 100000000 &
taskset -c 34 ./mem_bench B 100000000 &

同 socket 不同物理核

1
2
3
4
5
taskset -c 2 ./smt_bench A 2000000000 &
taskset -c 4 ./smt_bench B 2000000000 &

taskset -c 2 ./mem_bench A 100000000 &
taskset -c 4 ./mem_bench B 100000000 &

不同 socket

1
2
3
4
5
taskset -c 2 ./smt_bench A 2000000000 &
taskset -c 3 ./smt_bench B 2000000000 &

taskset -c 2 ./mem_bench A 100000000 &
taskset -c 3 ./mem_bench B 100000000 &

后续扩展

如果要把这组实验继续扩展成“调度与性能联合分析”专题,后续值得做的有:

  • 同一个逻辑 CPU 上比较 niceSCHED_BATCHSCHED_IDLE
  • 再结合 perf statnumastatperf schedmpstat -P ALL 做交叉验证

这样就能把:

  • 时间片与调度份额
  • SMT 共享
  • NUMA / 内存带宽

三个层面的影响完整串起来。