性能分析基础:常用性能指标与性能模型
做SDK优化这几年,我最大的体会是:没有指标,就没有优化。很多同学上来就调参数、改代码,结果跑完一看,瓶颈压根不在那儿。我自己早期也踩过这种坑——一个网络库的延迟问题,我反复调内存池大小,折腾了两周,最后用perf一看,CPU都在等网卡中断…嗯,方向错了,再多努力都是白费。
这一章,咱们就把性能分析的基础打牢。我会从四个核心维度展开:CPU、内存、IO、网络,然后聊聊性能模型怎么帮我们做系统级判断。你想想看,搞清这些,后面做优化才能指哪打哪。
一、CPU指标:到底在忙什么?
CPU 是最容易让人迷惑的指标——「利用率90%」不代表系统就快满了,还得看是用户态还是内核态。我个人习惯先看三个东西:用户态占比(us)、系统态占比(sy)、空闲/等待(id + wa)。
| 指标 | 含义 | 典型异常 |
|---|---|---|
| us(user) | 应用程序代码执行时间 | 高说明计算密集 |
| sy(system) | 内核态耗时(系统调用、锁、中断) | 高可能遇到内核瓶颈 |
| id(idle) | CPU空闲 | 低不一定坏,但要结合负载 |
| wa(iowait) | CPU等待IO完成 | 高说明磁盘/网络拖了后腿 |
| st(steal) | 虚拟化宿主机抢占 | 高 → 虚机被超售 |
看top或mpstat时,我通常会持续观察几轮。一次快照意义不大——流量波动、GC、外部服务抖动都会造成瞬态波动。我曾经帮一个团队排查CPU飙升,他们只看了top一次,看到sy占60%就认为是内核bug,结果我让跑perf top一查,其实是频繁的clock_gettime系统调用。改成rdtsc硬件指令后,sy直接降到5%。
mpstat -P ALL 1可以看每个核的分布。如果某个核满载而其他核空闲,大概率是绑核或锁竞争。
二、内存指标:不只是「用了多少」
内存指标里,我特别关注两个维度:容量和带宽。容量好理解,看看free -h就行。但带宽往往被忽略——很多SDK处理大量小对象时,内存带宽反而先成为瓶颈。
通常我会用/proc/meminfo捞关键字段:
- MemTotal / MemFree:总量和空闲,注意available才是真正可用的(包括可回收的cache)
- SwapTotal / SwapUsed:交换分区使用量——一旦开始换页,性能雪崩
- Dirty:待写回磁盘的脏页数量,过大会导致IO突发
- Slab:内核对象缓存,比如dentry、inode。太高说明文件操作频繁
我记得有一次做图片SDK优化,free显示内存只用了30%,但vmstat的si和so一直在跳动——系统在频繁换页。调查后发现是内存碎片导致kmalloc分配大块失败,转而用了vmalloc。嗯,内存碎片这种隐形杀手,不看/proc/buddyinfo根本发现不了。
jemalloc/tcmalloc替代glibc的malloc——碎片率能降一个数量级。
三、IO指标:磁盘才是真正的慢牛
IO 延迟的典型范围:内存 100ns 级,SSD 100μs 级,HDD 10ms 级。你的代码如果频繁读写磁盘,再多的CPU优化都白搭。
关键指标:
- IOPS(每秒IO次数):对随机读写敏感
- Throughput(吞吐量):对顺序读写敏感
- latency(延迟):平均/99分位延迟,看
iostat -x 1的await和svctm - queue depth(队列深度):可以简单用
avgqu-sz看,过高说明磁盘来不及处理
我曾经遇到一个日志SDK:写完日志文件后做fsync,结果每次调用都要卡几十毫秒。查iostat发现%util接近100%,但IOPS很低。后来改成fdatasync(只刷数据不刷metadata)+ 批量提交,延迟降到3ms以下。
四、网络指标:理解延迟和丢包
网络指标在SDK性能中越来越重要,尤其是微服务架构下。我的习惯是看四个维度:
- 带宽(bits/s):瓶颈通常不在带宽,而在小包处理能力
- PPS(packets per second):网卡能收发的包数量,很多云服务器PPS上限很低
- 延迟(rtt、tcp_rtt):
ping和ss -ti可以看TCP重传和RTT - 重传率(retrans):
netstat -s | grep retrans,超过1%就要警惕
平时排查网络问题,我会先用sar -n DEV 1看吞吐和错误包,再用tcpdump抓采样包做时延分析。你想想看,一个请求从客户端到服务端,中间可能经过五六个跳点,每个点都可能引入毫秒级延迟。
ss -i可以显示每个TCP连接的cwnd、rtt、mss。如果rtt突然变大,往往伴随着丢包或拥塞。
五、性能模型:从指标到决策
光有指标还不够,得有一套模型帮我们关联不同指标,预测优化效果。我在项目中主要用这三种:
| 模型 | 核心公式/思想 | 适用场景 |
|---|---|---|
| UTL模型(利用率-吞吐-延迟) | U = λ / μ(到达率/服务率) T = W + S(等待+服务) | 系统容量规划、瓶颈识别 |
| 排队论模型 | Little定律:L = λW | 连接池、线程池、队列深度设计 |
| Roofline模型 | 性能上限 = min(计算能力, 带宽 × 计算强度) | 计算密集型SDK(如编解码、AI推理) |
| Amdahl定律 | 加速比 ≤ 1 / ( (1-P) + P/N ) | 评估并行优化收益 |
举个例子,我们用排队论设计线程池。假设任务到达率λ=2000/s,每个任务处理时间S=0.4ms,那么系统负载U=λ×S=0.8。根据M/M/1队列,平均响应时间T=S/(1-U)=2ms。如果U>0.9,响应时间会急剧恶化——这就是为什么我建议把线程池利用率控制在70%~80%之间,留足余量应对突发。
至于Roofline模型,我在优化图像处理SDK时用过一次。先通过perf stat拿到实际FLOPS和内存带宽,然后画出房顶曲线,一眼就看出瓶颈在带宽——计算强度太低。于是改成分块处理提高缓存命中率,FLOPS从30%的理论峰值提到了70%。
好了,这一章就讲这些。指标和模型是性能优化的「坐标系」——有了它们,你才知道自己站在哪里,该往哪个方向走。