C++应用程序性能优化(一)——应用程序性能优化简介

C++应用程序性能优化(一)——应用程序性能优化简介

一、程序性能优化简介

1、程序性能优化简介

在计算机发展的早期阶段,硬件资源相对而言是非常昂贵的,CPU运行时间与内存容量给程序开发人员设置了极大限制。因此,早期的程序对运行性能和内存空间占用的要求是非常严格的,很多开发人员为了减少1%的CPU运行时间,为减少几十个甚至几个字节而不懈努力。随着计算机技术的快速发展,硬件资源变得相对便宜。但如果认为软件开发时,程序的性能优化不再重要,硬件将解决性能问题也是片面的。计算机硬件的发展解决了部分软件的性能问题,但随着硬件计算能力的提高,用户对软件功能的要求也越来越高,软件功能也变得越来越复杂,给用户的界面和操作体验也越来越智能和友好。但复杂的用户需求带来软件性能上的要求是硬件不能完全解决的。众多实际项目经验证明,如果在开发软件时不重视性能优化,最终实现了软件的功能要求,但软件的运行效率低下,最终也不能给用户带来很好的效益。但另一方面,计算机硬件越来越便宜,而优秀的软件开发工程师则越来越昂贵,在软件开发过程中无限制的性能优化同样会导致软件开发过程中人力成本的大幅增加。因此,软件开发过程中的性能优化必须在便宜的计算机硬件和昂贵的优秀工程师之间找到一个平衡点。

2、程序性能优化的流程

应用程序性能优化的流程如下:

(1)性能测量,对于规模较大、较为复杂的软件系统,测量性能数据是进行性能优化的基础。只有获取真实的数据才能分析数据找出系统的性能瓶颈。
(2)分析数据,找到系统的性能瓶颈。性能瓶颈必须建立在客观真实的性能数据基础上,不能是主观臆测的。
(3)分析原因,修改程序,是程序性能优化的核心。程序的性能包括启动速度、运行速度、运行时占用内存等。影响程序性能的因素主要分为两类:
(1)软件编程设计因素:算法和数据结构的选择,编程语言的使用。
(2)软件系统结构因素:动态库、静态库的组织,外部数据的存储以及网络环境等。
软件编程设计因素是对软件性能影响较大的因素,只有对算法、数据结构、编程语言有深入的了解才能分析出原因,并且找到解决性能问题的方法。
软件系统结构因素通常与操作系统紧密相关。对于现代软件,由于功能复杂,通常采用组件形式,以最大限度的提高可复用性。因此,一般会包含一些动态库、静态库,库文件的组织也会影响到软件系统的性能。

二、程序性能的定义

1、性能指标定义

应用程序的性能指标通常是多维的,比如响应时间、并发量等。对于桌面应用程序,其服务对象通常为终端用户。因此,桌面应用程序最重要的性能指标是响应时间,即针对某一个具体的操作,用户从发出命令到应用程序完成任务并响应用户的时间,响应时间越短越好。
除了响应时间,内存使用也是桌面应用程序的重要指标之一。内存使用包括进程工作集(任务管理器看到的内存使用)和虚拟内存使用两个指标,越小越好。如果一个应用程序占用内存过高,会影响其它正在运行的应用程序的响应时间。
根据可用性设计,桌面应用程序的设计原则如下:
(1)小于0.1秒的响应时间,用户感觉是即时的。
(2)小于1秒的响应时间,用户感觉是可接受的。
(3)大于1秒的操作应该有一个简单标示(如鼠标变成沙漏)。
(4)大于10秒的操作应该有明显的提示(如进度条)。

2、性能基准

桌面应用程序的性能指标包括响应时间和内存使用,但响应时间和内存使用指标通常针对单个操作。现代软件系统通常包括多项功能,例如一个文字处理软件能够提供的功能不下数百种,每种功能作用在不同类型和大小的文档上会表现出不同的性能,性能基准就是用于定义程序的总体性能的。
性能基准(Performance Benchmark)是用来衡量应用程序整体性能的一套体系,通过为应用程序输入预先设计好的工作负载,运行一批基准用例,运行结果可以反映应用程序在通常情况下的性能。因此,性能基准=基准负载+基准用例。
(1)基准负载
对于桌面应用程序,运行性能基准时需要的基准负载通常表现为一系列基准文件。基准文件应该是具有典型大小和典型内容的文件,而基准文件选取的优劣直接影响性能基准的准确性。
对于通用文字处理软件,主要功能是创建、打开文档,修改并保存文档,支持的文档类型包括.doc,.dot,.odt,.ott,.txt,.lwp等,支持文字、图片、文本框、表格、图形等。设计基准文件时,从文档类型考虑,在兼顾到主要的文档类型又要排除类似的文档类型;从文档内容考虑,需要覆盖用户最常用的内容对象类型和文档大小,具体基准文件列表如下:

对于同一种文档类型,每一种文档类型包含两个基准文件,分别有不同的文档内容。
(2)基准用例
基准用例是性能基准测试时需要执行的一系列用例。基准用例的选择有一定原则,既要尽可能全面地覆盖应用程序的主要功能,又不能像功能测试用例那样复杂,因此,基准用例应该是用户日常操作经常遇到的情形。
不同的基准用例在性能基准中的地位并不相同,每一个基准用例都需要一个权值来表明它对整体性能基准的贡献度。权值的定义依据具体情况各有不同,一个比较实用的定义公式如下:
权值=用例频率X用例重要性
用例频率是用户一定时间内执行该用例的平均次数。理想的用例频率应该通过用户行为数据反馈获得。例如通用文字处理软件的用户一天内可能会执行“打开文档”用例5次,执行“保存文档”的用例15次。
用例重要性是一个修正系数,反映用例没有完成前对用户工作的影响程度。文字处理软件打开一个文档时有“异步打开”的功能,即程序会首先读入文档的部分内容并显示给用户,然后在后台继续读入文档的后续内容。对于“异步打开”功能可以定义两个用例,“第一页显示”(从用户选择打开文档到文档的第一页显示出来的过程)和“全部读入完毕”(从用户选择打开文档到文档的所有页的内容已经加载完毕的过程)。“第一页显示”用例的重要性为1,表示不执行完本用例,用户不能继续工作;“全部读入完毕”用例的重要性为0.5,表示本用例不会显著影响用户的工作,文档在后台加载,用户前台已经可以编辑,除非用户需要编辑的内容还没有加载出来。
文字处理程序的部分基准用例如下:

相同的操作步骤操作不同类型或不同内容的基准文件会形成不同的基准用例。

3、性能基准的运行

准备好基准文件和基准用例后就可以运行性能基准并得出基准结果。
为了保证性能基准运行的准确性,性能基准的测试环境必须满足一定要求。例如保证固定的基准测试平台(软件和硬件平台不变),尽可能排除其它应用程序对目标应用层程序的影响。基准测试平台也可以有同时运行在多种硬件平台上的考量:运行在4G内存和8G内存时应用程序的性能表现的差别;运行在三年前硬件配置和当前主流硬件配置的性能表现的差别。
运行基准测试的过程是在固定的基准测试环境中针对基准文件顺序执行一系列基准用例并记录下每个用例结果的过程,执行过程分为手动执行和自动执行,结果记录也可以分为手动记录和自动记录。
通常手工执行和记录是基础,最能反映最终用户的体验。
自动运行和记录是目标,可以大幅提升工作效率,并排除人工不稳定的结果。但自动运行的脚本必须保证多次自动运行结果的稳定,保证自动运行结果和手动运行结果的可比较。
性能基准运行的过程需要注意:
(1)每一个用例需要运行多次求平均值,如需要去除最大值、最小值,然后取平均值。
(2)多个用例执行的先后顺序必须固定,否则很难得到稳定的性能基准结果。

4、性能基准结果

性能基准运行结果的原始数据是一系列的绝对数值,可以根据不同的需要生成不同的报告,如:
整体性能水平分值:每个用例绝对值结合每个用例的权值,可以给整体性能水平打分。
性能变化趋势图:历史上不同版本的整体性能分值曲线可以体现出性能变化趋势。
关键性能指标图表:给用户演示的重点用例的结果图表。
产品性能对比图:和其它产品的性能对比图,包括绝对值的对比和加权后分值的对比。
文字处理程序的部分基准用例运行结果数据如下:

性能基准可以反映应用程序的总体性能,定义良好的性能基准用途如下:
(1)应用程序性能的绝对指标。任何想要了解产品性能的人,无论是管理层还是客户,都可以通过产品性能报告了解产品的性能。
(2)通过比较不同版本的基准结果,提前发现性能下降的问题和验证性能提升的设计结果。软件开发过程中通常都会进行每日构建,性能基准也可以在每日构建的基础上每日运行,及时发现性能问题,而不是在产品即将发布时进行性能优化。
(3)比较不同厂商的类似软件的性能。横向的比较需要性能基准,可以找出自己软件产品的性能薄弱环节,集中力量进行优化。

三、程序性能分析方法

1、性能分析方法简介

拥有定义良好的性能基准后,可以轻易发现应用程序存在的性能问题。发现性能问题后需要对性能问题进行分析,程序的性能分析过程包括:性能问题分类、查找性能瓶颈、进行性能优化。

2、性能问题分类

一个操作执行太慢,需要首先分类是IO操作密集引起的问题还是CPU相关的计算密集型问题。正确的分类将直接影响进一步的问题分析。
区别IO相关还是CPU相关问题的简单方法是隔离IO影响后,看性能是否得到改善,例如同时在机械硬盘和SSD硬盘上测试,如果性能显著提高,则是IO相关的问题。
对于文字处理软件,冷启动需要10.5秒,热启动需要2.1秒,因此冷启动的主要问题在IO。无论是冷启动还是热启动,应用程序都是完全退出后再重新启动,执行的代码流程完全一样,唯一区别在于IO:冷启动后操作系统会缓存很多动态库的代码页在内存。

3、查找性能瓶颈

对性能问题分类后,可以使用性能分析工具在代码层次查找性能瓶颈,性能分析工具有监测工具和注入工具两类。
监测工具如下:
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工具,分析共享对象间的依赖关系。

4、查找性能优化机会

代码层次的性能优化设计的改动通常局限在有限的函数调用内,相对比较容易完成。进一步的性能提升的机会需要在设计层次进行查找。设计层面的性能分析需要性能优化者对软件的整体架构有比较深入的了解,需要具体问题具体分析。

四、程序性能优化方法

性能问题分析完成后,需要进行性能优化。根据性能分析结果的不同,优化方法也各有不同。

1、针对IO瓶颈的性能优化

每次IO操作大概在10ms量级,100次就需要1秒左右,因此尽量避免不必要的IO操作。具体做法如下:
(1)预先顺序读文件避免随机访问。
(2)合并多个小文件为单个大文件。
(3)优化动态库文件的加载。
(4)交错IO时间和CPU时间。

2、针对计算密集的性优化

计算密集的性能问题主要有内存分配性能、字符串操作、共享变量的互斥锁保护等,具体优化方法如下:
(1)去除冗余代码。
(2)字符串操作优化。
(3)减少内存分配、释放操作,例如使用内存池。
(4)减少不必要的互斥锁操作。
(5)根据性能需求选择数据结构。
(6)延迟工作,按需执行。
(7)减少跨进程的调用。
(8)使用高性能的函数库。

3、C++语言特性相关的性能优化

C++语言特性相关的性能优化包括内联函数、引用、编译优化选项等。

4、用户体验的性能优化

有些设计不能真正提升性能,但让用户体验到了性能提升。如:
(1)流式播放设计,用户不需要等到视频文件下载完成再播放,可以边下载边播放。
(2)线程化设计,对于需要较长时间完成的操作,可以设计为非阻塞式的,用户可以在等待时间完成其它操作任务。

5、设计层面的性能优化

设计层面的性能优化需要根据软件整体架构具体问题具体分析。

原文地址:https://blog.51cto.com/9291927/2388608

时间: 2024-08-26 14:44:13

C++应用程序性能优化(一)——应用程序性能优化简介的相关文章

《Java程序性能优化》学习笔记 Ⅰ设计优化

豆瓣读书:http://book.douban.com/subject/19969386/ 第一章 Java性能调优概述 1.性能的参考指标 执行时间: CPU时间: 内存分配: 磁盘吞吐量: 网络吞吐量: 响应时间: 2.木桶定律   系统的最终性能取决于系统中性能表现最差的组件,例如window系统内置的评分就是选取最低分.可能成为系统瓶颈的计算资源如,磁盘I/O,异常,数据库,锁竞争,内存等. 性能优化的几个方面,如设计优化,Java程序优化,并行程序开发及优化,JVM调优,Java性能调

【SQL server初级】数据库性能优化三:程序操作优化

数据库优化包含以下三部分,数据库自身的优化,数据库表优化,程序操作优化.此文为第三部分 数据库性能优化三:程序操作优化 概述:程序访问优化也可以认为是访问SQL语句的优化,一个好的SQL语句是可以减少非常多的程序性能的,下面列出常用错误习惯,并且提出相应的解决方案 一.操作符优化 1. IN.NOT IN 操作符 IN和EXISTS 性能有外表和内表区分的,但是在大数据量的表中推荐用EXISTS 代替IN . Not IN 不走索引的是绝对不能用的,可以用NOT EXISTS 代替 2. IS 

数据库性能优化三:程序操作优化

数据库优化包含以下三部分,数据库自身的优化,数据库表优化,程序操作优化.此文为第三部分 数据库性能优化三:程序操作优化 概述:程序访问优化也可以认为是访问SQL语句的优化,一个好的SQL语句是可以减少非常多的程序性能的,下面列出常用错误习惯,并且提出相应的解决方案 一.操作符优化 1. IN.NOT IN 操作符 IN和EXISTS 性能有外表和内表区分的,但是在大数据量的表中推荐用EXISTS 代替IN . Not IN 不走索引的是绝对不能用的,可以用NOT EXISTS 代替 2. IS 

90 % Java 程序员被误导的一个性能优化策略

我们经常看到一些 Java 性能优化的书或者理念,说不要在循环内定义变量,这样会占用过多的内存影响性能,而要在循环外面定义.接触 Java 这么久以来,相信很多 Java 程序员都被这种代码性能优化策略所误导. 看下面两个示例,示例1在循环外定义变量,示例2是在循环内定义变量. /** * 循环外定义变量 */ private static void outer() { Javastack javastack = null; for (int i = 0; i < 10; i++) { java

如何优化cocos2d程序的内存使用和程序大小

在我完成第一个游戏项目的时候,我深切地意识到"使用cocos2d来制作游戏的开发者们,他们大多会被cocos2d的内存问题所困扰".而我刚开始接触cocos2d的时候,社区里面的人们讨论了一个非常有意义的话题:"请简单地讲述你认为新手cocos2d程序员在他开始编码之前,最应该先知道,或者应该关注和注意的事项."这个问题的答案很多,有人讲是"如何加载和保存游戏数据",有人讲的是"如何实现有限状态机"等等.而最吸引我的则是,有一

理解性能的奥秘——应用程序中慢,SSMS中快(6)——SQL Server如何编译动态SQL

本文属于<理解性能的奥秘--应用程序中慢,SSMS中快>系列 接上文:理解性能的奥秘--应用程序中慢,SSMS中快(5)--案例:如何应对参数嗅探 我们抛开参数嗅探的话题,回到了本系列的最初关注点中:为什么语句在应用程序中慢,但是在SSMS中快?到目前为止,都是在说存储过程的情况.而存储过程的问题通常是因为SET ARITHABORT的不同设置项的原因.如果你的应用不使用存储过程,而是通过中间层提交客户端的查询,那么也有几个原因可能让你的查询因为不同的缓存条目从而使得在SSMS和应用程序中的运

理解性能的奥秘——应用程序中慢,SSMS中快(5)——案例:如何应对参数嗅探

本文属于<理解性能的奥秘--应用程序中慢,SSMS中快>系列 接上文:理解性能的奥秘--应用程序中慢,SSMS中快(4)--收集解决参数嗅探问题的信息 首先我们需要明白,参数嗅探本身不是问题,而是一个特性,避免SQL Server做出盲目的假设,从而产生次优查询计划.但是有些情况下,参数嗅探却会带来负面影响.通常有下面三种典型的情况: 查询使用的参数嗅探完全不合适.也就是说,查询计划对于这次执行是合适的,但是对于下一次执行就可能不合适. 应用程序中存在特定的调用模式,而且与其他大部分调用模式差

理解性能的奥秘——应用程序中慢,SSMS中快(4)——收集解决参数嗅探问题的信息

本文属于<理解性能的奥秘--应用程序中慢,SSMS中快>系列 接上文:理解性能的奥秘--应用程序中慢,SSMS中快(3)--不总是参数嗅探的错 前面已经提到过关于存储过程在SSMS中运行很快,但在应用程序中运行很慢的可能原因:因为ARITHABORT的不同选项会导致不同的缓存词目,另外由于SQL Server使用了参数嗅探导致获得了不同的执行计划. 虽然已经说明了这个现象的原因,但是还没解释:如何定位和解决这个问题?到目前为止,大家都知道了如何快速处理,如果这个问题很紧急,可以直接使用: EX

理解性能的奥秘——应用程序中慢,SSMS中快(3)——不总是参数嗅探的错

本文属于<理解性能的奥秘--应用程序中慢,SSMS中快>系列 接上文:理解性能的奥秘--应用程序中慢,SSMS中快(2)--SQL Server如何编译存储过程 在我们开始深入研究如何处理参数嗅探相关的性能问题之前,由于这个课题过于广泛,所以首先先介绍一些跟参数嗅探没有直接关系的内容,但是又会导致语句在SSMS和应用程序中存在性能差异的情况. 替换变量和参数: 前面已经接触过,但是在这里对其进行扩展.有时会看到论坛上有人说,某个存储过程很慢,但是把相同的语句提取出来单独执行就很快.真相就是:语

理解性能的奥秘——应用程序中慢,SSMS中快(2)——SQL Server如何编译存储过程

接上文:理解性能的奥秘--应用程序中慢,SSMS中快(1)--简介 本文介绍SQL Server如何编译存储过程并使用计划缓存.如果你的应用程序完全没有用到存储过程,而是直接使用SQL语句提交请求,那么本文大部分内容也是有效的.但是关于动态SQL的编译会在后面章节介绍,这里重点关注让人头痛的存储过程问题. 什么是存储过程? 虽然这个问题有点愚蠢,但是实际的问题是:什么对象有自己的查询计划?SQL Server为下面四类对象创建查询计划: 存储过程. 标量用户自定义函数. 多步表值函数. 触发器