一 绪论
性能是一个庞大的话题,它涵盖整个系统的软硬件和数据传输过程中遇到的种种事件。通常,性能分析要做以下事情(这是理想的执行顺序,实际运用可能会缺斤少两):
- 设置性能目标和建立性能模型
- 基于软件或硬件原型进行性能特征归纳
- 对开发代码进行性能分析(软件整合之前)
- 执行软件非回归性测试(软件发布前或者后)
- 针对软件发布版本的基准测试
- 目标环境中的的概念验证(Proof-of-concept)测试
- 生产环境部署的配置优化
- 监控生产环境中运行的软件
- 特定问题的性能分析
值得注意的是,步骤 6 是分水岭, 1~5 是产品开发过程中要做的事, 6~9 是产品发布后要做的事。随着软件开发工作的进行,修复性能问题的难度会变得越来越大,性能分析要趁早,比如在硬件造型和软件开发之前,就可以完成步骤 1 。
性能问题
性能分析是主观的,很多指标都没有好坏之分,比如:
磁盘的平均 I/O 响应时间是 1ms
有人说好,有人说坏,解决办法是,与开发人员和用户达成协议,将主观的指标客观化!
复杂的软件会存在多个性能问题,不可能对每个性能问题都进行优化。于是,需要对问题的重要程度进行量化,并且估算出问题修复后,可以增速多少,有一个指标适合量化性能问题:延时( latency )。
延时表示所有操作完成的耗时,比如一次数据库查询耗时 10 s。延时也可以估计最大增速,比如数据库查询需要 100 ms ,其中 80 ms 等待磁盘读取,通过减少磁盘数据的读取时间(如使用缓存)可达到 5 倍速提升。
动态跟踪
动态跟踪技术可以监控所有软件,可以用在真实的生产环境中!很多问题在测试环境中不会发现,但到了生产环境错误百出,通常解决手段是将机器拉下线,然后分析问题,但脱离了线上真实环境,没有真实流量,问题太难复现!动态跟踪使用内存中的 CPU 指令构建监测数据,这让它可以监控任何运行软件。常见的工具是 DTrace (以 Solaris , MAC 为主),System Tap( Linux 为主) 。
模型
有两种常见的模型,它们阐述了系统性能的基本原则。
1.受测系统
受测系统(SUT,system under test)如下图:
其中的扰动( perturbation )是干扰的意思,比如定时执行的系统活动,其他用户的工作负载。
2.排队系统
很多组件和资源调度都会用到排除系统,后面会详说:
二 概念
延时
延时是操作之前花费的时间。比如网络服务的传输数据请求,在发送请求之前 ,必须等待网络连接的建立 ,这就是延时:
延时可以衡量性能问题,由于延时的单位是时间,适合进行比较,尽量把其他指标转换成为延时或时间。比如 100 个网络 I/O 和 50 个磁盘 I/O 哪个性能更好?由于包含了很多元素:网络跳数,网络丢包率, I/O 大小,磁盘类型等等,很难得出结果,但是,如果比较 100ms 的网络 I/O 延时和 50ms 的磁盘 I/O 延时,差别一目了然。下表列出常见的时间换算:
时间量级
系统不同组件的延时不同,而且相差甚大,将时间等比例放大,假设 1 个 CPU 周期为 0.3 ns ,将之等比例放大为 1 s ,请看下面对比图:
权衡三角
存在着某些性能权衡三角关系,比如性能,及时,成本低三者选其二,很多公司会选择及时和成本低,这种决定会让后期寸步难行。
调整的影响
性能调整越接近工作执行的方法,效果就越明显,比如数据库请求有 20s 延时,是否可以在程序中去掉数据库请求!
许多环境都力争快速部署,留给性能测量和优化的空间非常小,只能从应用程序调优,但这个层次观测效果不一定最显著,比如数据库查询缓慢,可以从花费的 CPU 时间,文件系统和执行的 I/O 方面来考察原因。
合适的层级
不同的公司和环境对于性能要求不同,有的公司甚至不进行性能分析,这不代码他们是错的,这取决于技术投入的投资回报率(ROI),比如大型数据中心和云环境公司非常需要性能工程师来分析所有事情,再比如股票交易所对延时非常敏感,从纽约交易所到伦敦交易所的光缆耗资 3 亿美金,用以减少 6ms 的延时。
负载和架构
应用程序性能差可能由于太多负载,导致排队和长延时,也有可能因为软件 配置和硬件问题(架构问题)。
- 架构问题:单线程应用只在单个 CPU 上忙碌,从而导致排队
- 负载问题:请求过多,导致多线程程序在所有的 CPU 上都忙碌
常见指标
- IOPS:每秒的 I/O 操作次数
- 吞吐量: 每秒数据量或者操作量
- 使用率:
- 延时
IOPS 也是吞吐量,它是指 I/O 操作的吞吐量,也存在其他吞吐量,比如网络吞吐量度量每秒比特数目。这些指标不是免费的,监控这些指标会消耗 CPU 周期,称之为观察者效应( observer effect )。
使用率
描述设备的使用情况,有两种计算方式。
1.基于时间
基于时间的使用率公式如下:
U = B/T
- U:使用率
- B:T时间内系统的繁忙时间
- T:观测周期
当达到 100% 使用率时,性能不一定会下降,比如电梯不停的在各楼层移动,但只有一个人在里面,虽然电梯的使用率是 100% ,但它还可以容纳更多的人。
2.基于容量
使用率的计算基于容量,与基于时间的使用率不同, 100% 使用率(基于容量)的磁盘,无法接收更多的工作。 100% 容量的电梯乘满乘客,无法加入新的人。
后面的使用率默认是基于时间。基于容量的使用率会用别的指标代替,比如内存使用情况等。
饱和度
当使用率(基于容量)达到 100% 时,多出的工作无法处理,会出现排队。超过资源所能处理的程度叫做饱和度。
剖析
按照特定的时间间隔对系统的状态进行采样 ,然后研究这些样本。比如对 CPU 程序计数器进行采样,以一定的频率间隔进行栈回溯跟踪还收集消耗 CPU 资源的代码路径的统计数据。
缓存
将较慢的存储层内容存储在较快的存储层中,比如 CPU 与内存的存在速度差异(可观察上面的时间量级表格),于是 CPU 利用三级缓存( L1, L2 和 L3 )临时存放关键数据,当 CPU 与 内存数据交互时,会优先选用三级缓存, L1 小而快,后续的 L2 和 L3 逐渐大而慢。缓存存在两个指标:
1.命中率
缓存的命中率即所需的数据在缓存中找到的次数,计算公式如下:
命中率 = 命中次数/(命中次数 + 失效次数)
k = c1/( c1 + c2 )
命中率越高越好,但请注意,性能与命中率的关系不是线性增长,它是曲线上升,比如命中率从 98% 提升到 99% 是质的飞越,但是从 10% 提升到 11% ,效果并不会很显著。
之所以出现曲线,是因为缓存命中时,在缓存上进行数据处理,处理速度快,但是缓存失效时,处理速度会变慢,两个存储层级速度差异越大,曲线越陡峭,假定工作是串行。
有缓存算法决定在缓存中存放哪此数据:
- 最近最常使用算法(MRU):把最近使用次数最多的数据保留在缓存里
- 最近最少使用算法(LRU): 把最近不常用的数据移出缓存
- 最常使用算法(MFU)
- 最不常使用算法(LFU)
- 不常使用算法(NFU)
缓存有多种状态,比如:
- 冷:缓存为空或者无用数据,命中率为 0 ,或者接近 0
- 热:缓存中都是常用使用,命中率很高,比如超过 99%
- 温:命中率一般
- 热度:缓存的冷热程度,类似命中率,热度高,命中率就高
缓存变温需要时间,缓存初始化后是冷的,过一段时间后才会变温,注意!这个变化过程可能 很久,因为有的缓存非常大,一级缓存可以达到 100GB 以上。
建模
建立分析模型可用于扩展性分析:当负载或者资源扩展时,性能会如何变化 。