C++性能优化(一)——应用程序性能优化简介_c++cpu问题分析-程序员宅基地

技术标签: 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等,支持文字、图片、文本框、表格、图形等。设计基准文件时,从文档类型考虑,在兼顾到主要的文档类型又要排除类似的文档类型;从文档内容考虑,需要覆盖用户最常用的内容对象类型和文档大小,具体基准文件列表如下:

名字

内容

大小(KB)

类型

复杂文档100.doc

100页,100图片,16表格,60文本框,其它

3082

doc

复杂文档100.odt

100页,100图片,16表格,60文本框,其它

1249

odt

复杂文档100.lwp

100页,100图片,16表格,60文本框,其它

1246

lwp

简单文档120.doc

120页,纯文本

672

doc

简单文档120.odt

120页,纯文本

19

odt

简单文档120.lwp

120页,纯文本

13

lwp

对于同一种文档类型,每一种文档类型包含两个基准文件,分别有不同的文档内容。

(2)基准用例

基准用例是性能基准测试时需要执行的一系列用例。基准用例的选择有一定原则,既要尽可能全面地覆盖应用程序的主要功能,又不能像功能测试用例那样复杂,因此,基准用例应该是用户日常操作经常遇到的情形。

不同的基准用例在性能基准中的地位并不相同,每一个基准用例都需要一个权值来表明它对整体性能基准的贡献度。权值的定义依据具体情况各有不同,一个比较实用的定义公式如下:

权值=用例频率X用例重要性

用例频率是用户一定时间内执行该用例的平均次数。理想的用例频率应该通过用户行为数据反馈获得。例如通用文字处理软件的用户一天内可能会执行“打开文档”用例5次,执行“保存文档”的用例15次。

用例重要性是一个修正系数,反映用例没有完成前对用户工作的影响程度。文字处理软件打开一个文档时有“异步打开”的功能,即程序会首先读入文档的部分内容并显示给用户,然后在后台继续读入文档的后续内容。对于“异步打开”功能可以定义两个用例,“第一页显示”(从用户选择打开文档到文档的第一页显示出来的过程)和“全部读入完毕”(从用户选择打开文档到文档的所有页的内容已经加载完毕的过程)。“第一页显示”用例的重要性为1,表示不执行完本用例,用户不能继续工作;“全部读入完毕”用例的重要性为0.5,表示本用例不会显著影响用户的工作,文档在后台加载,用户前台已经可以编辑,除非用户需要编辑的内容还没有加载出来。

文字处理程序的部分基准用例如下:

名字

描述

频率

重要性

权值

冷启动

操作系统重新启动后应用程序第一次启动的过程

1

1

1

热启动

应用程序再次启动的过程

6

1

6

关闭

从用户关闭到程序完全退出的过程

7

0.2

1.4

新建文档

新建文档到输入光标闪动的过程

3

1

2

第一页显示(简单doc文档)

从用户选择文档打开到第一页显示并可编辑的过程

5

1

5

全部读入完毕(简单doc文档)

从用户选择文档打开到文档所有页的内容全部加载的过程

5

0.5

2.5

保存文档(简单doc文档)

用户插入一个字符,从保存开始到保存结束的过程

15

1

15

第一页显示(复杂doc文档)

从用户选择文档打开到第一页显示并可编辑的过程

5

1

5

全部读入完毕(复杂doc文档)

从用户选择文档打开到文档所有页的内容全部加载的过程

5

0.5

2.5

保存文档(复杂doc文档)

用户插入一个字符,从保存开始到保存结束的过程

15

1

15

插入图片

在第一段插入特定图片的过程

10

1

10

翻页

从第一页翻到第二页的过程

100

1

100

粘贴

复制最后一个段落粘贴到第一个段落

50

1

50

打印预览

打印预览整篇文档的过程

5

1

5

查找

从查找特定字符开始到找到的过程

20

1

20

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

3、性能基准的运行

准备好基准文件和基准用例后就可以运行性能基准并得出基准结果。

为了保证性能基准运行的准确性,性能基准的测试环境必须满足一定要求。例如保证固定的基准测试平台(软件和硬件平台不变),尽可能排除其它应用程序对目标应用层程序的影响。基准测试平台也可以有同时运行在多种硬件平台上的考量:运行在4G内存和8G内存时应用程序的性能表现的差别;运行在三年前硬件配置和当前主流硬件配置的性能表现的差别。

运行基准测试的过程是在固定的基准测试环境中针对基准文件顺序执行一系列基准用例并记录下每个用例结果的过程,执行过程分为手动执行和自动执行,结果记录也可以分为手动记录和自动记录。

通常手工执行和记录是基础,最能反映最终用户的体验。

自动运行和记录是目标,可以大幅提升工作效率,并排除人工不稳定的结果。但自动运行的脚本必须保证多次自动运行结果的稳定,保证自动运行结果和手动运行结果的可比较。

性能基准运行的过程需要注意:

(1)每一个用例需要运行多次求平均值,如需要去除最大值、最小值,然后取平均值。

(2)多个用例执行的先后顺序必须固定,否则很难得到稳定的性能基准结果。

4、性能基准结果

性能基准运行结果的原始数据是一系列的绝对数值,可以根据不同的需要生成不同的报告,如:

整体性能水平分值:每个用例绝对值结合每个用例的权值,可以给整体性能水平打分。

性能变化趋势图:历史上不同版本的整体性能分值曲线可以体现出性能变化趋势。

关键性能指标图表:给用户演示的重点用例的结果图表。

产品性能对比图:和其它产品的性能对比图,包括绝对值的对比和加权后分值的对比。

文字处理程序的部分基准用例运行结果数据如下:

名字

结果值(秒)

权值

分值

冷启动

10.5

1

10.5

热启动

2.1

6

12.6

关闭

2.3

1.4

3.22

新建文档

0.8

3

2.4

第一页显示(简单doc文档)

0.5

5

2.5

全部读入完毕(简单doc文档)

2

2.5

5

保存文档(简单doc文档)

0.2

15

3

第一页显示(复杂doc文档)

0.8

5

4

全部读入完毕(复杂doc文档)

4.8

2.5

12

保存文档(复杂doc文档)

0.8

15

12

插入图片

0.1

10

1

翻页

0.02

100

2

粘贴

0.03

50

1.5

打印预览

0.8

5

4

查找

0.01

20

0.2

总分

71.51

性能基准可以反映应用程序的总体性能,定义良好的性能基准用途如下:

(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、设计层面的性能优化

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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/A642960662/article/details/123029121

智能推荐

手把手教你安装Eclipse最新版本的详细教程 (非常详细,非常实用)_eclipse安装教程-程序员宅基地

文章浏览阅读4.4k次,点赞2次,收藏16次。写这篇文章的由来是因为后边要用这个工具,但是由于某些原因有部分小伙伴和童鞋们可能不会安装此工具,为了方便小伙伴们和童鞋们的后续学习和不打击他们的积极性,因为80%的人都是死在工具的安装这第一道门槛上,这门槛说高也不高说低也不是太低。所以就抽时间水了这一篇文章。_eclipse安装教程

分享11个web前端开发实战项目案例+源码_前端项目实战案例-程序员宅基地

文章浏览阅读4.1w次,点赞12次,收藏193次。小编为大家收集了11个web前端开发,大企业实战项目案例+5W行源码!拿走玩去吧!1)小米官网项目描述:首先选择小米官网为第一个实战案例,是因为刚开始入门,有个参考点,另外站点比较偏向目前的卡片式设计,实现常见效果。目的为学者练习编写小米官网,熟悉div+css布局。学习资料的话可以加下web前端开发学习裙:600加上610再加上151自己去群里下载下。项目技术:HTML+CSS+Div布局2)迅雷官网项目描述:此站点特效较多,所以通过练习编写次站点,学生可以更多练习CSS3的新特性过渡与动画的实_前端项目实战案例

计算质数-埃里克森筛法(间隔黄金武器)-程序员宅基地

文章浏览阅读73次。素数,不同的质数,各种各样的问题总是遇到的素数。以下我们来说一下求素数的一种比較有效的算法。就是筛法。由于这个要求得1-n区间的素数仅仅须要O(nloglogn)的时间复杂度。以下来说一下它的思路。思路:如今又1-n的数字。素数嘛就是除了1和本身之外没有其它的约数。所以有约数的都不是素数。我们从2開始往后遍历,是2的倍数的都不是素数。所以我们把他们划掉然后如...

探索Keras DCGAN:深度学习中的创新图像生成-程序员宅基地

文章浏览阅读532次,点赞9次,收藏14次。探索Keras DCGAN:深度学习中的创新图像生成项目地址:https://gitcode.com/jacobgil/keras-dcgan在数据驱动的时代,图像生成模型已经成为人工智能的一个重要领域。其中,Keras DCGAN 是一个基于 Keras 的实现,用于构建和训练 Deep Convolutional Generative Adversarial Networks(深度卷积生...

org.apache.ibatis.binding.BindingException: Invalid bound statement (not found):_spring-could org.apache.ibatis.binding.bindingexce-程序员宅基地

文章浏览阅读116次。今天在搭建springcloud项目时,发现如上错误,顺便整理一下这个异常:1. mapper.xml的命名空间(namespace)是否跟mapper的接口路径一致<mapper namespace="com.baicun.springcloudprovider.mapper.SysUserMapper">2.mapper.xml接口名是否和mapper.java接..._spring-could org.apache.ibatis.binding.bindingexception: invalid bound state

四种高效数据库设计思想——提高查询效率_数据库为什么能提高效率-程序员宅基地

文章浏览阅读1.1k次。四种高效数据库设计思想——提高查询效率:设计数据库表结构时,我们首先要按照数据库的三大范式进行建立数据。1. 1NF每列不可拆分2. 2NF确保每个表只做一件事情3. 3NF满足2NF,消除表中的依赖传递。三大范式的出现是在上世纪70年代,由于内存资源比较昂贵,所以严格按照三大范式进行数据库设计。而如今内存变得越来越廉价,在考虑效率和内存的基础上我们可以做出最优选择以达到最高效率。_数据库为什么能提高效率

随便推点

HTML标签分类及转义字符_ol是单标记还是双标记-程序员宅基地

文章浏览阅读302次。一. HTML标签分类1.根据标签个数分类。 单标签:只有一个标签。 <br>, <hr>,<img>,<meta>, 实现一个特定的功能。 双标签:既有开始标签,也有结束标签。 Html,head,Body,title,h1~h6,p,a,ul,li,ol,strong,em。2.根据标签特性分类(网页效果)。 2.1行属性..._ol是单标记还是双标记

什么是配置_基于配置是什么意思-程序员宅基地

文章浏览阅读1.6k次。应用程序在启动和运行的时候往往需要读取一些配置信息,配置基本上伴随着应用程序的整个生命周期,比如:数 据库连接参数、启动参数等。配置主要有以下几个特点:配置是独立于程序的只读变量配置对于程序是只读的,程序通过读取配置来改变自己的行为,但是程序不应该去改变配置配置伴随应用的整个生命周期配置贯穿于应用的整个生命周期,应用在启动时通过读取配置来初始化,在运行时根据配置调整行为。比如:启动时需要读取服务的端口号、系统在运行过程中需要读取定时策略执行定时任务等。配置可以有多种加载方式常见的有程序内部_基于配置是什么意思

二、使用GObject——一个简单类的实现-程序员宅基地

文章浏览阅读170次。Glib库实现了一个非常重要的基础类--GObject,这个类中封装了许多我们在定义和实现类时经常用到的机制: 引用计数式的内存管理 对象的构造与析构 通用的属性(Property)机制 Signal的简单使用方式 很多使用GObject..._

golang 定时任务处理-程序员宅基地

文章浏览阅读6.3k次,点赞2次,收藏9次。在 golang 中若写定时脚本,有两种实现。一、基于原生语法组装func DocSyncTaskCronJob() { ticker := time.NewTicker(time.Minute * 5) // 每分钟执行一次 for range ticker.C { ProcTask() }}func ProcTask() { log.Println("hello world")}二、基于 github 中封装的 cron 库实现package taskimport (_golang 定时任务

VC获取精确时间的方法_vc 通过线程和 sleep 获取精准时间-程序员宅基地

文章浏览阅读2.1k次。 来源:http://blog.csdn.net/clever101/archive/2008/10/18/3096049.aspx 声明:本文章是我整合网上的资料而成的,其中的大部分文字不是我所为的,我所起的作用只是归纳整理并添加我的一些看法。非常感谢引用到的文字的作者的辛勤劳动,所参考的文献在文章最后我已一一列出。 对关注性能的程序开发人员而言,一个好的计时部件既是益友,也_vc 通过线程和 sleep 获取精准时间

wml入门-程序员宅基地

文章浏览阅读58次。公司突然说要进行wap开发了,以前从没了解过,但我却异常的兴奋,因为可以学习新东西了,呵呵,我们大家一起努力吧。首先说说环境的搭建。可以把.wml的文件看做是另一种的html进行信息的展示,但并不是所有的浏览器都支持,好用的有Opera,还有WinWap。编写wml文件语法比较严格,不好的是我还没有找到好的提示工具,就先用纯文本吧。我找到了一个很好的学习网站:http://w3sc..._winwap学习