====================================================== 大家请把我的文章当参考,详细内容 还请参照 权威书 籍如果文中有错误和遗漏, 请指出,Aear会尽力更正, 谢谢! Aear Blog: http://blog.sina.com.cn/u/1261532101 ======================================================
今天讲的是inline. 其实大家都知道,inline并不是c++独有的特性。其实Aear的个人观点,inline根本就不应该是语言标准的一部分。因为inline的具体实现,跟compiler有很大的关系,把inline说成compiler的一个开关更合适一些。今天我们就说下在什么情况下可以使用inline, 使用inline的好处和坏处。
================= Inline能做什么 =================
关于inline能做什么,什么原理,相信很多人都知道。Aear再在这里重复一下:
1. inline能够去掉function call的overhead 2. inline能够帮助函数调用时候常量参数进行优化
举个例子来说明一:
void function(void) {
}
function();
在调用function()前,一般会进行下面的操作(根据操作系统和compiler来决定,并不一定按照这个顺序): 保存IP FP SP等寄存器的值(压栈) 把参数压栈 建立新的 IP FP SP等以便进行函数调用 函数有可能还需要保存一些寄存器数据到内存。
在完成function()调用后,也会进行一系列的操作: 如果有返回值,一般在一个寄存器中,需要进行一系列的处理工作 恢复IP FP SP等保存的寄存器值。
整个过程大约需要花费20-150个CPU clock cycle.具体要根据参数的多少,还有函数的具体实现来决定。使用inline function,可以去掉这些函数调用前和调用后的工作,从而节省CPU clock cycle,提高程序的运行效率。
关于2的情况,例子如下: float test = cos(0);
如果cos不是inline,compiler有可能会生成cos()的调用代码,计算cos(0),然后返回给test.如果cos被inline了,由于cos(0) == 1,所以经过优化的代码可是直接生成test = 1.
================= Inline 坏处 =================
Aear个人认为,inline只在特定的情况下起到正面的作用,在大部分情况下,起到负面的作用,也就是说,坏处多过好处。使用inline的缺点包括:
1. 不容易debug,并不是所有的debugger都支持在inline function中设置断点,不过幸运的是,VS .net支持。
2. 很容易增加代码的长度,在很多情况下,代码长度的增加直接导致了代码执行速度的减慢,特别是在循环中,因为整个循环的长度超过了cache的容量,从而使得cache miss的几率增大
3. 使得编译的时间过长。如果一个inline function被更改,所有调用这个程序的代码都要重新编译。对于一般的游戏代码,整个编译时间可能会有1,2个小时。
4. 暴露了代码实现细节。由于inline function必须在header file中定义,所以导致使用者能够看到你的代码实现细节。
Inline并不是一个好东西,也不是一个万能药,在很多时候,它会给你带来无穷无尽的麻烦。所以在inline的时候,最好慎重慎重。
================= 如何使用 Inline =================
下面给出几种inline的用法。由于个人的代码结构,平台,实现细节的不同,请参考相关资料自行决定.
========= Platform Inline ========= 首先要指出的是,inline并不是一条命令,而是对compiler的建议。compiler会具体分析你的代码,如果合适inline,才会进行inline的工作。在VS .net下,一共有3个关于inline的定义:
inline __inline (__inline__ for GCC) __forceinline
inline和__inline相同,只有compiler认为inline合适,才会进行。__forceinline是强制compiler进行inline. 比如:
__forceinline void f(void);
如果你使用了inline或者__inline, 并且打开了编译器的 /Ob开关,那么compiler就会告诉你一个function是不是被真正的inline了。
========= Macro Inline =========
使用一个macro来控制inline,我们可以做如下定义:
#ifdef _DEBUG #define COND_INLINE #else #define COND_INLINE inline #endif
// inline function declaration COND_INLINE function();
这样我们在debug模式下,就不会有inline带来的烦恼了。只有在release version,编译器才会进行真正的inline.
========= Inline Condition =========
一般来讲,根据程序员主观感觉inline得到的很有可能就是负面的效果。下面的一些情况,能够帮大家决定是否inline一个函数:
1. 使用profiler分析的结果告诉你需要inline一个function。这个是最有把握的。windows下比较流行的profiler是vTune
2. class的属性访问函数(Accessor Methods)。一般来讲,这些是可以inline的,而且很多compiler都会帮你自动inline这些函数。
3. 小函数。这个需要举个例子说明,代码如下:
int Cal(int x, int y, int z) { return x+y-z; }
我们可以看到 Cal的代码很少,就是计算 x+y-Z,最多也就3,4条汇编代码。但是如果不inline, Cal函数的调用和返回过程所产生的代码,都比cal实际运行的代码多。因此Cal最好inline,通过消除Cal的调用和返回的代码,可以减少可执行程序的大小,同时加快运行的速度。
因此,一般来讲,5行内逻辑结构简单的代码,可以比较放心的inline,大过5行的代码,就需要仔细的斟酌了。
================= 不能Inline的情况 =================
1. 包含Static Member的function. 很多compiler不能inline有static member的function.或者即使inline了,也会生成错误的代码(把static当成local处理)。所以最好先测试下compiler支持不支持static inline.测试代码可以这么写:
inline void f(void) { static int k = 1; k++; std::cout<<k<<endl; }
f(); f();
2. virtual member function. Virtual Member Function在很多情况下是不能被inline的(根据compiler而定),因为具体virtual的哪个版本被调用,要在运行时候才能决定。不过我们可以通过创建一个新的可以inline的non-virtual member function,来提高运行速度。
================= 结论 =================
总的来说,inline的坏处多过好处,请大家inline的时候,慎重慎重再慎重,并不是所有的函数都适合inline的。
|