CPU并发程序设计与性能优化 - 第1节
并发与并行:别搞混了,这两件事不一样
先问个问题:你手上有两个任务,是同时做还是轮流做?
很多人把「并发」和「并行」当同义词用。但在 CPU 优化领域,这两个词差了十万八千里。我当年刚接触多线程时也犯过这个错,调了半个月的性能瓶颈,结果发现根本没用上多核——说白了,我是并发,不是并行。
- 并发(Concurrency):多个任务在同一时间段内交替执行。单核 CPU 也能做到,靠的是时间片轮转。
- 并行(Parallelism):多个任务在同一时刻真正同时执行。必须有多核或多 CPU 支持。
一句话区分:并发是逻辑上的同时发生,并行是物理上的同时发生。
举个实际例子。我做过一个日志采集系统,单核 CPU 上跑 8 个线程,每秒轮询 100 个文件。看起来线程很多,但其实 CPU 一秒内切来切去,每次只干一件事。这就是并发。后来换了 8 核服务器,每个线程分配到独立核上,同时干活,这才是并行。
CPU 架构对并发的支持
现在的 CPU 为了榨干性能,从硬件层面就给了大量并发支持。我们来拆开看看。
| 硬件特性 | 作用 | 实际影响 |
|---|---|---|
| 多核 / 多 CPU | 真正并行执行 | 多个线程分配到不同核心上,同时运行 |
| 超线程(Hyper-Threading) | 一个物理核心模拟两个逻辑核心 | 提升指令级并行度,但不是真并行 |
| SIMD(单指令多数据流) | 一条指令处理多个数据 | 向量化运算加速,适合批量计算 |
| 乱序执行(Out-of-Order) | 动态调整指令执行顺序 | 隐藏流水线停顿,提升吞吐 |
| 缓存层次(L1/L2/L3) | 减少内存访问延迟 | 缓存命中率直接影响并发性能 |
这里我特别想说一下超线程。别被「逻辑核心」骗了。我曾经在压测时发现,把线程数设成 CPU 逻辑核心数(比如 16 线程跑在 8 核 16 逻辑核上),反而比设成物理核心数(8 线程)更慢。为什么会这样?因为超线程共享执行单元和缓存,如果两个逻辑核上的线程争抢资源,反而会互相拖累。嗯,这个坑我踩得比较深。
我的建议:初期调优时,线程数先取物理核心数,观察 CPU 利用率再往上加。别盲目按逻辑核心数来。
并发优化的目标
聊了这么多硬件,那我们应该往哪个方向使力?并发优化的核心目标可以归纳为三个:
- 减少等待:让 CPU 尽量不闲着。等待 I/O 时干点别的,用异步 / 非阻塞替代同步。
- 减少争用:多个线程抢同一把锁,效率直线下降。优化锁的粒度,甚至用无锁数据结构。
- 利用局部性:缓存不命中,CPU 要等几百个周期去主存拿数据。尽量让数据和代码挨得近。
你看,这三个目标其实是互相关联的。减少争用往往能顺便减少等待。利用局部性又能减少缓存 MISS,间接提升了并行效率。
注意:不要所有场景都开多线程。如果任务本身很轻,线程创建切换的开销可能比任务本身还大。我见过有人对几十微秒的运算也用线程池——结果不如单线程快。
本章知识体系
下面这张图把本章的核心逻辑串了一遍。从基本概念出发,到硬件支持,再到优化目标。记不住没关系,后面每一章都会对其中一个点深入展开。
这张图我建议你存下来。后面每讲一个优化技巧,你都可以回来看看它落在图中哪个位置——这样就不会迷路了。
小结:现在你心里有数了
嗯,这一章我们把地基打好了。你知道了并发和并行的本质区别,也了解了 CPU 硬件给了哪些武器,还清楚了优化到底在优化什么。
我记得带团队做第一个高性能服务时,大家对着性能火焰图一脸懵。后来我拉着他们对照这张知识体系图,一个个排查——发现是锁的粒度太粗,CPU 在争用上浪费了大量时间。改完之后吞吐直接翻倍。
所以别急,后面每一章都是针对这张图里的一个具体节点深挖。你会学会怎么测量、怎么定位、怎么改。咱们一章一章来。