2. 调度目标:CPU利用率、吞吐量、周转时间、等待时间、响应时间
聊调度目标之前,我先说个自己的经历。早些年我做嵌入式系统,有个项目跑的是非抢占式调度。产品上线后,用户反馈说「机器偶尔卡顿」。我查了三天,发现是一个后台任务占着CPU不放,前台按键响应被活活饿死了。说白了,这就是调度目标没设计好——响应时间被牺牲了。
所以,调度目标到底是什么?我习惯把它们拆成五个核心指标。你想想看,任何一个调度算法,最终都要用这五个维度来打分。
2.1 CPU利用率
CPU利用率,就是CPU干活的时间占比。别让它闲着。
公式很简单:
CPU利用率 = CPU忙碌时间 / 总时间 × 100%
举个例子。假设一秒内,CPU忙了0.8秒,闲了0.2秒。利用率就是80%。
我个人习惯把利用率看作「花钱买来的设备有没有在干活」。你花了几万块买服务器,结果CPU利用率只有20%,那剩下的80%就是浪费。非抢占式调度下,如果某个进程长时间占用CPU,利用率可能很高,但其他进程就得排队等着。抢占式调度则能更均衡地分配时间片,利用率通常更稳定。
2.2 吞吐量
吞吐量,就是单位时间内完成的任务数。说白了,就是「干活快不快」。
公式:
吞吐量 = 完成任务数 / 总时间
比如,一分钟内处理了30个请求,吞吐量就是30个/分钟。
抢占式调度下,时间片切得小,任务切换频繁,吞吐量可能反而下降——因为切换本身有开销。非抢占式调度呢,任务一旦开始就跑到底,切换少,吞吐量可能更高。但这里有个坑:如果有个长任务卡着,短任务就得等,整体吞吐量反而会掉。
2.3 周转时间
周转时间,就是从任务提交到任务完成的总时间。包括等待、执行、I/O等所有时间。
公式:
周转时间 = 完成时间 - 提交时间
举个例子。你提交一个任务,等了2秒才开始跑,跑了3秒才结束。周转时间就是5秒。
我习惯把周转时间理解为「用户从点击到拿到结果的总时长」。非抢占式调度下,如果前面有个大任务,后面小任务的周转时间会非常难看。抢占式调度通过时间片轮转,能让每个任务都「雨露均沾」,周转时间更平均。
2.4 等待时间
等待时间,就是任务在就绪队列里排队的时间。不包括执行时间,也不包括I/O时间。
公式:
等待时间 = 周转时间 - 执行时间 - I/O时间
说白了,就是「干等着啥也没干」的时间。
抢占式调度下,每个任务都会被频繁打断,等待时间可能分散成很多小段。非抢占式调度呢,等待时间可能集中在开头——等前面那个大任务跑完。哪种更好?看场景。交互式系统希望等待时间分散,这样响应更快;批处理系统可能无所谓。
我记得有一次优化数据库连接池,发现大量线程在等锁。那就是等待时间过长。后来改成抢占式调度思路,把锁粒度拆小,等待时间直接降了60%。
2.5 响应时间
响应时间,就是从提交任务到产生第一次输出(或响应)的时间。注意,不是完成时间,是第一次响应。
公式:
响应时间 = 首次响应时间 - 提交时间
比如你按下一个键,屏幕0.1秒后就有反应了。响应时间就是0.1秒。至于这个任务什么时候跑完,那是周转时间的事。
非抢占式调度下,如果CPU被一个长任务占着,你按个键可能要等好几秒才有反应——这就是响应时间差。抢占式调度通过时间片,能保证每个任务都能在短时间内获得CPU,响应时间自然就好。
2.6 五个指标的关系
这五个指标不是孤立的。它们之间互相影响,甚至互相矛盾。我画了一张图,帮你理清关系。
从这张图你能看到:
- CPU利用率和吞吐量通常是正相关——CPU越忙,干完的活越多。
- 周转时间和等待时间是正相关——等得越久,总时间越长。
- 响应时间和周转时间有时是矛盾的——你想让响应快,就得频繁切换,但切换多了,周转时间反而变长。
所以,没有完美的调度算法。你只能根据场景做取舍。比如:
- 批处理系统:看重吞吐量和CPU利用率,可以牺牲响应时间。
- 交互式系统:看重响应时间,可以适当牺牲吞吐量。
- 实时系统:响应时间是硬指标,其他都可以妥协。
好了,这一节就到这里。下一节我们聊聊具体的调度算法,看看它们是怎么在这五个指标上「跳舞」的。