- 前言
从这一章开始我们就跳开设计的界限,开始着眼于性能。这就意味着,接下来的设计可能会为了追求极致的性能放弃一些设计。 - 什么是内联
内联是一种类似宏展开的方法调用,在调用方法的内部将被调用方法展开。类似的调用形式就是在函数的开头加上inline关键字。
inline int func() {
return -1;
}
- 方法调用代价
方法调用是有代价的,调用时涉及到不同的寄存器的存取,寄存器的调用序列大致是:先将参数以相反的顺序压栈;要返回的指令地址被压栈,调用指令从调用方法的第一条指令开始;存储所有用到的寄存器到堆栈。函数返回的顺序依次是:返回值从寄存器0或者1中将值取出并返回;恢复函数调用之前的数据以及上下文信息。上下文的寄存器其实有很多,因此函数调用的时候实际上会消耗许多的时钟(25-100)周期。再者,返回值的类型也会影响时钟,如果返回的对象很复杂,再加上默认的一些方法,那么势必也会增加执行的时间。
因此使用内联的方式,就可以优化上面所说的几点消耗,使用内联的方法时不需要函数的跳转。还有一些与性能相抵触的因素,编译器可以很好的优化小的窗口,但是没办法优化跳转的函数。 - 为何使用内联
一般来说一个大的系统中函数调用带来的性能损耗也是不容小觑的。作者在文中也用他自己的例子做例子,10000行的网络架构的代码在最后两天优化内联之后,性能提升了40%。内联可以说是c++中最直接的性能提升的方法,因为可以直接的减少函数的调用。 - 内联详述
尽管表面上看相对简单,但实际上内联还是挺复杂的。内联说到底其实看的是编译器、配置器、优化器方面的优化工作。目前的编译器有些缺乏自动化,有些不支持内联包含循环或者类的静态变量。
保留inline关键字是一种建议,它能提醒编译器当前的代码采用嵌入而不是调用的方式。同时,有时候编译器其实会在编译的时候对代码进行内联,因为我们也不知道自己写的代码在编译的时候会不会变成内联的形式。 - 内联虚方法
虚方法我们一般是不会进行内联使用的,因为虚函数实际上内部是调用指针,在内联这种直接替代的形式会调用错误。但是某些虚函数的场景也可以使用,我们知道虚函数实现多态都是需要申明基类的指针,然后多态的调用子类的函数,如果我们直接使用确定的类和函数,那么就不存在虚函数的多态的作用,就相当于一个正常的函数调用,这样的话就可以实现内联。当然这样的方法也一般不会推崇的,代码会看起来比较奇怪,我们如果确定要使用内联,不得已的情况下还不是用到虚函数。 - 通过内联获得性能
我们举一个简单的例子:
int add(int a, int b)
{
return a+b;
}
这个函数如果单单看函数内部的逻辑可能也就只有一到两个生命周期,但是作为正常的函数,调用的时候会浪费掉20个时钟周期,这样的话就会导致性能的下降,那如果这样的情况发生一百万次呢?这将会是一个不小的消耗。因此内联的使用不仅可以使程序在编译器中更加的简洁,也可以减少时钟周期,提高性能。
- 总结
内联其实就是一种减少调用开销,代替调用方法的手段,主要是一种执行时优化,但也会产生较小的执行映像。