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

    文章

    3864

    浏览量

    61307
  • 代码
    +关注

    关注

    30

    文章

    4554

    浏览量

    66732
  • 编译器
    +关注

    关注

    1

    文章

    1575

    浏览量

    48606

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

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

收藏 人收藏

    评论

    相关推荐

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

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

    求助,如何将C++代码从GNU移植到Tasking编译器?

    TC399 控制器支持且 CAN 编译C/C++ 标准库?或者您是否提供任何帮助/指导来修复移植问题? 请注意:我已经阅读了文档—— “GCC to TASKING 英飞凌AURIX-WEB 迁移指南”。 但这并不能帮助
    发表于 01-31 07:29

    C++写STM32程序编译后会有执行效率慢的问题吗?

    C++写STM32程序编译后,会有执行效率慢的问题吗
    发表于 09-20 07:56

    请问怎么用KendryteIDE编译c++开发k210?

    请问怎么用KendryteIDE编译c++开发k210?
    发表于 09-14 08:58

    如何为原生C++开发设置Android Studio

    。 为了进一步提高性能,您需要程序集使用程序代码。 但是,使用ARM Neon内部函数可以避免编写汇编函数的复杂性。 相反,您只需要用C/
    发表于 08-17 08:28

    C++演示中的推理速度比Python演示中的推理速度更快是为什么?

    在同一主机机上采用相同型号的 Ran Object Detection C++ 演示 和 对象检测 Python 演示 。 C++ 演示中的推理速度比 Python 演示中的推理速度
    发表于 08-15 06:52

    Arm C/C++编译器22.1版开发人员和参考指南

    提供帮助您使用ARM®编译器Linux版的ARM®C/C++编译器组件的信息。 ARM®C/C++
    发表于 08-11 07:46

    Arm C/C++编译器开发人员和参考指南

    提供帮助您使用Arm®编译器Linux版的Arm®C/C++编译器组件的信息。Arm®C/C++
    发表于 08-10 06:17

    如何为Arm编译Cc++代码

    编写CC++应用程序时,需要使用编译器工具链将其编译为机器代码。然后,您可以在基于Arm的处理
    发表于 08-02 17:28

    如何提高编译速度

    程序员做软件开发,比较讨厌一件事,那就是编译代码速度很慢的问题。 尤其是C/C++代码,编码
    的头像 发表于 07-06 10:53 925次阅读
    如何<b class='flag-5'>提高</b><b class='flag-5'>编译</b><b class='flag-5'>速度</b>

    H8S、H8/300系列C/C++编译程序、汇总程序、优化连接编译程序(RCJ10B0001-0100)

    H8S、H8/300系列C/C++编译程序、汇总程序、优化连接编译程序(RCJ10B0001-0100)
    发表于 06-28 18:50 0次下载
    H8S、H8/300系列C/<b class='flag-5'>C++</b><b class='flag-5'>编译程序</b>、汇总<b class='flag-5'>程序</b>、优化连接<b class='flag-5'>编译程序</b>(RCJ10B0001-0100)

    如何为xtensa编译C++

    我想为 xtensa 编译简单的 C++ 代码,但我不能。 代码:全选#include \"ets_sys.h\" #include \"osapi.h\"
    发表于 06-09 07:02

    H8S、H8/300系列C/C++编译程序、汇总程序、优化连接编译程序(RCJ10B0001-0100)

    H8S、H8/300系列C/C++编译程序、汇总程序、优化连接编译程序(RCJ10B0001-0100)
    发表于 05-09 20:00 0次下载
    H8S、H8/300系列C/<b class='flag-5'>C++</b><b class='flag-5'>编译程序</b>、汇总<b class='flag-5'>程序</b>、优化连接<b class='flag-5'>编译程序</b>(RCJ10B0001-0100)

    支持Devops,C/C++/Java等的代码静态测试工具Klocwork快速入门 #代码静态测试

    JAVAC++
    北汇信息POLELINK
    发布于 :2023年05月09日 18:05:24

    SuperHTM RISC引擎C/C++编译程序、汇总程序、优化连接编译程序 编译程序包V.9.01 用户手册

    SuperHTM RISC引擎C/C++编译程序、汇总程序、优化连接编译程序 编译程序包V.9.01 用户手册
    发表于 05-04 18:40 0次下载
    SuperHTM RISC引擎C/<b class='flag-5'>C++</b><b class='flag-5'>编译程序</b>、汇总<b class='flag-5'>程序</b>、优化连接<b class='flag-5'>编译程序</b> <b class='flag-5'>编译程序</b>包V.9.01 用户手册