程序优化——C/C++参数优化
来源:互联网 发布:微信小程序服务器php 编辑:程序博客网 时间:2024/06/05 21:07
C/C++变量优化:
C/C++语言中变量依据其定义的方式不同,其存放位置可以分为寄存器、栈区、堆区和静态存储区三种区域。
(1)寄存器上分配。当函数中定义的局部变量不多,且没有对局部变量的取地址操作时,则会将该变量会分配在寄存器中。当进行运算时,直接读寄存器,速度非常快。
(2)在栈上分配。用户在函数体中定义了较多局部变量后,或对变量进行取地址操作,通过结构体返回值,则相应的变量会放在栈空间。函数执行结束后,这些存储单元自动被释放。一般的情况,由于栈区中数据在函数中都会被重复用到,加载时都能够Cache命中,一个周期内完成,效率很高,但是其分配的内存容量有限。
(3)从静态存储区域分配。在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量、static变量。在ARM9平台,从静态区加载数据到寄存器,一般需要3个周期。如果在循环中,无序访问数据造成Cache不能命中,那么每次都需要3个周期加载,则比较费时。所以尽量让数据顺序访问,提高Cache命中率和访问速度。
(4)从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请的内存,程序员自己负责用free或delete释放内存,其访问速度和静态区相同。
参看下面的代码:
比较左边的C代码,差别在于将step指向的值赋给局部变量temp。程序员一般会认为,A框中列出的 C代码,节约了一个整形变量的存储空间,而且直接用指针进行运算,少了一条赋值语句,速度应该更快。其实不然,将两部分C代码汇编后,再比较右边汇编指令,并没有节约存储空间(原因是变量分配在寄存器中),而且b中汇编代码到在计算每二个表达式时,少用了一条LDR指令。为什么呢?因为编译器不能断言step指针,是否和t1指向同一个地址,即在计算第一个表达式后,其指向值是否发生了改变。为了保证程序正确性,在第二次引用时,只能再次从内存加载step,增加了一个LDR指令周期数。对于这种指针型参数,编译器不能做到优化,如果step处于循环中,情况则更加糟糕。所以只能由程序员自己控制,配合编译器做好优化工作。
建议2:尽量选用32位的数据类型变量。
ARM 指令集支持有符号/无符号的8 位、16 位、32位整型变量。恰当的使用变量类型,不仅可以节省目标代码指令,并且可以提高代码运行效率。在程序编写过程中,应该尽可能地避免使用char、short 型的局部变量,因为操作8位/16位局部变量往往比操作32位变量需要更多指令, 请对比下列函数和它们的汇编代码。
首先比较左边的C代码,唯一的差别是计数器变量i的定义不同。一般认为,采用short类型变量比int节约了两个字节。其实并没有,因为ARM是32位运算(除非处于Thumb模式),寄存器也是32位,并不存在节约了两个字节。在i定义为32整型时,在对应的汇编代码中可以说编译器已经做到极致优化,其将循环结束条件都进行了优化。而在计数器定义为16位整形时,编译器则不但没有优化循环条件,而且还将计数器每次加1之后,利用移位操作对其进行宽度调整,将32位整形其转化为16位整型,增加了两条MOV指令。所以,在变量定义中,尽量使用32位宽度的数据类型,小于32位宽度的变量不但没有节约内存,还增加了很多无用的指令操作。在定义局部变量时,选用32位的数据有利于编译器优化。
(2)、参数传递的优化
ARM在函数调用时,如果参数少于四个,则通过R0-R3传递参数。如果多于四个参数,则会将参数从右向左的顺序入栈。所以,在函数设计时,尽量限制函数的参数,不要超过4个,这样可以省去参数入栈操作,提高函数调用效率。函数func_1以传值的方式传递参数,必然会调用CString的拷贝构造函数和析构函数,函数运行过程中不会改变调用者的内容。
函数func_2以指针的方式传递参数,不会调用CString的拷贝构造函数和析构函数,但函数运行过程中,可以对调用者参数所指向的内容进行改变,会造成不安全性。为了保证调用者的值不会被改变,同时也不调用拷贝函数,那么就可以引用传递方式传递参数,在类型前加上const关键字。
(3)、合理使用内联函数
在编译内联函数时,编译器首先对参数和返回值进行检查,确认正确后,内联函数的代码就会直接替换函数调用。这样,就可以省去函数调用的开销,提高函数的执行效率。但是,每一内联函数的调用处都会有其一份拷贝,无疑增大了代码体积,程序加载后消耗更多的内存空间。所以,内联函数是典型的“以空间换时间”的优化策略。如果函数体内代码的执行时间远大于函数调用开销,那么内联的意义就不大了。
所以,使用内联函数时注意以下事项:
(1)将大多数内联函数限制在小型、被频繁调用的函数身上。
(2)内联会导致目标代码体积变大,在空间有限的情况下,不宜使用内联函数。
现在的编译器,都会拒绝将过于复杂的函数内联,自动地取消不值得内联的函数。
(4)、分支优化
下面将讨论条件分支的一些有效优化方法。
1.把条件分支移动到循环外
先看如下代码,
对于A代码中奇偶模式的随机分支,即使当前最好的CPU也不可能做出好的预测,对于这种情况就要改写成两个for循环,一个处理偶数,一个处理奇数,如代码B。
2.单独处理条件分支。
图像处理算法,经常需要判断边界像素点,进行特殊处理;可以考虑的优化方案是把边界区域和内部区域分开处理;或者条件允许的话,扩大原图像的边界,形成"哨兵"数据,这样访问像素的时候就不用考虑越界的问题了。
3.合并多个条件来减少条件分支
编写代码过程中,常常使用 if((pStr1==0)&&( pStr2==0)&&( pStr3==0)),编译器将生成3个条件跳转指令,而且使分支可预测性大大降低,可以改写为: if((pStr1|pStr2|pStr3)==0) 从而同时改进代码和分支预测准确率。经测试,在GUN ARM编译器的O1优化级别上,能够对于上述语句自动优化。对下面的语句,编译器不能做到优化(因为编译器不能断定b0,b1,b2>=0),会产生三个比较指令和三个跳转指令,这种情形就需要程序员自己优化。
if((b0>=64)||(b1>=64)||(b2>=64)) //b0,b1,b2>=0
改写为:if((b0|b1|b2)>=64)
- 程序优化——C/C++参数优化
- c程序性能优化
- C程序优化
- C 程序优化技术
- C程序优化方法
- C 程序性能优化
- 优化C语言程序
- c语言程序优化
- c/c++程序优化
- 单片机C程序优化
- C程序优化
- C程序优化之路
- C程序优化之路
- C语言程序优化方法
- c程序性能优化读书笔记
- C语言程序如何优化
- 【C语言】程序中的优化
- 程序优化的方法(C/C++)
- Vue2 指令v-on v-model 各种表单控件
- 管理表空间和数据文件
- Netty4.0学习笔记系列之二:Handler的执行顺序
- Android二维码识别技术
- Netty4.0学习笔记系列之三:构建简单的http服务
- 程序优化——C/C++参数优化
- 2017阿里巴巴校招在线笔试——货架格子编号
- Netty4.0学习笔记系列之四:混合使用coder和handler
- GIT-子模块
- Mybatis association与collection关联查询
- **a[3][4],(**a)[3][4], *(*a)[3][4], *(*a[3])[4]等变量占用的内存
- html,css,js代码加载顺序问题
- java 获取kafka offsets(偏移量)
- Netty4.0学习笔记系列之五:自定义通讯协议