背景
在双路服务器上讨论性能问题时,很多人会把几类现象混在一起:
- 时间片与调度优先级
- SMT(超线程)带来的资源共享
- 同一 socket 内部 LLC / 内存带宽共享
- 不同 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 次迭代,输出:
elapsediter_per_secfinal
其中 final 用来确认每轮完成的是相同计算,避免被编译器错误优化掉。
内存型 workload
顺序遍历大数组,固定处理 100000000 个元素,输出:
elapsedelem_per_secfinal_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 0vsnice 0nice 0vsnice 10SCHED_OTHERvsSCHED_BATCHSCHED_OTHERvsSCHED_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 上比较
nice、SCHED_BATCH、SCHED_IDLE - 再结合
perf stat、numastat、perf sched、mpstat -P ALL做交叉验证
这样就能把:
- 时间片与调度份额
- SMT 共享
- NUMA / 内存带宽
三个层面的影响完整串起来。