排查到性能瓶颈之后,就需要做优化。当然优化本身就需要具体问题具体分析,所以没办法一一列举。只能给出思路和方法论。
那么在性能优化之前,需要来个提问三板斧:
- 如何判断优化是否有效?
-
- 确定优化前后的性能基准(如响应时间、吞吐量、资源利用率等)。
- 比较优化后性能提升的量化指标。
- 多个性能问题,先优化哪一个?
-
- 优先解决对系统影响最大的瓶颈(如 CPU、I/O、内存等)。
- 聚焦关键路径,找出最主要的性能阻碍。
- 多种优化方法,如何选择?
-
- 不一定总选最大提升的方法,需综合考虑:
-
-
- 投入与收益:成本是否合理。
- 实施难度:可操作性和风险。
- 长期效果:对系统稳定性和维护性的影响。
-
对于这三个问题没有疑问,就可以进行优化,否则需要对这三个进一步做深入分析。
先看第一个问题,如何判断优化是否有效?
- 确定基准指标(Baseline Metrics)
- 优化前收集性能基准数据,作为对比依据。
- 常见指标:
-
- 响应时间:平均、P95、P99 等。
- 吞吐量:每秒处理的请求数或事务数。
- 资源利用率:CPU、内存、磁盘 I/O、网络带宽等。
- 监控优化前后的变化
- 对比优化前后的指标变化,关注是否有明显提升。
- 使用图表或数据对比工具展示关键性能指标的差异。
- 关注用户体验
- 收集用户反馈,确认优化是否改善了实际使用体验。
- 例如:页面加载时间、操作响应速度是否变快。
多个性能问题同时存在,要怎么选择?
这个问题可以借助比较有名的“二八原则”,也就是说 80% 的问题都是由 20% 的代码导致的。只要找出这 20% 的位置,你就可以优化 80% 的性能。
有多种优化方法时,要如何选择?
这个问题可以从成本考虑,因为性能优化本身是有成本,在多个方法中,在能满足性能优化目标的同时选择成本较低的即可。
最后,我们细化CPU优化工作,从两个角度出发:
应用程序优化
- 排除不必要的工作:
-
- 减少循环层次、递归和动态内存分配。
- 优化方法:
-
- 编译器优化:开启优化选项(如
gcc -O2)。 - 算法优化:选择复杂度更低的算法(如快速排序替代冒泡排序)。
- 异步处理:用事件通知替代轮询,提升并发能力。
- 多线程替代多进程:降低上下文切换开销。
- 善用缓存:缓存常用数据或中间结果,加速访问。
- 编译器优化:开启优化选项(如
系统优化
- 利用 CPU 缓存:
-
- CPU 绑定:绑定进程到特定 CPU,提高缓存命中率。
- CPU 独占:分组并分配特定进程独占 CPU。
- 进程管理:
-
- 优先级调整:通过
nice优化进程优先级分配。 - 资源限制:用
cgroups设置 CPU 使用上限。
- 优先级调整:通过
- NUMA 优化:
-
- 优化本地内存访问,减少跨节点访问。
- 中断负载均衡:
-
- 开启
irqbalance或配置smp_affinity,均衡中断处理负载。
- 开启
最后,避免过早优化。高德纳说过,“过早优化是万恶之源”。
附录:
评论区看到了一段话,也改正了一些之前的理解:
- pidstat 中, %wait 表示进程等待 CPU 的时间百分比。此时进程是运行状态。
- top 中 ,iowait% 则表示等待 I/O 的 CPU 时间百分比。此时进程处于不可中断睡眠态。
- 等待 CPU 的进程已经在 CPU 的就绪队列中,处于运行状态;而等待 I/O 的进程则处于不可中断状态。