🐧 第四章 进程调度器
📄 一句话概括

四十个小活儿:核数 1→4,总耗时腰斩再腰斩,平均周转也一起变好。但一个不可拆的大活儿:核数从 1 加到 4,一拍都没省。并行不是万能的,它只对【能拆】的活儿有效。

📝 本节和前面几节的关系

4.14 看到了天花板,这一节看另一面:能拆的活儿,多核确实能同时改善吞吐与延迟;不能拆的,一点都改不了。

一、大批小活儿:多核真能两头都好

四十个各 2 拍的活儿,核数 1/2/4。 总耗时大致按核数下降,平均周转也跟着降——因为每个活儿等的时间短了(前面排的人少了)。 这是少见的【两个指标同时变好】的情形。条件是:活儿相互独立、数量远多于核数。 所以 Web 服务、批处理、渲染农场这些场景,多核几乎总是划算的。

二、单个大活儿:多核一点用都没有

一个 40 拍的活儿,核数 1/2/4,总耗时全是 40 拍。 因为一个进程只能在一个核上跑(除非它自己开线程拆任务)。 这条特别容易误解:很多人以为【机器有 8 核,我的程序就会快 8 倍】。不会。你的程序只用一个核。 所以【服务器 CPU 核多】不等于【你的单线程程序快】——单线程性能看的是单核频率与缓存,不是核数。

三、两头都好的前提

一、任务数 ≫ 核数。任务太少,多出来的核闲着(4.14 那个天花板)。 二、任务之间不共享。共享锁、共享内存、同一个文件,都会把并行变成排队。 三、瓶颈在 CPU。如果瓶颈在磁盘或网络,加核只是让它们抢得更凶。 这三条是【并行值不值得做】的判据,比任何框架选型都重要。

四、延迟与吞吐在多核下的关系

多核同时改善两者,只在【任务独立且数量多】时成立。 一旦有共享:吞吐可能上去,延迟反而变差——因为大家排队抢锁,每个人等的时间更长。 这就是【加机器反而更慢】的常见原因:共享点被抢爆了,机器越多抢得越凶。 判据:加机器之后如果 P99 延迟变差,先找共享点,别再加机器。

五、动手 Demo:吞吐量与延迟——多核也不是万能

两组对照:四十个小活儿(该随核数变快)与一个不可拆的大活儿(不该变)。 看第二张表那一列永远不变的 40。

# ① 四十个各 2 拍的活儿
for ncpu in (1, 2, 4):
    ps = [Proc("T%d" % i, 2) for i in range(1, 41)]
    done, sw, _tl, total = run(ps, quantum=1, ncpu=ncpu)

# ② 一个 40 拍的活儿
for ncpu in (1, 2, 4):
    ps = [Proc("Big", 40)]
    done, sw, _tl, total = run(ps, quantum=4, ncpu=ncpu)

实跑输出:

--------------------------------------------------------------------
吞吐量与延迟——多核也不是万能
--------------------------------------------------------------------
① 大批小活儿:核多确实快

  核数     总耗时     平均周转     切换
  --------------------------------------
  1        80         60.5         79
  2        40         30.5         78
  4        20         15.5         76

② 单个大活儿:核再多也只能用一个

  核数     跑完用
  ----------------
  1        40
  2        40
  4        40

  **一个不能拆的活儿,给它八个核也还是那么久。**
  **所以【并行】的前提是【能拆】**——拆不开的,核数只是一堆闲着的硅。

第一张表:总耗时按核数下降,平均周转也跟着降。这是两个指标同时变好的少见情形,条件是任务独立且数量多。 第二张表:总耗时永远是 40 拍。一个进程只能在一个核上跑——这是【进程】这个抽象的最基本约束。 所以【机器有 8 核】不等于【你的程序快 8 倍】。单线程程序只用一个核,核多只是让别的程序不跟它抢。 加机器之后如果延迟反而变差,先找共享点(锁、同一块内存、同一个磁盘)。

30 秒实验

  1. top 里看一个单线程程序,CPU 列永远在 100% 附近,核再多也不涨。
  2. time 一个多线程程序:user 时间超过 real 时间,才说明真用了多核。
  3. nproc 与 lscpu 对比:逻辑核数往往是物理核的两倍(超线程),而超线程只提升一部分场景。

常见误区

  1. 【核多就一定快】。单线程程序只用一个核。核多的好处是【不跟别人抢】,不是【自己变快】。
  2. 【多核能让所有指标都变好】。有共享资源时,吞吐上去了、延迟反而变差。
  3. 【超线程等于两个核】。它是共享执行单元的,只提升一部分场景,重计算负载常常只能提升两三成。
自测三问(点开对答案) 1. 为什么一个 40 拍的活儿加核也不会变快? —— **一个进程只能在一个核上跑。**要变快必须自己开线程把任务拆开。 2. 多核什么时候能同时改善吞吐与延迟? —— 任务独立、数量远多于核数、瓶颈在 CPU。**有共享资源时就做不到了。** 3. 加机器之后延迟反而变差,先查什么? —— 查共享点:锁、同一块内存、同一个磁盘或网络。**共享点被抢爆了。**

相关笔记