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

墙上时钟与 CPU 时间是两回事:纯计算 200 万次加法,两者几乎一样(0.13 秒 vs 0.12 秒);30 次 sleep 共 0.33 秒,占 CPU 是 0.000 秒。两者对不上,说明有别的东西在拖。

📝 本节和前面几节的关系

4.16 讲怎么算加速比,这一节把【时间】这个词拆开——你以后量任何东西,都得先问清【量的哪种时间】。

一、两种时间,两个意思

运行时间(墙上时钟/real):从开始到结束,你表上过去多久。这是用户感受到的那个时间。 执行时间(CPU 时间/user+sys):CPU 真正花在这个进程上的时间。它不包括等待。 两者的关系:real ≥ user + sys(单核时)。差值就是【等】的时间——等 IO、等锁、被别人挤。 多核时 real 可以小于 user+sys:多个核同时算,加起来超过墙上时间。

二、实测:两种活儿的两种时间

纯计算 200 万次加法:墙上 0.132 秒,占 CPU 0.125 秒。几乎没差。 30 次 sleep 各 10 毫秒:墙上 0.328 秒,占 CPU 0.000 秒。全在等。 同样是【跑了零点几秒】,一个是真干了活,一个是躺着等。 这个差别在排查问题时有决定性的作用:不知道是哪种,就会往错的方向优化。

三、三种组合与三种结论

real 大、user 小 → 在等 IO 或等锁 → 去查磁盘、网络、锁竞争。 real 大、user 也大 → 真在算 → 去查算法复杂度、向量化、缓存命中。 real 小、user 大 → 吃了多核 → 去确认是不是真并行、有没有超订。 这三条是性能排查的第一步,比任何 profiler 都快——time 就够了。

四、为什么 Linux 要分开记这两笔

因为调度器只认 CPU 时间:它按【谁占了多少 CPU】来分配。一个一直在等的进程,对调度器来说是【没占资源】的。 而用户只认墙上时间:他不管你在等还是在算,等了就是等了。 两套账各服务一个目的:内核用 CPU 时间做公平,用户用墙上时间评价体验。 所以【程序只用了 0.1 秒 CPU,但你等了三秒】这种情况完全可能——剩下 2.9 秒全在等。

五、动手 Demo:运行时间和执行时间——墙上时钟与 CPU 时间

两种活儿各跑一次,同时记墙上时钟与 CPU 时间:一段纯计算,一段全是 sleep。看两张表差在哪。

# ① 纯计算
t0, c0 = time.perf_counter(), time.process_time()
s = 0
for i in range(2000000):
    s += i
t1, c1 = time.perf_counter(), time.process_time()
print("墙上", t1 - t0, "CPU", c1 - c0)

# ② 全在等
t2, c2 = time.perf_counter(), time.process_time()
for _ in range(30):
    time.sleep(0.01)
t3, c3 = time.perf_counter(), time.process_time()
print("墙上", t3 - t2, "CPU", c3 - c2)

实跑输出:

--------------------------------------------------------------------
运行时间和执行时间——墙上时钟与 CPU 时间
--------------------------------------------------------------------
① 两种活儿的两种时间

  干的活                    墙上时钟     占 CPU       差值去哪了
  ---------------------------------------------------------------
  纯计算 200 万次加法       0.125 秒     0.125 秒     几乎没差
  sleep 30 次各 10 毫秒     0.329 秒     0.000 秒     全在等

      **同样是在【跑】,一个是真干了活,一个是躺着等。**

  **这就是 `time` 命令为什么要输出三行**:real 是墙上时钟,
  user 与 sys 才是真花在 CPU 上的。**三者对不上,说明有别的东西在拖。**

② 这个区别在排查问题时最有用

  现象                   说明什么           接下来查什么
  -------------------------------------------------------------
  real 大、user 小       在等 IO 或等锁     磁盘、网络、锁竞争
  real 大、user 也大     真在算             算法复杂度、向量化
  real 小、user 大       多核并行           是不是真并行了

纯计算那组两个数几乎相等——0.13 秒与 0.12 秒,差的那点花在启动与收尾上。 sleep 那组差了两个数量级:墙上 0.328 秒,CPU 是 0.000 秒——因为睡着的时候一次也没占 CPU。 这就是 time 命令为什么要输出三行。real 是墙上,user 与 sys 才是真花在 CPU 上的。 排查时先看这一对:对不上就说明在等,顺着【在等什么】查下去,比盲猜快得多。

30 秒实验

  1. time sleep 1——real 约 1 秒,user+sys 接近 0。这就是【纯等】的样子。
  2. time yes > /dev/null(按 Ctrl+C 停)——看 user 与 sys 各占多少。
  3. time dd if=/dev/zero of=/tmp/x bs=1M count=200——看 real 与 CPU 的差,那部分就是 IO 等待。

常见误区

  1. 【real 就是程序的执行时间】。real 含等待。程序本身的执行时间是 user+sys。
  2. 【CPU 时间短就说明程序高效】。它可能只是【大部分时间在等】。要看它等的是什么。
  3. 【多核下 real 一定比 user 小】。不一定。只有真并行且负载足够时才成立。
自测三问(点开对答案) 1. 运行时间与执行时间分别是什么?两者关系如何? —— 运行时间=墙上时钟;执行时间=CPU 时间(user+sys)。**单核时 real ≥ user+sys,差值就是等的时间**;多核时 real 可以小于它。 2. real 大而 user 小,说明什么?该往哪查? —— **在等** IO 或锁。**查磁盘、网络、锁竞争。** 3. 为什么内核和用户要记两套时间? —— 内核按 CPU 时间做公平分配;用户按墙上时间评价体验。**两套账服务两个目的。**

相关笔记