什么是 IO 密集,什么是 CPU 密集?

#计算机网络#操作系统#面试八股

什么是 IO 密集,什么是 CPU 密集?

按瓶颈资源给任务分类:CPU 密集(CPU-bound)是算力打满、CPU 一直在算; IO 密集(IO-bound)是大部分时间在等磁盘、网络、数据库返回,CPU 反而闲。 判断标准很硬:CPU 时间占墙钟时间的比例。这个分类直接决定线程池大小、要不要用协程、以及优化往哪使劲。

1. 两类任务的特征

维度CPU 密集IO 密集
时间花在计算、循环、加解密、压缩等网络/磁盘/DB 响应
CPU 利用率接近 100%低,常低于 20%
线程状态一直在 R大量在 S(可中断睡眠)
加线程有用吗没用,超过核数只会增加切换有用,等待可以重叠
优化手段并行化、算法、SIMD、缓存批量、异步、连接池、减少往返
典型场景视频转码、哈希、排序、风控计算Web API、爬虫、网关、DB 访问
CPU 密集:  [算 100ms][算 100ms][算 100ms]      墙钟 300ms,CPU 300ms
IO  密集:  [等 90ms][算 10ms][等 90ms][算 10ms]  墙钟 200ms,CPU 20ms
不同任务可以交叠:
  T1  [等db 90ms][算10ms]
  T2      [等db 90ms][算10ms]        ← 等待期间 CPU 可服务别的任务

2. 怎么判定

CPU 占比 = (utime + stime) / real_time × 100%
  > 80%  → CPU 密集
  < 20%  → IO 密集
中间地带 → 混合型,按主要耗时归类
# 方法一:time 直接给出三者
time ./job
# real 0m2.010s  user 0m0.180s  sys 0m0.020s   → CPU 只占 10%,IO 密集

# 方法二:运行中观察
pidstat -u -p 1234 1        # %CPU 长期很低但 QPS 不低 → IO 密集
pidstat -w -p 1234 1        # cswch/s 高(自愿切换)多半在等 IO

# 方法三:火焰图看栈里是不是卡在 syscall
perf record -F 99 -p 1234 -g -- sleep 30 && perf report --stdio | head -30

3. 线程池该开多大

IO 密集的经验公式(Little 定律的工程化版本):

最佳并发数 ≈ 核数 × (1 + 平均等待时间 / 平均计算时间)

例如 8 核、一次请求等 DB 90ms、计算 10ms:8 × (1 + 9) = 80 个工作线程比较合适。 再往上加只会增加上下文切换和内存占用。

// Go 里不必手算线程数:协程阻塞在 IO 时自动让出,M 的数量由 GOMAXPROCS 控制
var wg sync.WaitGroup
sem := make(chan struct{}, 200)        // 用信号量控制对下游的并发压力
for _, url := range urls {
    wg.Add(1)
    sem <- struct{}{}
    go func(u string) {
        defer wg.Done()
        defer func() { <-sem }()
        resp, err := http.Get(u)       // 阻塞在这里,但底层 M 可以去跑别的 G
        if err == nil { resp.Body.Close() }
    }(url)
}
wg.Wait()

并发数不是越大越好:下游数据库连接数、文件句柄、带宽都会先到瓶颈, 压测时要观察的是下游的排队时间,而不是本机的线程数。

4. 常见追问

Q:IO 密集为什么适合协程而不是线程? 线程的栈按 MB 计,切换要进内核;协程栈初始只有 KB 级,切换在用户态完成,几万个并发也只占几百 MB。 而且协程"遇到阻塞自动让出"的语义,让同步写法获得异步的并发度。

Q:混合型任务怎么处理? 拆开:CPU 部分丢进固定大小的计算线程池(等于核数),IO 部分用大量轻量协程/异步调用。 不要用同一个池子,否则慢 IO 会把计算线程占满,形成饥饿。

Q:CPU 密集任务真的不能加线程吗? 可以少量加(核数 + 1~2),用来掩盖偶发的缺页、GC 停顿。但超过核数后,吞吐不会提升, 反而因为缓存失效和调度开销而下降。

Q:为什么说"IO 密集的服务加机器没用"? 如果瓶颈在下游数据库,加应用机器只会把更多并发压到数据库上,延迟更糟。 此时应该加缓存、批量化、读写分离,先降低单位请求的 IO 次数。

小结

分类看的是"CPU 时间 / 墙钟时间"这个比值。CPU 密集靠并行化和算法,线程数贴近核数; IO 密集靠重叠等待,并发数按"核数 × (1 + 等待/计算)"估,并受下游容量约束。 答这题时给公式、给 time 的输出示例,再点一句"瓶颈可能在下游",深度就够了。

笔记加载中…