0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

浅析C/C++代码并提高程序的编译速度

strongerHuang 来源:技术让梦想更伟大 作者:技术让梦想更伟大 2021-04-22 10:02 次阅读

代码的编译速度和代码的质量,可能与多种因素决定,今天给大家分享一下代码编译速度的问题。

硬件、编译器造成的

使用好点的电脑无疑是一个操作上的最佳选择,其次,对于编译器也是可以编译选项优化的,例如在VS环境中,可以通过配置属性来实现,具体步骤如下,大家可以参考:https://blog.csdn.net/yizhou2010/article/details/52635288

代码编写风格

多使用自加、自减指令和复合赋值表达式

你觉得使用i++ ,i = i + 1,i += 1有区别吗?我们来测试一下C代码:

void asd() {} int main() { int i=0; i++; asd();//方便区分上下文 i=i+1; asd(); i+=1; return 0; }

汇编

mov [rbp+i], 0//i的初始化 add [rbp+i], 1//i++; call _Z3asdv ; asd(void) add [rbp+i], 1//i=i+1; call _Z3asdv ; asd(void) add [rbp+i], 1//i+=1;

我们看到这个结果是一样的,但是在更加复杂的表达式中就会多生成几个指令了,而且用 i += 1 的,总是比写 i = i + 1的要稍微那么好看些。

除法换成乘法或者移位来表达

除法就是由乘法的过程逆推来的,依次减掉(如果x够减的)y^(2^31),y^(2^30),。。.y^8,y^4,y^2,y^1。减掉相应数量的y就在结果加上相应的数量,一般来说,更耗时间一些,用一个demo来测试一下

auto time_start = std::now(); int iCount = 100000; double k ; for (int i = 0; i 《 1000000; i++) { tmp = iCount / 2; } std::duration《double》 time_spend = std::now() - time_start; double test1 = time_spend.count() * 1000; cout《《“test1 cost ”《《time_cost《《“ ms”《《endl; time_start = std::now() ; for (int i = 0; i 《 1000000; i++) { tmp = iCount * 0.5f; } time_spend = std::now() - time_start; test2 = time_spend.count() * 1000; cout《《“test2 cost ”《《time_cost《《“ ms”《《endl; time_start = std::now() ; for (int i = 0; i 《 1000000; i++) { tmp = iCount 》》1; } time_spend = std::now() - time_start; test3 = time_spend.count() * 1000; cout《《“test3 cost ”《《time_cost《《“ ms”《《endl;

我们输出结果会发现,移位和乘法比除法要省3-5倍时间,移位相对而言是最省时间的。

多用直接初始化,少用拷贝初始化

string s1 = “hiya”; // 拷贝初始化 string s2(“hello”); // 直接初始化 string s3(10, ‘c’); // 直接初始化

当我们使用拷贝初始化时,我们要求编译器将右侧运算对象拷贝到正在创建的对象中,如果需要的话还要进行类型转换,会浪费一定的资源时间,而直接初始化是要求编译器使用普通的函数匹配来选择与我们提供的参数最匹配的构造函数和拷贝构造函数。

我们来看看Primer中怎么说的

当用于类类型对象时,初始化的复制形式和直接形式有所不同:直接初始化直接调用与实参匹配的构造函数,复制初始化总是调用复制构造函数。复制初始化首先使用指定构造函数创建一个临时对象,然后用复制构造函数将那个临时对象复制到正在创建的对象”

还有一段说到:

通常直接初始化和复制初始化仅在低级别优化上存在差异,然而,对于不支持复制的类型,或者使用非explicit构造函数的时候,它们有本质区别:

ifstream file1(“filename”)://ok:direct initialization ifstream file2 = “filename”;//error:copy constructor is private

局部变量、静态局部变量、全局变量与静态全局变量

局部变量是存在于堆栈中的,对其空间的分配仅仅是修改一次esp寄存器的内容即可;

静态局部变量是定义在函数内部的,静态局部变量定义时前面要加static关键字来标识,静态局部变量所在的函数在多调用多次时,只有第一次才经历变量定义和初始化;

当一个文件或者数据反复使用时,应该存储在全局变量中,避免重复加载使用;

静态全局变量是静态存储方式,静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效,在同一源程序的其它源文件中不能使用它。

静态变量是低效的,当一块数据被反复读写,其数据会留在CPU的一级缓存(Cache)中

代码冗余度

避免大的循环,循环中避免判断语句

在写程序过程中,最影响代码运行速度的往往都是循环语句,我记得当时在写matlab的时候,处理大数据,都是禁止用循环的,特别是多层嵌套的循环语句。

其次,尽量将循环嵌套控制在 3 层以内,有研究数据表明,当循环嵌套超过 3 层,程序员对循环的理解能力会极大地降低。同时,这样程序的执行效率也会很低。因此,如果代码循环嵌套超过 3 层,建议重新设计循环或将循环内的代码改写成一个子函数。

for (i=0;i《100;i++) { for (j=0;j《5;j++) { for (j=0;j《5;j++) { /*处理代码*/ } } }

多重 for 循环中,如果有可能,应当尽量将最长的循环放在最内层,最短的循环放在最外层,以减少 CPU 跨切循环层的次数

for (i=0;i《100;i++) { for (j=0;j《5;j++) { /*处理代码*/ } }

改为:

for (j=0;j《5;j++) { for (i=0;i《100;i++) { /*处理代码*/ } }

逻辑判断不要在循环中使用,当 for 循环的次数很大时,执行多余的判断不仅会消耗系统的资源,而且会打断循环“流水线”作业,使得编译器不能对循环进行优化处理,降低程序的执行效率

if (condition) { for (i = 0;i 《 n;i++) { /*处理代码*/ } } else { for (i = 0;i 《 n;i++) { /*处理代码*/ } }

尽量避免递归,递归就是不停的调用自身,所以非常消耗资源,甚至造成堆栈溢出和程序崩溃等等问题!

int Func(int n) { if(n 《 2) return 1; else return n*Func(n-1); }

因此,掌握循环优化的各种实用技术是提高程序效率的利器,也是一个高水平程序必须具备的基本功。

尽量不使用继承和多重继承

多重继承增加了类的继承层次的复杂性,调试难度增加当然风险也增加了,而且使用父类指针指向子类对象变成了一件复杂的事情,得用到C++中提供的dynamic_cast来执行强制转换。但是dynamic_cast是在运行期间而非编译期间进行转换的,因此会会带来一些轻微的性能损失,建议类型转换尽量采用c++内置的类型转换函数,而不要强行转换

少用模板,因为模板是编译期技术,大量采用模板也会增加编译时间

在c++primer3中,有一句话:

在多个文件之间编译相同的函数模板定义增加了不必要的编译时间简单点说,对于一个zhidaovector的函数,比如size(),如果在不同的cpp中出现,在这些文件编译的时候都要把vector::size()编译一遍。然后在链接的时候把重复的函数去掉,很显然增加了编译时间。模版函数需要在编译的时候实例化zhidao,所以呢,不把模版的实现代码放到头文件中的话(在头文件中实例化),那么每个使用到这个模版的cpp的都要把这个模版重新实例化一遍,所以增加了编内译时间

编码依赖性

声明与实现分离,删除不必要的#include

使用include时,只需要include这个接口头文件就好

并不是所有的文件都需要包含头文件 iostream,定义了输出函数引用就好

ostream头文件也不要,替换为 iosfwd, 为什么,参数和返回类型只要前向声明(forward declared )就可以编译通过

尽量减少参数传递,多用引用来传递参数。

bool func1(string s1, string s2) bool func2(string *s1, string *s2) bool func3(string &s1, string &s2)

指针和引用都不会创建新的对象,函数func2和func3不需要调用析构和构造函数,函数func1使用值传递在参数传递和函数返回时,需要调用string的构造函数和析构函数两次。

适当的采用PIMPL模式

很实用的一种基础模式,通过一个私有的成员指针,将指针所指向的类的内部实现数据进行隐藏。将实现放到CPP里,主要作用在于编译分离,其实是增加了编码量以及初次编译时长,增量编译才体现作用。例如:指针的大小为(64位)或32(8位),X发生变化,指针大小却不会改变,文件c.h也不需要重编译。

最后

方法还有很多,比如使用多线程,多任务并行编译,分布式编译,预编译等等,另外,在编译大型项目时,分布式编译更优,往往能够大幅度提升性能。

编译速度有多种原因决定,可能有些适用当下环境,也有些不适用。所以,需要结合自身情况,改进编译速度。

编辑:jq

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 函数
    +关注

    关注

    3

    文章

    4338

    浏览量

    62770
  • 代码
    +关注

    关注

    30

    文章

    4803

    浏览量

    68758
  • 编译器
    +关注

    关注

    1

    文章

    1636

    浏览量

    49176

原文标题:分析C/C++代码并提高程序的编译速度

文章出处:【微信号:strongerHuang,微信公众号:strongerHuang】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    HighTec C/C++编译器套件全面支持芯来RISC-V IP

    德国萨尔布吕肯,2024年12月5日——世界领先的汽车C/C++编译器解决方案提供商HighTec EDV Systeme GmbH宣布全面支持芯来科技的RISC-V IP。HighTec编译
    的头像 发表于 12-23 09:04 229次阅读
    HighTec <b class='flag-5'>C</b>/<b class='flag-5'>C++</b><b class='flag-5'>编译</b>器套件全面支持芯来RISC-V IP

    HighTec C/C++编译器支持Andes晶心科技RISC-V IP

    汽车编译器解决方案领先供货商HighTec EDV-Systeme GmbH宣布其针对汽车市场的高度优化C/C++编译器支持Andes晶心科技的RISC-V IP。这项支持对汽车软件开
    的头像 发表于 12-12 16:26 274次阅读

    MSP430优化C/C++编译器v21.6.0.LTS

    电子发烧友网站提供《MSP430优化C/C++编译器v21.6.0.LTS.pdf》资料免费下载
    发表于 11-08 14:57 0次下载
    MSP430优化<b class='flag-5'>C</b>/<b class='flag-5'>C++</b><b class='flag-5'>编译</b>器v21.6.0.LTS

    ARM优化C/C++编译器 v20.2.0.LTS

    电子发烧友网站提供《ARM优化C/C++编译器 v20.2.0.LTS.pdf》资料免费下载
    发表于 11-07 10:46 0次下载
    ARM优化<b class='flag-5'>C</b>/<b class='flag-5'>C++</b><b class='flag-5'>编译</b>器 v20.2.0.LTS

    TMS320C6000优化C/C++编译器v8.3.x

    电子发烧友网站提供《TMS320C6000优化C/C++编译器v8.3.x.pdf》资料免费下载
    发表于 11-01 09:35 0次下载
    TMS320<b class='flag-5'>C</b>6000优化<b class='flag-5'>C</b>/<b class='flag-5'>C++</b><b class='flag-5'>编译</b>器v8.3.x

    TMS320C28x优化C/C++编译器v22.6.0.LTS

    电子发烧友网站提供《TMS320C28x优化C/C++编译器v22.6.0.LTS.pdf》资料免费下载
    发表于 10-31 10:10 0次下载
    TMS320<b class='flag-5'>C</b>28x优化<b class='flag-5'>C</b>/<b class='flag-5'>C++</b><b class='flag-5'>编译</b>器v22.6.0.LTS

    C7000优化C/C++编译

    电子发烧友网站提供《C7000优化C/C++编译器.pdf》资料免费下载
    发表于 10-30 09:45 0次下载
    <b class='flag-5'>C</b>7000优化<b class='flag-5'>C</b>/<b class='flag-5'>C++</b><b class='flag-5'>编译</b>器

    OpenHarmony标准系统C++公共基础类库案例:HelloWorld

    1、程序简介该程序是基于凌蒙派OpenHarmony-v3.2.1标准系统C++公共基础类库的简单案例:HelloWorld。主要讲解C++公共基础类库案例如何搭建和
    的头像 发表于 08-13 08:23 546次阅读
    OpenHarmony标准系统<b class='flag-5'>C++</b>公共基础类库案例:HelloWorld

    C++中实现类似instanceof的方法

    并执行特定于类型的操作。无论是在编译时检查类型,还是在运行时动态标识对象类型,C++ 都提供了强大的机制来获取类型信息
    的头像 发表于 07-18 10:16 614次阅读
    <b class='flag-5'>C++</b>中实现类似instanceof的方法

    OpenCV图像识别C++代码

    的头文件 在您的C++代码中,包含以下必要的头文件: # include # include # include # include # include # include # include 读取图像
    的头像 发表于 07-16 10:42 2140次阅读

    SEGGER编译器优化和安全技术介绍 支持最新CC++语言

    代码生成,SEGGER编译器生成非常小的代码,非常适合内存受限的环境,而不会牺牲执行速度。 2) 速度优化:在最高优化级别,SEGGER
    的头像 发表于 06-04 15:31 1492次阅读
    SEGGER<b class='flag-5'>编译</b>器优化和安全技术介绍 支持最新<b class='flag-5'>C</b>和<b class='flag-5'>C++</b>语言

    keil用c++编译含有rtos模块时的错误问题怎么解决?

    thePool = pvPortMalloc(sizeof(os_pool_cb_t));由于不懂c++,查阅资料得知c99编译器是允许暗含转换的,但是c++不允许,因此出现错误。我的
    发表于 05-09 08:29

    C/C++代码动态测试工具VectorCAST插桩功能演示#代码动态测试 #C++

    C++代码
    北汇信息POLELINK
    发布于 :2024年04月18日 11:57:45

    C语言#define的应用

    C/C++ 编程语言中,当程序编译时,被发送到编译器,编译器将
    发表于 03-06 11:29 393次阅读
    <b class='flag-5'>C</b>语言#define的应用

    RX系列V3.06.00的C/C++编译器包数据手册

    电子发烧友网站提供《RX系列V3.06.00的C/C++编译器包数据手册.pdf》资料免费下载
    发表于 01-26 15:57 1次下载
    RX系列V3.06.00的<b class='flag-5'>C</b>/<b class='flag-5'>C++</b><b class='flag-5'>编译</b>器包数据手册