Effective C++ 条款30_透彻了解 Inlining 的里里外外_非同凡响

Understand the ins and outs of inlining

Inline 函数,多棒的主意呀!它们看起来像函数,动作像函数,比宏好得多(见条款 2 ),可以调用它们又不需蒙受函数调用所招致的额外开销。

更棒的是,你获得的比你想到的还多,因为 “ 免除函数调用成本 ” 只是故事的一部分而已。编译器最优机制通常被设计用来浓缩那些 “ 不含函数调用 ” 的代码,所以当你 inline 某个函数,或许编译器就因此有能力对它(函数本体)执行语境相关最优化。大部分编译器绝不会对着一个 “ outlined 函数调用 ” 动作执行如此之最优化。

然而编写成型就像现实生活一样,天底下没有免费的午餐。inline 函数也不例外。inline 函数背后的整体观念是,将 “ 对此函数的每一个调用 ” 都以函数本体替换之。我想不用叫统计学博士来告诉你答案你都应该懂得这样做可能增加你的目标码的大小。
在一台内存吃紧的机器上,过度热衷 inlining 会造成程序体积太大(对可用空间而言)。即使拥有虚内存,inline 造成的代码膨胀亦会导致额外的换页行为,降低指令高速缓存的击中率,以及伴随这些带来的效率损失。

换个角度说,如果 inline 函数的本体很小,编译器针对 “ 函数本体 ” 所产出的码可能比针对 “ 函数调用 ” 所产出的码更小。果真如此,将函数 inlining 确实可能导致较小的目标码和较高的指令高速缓存装置击中率。

记住,inline 只是对编译器的一个申请,不是强制命令。这项申请可以隐喻提出,也可以显式提出。隐喻方式是将函数定义于 class 定义式内:

class Person {
public:
	...
	int age() const { return theAge; }	// 一个隐喻的 inline 申请:
	...									// age 被定义于 class 定义式内
private:
	int theAge;
};

这样的成员函数通常是隐喻的 inline 申请,但 friend 函数也可以被定义于 class 内,如果真那样,它们也是被隐喻声明为 inline。

注:对于一般函数而言,编译器在编译期自动判别是否将其归为 inline 函数。

显式声明 inline 函数的做法则是在其定义式前加上关键字 inline。例如标准的 max template(来自< algorithm>)往往这样实现出来:

template<typename T>
inline const T& std::max(const T& a,const T& b){
	return a > b ? a : b;
}

Inline 函数通常一定被置于头文件内,因为大多数建置环境在编译过程中进行 inlining,而为了将一个 “ 函数调用 ” 替换为 “ 被调用函数的本体 ”,编译器必须知道那个函数长什么样子。
Inlining 在大多数 C++ 程序中是编译期行为。

Templates 通常也被置于头文件内,因为它一旦被使用,编译器为了将它具现化,需要知道它长什么样子。
Template 的具现化与 inlining 无关。

现在让我们先结束 “ inline 是个申请,编译器可加以忽略 ” 的观察。
大部分的编译器拒绝将太过复杂(带有递归或循环等)的函数 inlining,而且所有对 virtual 函数的调用(除非是最平淡无奇的)也都会使 inlining 落空。这不应该令你惊讶,因为 virtual 意味 “ wait ”,直到运行期才确定调用哪个函数,而inline 意味 “ 执行前,先将调用动作替换为被调用函数的本体 ”。

inline 认知:
程序库设计者必须评估 “ 将函数声明为 inline ” 的冲击:inline 函数无法伴随着程序库的升级而升级。换句话说,如果 f 是程序库内的一个 inline 函数,客户将 ” f 函数本体 “ 编进其程序中,一旦程序库设计者决定改变 f,所有用到 f 的客户端程序都必须重新编译。这往往是大家不愿看到的。然而如果 f 是 non-inline 函数,一旦它有任何修改,客户端只需重新连接就好,远比重新编译的负担少很多。如果程序库采取动态连接,升级版函数甚至可以不知不觉地被应用程序吸纳。
一开始先不要将任何函数声明为 inline。
不要忘记 80-20 法则:平均而言一个程序往往将 80% 的执行时间花费在 20% 的代码上。
因为它提醒你,作为一个软件开发者,你的目标是找出那些可以有效增进程序整体效率的 20% 的代码,然后将它 inline 或竭尽所能将它瘦身。


请记住

  • 将大多数 inlining 限制在小型、被频繁调用的函数身上。这可使日后的调试过程和二进制升级更容易,也可使潜在的代码膨胀问题最小化,使程序的速度提升最大化。
  • 不要只因为 function templates 出现在头文件,就将它们声明为 inline。
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值