1.尽量减少值传递,多用指针和引用来传递参数
值传递要拷贝对象,引用传递不用
2.++i和i++的效率问题
i++开辟了临时变量,效率低
3.将小粒度函数声明为内联函数
内联函数直接会直接展开,不需要函数调用的开销
4.减少函数调用
函数调用需要两次跳转,外加栈帧的内存操作
5.优先使用迭代而不是递归
理由和上面一样
6.多使用直接初始化,而不是拷贝初始化
1)当对一个类对象初始化时,如果“=”右边不是一个类对象时,会先调用转换构造函数生成一个临时变量作为拷贝构造函数的形参,再调用拷贝构造函数;编译器可能会给你优化,把 ClassTest ct2 = "ab"; 优化为 ClassTest ct2("ab") ,直接调用转换构造函数初始化,但有的编译器它也不优化
参考资料:https://www.jb51.net/article/54773.htm
2)所以,拷贝初始化有时会就多一个临时对象的开销
7.尽量少使用dynamic_cast
dynamic_cast运算符用于将基类的指针或引用安全地转换成派生类的指针或引用,这种转换是非常低效的,对程序的性能影响也比较大,尽量少用
8.gcc编译优化
gcc编译的时候有O1~O3的优化选项,O0为不优化,gcc编译时的默认值
9.循环引发的选择
//代码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(); }
1)如果这多个函数的代码语句很少,则代码1的运行效率高一些,但是若fun1和fun2的语句有很多,规模较大,则代码2的运行效率会比代码1显著高得多
2)由于CPU只能从内存在读取数据,而CPU的运算速度远远大于内存,所以为了提高程序的运行速度有效地利用CPU的能力,在内存与CPU之间有一个叫Cache的存储器,它的速度接近CPU。而Cache中的数据是从内存中加载而来的,这个过程需要访问内存,速度较慢
3)Cache的设计原理:时间局部性和空间局部性,时间局部性是指如果一个存储单元被访问,则可能该单元会很快被再次访问,这是因为程序存在着循环。空间局部性是指如果一个储存单元被访问,则该单元邻近的单元也可能很快被访问,这是因为程序中大部分指令是顺序存储、顺序执行的,数据也一般也是以向量、数组、树、表等形式簇聚在一起的
4)如果fun1和fun2的代码量很大,例如都大于Cache的容量,则在代码1中,就不能充分利用Cache了(由时间局部性和空间局部性可知),因为每循环一次,都要把Cache中的内容踢出,重新从内存中加载另一个函数的代码指令和数据,而代码2则更很好地利用了Cache,利用两个循环语句,每个循环所用到的数据几乎都已加载到Cache中,每次循环都可从Cache中读写数据,访问内存较少,速度较快,理论上来说只需要完全踢出fun1的数据1次即可
10.局部变量VS静态变量
1)很多人认为局部变量在使用到时才会在内存中分配储存单元,而静态变量在程序的一开始便存在于内存中,知道程序结束才销毁,所以使用静态变量的效率应该比局部变量高,其实这是一个误区
2)局部变量使用的是栈,当一个局部变量被反复读取的时候,变量会留在CPU的Cache中,访问速度非常快,这样来看就是局部变量效率更高了
11.在稿纸上简化方程式
12.时间复杂度和空间复杂度的置换
13.尽量用移位操作>>和<<来代替乘除法
14.字符数组的初始化
字符数组如果可以不memset()就不memset()
15.对于确实不会抛出异常的函数,用throw()或noexcept指明
1)noexcept说明符:若修饰函数(紧跟在参数列表后面),则意为承诺编译器这个函数不抛出任何异常
2)编译器看到这个承诺,就不会创建额外的异常处理代码,程序得到优化
3)C++11之前使用throw()来指明某个函数不会抛出异常
4)以下情形鼓励使用noexcept:
- 移动构造函数(move constructor)
- 析构函数(destructor):在新版本的编译器中,析构函数是默认加上关键字noexcept的
5)在不是以上情况或者没把握的情况下,不要轻易使用noexcept
参考资料:
https://blog.csdn.net/yunhua_lee/article/details/6381866
https://www.jb51.net/article/54792.htm
来源:https://www.cnblogs.com/Joezzz/p/10568057.html