一.影响C++性能的基本原理:
1. I/O的开销是最昂贵的
2. 函数调用的开销是一个因素,因此我们应该内联短小,频繁调用的函数
3. 复制对象的开销是昂贵的。最好选择按引用传递,而不是值传递。
4. 最好采用栈内创建对象,而不是采用堆创建对象 一般在堆内创建对象是在栈内创建对象花费的时间是20备左右。
5. 理解每种操作的代价。
二.临时对象:
产生临时对象一般来说有如下两种场合。
(1) 当实际调用函数时传入的参数与函数定义中声明的变量类型不匹配。
(2) 按值传递
(3) 当函数返回一个对象时
class Rational
{
Public :Rational(int a=0,int = 1):m(1),n(b){}
Private:
Int m;
Int n;
}
Rational r1(100);
Rational r2= Rational(100);
Rational r2=100; //两次构造和一次析构
优化方式:
1. 传值和传引用
2. 返回引用
3. 构造函数申明为explicit并重载+,-,=之类的运算符。
三.内联:
函数调用的开销:
Void foo()
{
I=func(a,b,c);
}
调用者(这里是foo)在调用前需要执行如下操作。
(1)参数压栈:这里是a、b和c。压栈时一般都是按照逆序,因此是c->b->c。如果a、b和c有对象,则需要先进行拷贝构造。
(2)保存返回地址:即函数调用结束返回后接着执行的语句的地址,这里是②处语句的地址。
(3)保存维护foo函数栈帧信息的寄存器内容:如SP(堆栈指针)和FP(栈帧指针)等。到底保存哪些寄存器与平台相关,但是每个平台肯定都会有对应的寄存器。
(4)保存一些通用寄存器的内容:因为有些通用寄存器会被所有函数用到,所以在foo调用func之前,这些寄存器可能已经放置了对foo有用的信息。这些寄存器在进入func函数体内执行时可能会被func用到,从而被覆写。因此foo在调用func前保存一份这些通用寄存器的内容,这样在func返回后可以恢复它们。
接着调用func函数,它首先通过移动栈指针来分配所有在其内部声明的局部变量所需的空间,然后执行其函数体内的代码等。
最后当func执行完毕,函数返回时,foo函数还需要执行如下善后处理。
(1)恢复通用寄存器的值。
(2)恢复保存foo函数栈帧信息的那些寄存器的值。
(3)通过移动栈指针,销毁func函数的栈帧,
(4)将保存的返回地址出栈,并赋给IP寄存器。
(5)通过移动栈指针,回收传给func函数的参数所占用的空间。
缺点:增大代码编译后长度
对于小型的,频繁调用的函数,而对于很长的函数基本上不适用
四.内存池
new/delete或malloc/free在堆上分配和释放内存的开销:
1.在堆上分配内存有额外开销。
2.造成内存碎片,影响系统性能。
应用实例:linux slab分配器,apache内存池
设计考虑固定大小还是变长,单线程还是多线程。六.缓存
更新策略FIFO LRU LFU
Memcache
五.理解STL的应用场合
Vector 动态数组,内存空间是连续的
List 双向;列表
Multiset 按序排列的容器,内部采用红黑树
| 数组 | Vector | list | Multiset | 原因 |
后端插入 | 70ms | 250ms | 930ms | 6500ms | Vector动态增长时所带来的开销 List指针所带来的开销 |
后端删除 |
| 60ms | 750ms |
| List指针操作所带来的开销 |
前端删除 |
| 700ms | 7ms |
| 复制所带来的开销 |
遍历( | 110ms | 110ms | 2600ms |
| 局部性原理指针访问的开销 |
查找 | 40ms | 40ms | 800ms | 0.06ms | List程序局部性原理指针访问的开销 Multiset 红黑树高效查找 |