八个互不相干的活儿,核数 1→2→4→8 时总耗时 80→40→20→10,完美线性。但只要活儿之间要互相等,加核就远不到线性——加核能不能提速,取决于瓶颈在哪。
4.11 讲的是单核上的分配,这一节把核数变成变量:核多了以后,调度多了两个新问题——谁上哪个核、负载怎么平衡。
一、多核带来的两个新问题
一、亲和性(谁上哪个核)。一个进程在某个核上跑热了,换到别的核缓存全凉。所以调度器尽量让它待在原核。 二、负载均衡。有的核上排了一堆,有的核闲着一半。调度器要定期把任务搬过去——而搬一次也是一次切换加一次缓存失效。 这两件事在单核时代根本不存在,所以多核调度不是【单核调度乘几份】。
二、完美线性的条件
八个一样的活儿、每个 10 拍、互相不干扰:一核 80 拍、八核 10 拍。这是完美线性——8 倍核换来 8 倍速。 条件有三个:活儿能拆开、彼此不共享东西、没有别的瓶颈。三个条件缺一个,加速比就掉下来。 判据很实用:你只有在【三个条件全满足】时才该期待线性加速。任何共享资源(锁、同一块内存、同一个磁盘)都会把你拉回来。
三、IO 密集时加核没用
八个活儿、每跑 5 拍就等 20 拍 IO:加核确实有加速,但远不到线性——八核只快了 3 倍出头,不是 8 倍。 因为一部分时间卡在 IO 上,CPU 再多也只能干等。核是给【算】用的,不是给【等】用的。 这个结论在工程上很值钱:一台 IO 密集的服务加 CPU 核,钱花了、QPS 涨不了多少。该换的是存储或缓存。
四、现实中多核调度的样子
每个核有自己的就绪队列,不是所有核抢一个队列——抢一个队列会带来严重的锁竞争。 核之间定期做负载均衡:把任务从忙的核搬到闲的核。 NUMA 机器上更复杂:跨节点访问内存比本地慢几倍,所以调度器还要考虑【进程和它的内存是不是在同一个节点】。 所以你看到的【负载不均匀】,很多时候不是 bug,是调度器在【少搬以减少缓存失效】。
五、动手 Demo:多个逻辑 CPU——核多了,活就能快点干完吗
两组实验:八个互不相干的活儿(应该线性),与八个每跑 5 拍就等 20 拍 IO 的活儿(应该远不到线性)。 重点看第二张表的加速比。
# ① 八个独立活儿
for ncpu in (1, 2, 4, 8):
ps = [Proc("P%d" % i, 10) for i in range(1, 9)]
done, sw, _tl, total = run(ps, quantum=4, ncpu=ncpu)
# ② 八个爱等的活儿
for ncpu in (1, 2, 4, 8):
ps = [Proc("P%d" % i, 10, io_each=5, io_len=20) for i in range(1, 9)]
done, sw, _tl, total = run(ps, quantum=2, ncpu=ncpu)
实跑输出:
--------------------------------------------------------------------
多个逻辑 CPU——核多了,活就能快点干完吗
--------------------------------------------------------------------
① 八个一样的活儿,核数从 1 加到 8
核数 跑完用 加速比 切换次数
-----------------------------------------
1 80 1.00 倍 23
2 40 2.00 倍 22
4 20 4.00 倍 20
8 10 8.00 倍 0
**八个活儿、每个 10 拍:一核 80 拍,八核 10 拍——这时候是完美线性。**
**因为每个活儿之间一点关系都没有,谁也不用等谁。**
② 但如果活儿之间要互相等
核数 跑完用 比一核快多少
---------------------------------
1 92 1.00 倍
2 56 1.64 倍
4 38 2.42 倍
8 29 3.17 倍
**这批活儿每跑 5 拍就要等 20 拍 IO——加核有用,但远不到线性。**
**因为一部分时间卡在 IO 上,CPU 再多也只能干等。**
**所以【加核能不能加速】取决于瓶颈在哪**:CPU 密集能线性,IO 密集基本不能。
第一张表是完美的:80/40/20/10,加速比 1/2/4/8。因为八个活儿之间一点关系都没有。 第二张表就掉下来了:八核只快了 3 倍出头。因为有一大半时间在等 IO,那段时间加核也没人用得上。 这就是【阿姆达尔定律】的直觉版:能并行的部分才受益,不能并行的部分(包括等待)原封不动。 推论:判断该不该加核,先看【CPU 占用率】。占用率上不去,加核是白花钱。
30 秒实验
nproc看你有几个逻辑核,再lscpu | head看物理核与超线程。taskset -c 0 ./程序与不加taskset各跑一次,看耗时差多少。mpstat -P ALL 1 3(sysstat)看每个核的占用,多数时候是不均匀的。
常见误区
- 【核数翻倍,速度就翻倍】。只有【活儿能拆、不共享、没别的瓶颈】时才成立。三个条件缺一个就掉。
- 【多核一定能提高吞吐】。如果瓶颈在 IO 或锁,加核只会让它们抢得更凶。
- 【负载不均匀是 bug】。很多时候是调度器故意少搬,为了保住缓存局部性。
自测三问(点开对答案)
1. 多核给调度带来哪两个新问题? —— **亲和性**(让进程待在跑热的核上)与**负载均衡**(把任务从忙核搬到闲核)。 2. 完美线性加速的三个条件是什么? —— 活儿能拆开、彼此不共享资源、没有别的瓶颈。**缺一个,加速比就掉下来。** 3. IO 密集的负载加核为什么没用? —— 时间卡在等待上,**核是给【算】用的,不是给【等】用的**。该换的是存储或加缓存。- ☐ 4.13 实验方法
相关笔记
- 现实中的系统 — 单核分配
- 实验方法 — 怎么在真机上量
- 实验结果 — 下一步的数据
- 🐧 Linux是怎样工作的 · 索引