Now, I know it\'s because there\'s not the overhead of calling a function, but is the overhead of calling a function really that heavy (and worth the bloat of having it inli
Consider a simple function like:
int SimpleFunc (const int X, const int Y)
{
return (X + 3 * Y);
}
int main(int argc, char* argv[])
{
int Test = SimpleFunc(11, 12);
return 0;
}
This is converted to the following code (MSVC++ v6, debug):
10: int SimpleFunc (const int X, const int Y)
11: {
00401020 push ebp
00401021 mov ebp,esp
00401023 sub esp,40h
00401026 push ebx
00401027 push esi
00401028 push edi
00401029 lea edi,[ebp-40h]
0040102C mov ecx,10h
00401031 mov eax,0CCCCCCCCh
00401036 rep stos dword ptr [edi]
12: return (X + 3 * Y);
00401038 mov eax,dword ptr [ebp+0Ch]
0040103B imul eax,eax,3
0040103E mov ecx,dword ptr [ebp+8]
00401041 add eax,ecx
13: }
00401043 pop edi
00401044 pop esi
00401045 pop ebx
00401046 mov esp,ebp
00401048 pop ebp
00401049 ret
You can see that there are just 4 instructions for the function body but 15 instructions for just the function overhead not including another 3 for calling the function itself. If all instructions took the same time (they don't) then 80% of this code is function overhead.
For a trivial function like this there is a good chance that the function overhead code will take just as long to run as the main function body itself. When you have trivial functions that are called in a deep loop body millions/billions of times then the function call overhead begins to become large.
As always, the key is profiling/measuring to determine whether or not inlining a specific function yields any net performance gains. For more "complex" functions that are not called "often" the gain from inlining may be immeasurably small.
Because there's no call. The function code is just copied
Aside from the fact that there's no call (and therefore no associated expenses, like parameter preparation before the call and cleanup after the call), there's another significant advantage of inlining. When the function body is inlined, it's body can be re-interpreted in the specific context of the caller. This might immediately allow the compiler to further reduce and optimize the code.
For one simple example, this function
void foo(bool b) {
if (b) {
// something
}
else {
// something else
}
}
will require actual branching if called as a non-inlined function
foo(true);
...
foo(false);
However, if the above calls are inlined, the compiler will immediately be able to eliminate the branching. Essentially, in the above case inlining allows the compiler to interpret the function argument as a compile-time constant (if the parameter is a compile-time constant) - something that is generally not possible with non-inlined functions.
However, it is not even remotely limited to that. In general, the optimization opportunities enabled of inlining are significantly more far-reaching. For another example, when the function body is inlined into the specific caller's context, the compiler in general case will be able to propagate the known aliasing-related relationships present in the calling code into the inlined function code, thus making it possible to optimize the function's code better.
Again, the possible examples are numerous, all of them stemming from the basic fact that inlined calls are immersed into the specific caller's context, thus enabling various inter-context optimizations, which would not be possible with non-inlined calles. With inlining you basically get many individual versions of your original function, each version is tailored and optimized individually for each specific caller context. The price of that is, obviously, the potential danger of code bloat, but if used correctly, it can provide noticeable performance benefits.
Because no jump is performed.