在计算机发展的早期阶段,硬件资源相对而言是非常昂贵的,CPU运行时间与内存容量给程序开发人员设置了极大限制。因此,早期的程序对运行性能和内存空间占用的要求是非常严格的,很多开发人员为了减少1%的CPU运行时间,为减少几十个甚至几个字节而不懈努力。随着计算机技术的快速发展,硬件资源变得相对便宜。但如果认为软件开发时,程序的性能优化不再重要,硬件将解决性能问题也是片面的。计算机硬件的发展解决了部分软件的性能问题,但随着硬件计算能力的提高,用户对软件功能的要求也越来越高,软件功能也变得越来越复杂,给用户的界面和操作体验也越来越智能和友好。但复杂的用户需求带来软件性能上的要求是硬件不能完全解决的。众多实际项目经验证明,如果在开发软件时不重视性能优化,最终实现了软件的功能要求,但软件的运行效率低下,最终也不能给用户带来很好的效益。但另一方面,计算机硬件越来越便宜,而优秀的软件开发工程师则越来越昂贵,在软件开发过程中无限制的性能优化同样会导致软件开发过程中人力成本的大幅增加。因此,软件开发过程中的性能优化必须在便宜的计算机硬件和昂贵的优秀工程师之间找到一个平衡点。
应用程序性能优化的流程如下:
(1)性能测量,对于规模较大、较为复杂的软件系统,测量性能数据是进行性能优化的基础。只有获取真实的数据才能分析数据找出系统的性能瓶颈。
(2)分析数据,找到系统的性能瓶颈。性能瓶颈必须建立在客观真实的性能数据基础上,不能是主观臆测的。
(3)分析原因,修改程序,是程序性能优化的核心。程序的性能包括启动速度、运行速度、运行时占用内存等。影响程序性能的因素主要分为两类:
(1)软件编程设计因素:算法和数据结构的选择,编程语言的使用。
(2)软件系统结构因素:动态库、静态库的组织,外部数据的存储以及网络环境等。
软件编程设计因素是对软件性能影响较大的因素,只有对算法、数据结构、编程语言有深入的了解才能分析出原因,并且找到解决性能问题的方法。
软件系统结构因素通常与操作系统紧密相关。对于现代软件,由于功能复杂,通常采用组件形式,以最大限度的提高可复用性。因此,一般会包含一些动态库、静态库,库文件的组织也会影响到软件系统的性能。
应用程序的性能指标通常是多维的,比如响应时间、并发量等。对于桌面应用程序,其服务对象通常为终端用户。因此,桌面应用程序最重要的性能指标是响应时间,即针对某一个具体的操作,用户从发出命令到应用程序完成任务并响应用户的时间,响应时间越短越好。
除了响应时间,内存使用也是桌面应用程序的重要指标之一。内存使用包括进程工作集(任务管理器看到的内存使用)和虚拟内存使用两个指标,越小越好。如果一个应用程序占用内存过高,会影响其它正在运行的应用程序的响应时间。
根据可用性设计,桌面应用程序的设计原则如下:
(1)小于0.1秒的响应时间,用户感觉是即时的。
(2)小于1秒的响应时间,用户感觉是可接受的。
(3)大于1秒的操作应该有一个简单标示(如鼠标变成沙漏)。
(4)大于10秒的操作应该有明显的提示(如进度条)。
桌面应用程序的性能指标包括响应时间和内存使用,但响应时间和内存使用指标通常针对单个操作。现代软件系统通常包括多项功能,例如一个文字处理软件能够提供的功能不下数百种,每种功能作用在不同类型和大小的文档上会表现出不同的性能,性能基准就是用于定义程序的总体性能的。
性能基准(Performance Benchmark)是用来衡量应用程序整体性能的一套体系,通过为应用程序输入预先设计好的工作负载,运行一批基准用例,运行结果可以反映应用程序在通常情况下的性能。因此,性能基准=基准负载+基准用例。
(1)基准负载
对于桌面应用程序,运行性能基准时需要的基准负载通常表现为一系列基准文件。基准文件应该是具有典型大小和典型内容的文件,而基准文件选取的优劣直接影响性能基准的准确性。
对于通用文字处理软件,主要功能是创建、打开文档,修改并保存文档,支持的文档类型包括.doc,.dot,.odt,.ott,.txt,.lwp等,支持文字、图片、文本框、表格、图形等。设计基准文件时,从文档类型考虑,在兼顾到主要的文档类型又要排除类似的文档类型;从文档内容考虑,需要覆盖用户最常用的内容对象类型和文档大小,具体基准文件列表如下:
对于同一种文档类型,每一种文档类型包含两个基准文件,分别有不同的文档内容。
(2)基准用例
基准用例是性能基准测试时需要执行的一系列用例。基准用例的选择有一定原则,既要尽可能全面地覆盖应用程序的主要功能,又不能像功能测试用例那样复杂,因此,基准用例应该是用户日常操作经常遇到的情形。
不同的基准用例在性能基准中的地位并不相同,每一个基准用例都需要一个权值来表明它对整体性能基准的贡献度。权值的定义依据具体情况各有不同,一个比较实用的定义公式如下:
权值=用例频率X用例重要性
用例频率是用户一定时间内执行该用例的平均次数。理想的用例频率应该通过用户行为数据反馈获得。例如通用文字处理软件的用户一天内可能会执行“打开文档”用例5次,执行“保存文档”的用例15次。
用例重要性是一个修正系数,反映用例没有完成前对用户工作的影响程度。文字处理软件打开一个文档时有“异步打开”的功能,即程序会首先读入文档的部分内容并显示给用户,然后在后台继续读入文档的后续内容。对于“异步打开”功能可以定义两个用例,“第一页显示”(从用户选择打开文档到文档的第一页显示出来的过程)和“全部读入完毕”(从用户选择打开文档到文档的所有页的内容已经加载完毕的过程)。“第一页显示”用例的重要性为1,表示不执行完本用例,用户不能继续工作;“全部读入完毕”用例的重要性为0.5,表示本用例不会显著影响用户的工作,文档在后台加载,用户前台已经可以编辑,除非用户需要编辑的内容还没有加载出来。
文字处理程序的部分基准用例如下:
相同的操作步骤操作不同类型或不同内容的基准文件会形成不同的基准用例。
准备好基准文件和基准用例后就可以运行性能基准并得出基准结果。
为了保证性能基准运行的准确性,性能基准的测试环境必须满足一定要求。例如保证固定的基准测试平台(软件和硬件平台不变),尽可能排除其它应用程序对目标应用层程序的影响。基准测试平台也可以有同时运行在多种硬件平台上的考量:运行在4G内存和8G内存时应用程序的性能表现的差别;运行在三年前硬件配置和当前主流硬件配置的性能表现的差别。
运行基准测试的过程是在固定的基准测试环境中针对基准文件顺序执行一系列基准用例并记录下每个用例结果的过程,执行过程分为手动执行和自动执行,结果记录也可以分为手动记录和自动记录。
通常手工执行和记录是基础,最能反映最终用户的体验。
自动运行和记录是目标,可以大幅提升工作效率,并排除人工不稳定的结果。但自动运行的脚本必须保证多次自动运行结果的稳定,保证自动运行结果和手动运行结果的可比较。
性能基准运行的过程需要注意:
(1)每一个用例需要运行多次求平均值,如需要去除最大值、最小值,然后取平均值。
(2)多个用例执行的先后顺序必须固定,否则很难得到稳定的性能基准结果。
性能基准运行结果的原始数据是一系列的绝对数值,可以根据不同的需要生成不同的报告,如:
整体性能水平分值:每个用例绝对值结合每个用例的权值,可以给整体性能水平打分。
性能变化趋势图:历史上不同版本的整体性能分值曲线可以体现出性能变化趋势。
关键性能指标图表:给用户演示的重点用例的结果图表。
产品性能对比图:和其它产品的性能对比图,包括绝对值的对比和加权后分值的对比。
文字处理程序的部分基准用例运行结果数据如下:
性能基准可以反映应用程序的总体性能,定义良好的性能基准用途如下:
(1)应用程序性能的绝对指标。任何想要了解产品性能的人,无论是管理层还是客户,都可以通过产品性能报告了解产品的性能。
(2)通过比较不同版本的基准结果,提前发现性能下降的问题和验证性能提升的设计结果。软件开发过程中通常都会进行每日构建,性能基准也可以在每日构建的基础上每日运行,及时发现性能问题,而不是在产品即将发布时进行性能优化。
(3)比较不同厂商的类似软件的性能。横向的比较需要性能基准,可以找出自己软件产品的性能薄弱环节,集中力量进行优化。
拥有定义良好的性能基准后,可以轻易发现应用程序存在的性能问题。发现性能问题后需要对性能问题进行分析,程序的性能分析过程包括:性能问题分类、查找性能瓶颈、进行性能优化。
一个操作执行太慢,需要首先分类是IO操作密集引起的问题还是CPU相关的计算密集型问题。正确的分类将直接影响进一步的问题分析。
区别IO相关还是CPU相关问题的简单方法是隔离IO影响后,看性能是否得到改善,例如同时在机械硬盘和SSD硬盘上测试,如果性能显著提高,则是IO相关的问题。
对于文字处理软件,冷启动需要10.5秒,热启动需要2.1秒,因此冷启动的主要问题在IO。无论是冷启动还是热启动,应用程序都是完全退出后再重新启动,执行的代码流程完全一样,唯一区别在于IO:冷启动后操作系统会缓存很多动态库的代码页在内存。
对性能问题分类后,可以使用性能分析工具在代码层次查找性能瓶颈,性能分析工具有监测工具和注入工具两类。
监测工具如下:
perfmon,Windows工具,可以监测所有的性能指标。
FileMon,Windows工具,监测IO操作。
ProcessExplorer,Windows工具,监测进程相关的所有操作。
sysstat,Linux工具,监测所有的性能指标。
iostat,Linux工具,监测IO操作。
vmstat,Linux工具,监测内存变化。
注入工具如下:
IBM rational quantify,Windows工具,针对C++应用程序代码注入,可以计算函数调用次数、时间等。
Valgrind,Linux工具,针对C++应用程序代码注入,可以计算函数调用次数、事件、内存分配、内存泄漏检测等。
IBM rational purify,Windows工具,针对C++应用程序代码注入,可以进行内存分析。
WinDbg,Windows工具,调试工具。
GDB,Linux工具,调试工具。
Dependency walker,Windows工具,分析动态链接库之间的动态、静态依赖关系。
ldd,Linux工具,分析共享对象间的依赖关系。
代码层次的性能优化设计的改动通常局限在有限的函数调用内,相对比较容易完成。进一步的性能提升的机会需要在设计层次进行查找。设计层面的性能分析需要性能优化者对软件的整体架构有比较深入的了解,需要具体问题具体分析。
性能问题分析完成后,需要进行性能优化。根据性能分析结果的不同,优化方法也各有不同。
每次IO操作大概在10ms量级,100次就需要1秒左右,因此尽量避免不必要的IO操作。具体做法如下:
(1)预先顺序读文件避免随机访问。
(2)合并多个小文件为单个大文件。
(3)优化动态库文件的加载。
(4)交错IO时间和CPU时间。
计算密集的性能问题主要有内存分配性能、字符串操作、共享变量的互斥锁保护等,具体优化方法如下:
(1)去除冗余代码。
(2)字符串操作优化。
(3)减少内存分配、释放操作,例如使用内存池。
(4)减少不必要的互斥锁操作。
(5)根据性能需求选择数据结构。
(6)延迟工作,按需执行。
(7)减少跨进程的调用。
(8)使用高性能的函数库。
C++语言特性相关的性能优化包括内联函数、引用、编译优化选项等。
有些设计不能真正提升性能,但让用户体验到了性能提升。如:
(1)流式播放设计,用户不需要等到视频文件下载完成再播放,可以边下载边播放。
(2)线程化设计,对于需要较长时间完成的操作,可以设计为非阻塞式的,用户可以在等待时间完成其它操作任务。
设计层面的性能优化需要根据软件整体架构具体问题具体分析。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。