C++程序运行效率的10个简单方法

深圳中心C++教研部 韩老师

对于每一个程序员来说,程序的运行效率都是一个值得重视,并为之付出努力的问题。本文介绍提高C++程序运行效率的10个简单方法,包括循环、变量、继承等应用的技巧,非常具有实用价值,具体分析如下:

一、尽量减少值传递,多采用引用来传递参数

如果参数是int等语言自定义的类型可能对性能的影响不是很大,但是如果参数是一个类的对象,那么其效率问题就非常突出了。例如一个判断两个字符串是否相等的函数,其声明如下:

其中若使用第一个函数(值传递),则在参数传递和函数返回时,需要调用String的构造函数和析构函数两次(即共多调用了四个函数),而其他的三个函数(指针传递和引用传递)则不需要调用这四个函数。因为指针和引用都不会创建新的对象。如果构造一个对象和析构一个对象的开销是庞大的,这就会对效率造成一定的影响。

然而在很多人看来,指针是一个“恶梦”,使用指针容易犯错误,那么就使用引用吧!它与使用普通值传递一样方便直观,同时具有指针传递的高效和能力。因为引用是一个变量的别名,对其操作等同于对实际对象操作,所以当你确定你的函数是不会或不需要变量参数的值时,就大胆地在声明的前面加上一个const吧,就如最后的一个函数声明一样。加上一个const还有一个好处,就是可以对常量进行引用,若不加const修饰符,引用是不能引用常量的。

二、++i和i++引申出的效率问题

在C++中重载运算符++时,一般都会把前加和后加都重载。即使你的代码中不重载++运算符,但是++运算符的重载运用广泛,比如迭代器类。

从后加的实现方式可以知道,对象利用自己创建一个临时对象(自己在函数调用的一个复制),然后改变自己的状态,并返回这个临时对象,而前加的实现方式时,直接改变自己的内部状态,并返回自己的引用。

后加实现时会调用复制构造函数,在函数返回时还要调用析构函数,而前加实现方式直接改变对象的内部状态,并返回自己的引用,自始至终也没有创建新的对象,所以也就不会调用构造函数和析构函数。

迭代器通常是用来遍历容器的,它大多应用在循环中,若链表有100个元素,采用第二种方式你就要多调用100次构造函数和析构函数,其对效率的影响不可忽视。

三、循环讨论1(循环内定义,还是循环外定义对象)

分析下面两段代码:

代码1:

代码2:

你觉得哪段代码的运行效率较高呢?其实这种情况下,哪段代码的效率更高是不确定的,或者说是由这个类ClassTest本身决定的,分析如下:

对于代码1:需要调用ClassTest的构造函数1次,赋值操作函数(operator=)100次;对于代码2:需要调用构造函数100次,析构函数100次。

如果调用赋值操作函数的开销比调用构造函数和析构函数的总开销小,则第一种效率高,否则第二种的效率高。

四、循环讨论2(避免过大的循环)

分析下面两段代码,

代码1:

代码2:

注:这里的fun1()和fun2()是没有关联的,即两段代码所产生的结果是一样的。

从代码的层面上看,似乎是代码1的效率更高,因为毕竟代码1少了n次的自加运算和判断,因为自加运算和判断也是需要时间的。但是现实真的是这样吗?

这就要看fun1和fun2这两个函数的规模(或复杂性)了,如果这两个函数的代码语句很少,则代码1的运行效率高一些,但是若fun1和fun2的语句有很多,规模较大,则代码2的运行效率会比代码1高得多。下面分析原因。

首先由计算机硬件说起,由于CPU只能从内存读取数据,而CPU的运算速度远远大于内存,所以为了提高程序的运行速度有效地利用CPU的能力,在内存与CPU之间有一个叫Cache的存储器,它的速度接近CPU。而Cache中的数据是从内存中加载而来的,这个过程需要访问内存,速度较慢。

Cache的设计原理,就是时间局部性和空间局部性。时间局部性是指如果一个存储单元被访问,则可能该单元会很快被再次访问,这是因为程序存在着循环。空间局部性是指如果一个储存单元被访问,则该单元邻近的单元也可能很快被访问,这是因为程序中大部分指令是顺序存储、顺序执行的,数据也一般是以向量、数组、树、表等形式簇聚在一起的。

看到这里你可能已经明白其中的原因了。如果fun1和fun2的代码量很大,例如都大于Cache的容量,则在代码1中,就不能充分利用Cache了(由时间局部性和空间局部性可知),因为每循环一次,都要把Cache中的内容踢出,重新从内存中加载另一个函数的代码指令和数据,而代码2则更很好地利用了Cache,利用两个循环语句,每个循环所用到的数据几乎都已加载到Cache中,每次循环都可从Cache中读写数据,访问内存较少,速度较快,理论上只需要完全踢出fun1的数据1次即可。

五、局部变量VS静态变量

使用局部变量的效率比使用静态变量要高。这是因为局部变量存在于堆栈中,对其空间的分配仅仅是修改一次esp寄存器的内容(即使定义一组局部变量也是修改一次)。而局部变量存在于堆栈中最大的好处是,函数能重复使用内存,当一个函数调用完毕时,退出程序堆栈,内存空间被回收,当新的函数被调用时,局部变量又可以重新使用相同的地址。当一块数据被反复读写,其数据会留在CPU的一级缓存(Cache)中,访问速度非常快。而静态变量却不存在于堆栈中,可以说静态变量是低效的。

六、避免使用多重继承

在C++中,支持多继承,即一个子类可以有多个父类。书上都会跟我们说,多重继承的复杂性和使用的困难,并告诫我们不要轻易使用多重继承。其实多重继承并不仅仅使程序和代码变得更加复杂,还会影响程序的运行效率。

这是因为在C++中每个对象都有一个this指针指向对象本身,而C++中类对成员变量的使用是通过this的地址加偏移量来计算的,而在多重继承的情况下,这个计算会使变量更加复杂,从而降低程序的运行效率。为了解决二义性,而使用虚基类的多重继承对效率的影响更为严重,因为其继承关系更加复杂和成员变量所属的父类关系更加复杂。

七、尽量少使用dynamic_cast

dynamic_cast的作用是进行指针或引用的类型转换,dynamic_cast的转换需要目标类型和源对象有一定的关系:继承关系。实现从子类到基类的指针转换,实际上这种转换是非常低效的,对程序的性能影响也比较大,不可大量使用,而且继承关系越复杂,层次越深,其转换时间开销越大。因此,在程序中应该尽量减少使用。

八、减少除法运算的使用

无论是整数还是浮点数运算,除法都是一件运算速度很慢的指令,在计算机中实现除法是比较复杂的。所以要减少除法运算的次数,下面介绍一些简单方法来提高效率:

1.通过数学的方法,把除法变为乘法运算,如if(a > b/c),如果a、b、c都是正数,则可写成if(a*c > b)

2.让编译器有优化的余地,如里你要做的运算是int型的n/8的话,写成(unsigned)n/8有利于编译器的优化。若要让编译器有优化的余地,则除数必须为常数,而这也可以用const修饰一个变量来达到目的。

九、将小粒度函数声明为内联函数(inline)

调用函数是需要保护现场,为局部变量分配内存,函数结束后还要恢复现场等开销,而内联函数则是把它的代码直接写到调用函数处,所以不需要这些开销,但会使程序的源代码长度变大。

所以若是小粒度的函数,如下面的Max函数,由于不需要调用普通函数的开销,所以可以提高程序的效率。

十、多用直接初始化

与直接初始化对应的是复制初始化,举个简单的例子:

直接初始化是直接以一个对象来构造另一个对象,如用ct1来构造ct2,复制初始化是先构造一个对象,再把另一个对象值复制给这个对象,如先构造一个对象ct3,再把ct1中的成员变量的值复制给ct3,从这里可以看出直接初始化的效率更高一点,而且使用直接初始化还有一个好处,就是对于不能进行复制操作的对象,如流对象,是不能使用赋值初始化的,只能进行直接初始化。

最后,还有非常重要的一点:要避免不必要的优化,避免不成熟的优化,不成熟的优化的是错误的来源,因为编译器会为你做很多你所不知道的优化。

原文地址:https://www.cnblogs.com/mathyk/p/10587826.html

时间: 2024-07-31 05:37:23

C++程序运行效率的10个简单方法的相关文章

成为优秀程序员的10个有效方法

一个优秀的程序员就是那种即使是过单行道都要往两边看的人.——Doug Linder 作为一个在IT行业的软件程序员,每天促使我去上班的动力就是对编程的乐趣和激情.但是为了激发乐趣并获得永恒的快感,我们需要学习和遵守一些基础知识以成为一名优秀的程序员. 当然我总结的这些东西没有那么神,你说你要想学了之后就一定可以成为优秀程序员,那是不可能的.这只是从我的经验角度出发提供的一些捷径.对于优秀程序员的定义,此处可以理解为能开发出超棒的IT解决方案并且能促进行业整体发展的程序员. 1.掌握基础知识 不管

[转载]成为优秀程序员的10个有效方法

下文转载自http://news.cnblogs.com/n/511465/,作者是 码农网 – 小峰 . 一个优秀的程序员就是那种即使是过单行道都要往两边看的人.——Doug Linder 作为一个在 IT 行业的软件程序员,每天促使我去上班的动力就是对编程的乐趣和激情.但是为了激发乐趣并获得永恒的快感,我们需要学习和遵守一些基础知识以成为一名优秀的程序员. 当然我总结的这些东西没有那么神,你说你要想学了之后就一定可以成为优秀程序员,那是不可能的.这只是从我的经验角度出发提供的一些捷径.对于优

处理PHP字符串的10个简单方法;mysql出现乱码:character_set_server=utf8

PHP处理字符串的能力非常强大,方法也是多种多样,但有的时候你需要选择一种最简单且理想的解决方法.文章列举了10个PHP中常见的字符串处理案例,并提供了相对应的最理想的处理方法. 1.确定一个字符串的长度 这是文章中最明显的一个例子,其中的问题是我们如何来确定一个字符串的长度,这里我们不能不提的就是strlen()函数: $text = "sunny day"; $count = strlen($text); // $count = 9 2.截取文本,创建一个摘要 新闻性质的网站通常会

处理PHP字符串的10个简单方法

PHP处理字符串的能力非常强大,方法也是多种多样,但有的时候你需要选择一种最简单且理想的解决方法.文章列举了10个PHP中常见的字符串处理案例,并提供了相对应的最理想的处理方法. 1.确定一个字符串的长度 这是文章中最明显的一个例子,其中的问题是我们如何来确定一个字符串的长度,这里我们不能不提的就是strlen()函数: $text = "sunny day"; $count = strlen($text); // $count = 9 2.截取文本,创建一个摘要 新闻性质的网站通常会

改善C#程序,提高程序运行效率的50种方法

改善C#程序,提高程序运行效率的50种方法 转自:http://blog.sina.com.cn/s/blog_6f7a7fb501017p8a.html 一.用属性代替可访问的字段 1..NET数据绑定只支持数据绑定,使用属性可以获得数据绑定的好处: 2.在属性的get和set访问器重可使用lock添加多线程的支持. 二.readonly(运行时常量)和const(编译时常量) 1.const只可用于基元类型.枚举.字符串,而readonly则可以是任何的类型: 2.const在编译时将替换成

把C#程序(含多个Dll)合并成一个Exe的超简单方法

原文:把C#程序(含多个Dll)合并成一个Exe的超简单方法 开发程序的时候经常会引用一些第三方的DLL,然后编译生成的exe文件就不能脱离这些DLL独立运行了. 但是,很多时候我们本想开发一款只需要一个exe就能完美运行的小工具.那该怎么办呢? 下文介绍一种超简单的方法,不用写一行代码就可轻松实现. 这里我们需要用到一款名为Costura.Fody的工具.Costura.Fody是一个Fody框架下的插件,可通过Nuget安装到VS工程中.安装之后,就可以将项目所依赖的DLL(甚至PDB)文件

简单程序的编译链接三种方法(编译多个源文件,静态链接库、动态链接库)

一个程序简单的程序如下: 1 hello.h #ifndef HELLO_H#define HELLO_H void hello(const char *name); #endif 2 hello.c #include <stdio.h>#include <stdlib.h> void hello(const char *name){ printf("hello %s\n",name);} 3 main.c #include <stdio.h>#in

一种异构数据库同步的简单方法

标题有点高大上,是为了解决实际应用中的一个问题.做了一个Android应用,用于记录日常消费账单,开始是单机版的,我老婆说太low了,起码要能看到彼此的消费情况吧.为此,我还专门写了一套基于protobuf的RPC组件,用于网络通信,http://www.cnblogs.com/zmkeil/p/5176758.html. 应用本身比较简单,几张简单粗暴的UI,涵盖了增.删.改各种功能,外加一个后台service组件,用于上传账单,并同步他人账单.也算是麻雀虽小五脏俱全吧,看几张效果图.代码见h

[转]PHP实现页面静态化的超简单方法

为什么要页面静态化? 1.动态文件执行过程:语法分析-编译-运行 2.静态文件,不需要编译,减少了服务器脚本运行的时间,降低了服务器的响应时间,直接运行,响应速度快:如果页面中一些内容不经常改动,动态页面静态化是非常有效的加速方法.(纯静态,伪静态还是需要PHP解释器的) 3.生成静态URL利于SEO,利于蜘蛛抓取和收录,有利于提升排名 优化页面响应时间方法 1.动态页面静态化 2.优化数据库 3.负载均衡 4.使用缓存等等 //动态页面静态化一般用于不经常改动的地方,频繁改动的地方一般不适用静