什么是性能CI
性能CI可简单定义为CI(Continuous Integration)集成性能测试用例。主要用于监控系统运算速度、存储容量或网络I/O是否满足系统设置指标,而非发现故障。
性能测试通常包括:负载测试(Load Test)、压力测试(Stress Test)、容量测试(Volume Test)、峰值测试(Spike Test)等非功能型测试。
性能CI的价值
一方面,Donald Knuth告诉我们“过早的优化是万恶之源”,
We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%. - Computer Programming as an Art(1974) P671.
当然,我并无意挑起性能狂人与设计大师们的战争,但凡有些性能优化经验的同学定能理解其中要义,过早的优化至少会带来如下问题:
- 忽略整体设计
- 陷入局部优化
- 破坏可读性
- 增加偶发复杂度
另一方面,对于遗留系统,发现性能问题往往为时已晚。会带来:
- 定位成本高
- 优化难度大
- 波及影响多
- 测试成本高
所以,我们期望做到:
- 开发人员专注于设计、编码
- 提交代码出现性能问题及时告警
- 可视化性能数据,并可链接到劣化代码
- 提供定位信息协助分析
性能CI面临的主要问题
-
性能用例的有效性不高
简言之,监控的场景不能包含性能劣化路径。表现为内场表现良好,外场表现糟糕;局部性能良好,系统性能糟糕。
-
性能用例部署、执行时间较长
CI要求更早、更快的反馈。性能测试受制于物理环境、仪表资源、采样周期等限制,通常自动化部署困难,用例执行时间长。
-
性能测试数据的稳定性
判决让人信服的前提是准确。由于不同的构建机器、不同的部署环境、不同的用例配置、不同组件间的干扰、性能指示器数据的波动等原因,性能测试数据出很大波动性,导致难以判决劣化是由于合入代码还是波动所致。
-
自动化准确识别劣化点
劣化阈值设置过大可能漏掉某些劣化点,设置过小可能产生误报,准确识别劣化点困难。另外,由于诸多约束,很多判断需要人为干预,完全自动化较困难。
解决思路与方法
通过对性能CI面临主要问题的分析,核心要解决有效性、高效性、稳定性、扩展性四方面的问题。
- 提高性能测试用例的有效性
-
业务价值驱动,识别劣化路径
“程序的性能特质倾向高度的非直觉性”,性能优化靠猜是极其不告谱的。性能劣化路径的识别亦如此,我们需要根据系统在真实部署环境的业务场景,选择用例,确定用例部署优先级。
-
解耦系统内不同组件间的干扰
在一定程度上,可以对复杂系统的各个组件的性能分开验证,可以考虑牺牲一些不关注组件的真实性,换取关注组件的稳定性。
-
自动准确识别性能劣化点并分级告警
自动准确的识别劣化点,是判定性能CI是否有效的关键指标。需要动态找到某个阈值作为置信区间,我们可以参考异常检测方法。常见的异常检测方法有:极值分析,概率统计建模,线性回归模型,信息论模型,高维孤立点检测等。下面介绍一种实测中比较有效实时告警的方法:
Mean(N) ± SD(N),其中N表示样本容量,Mean表示平均值,SD表示标准差。样本容量选取过去一段时间产生的样本,不能选取过去的所有样本,否则置信区间无法收敛。
分级告警是指可以定义实时告警、触顶告警等不同级别告警。
- 实时告警:以过去一段时间的样本作为实时告警模型的输入并生成置信区间,当前数据超出该区间则告警。
- 触顶告警:以某个初始基准值作为参考,设置可以容忍的最大劣化/优化阈值并生成置信区间,当前数据超出该区间则告警。触顶告警一般用于实时告警无法捕捉到的、缓慢增加的劣化的场景,触顶告警一旦触发,预示需要启动专项优化了。
- 提高性能测试用例的高效性
-
测试用例分级部署
分级部署是指将性能测试用例分级部署在单元测试,集成测试,系统测试等不同阶段。用例间相互补充,可以更高效的利用测试资源,减少用例执行时间。
-
测试用例并行部署
如果测试用例部署环境充足,考虑并行部署用例,可以大大缩短用例执行时间,简化用例部署场景。
-
不同构建按不同粒度监控
如果做不到用户每次提交在给定的时间窗内给出执行结果,可以考虑将多次提交打包成一次构建执行,出现告警后打包提交逐单回退验证(需要部署镜像环境用于回退单验证)。
-
寻求慢速性能指标的替代指标
如果在用性能指示器的采样周期较长,在不影响稳定性的前提下,考虑寻求其他更快速性能指标进行替换。性能指标包括:CPU Utilization,Latency,IPC(Or CPI),Cycles,Instructions,CacheMiss.
- 提高性能测试数据的稳定性
-
部分模拟环境代替真实物理环境
真实物理环境相对于模拟环境更脆弱,更不易控制,在分级部署原则的指导下,对于不关注组件,可是考虑使用模拟环境代替真实物理环境。
-
增加系统内其他组件的稳定性
复杂系统是由多个组件组成,系统性能表现为它们共同作用的结果,通过打桩等手段,提高不关注组件的稳定性,有利于提高被监控组件的稳定性。
-
增大样本容量(冗余)
冗余是提高稳定性最常用的方法,挑战在于在样本量增加情况下,如何不增加执行时间。推荐使用Latency,IPC等指标代替CPU使用率。
-
多维度样本参照(相关性)
具有相关性、不同维度的性能数据相互参照,提高判定的准确性。
-
寻求波动性能指标替代指标
系统性越强的性能指示器(例如CPU使用率)波动性越大,考虑使用一些可被拆分、组合的指示器(例如函数时延,Cycles等)替代。
- 提高性能用例的扩展性
-
抽象出框架与API接口
良好的扩展性基于对操作的抽象、良好的分层、稳定的API接口,性能CI对日志数据的操作可以抽象为数据采集、数据处理、数据持久化、数据可视化四层,每层定义清晰的API接口,性能用例仅需要新增配置即可。 -
工作环境与测试环境分离
工作环境负责及时反馈性能劣化点,镜像环境负责对劣化点优化的验证,二者并行工作