如何做性能优化
- 确定优化目标
- 定位性能瓶颈
- 制定优化方法
- 测试优化效果
性能优化目标是什么
- 吞吐量,越大越好
- 延时,越低越好
同样的资源下(前提),吞吐量越高越好,响应时间越低越好。通俗的讲就是:多快好省。
如何定位系统性能的瓶颈呢
- 资源分析
- 代码分析
资源分析
资源分析以对系统资源的分析为起点,涉及的系统资源有:CPU,内存,磁盘,网卡。通过对资源使用率的分析,判断某项资源是否已经处于或接近极限。
常用的分析工具有:top,free,vmstat,iostat,iotop,netstat。更多工具见下图:
通常资源使用率超过 60% 可能会是问题,为什么呢?因为时间间隔的均值,掩盖了使用率 100% 的短期爆发。
定位瓶颈之后,可以通过增加资源来提升性能,也可以通过优化系统减少资源占用来优化性能。
代码分析
根据2:8原则来说,20%的代码消耗了80%的性能,找到那20%的代码,你就可以优化那80%的性能。
通常使用 Profiler 工具,来收集程序运行时的信息。如果 CPU 是瓶颈,寻找那些消耗 CPU 最多的代码去优化。如果内存是瓶颈,则优化消耗内存最多的代码。
常用的 Profiler,有 Linux 的 OProfile/perf,Java 的 JProfiler/JMC。
常见的代码性能优化方法
- Buffer:缓解应用系统上下层组件之间的性能差异,比如通常的 IO Buffer。
- Cache:通常用于读多写少的情况,也是用来缓解应用系统上下层组件之间的性能差异。比如,应用 local cache,Memcached/Redis。
- Batch:网络或磁盘里的批量操作,通过合并小任务或小请求为大任务大请求,来提升数据传输效率低。比如 TCP 中的 nagle 算法,就是通过合并多个小分组来提升网络传输效率的。
- Pool:通过减少对象创建,连接建立的开销来提升性能。比如线程池,连接池,对象池。
- Concurrency:通过将串行改为并行,能过有效的降低系统延时。在多核系统中,并行处理才能提升 CPU 使用率。
- Lock Less:多线程访问共享资源时,通常需要加锁。重量级的锁往往引起线程切换,而线程切换非常耗时。那么可以通过优化锁的使用来提升性能,优化锁有两种方法:
- 通过减小锁的粒度,分离竞争点来减少竞争。比如 Java 里 HashTable 中锁的粒度是整个对象,ConcurrentHashMap 中锁的粒度只是一个 Segment。
- 在竞争较少的情况下,使用轻量级锁来代替重量级锁,减少线程切换带来的性能消耗,比如自旋锁代替互斥锁。
如何做性能基准测试
什么是有效的性能基准测试呢?
基准测试是指通过设计科学的测试方法、测试工具和测试系统,实现对一类测试对象的某项性能指标进行定量的和可对比的测试。有以下特性:
1. 可重复:就像科学实验一样,能够被他人重复,才能被认可
2. 可观测:测试结果能过被分析和理解
3. 可对比:通过对比,才能知道性能优化的效果
3. 符合现实:测量结果符合现实情况
4. 易执行:开发人员能快速地修改系统并测试
基准测试有三种类型:微基准测试,模拟,回放。从下图可以看出来,回放最接近生产环境。
微基准测试利用人造的工作负载对某类特定的操作做测试,例如执行一种类型文件系统I/O,数据库查询,系统调用。其优势是简单:
1. 对组件的数量和所牵扯的代码路径做限制,能更容易地研究目标,从而快速地确定性能差异的根源。
2. 因为来自其他组件的变化以及尽可能的被玻璃了,所以测试通常是可重复的。
许多基准测试会模拟客户应用程序的工作负载,基于生产环境的工作负载特征,来决定所要模拟的特征。模拟所生成的结果与客户在真实世界所执行的工作负载是相似的。相较于微基准测试。模拟能覆盖复杂系统相互作用的影响,这点是微基准测试可能缺失的。
回放:用真实捕捉到的客户机的操作,来测试性能。有很多分布式服务框架带有复制线上流量,引流到测试机器的功能。
做性能测试时,要多去思考性能数据背后的根本原因,避免因各种外界因素的干扰导致测试失真。
参考资料:
《性能之巅:洞悉系统,企业与云计算》
Linux Performance