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

智能推荐

python简易爬虫v1.0-程序员宅基地

文章浏览阅读1.8k次,点赞4次,收藏6次。python简易爬虫v1.0作者:William Ma (the_CoderWM)进阶python的首秀,大部分童鞋肯定是做个简单的爬虫吧,众所周知,爬虫需要各种各样的第三方库,例如scrapy, bs4, requests, urllib3等等。此处,我们先从最简单的爬虫开始。首先,我们需要安装两个第三方库:requests和bs4。在cmd中输入以下代码:pip install requestspip install bs4等安装成功后,就可以进入pycharm来写爬虫了。爬

安装flask后vim出现:error detected while processing /home/zww/.vim/ftplugin/python/pyflakes.vim:line 28_freetorn.vim-程序员宅基地

文章浏览阅读2.6k次。解决方法:解决方法可以去github重新下载一个pyflakes.vim。执行如下命令git clone --recursive git://github.com/kevinw/pyflakes-vim.git然后进入git克降目录,./pyflakes-vim/ftplugin,通过如下命令将python目录下的所有文件复制到~/.vim/ftplugin目录下即可。cp -R ...._freetorn.vim

HIT CSAPP大作业:程序人生—Hello‘s P2P-程序员宅基地

文章浏览阅读210次,点赞7次,收藏3次。本文简述了hello.c源程序的预处理、编译、汇编、链接和运行的主要过程,以及hello程序的进程管理、存储管理与I/O管理,通过hello.c这一程序周期的描述,对程序的编译、加载、运行有了初步的了解。_hit csapp

18个顶级人工智能平台-程序员宅基地

文章浏览阅读1w次,点赞2次,收藏27次。来源:机器人小妹  很多时候企业拥有重复,乏味且困难的工作流程,这些流程往往会减慢生产速度并增加运营成本。为了降低生产成本,企业别无选择,只能自动化某些功能以降低生产成本。  通过数字化..._人工智能平台

electron热加载_electron-reloader-程序员宅基地

文章浏览阅读2.2k次。热加载能够在每次保存修改的代码后自动刷新 electron 应用界面,而不必每次去手动操作重新运行,这极大的提升了开发效率。安装 electron 热加载插件热加载虽然很方便,但是不是每个 electron 项目必须的,所以想要舒服的开发 electron 就只能给 electron 项目单独的安装热加载插件[electron-reloader]:// 在项目的根目录下安装 electron-reloader,国内建议使用 cnpm 代替 npmnpm install electron-relo._electron-reloader

android 11.0 去掉recovery模式UI页面的选项_android recovery 删除 部分菜单-程序员宅基地

文章浏览阅读942次。在11.0 进行定制化开发,会根据需要去掉recovery模式的一些选项 就是在device.cpp去掉一些选项就可以了。_android recovery 删除 部分菜单

随便推点

echart省会流向图(物流运输、地图)_java+echart地图+物流跟踪-程序员宅基地

文章浏览阅读2.2k次,点赞2次,收藏6次。继续上次的echart博客,由于省会流向图是从echart画廊中直接取来的。所以直接上代码<!DOCTYPE html><html><head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /&_java+echart地图+物流跟踪

Ceph源码解析:读写流程_ceph 发送数据到其他副本的源码-程序员宅基地

文章浏览阅读1.4k次。一、OSD模块简介1.1 消息封装:在OSD上发送和接收信息。cluster_messenger -与其它OSDs和monitors沟通client_messenger -与客户端沟通1.2 消息调度:Dispatcher类,主要负责消息分类1.3 工作队列:1.3.1 OpWQ: 处理ops(从客户端)和sub ops(从其他的OSD)。运行在op_tp线程池。1...._ceph 发送数据到其他副本的源码

进程调度(一)——FIFO算法_进程调度fifo算法代码-程序员宅基地

文章浏览阅读7.9k次,点赞3次,收藏22次。一 定义这是最早出现的置换算法。该算法总是淘汰最先进入内存的页面,即选择在内存中驻留时间最久的页面予以淘汰。该算法实现简单,只需把一个进程已调入内存的页面,按先后次序链接成一个队列,并设置一个指针,称为替换指针,使它总是指向最老的页面。但该算法与进程实际运行的规律不相适应,因为在进程中,有些页面经常被访问,比如,含有全局变量、常用函数、例程等的页面,FIFO 算法并不能保证这些页面不被淘汰。这里,我_进程调度fifo算法代码

mysql rownum写法_mysql应用之类似oracle rownum写法-程序员宅基地

文章浏览阅读133次。rownum是oracle才有的写法,rownum在oracle中可以用于取第一条数据,或者批量写数据时限定批量写的数量等mysql取第一条数据写法SELECT * FROM t order by id LIMIT 1;oracle取第一条数据写法SELECT * FROM t where rownum =1 order by id;ok,上面是mysql和oracle取第一条数据的写法对比,不过..._mysql 替换@rownum的写法

eclipse安装教程_ecjelm-程序员宅基地

文章浏览阅读790次,点赞3次,收藏4次。官网下载下载链接:http://www.eclipse.org/downloads/点击Download下载完成后双击运行我选择第2个,看自己需要(我选择企业级应用,如果只是单纯学习java选第一个就行)进入下一步后选择jre和安装路径修改jvm/jre的时候也可以选择本地的(点后面的文件夹进去),但是我们没有11版本的,所以还是用他的吧选择接受安装中安装过程中如果有其他界面弹出就点accept就行..._ecjelm

Linux常用网络命令_ifconfig 删除vlan-程序员宅基地

文章浏览阅读245次。原文链接:https://linux.cn/article-7801-1.htmlifconfigping &lt;IP地址&gt;:发送ICMP echo消息到某个主机traceroute &lt;IP地址&gt;:用于跟踪IP包的路由路由:netstat -r: 打印路由表route add :添加静态路由路径routed:控制动态路由的BSD守护程序。运行RIP路由协议gat..._ifconfig 删除vlan