提高C++程序运行效率,减少运行时间的方法

2023-05-16

大致方法:

1.优化业务逻辑,尽量少做事情。

2.减少网络访问,IO等对外操作。

3.如果有数据库,优化SQL和数据库结构。

4.优化算法,比如冒泡排序改成快排等。

5.优化代码的编写。这个就很多了。

6.减少系统调用的次数,减少系统对象的访问。因为用户态转内核态挺慢的。

7.修改编译选项。改成O2编译优化。

具体方法:

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

       至于其中的原因,相信大家也很清楚,如果参数是int等语言自定义的类型可能能性能的影响还不是很大,但是如果参数是一个类的对象,那么其效率问题就不言而喻了。例如一个判断两个字符串是否相等的函数,其声明如下

bool Compare(string s1, string s2)
bool Compare(string *s1, string *s2)
bool Compare(string &s1, string &s2)
bool Compare(const string &s1, const string &s2)

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

       然而在很多人的眼中,指针是一个恶梦,使用指针就意味着错误,那么就使用引用吧!它与使用普通值传递一样方便直观,同时具有指针传递的高效和能力。因为引用是一个变量的别名,对其操作等同于对实际对象操作,所以当你确定在你的函数是不会或不需要变量参数的值时,就大胆地在声明的前面加上一个const吧,就如最后的一个函数声明一样。

同时加上一个const还有一个好处,就是可以对常量进行引用,若不加上const修饰符,引用是不能引用常量的

二、++i和i++引申出的效率问题(多使用前++)

       看了上面的第一点,你可能觉得,那不就是多调用了四个函数而已,你可能对此不屑一顾。那么来看看下面的例子,应该会让你大吃一惊。

        至于整型变量的前加和后加的区别相信大家也是很清楚的。然而在这里我想跟大家谈的却是C++类的运算符重载,为了与整形变量的用法一致,在C++中重载运算符++时一般都会把前加和后加都重载。你可能会说,你在代码中不会重载++运算符,但是你敢说你没有使用过类的++运算符重载吗?迭代器类你总使用过吧!可能到现在你还不是很懂我在说什么,那么就先看看下面的例子吧,是本人为链表写的一个内部迭代器。

_SingleList::Iterator& _SingleList::Iterator::operator++()//前加
{
  pNote = pNote->pNext;
  return *this;
}
_SingleList::Iterator _SingleList::Iterator::operator++(int)//后加
{
  Iterator tmp(*this);
  pNote = pNote->pNext;
  return tmp;
}

        从后加的实现方式可以知道,对象利用自己创建一个临时对象(自己在函数调用的一个复制),然后改变自己的状态,并返回这个临时对象,而前加的实现方式时,直接改变自己的内部状态,并返回自己的引用。从第一点的论述可以知道后加实现时会调用拷贝构造函数,在函数返回时还要调用析构函数,而由于前加实现方式直接改变对象的内部状态,并返回自己的引用,至始至终也没有创建新的对象,所以也就不会调用构造函数和析构函数然而更加糟糕的是,迭代器通常是用来遍历容器的,它大多应用在循环中,试想你的链表有100个元素,用下面的两种方式遍历:

for(_SingleList::Iterator it = list.begin(); it != list.end(); ++it)
{
  //do something
} 

for(_SingleList::Iterator it = list.begin(); it != list.end(); it++)
{
  //do something
} 

如果你的习惯不好,写了第二种形式,那么很不幸,做同样的事情,就是因为一个前加和一个后加的区别,你就要调用多200个函数,其对效率的影响可就不可忽视了。

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

请看下面的两段代码:

//代码1
ClassTest CT;
for(int i = 0; i < 100; ++i)
{
  CT = a;
  //do something
}
//代码2
for(int i = 0; i < 100; ++i)
{
  ClassTest CT = a;
  //do something
}

你会觉得哪段代码的运行效率较高呢?代码1科学家是代码2?其实这种情况下,哪段代码的效率更高是不确定的,或者说是由这个类ClassTest本向决定的,分析如下:
对于代码1:需要调用ClassTest的构造函数1次,赋值操作函数(operator=)100次;对于代码2:需要高用(复制)构造函数100次,析构函数100次。如果调用赋值操作函数的开销比调用构造函数和析构函数的总开销小,则第一种效率高,否则第二种的效率高

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

现在请看下面的两段代码:

//代码1
for(int i = 0; i < n; ++i)
{
  fun1();
  fun2();
}
//代码2
for(int i = 0; i < n; ++i)
{
  fun1();
}
for(int i = 0; i < n; ++i)
{
  fun2();
}

注:这里的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函数,由于不需要调用普通函数的开销,所以可以提高程序的效率。

int Max(int a, int b)
{
  return a>b?a:b;
}

十、多用直接初始化(可以直接调用构造函数的,就不要去调用拷贝构造或运算符重载)

与直接初始化对应的是复制初始化,什么是直接初始化?什么又是复制初始化?举个简单的例子,

ClassTest ct1;
ClassTest ct2(ct1);  //直接初始化
ClassTest ct3 = ct1;  //复制初始化 (调用拷贝构造函数了)

       那么直接初始化与复制初始化又有什么不同呢?直接初始化是直接以一个对象来构造另一个对象,如用ct1来构造ct2,复制初始化是先构造一个对象,再把另一个对象值复制给这个对象,如先构造一个对象ct3,再把ct1中的成员变量的值复制给ct3,从这里,可以看出直接初始化的效率更高一点,而且使用直接初始化还是一个好处,就是对于不能进行复制操作的对象,如流对象,是不能使用赋值初始化的,只能进行直接初始化。可能我说得不太清楚,那么下面就引用一下经典吧!

以下是Primer是的原话:

“当用于类类型对象时,初始化的复制形式和直接形式有所不同:直接初始化直接调用与实参匹配的构造函数,复制初始化总是调用复制构造函数。复制初始化首先使用指定构造函数创建一个临时对象,然后用复制构造函数将那个临时对象复制到正在创建的对象”,还有一段这样说,“通常直接初始化和复制初始化仅在低级别优化上存在差异,然而,对于不支持复制的类型,或者使用非explicit构造函数的时候,它们有本质区别:
ifstream file1("filename")://ok:direct initialization
ifstream file2 = "filename";//error:copy constructor is private”

补充:

这里只是一点点的建议,虽然说了这么多,但是还是要说一下的就是:要避免不必要的优化,避免不成熟的优化,不成熟的优化的是错误的来源,因为编译器会为你做很多你所不知道的优化

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

提高C++程序运行效率,减少运行时间的方法 的相关文章

  • 棋盘格自动生成器——四种格式(格雷码棋盘格、圆点、二维码棋盘格)

    棋盘格生成器可以生成上面四种格式的标定板 想要多大想要几行几列都可以动态设置 非常好用 对于自己写代码或用cad画都比较浪费时间 这个生成器可以立刻生成pdf 只要打印机不设置缩放 即可正常尺寸打印 非常非常好用 介绍给大家这个好用的地址
  • python爬虫-验证码的处理

    在爬取网页数据时 xff0c 经常出现需要登录账户且要输入验证码的情况 以http www santostang com wp login php action 61 register该网页为例 xff0c 需要先使用浏览器的检查功能找到f
  • HTTP协议的解码和编码

    HTTP协议的解码和编码 编码规范URL的编码与解码 编码 规范实战 xff1a 使用fiddler来抓住http请求 相当于各省各地的人说不同的话 xff0c 大家互相听不懂 xff0c 那么http就相当于有一个翻译器 xff0c 能够
  • Linux服务器上请求接口说明

    Linux服务器上请求接口说明 一 参数指令说明 X 指定请求方法 x 指定HTTP请求的代理 H 指定请求标头 d 发送POST请求提交的数据 xff0c 使用 d参数后 xff0c 会自动将请求转为POST xff0c HTTP请求会自
  • 编写一个程序,将两个字符串连接起来,不要用stracat 函数

    可能写的不好 xff0c 希望对你们有帮助 include lt stdio h gt int main int a 61 0 b 61 0 c 61 0 m 61 0 i j char str1 80 str2 80 printf 请输入
  • Linux ulimit命令详解

    ulimit 是一个计算机命令 xff0c 用于shell启动进程所占用的资源 xff0c 可用于修改系统资源限制 命令常用参数 H 设置硬资源限制 S 设置软资源限制 a 显示当前所有的资源限制 c size 设置core文件的最大值 单
  • 几种CAN应用层协议介绍

    一 CanOpen n CAL 提供了所有的网络管理服务和报文传送协议 xff0c 但并 没有定义 CMS 对象的内容或者正在通讯的对象的类 型 而这正是 CANopen 切入点 n CANopen 是在 CAL 基础上开发的 xff0c
  • CImage类

    我们知道 xff0c Visual C 43 43 中的CBitmap类的功能简直太弱小了 xff0c 这曾经让Visual C 43 43 在图像处理方面的功能比较尴尬 之前笔记里面 xff0c 我们采用的CBitmap配合GDI进行透明
  • PTA 7-20 表达式转换 (25分)

    算术表达式有前缀表示法 中缀表示法和后缀表示法等形式 日常使用的算术表达式是采用中缀表示法 xff0c 即二元运算符位于两个运算数中间 请设计程序将中缀表达式转换为后缀表达式 输入格式 输入在一行中给出不含空格的中缀表达式 xff0c 可包
  • Template Mode(模板方法)

    结构化程序 程序库开发人员 class Library public void step1 void step3 void step5 应用程序开发人员 class Application piblic bool Step2 bool St
  • Strategy 模式

    enum TaxBase CN Tax US Tax DE Tax class SaleOrder TaxBase tax public if tax 61 61 CN Tax else if tax 61 61 US Tax else i
  • 观察者模式

    在软件的构建过程中 xff0c 我们需要为某些对象建立一种通知依赖关系 一个对象 xff08 目标对象 xff09 发生改变 所有的依赖对象 xff08 观察者对象 xff09 都将得到通知 xff0c 如果依赖关系过于紧密 xff0c 将
  • matlab数据分类 画直方图

    我是刚刚接触matlab的小白 xff0c 在度娘和广大网友的帮助下终于完成了这个小任务 所以想记录下 xff0c 也希望可以帮助那些学习matlab的人 小任务 xff1a 主要对txt文本里的数据 进行处理下 xff0c 然后通过mat
  • 树莓派跑一个简单c++小程序教程

    我用的是树莓派3代b型 xff0c 所使用的是Debian系统的衍生系统raspbian 对系统不太了解不清楚 树莓派开发c 43 43 程序需要的工具有编辑器vim 调试器gdb 编译器gcc或者g 43 43 xff08 大神飘过就行
  • typedef 函数指针用法

    进入正文 xff1a 代码简化 促进跨平台开发的目的 typedef 行为有点像 define 宏 xff0c 用其实际类型替代同义字 不同点 xff1a typedef 在编译时被解释 xff0c 因此让编译器来应付超越预处理器能力的文本
  • 关于红外相机热成像相机的一些sdk使用方法

    将红外热成像相机转化成c格式 xff0c 从而用opencv进行显示 先看一些开发手册的函数 定义一个函数指针 typedef long CBF IR long lData long lParam CBF IR pCBFframe 相机接口
  • opencv显示图像并转换成灰度图(c++) day1

    include lt opencv2 opencv hpp gt include lt iostream gt using namespace cv int main Mat src 61 imread 34 aa jpg 34 Mat g
  • 古典问题(兔子生崽):有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少对

    基础不夯实 xff0c 工作两行泪 include lt stdio h gt int main 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 1 1 2 3 5 8 13 21 34 int m 61
  • 如果你还在用STM32F103,那么你OUT了

    自从ARM公司2007年首推出Cortex内核 xff0c ST凭借基于ARM CORTEX M3内核的STM32F1 xff0c 无疑成为了最大的赢家之一 特别是STM32F103系列 xff0c 更是成为市场上最通用的MCU系列之一 不
  • 瑞芯微RK1808开发板之进入系统

    参照RK1808 EVB用户指南 V10 20181226 pdf用户指南配置 文档获取地址https github com rockchip linux docs tree master SoC 20platform 20related

随机推荐

  • 瑞芯微RK1808编译rknn_demo

    操作步骤在ubuntu16 04下编译 官方给的sdk里的rknn demo不能直接编译 缺少一些动态链接库 xff0c 还需要完善一些CMakeLists txt内容 首先在rknn demo下新建一个build 进入 build文件夹下
  • libcurl在嵌入式设备C 的使用

    libcurl在嵌入式设备C 的使用 bingqingsuimeng的专栏 CSDN博客 linux configure prefix 61 root work code curl 7 61 1 curl linux disable sha
  • 某些项目因位于工作空间目录中而被隐藏

    今天晚上突然想建一个小项目 xff0c 建在了F project projectname 建完后发现项目并没有导入到Eclipse中 xff0c 于是又自己重新导入 xff0c 导入到最后一步就报了如题错误 在网上找了下原因 按照这篇博客h
  • C++关于文件检索的源码转译技巧(R"()"方式)

    在C C 43 43 编写代码的过程中 xff0c 经常会用到检索文件路径的时候 xff0c 首先要明确一点 xff0c 由于 符号是C C 43 43 的保留符号 xff0c 因此直接粘贴路径字符串在代码中是不能使用的 例如下面的方法 x
  • 嵌入式课程 之 中断和串口通信实验

    版权归如下公司 xff0c 禁止非授权转载 xff1a 北京西普阳光教育科技股份有限公司 xff08 https www simpleware com cn xff09 维周机器人科技有限公司 xff08 http www vejoe co
  • Linux系统tcp连接设置

    目录 net ipv4 tcp syn retriesnet ipv4 ip local port rangenet core somaxconnnet ipv4 tcp max syn backlognet core netdev max
  • 一种简单有效的锂电池充电均衡电路

    一种简单有效的锂电池充电均衡电路 这个均衡电路用的是三个一模一样的并联稳压电路组成的 xff0c 每个电池上并一个 电路原理图如下 xff1a 每个稳压电源都调节到4 2V 均衡的原理是 xff0c 当电池电压都小于4 2V时 xff0c
  • 【嵌入式开发工具】Makefile和Cmake

    工具配置 首先 xff0c 方便代码编辑 xff0c 安装sublime和vim xff0c 其中安装sublime过程见下 https blog csdn net yunna520 article details 114021153 注意
  • Arduino Uno PWM和IRremote库冲突问题

    问题发生环境 xff1a Arduino UNO R3控制板 xff0c 用两个L298N驱动板驱动4轮小车 xff0c 然后通过控制4路PWM来控制4个轮子的速度 xff0c 遥控方式为红外遥控 xff0c 使用的红外库是IRremote
  • [ERROR] 两个jar包中存在Qualified Name完全相同的引用冲突问题 解决方案

    分析 最近在搞Jedis xff0c 在引入jedis 2 9 0 jar和commons pool2 2 4 2 jar后初始化JedisPoolConfig时 xff0c 发现很多属性无法设置 xff08 如最大空闲连接等 xff09
  • 如何使用Visual studio C++(VC++)编译C?图解,详!!!

    如何使用Visual studio C 43 43 xff08 VC 43 43 xff09 编译C xff1f 图解 xff0c 详 xff01 xff01 xff01 之前在网上找过关于这方面的东西 xff0c 但是一直都没有看到有详细
  • 头文件与类的声明

    我们在开始学习C 43 43 时 xff0c 就应该养成规范大气的编程习惯 xff0c 头文件 header 的布局就是其中很重要的一个点 我们需要知道头文件中的防卫式声明 ifndef COMPLEX define COMPLEX 前置声
  • ubuntu C++ 和windows C# socket TCP通信

    TCP客户端代码 windows C https www cnblogs com sunev archive 2012 08 05 2604189 html using System using System Net using Syste
  • 多进程和多线程的优缺点

    在Linux下编程多用多进程编程少用多线程编程 IBM有个家伙做了个测试 xff0c 发现切换线程context的时候 xff0c windows比linux快一倍多 进出最快的锁 xff08 windows2k的 critical sec
  • 【C语言】链表及单链表基本操作

    1 什么是链表 xff1f 链表的分类 xff1f 链表是一种物理存储结构上非连续 非顺序的存储结构 xff0c 数据元素的逻辑顺序是通过链表中的指针链接次序实现的 数据结构中 xff1a 2 链表的分类 共有8种链表结构 3 单链表的基本
  • 14串聚合物锂电池保护板和电路图(带均衡功能)

    转载自 xff1a http bbs mydigit cn read php tid 61 746827 之前发过14串三元锂组装的帖子 xff0c 有坛友对保护板感兴趣 xff0c 还有的说串联充电 xff0c 早死早超生 xff0c 哈
  • HTTP的长连接和短连接

    一 什么是长连接 HTTP1 1规定了默认保持长连接 xff08 HTTP persistent connection xff0c 也有翻译为持久连接 xff09 xff0c 数据传输完成了保持TCP连接不断开 xff08 不发RST包 不
  • libcurl库的异步用法

    multi接口的使用会比easy 接口稍微复杂点 xff0c 毕竟multi接口是依赖easy接口的 xff0c 首先粗略的讲下其使用流程 xff1a curl multi init初始化一个multi curl对象 xff0c 为了同时进
  • 在ubantu16.04 配置ROS开发realsense435功能包

    本文主要对intel的一款实感相机realsense435 进行配置 xff0c 将ROS对realsense435支持的功能包进行安装 一 安装SDK 1 简要介绍 鉴于自己安装过程中出现的问题 xff0c 现在在安装之前先确认几件事情
  • 提高C++程序运行效率,减少运行时间的方法

    大致方法 xff1a 1 优化业务逻辑 xff0c 尽量少做事情 2 减少网络访问 xff0c IO等对外操作 3 如果有数据库 xff0c 优化SQL和数据库结构 4 优化算法 xff0c 比如冒泡排序改成快排等 5 优化代码的编写 这个