Reputation: 2631
I keep hearing that the inline
keyword is not useful as a hint for modern compiler anymore but is used to avoid the multiple definition error in the multi-source project.
But today I encountered an example that compiler obeys the keyword.
Without inline
keyword, the following code
#include <iostream>
using namespace std;
void func(const int x){
if(x > 3)
cout << "HAHA\n";
else
cout << "KKK\n";
}
int main(){
func(5);
}
with the command g++ -O3 -S a.cpp
, generates the assembly code with the func
is not inlined.
However if I add inline keyword in front of the definition of func
, the func
is inlined into main
.
The part of the generated assembly code is
.LC0:
.string "HAHA\n"
.LC1:
.string "KKK\n"
.text
.p2align 4,,15
.globl _Z4funci
.type _Z4funci, @function
_Z4funci:
.LFB975:
.cfi_startproc
cmpl $3, %edi
jg .L6
movl $4, %edx
movl $.LC1, %esi
movl $_ZSt4cout, %edi
jmp _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
.p2align 4,,10
.p2align 3
main:
.LFB976:
.cfi_startproc
subq $8, %rsp
.cfi_def_cfa_offset 16
movl $5, %edi
call _Z4funci
xorl %eax, %eax
addq $8, %rsp
.cfi_def_cfa_offset 8
ret
.cfi_endproc
My compiler is gcc 4.8.1 / x86-64.
I suspect that the function can be inlined during the linking process but I am not sure that will happen and if so, how can I know?
My question is why this code snippet seems to be contradictory to the modern guideline such as When should I write the keyword 'inline' for a function/method?
Upvotes: 5
Views: 3091
Reputation: 44019
Today (in 2018), the inline
attribute is still used for optimization. Even in modern compilers.
The claim that they would ignore it and instead purely rely on their own cost models is not true, at least in the open source compilers GCC and Clang. Simon Brand wrote a nice blog post about it (Do compilers take inline as a hint?), where he debunked it by looking at the compiler's source code.
But it is not that these compiler will blindly follow the programmer's hint. If they have enough evidence that it will hurt performance, they will overrule you.
There are vendor specific extensions that will force inlining, even if the compilers thinks it is a bad idea. For example, in Visual Studio it is called __forceinline
:
The
__forceinline
keyword overrides the cost/benefit analysis and relies on the judgment of the programmer instead. Exercise caution when using__forceinline
. Indiscriminate use of__forceinline
can result in larger code with only marginal performance gains or, in some cases, even performance losses (due to increased paging of a larger executable, for example).
GCC and Clang call it inline __attribute__ ((__always_inline__))
.
In general, trusting the compiler with the decision is recommended, especially if you can use profile-guided optimization. One notable exception of a high quality code base that uses forced inlining is Boost (look for BOOST_FORCEINLINE
).
Upvotes: 1
Reputation: 129524
The inline
keyword has several effects. One of which is to hint to the compiler that you want the function to be inlined - however, that doesn't mean the compiler HAS to inline it [there's an extension in several compilers that says "inline this no matter what, if at all possible", such as MS's __forceinline
and gcc's __attribute__(always_inline)
].
The inline
keyword also has allows you to have multiple instances of a function with the same name if the function is inlined, without getting errors for "multiple definitions of the same function". [But the function must be the same source each time].
In this case, I'm a little surprised to see the compiler NOT inline func
. However, adding static
to func
makes it go inline too. So clearly the compiler decides this based on the fact that "some other function may be using func
too, so we need a copy anyway, and there isn't much gain from inlining it. In fact, if you make a function static, and it's only called once, even if the function is very large, gcc/g++ will almost certainly inline it.
If you want the compiler to inline something, it never hurts to add inline
. However, in many cases, the compiler will make a decent choice either way. For example, if I change the code to this:
const char* func(const int x){
if(x > 3)
return "HAHA\n";
else
return "KKK\n";
}
int main(){
cout << func(5);
}
it does inline the return "HAHA\n";
part that is left of func
.
The compiler's logic to decide to inline or not inline is complex, and part of that is "how much do we gain, vs how much more code-space does it take up" - it's likely that the overhead of calling operator<<(ostream& ,const char *)
was too much for the inliner in this case. Unfortunately, it's not always easy to understand why the compiler takes a certain decision...
Upvotes: 4
Reputation: 248269
First, it is not so black or white. The only absolute effect of the inline
keyword is to suppress the ODR rule and avoid multiple definition errors. Beyond that, the compiler is certainly free to take the keyword as a hint about inlining, but it may or may not do so. (And from what I have seen, in practice the compiler generally does ignore this optimization hint, because most people have no clue how often to inline, or what to inline, and the compiler can just do a better job of it). But it doesn't have to ignore the hint.
Second, there could well be another reason why the call is inlined with the inline
keyword but not without.
Without the inline
keyword, the function definition has to be exported, as another TU might need to link to it. And since we have to export the function definition, the code is there already, and inlining the call would just mean you effectively had the function body duplicated. More total code, larger executable size, a hit to instruction cache locality.
But with the inline
keyword, the compiler doesn't have to export the function definition, so it can inline the call and entirely remove the original definition. Then the total code size doesn't increase (instead of generating the function definition and a call to it, we just move the function body to the call site).
As an experiment, try marking the function as static
instead of inline
. That also means the compiler doesn't have to export the definition, and very likely, that will also result in it deciding that inlining is worthwhile.
Upvotes: 2
Reputation: 154047
What you keep hearing is false, or should be. The standard clearly
specifies the intent of inline
: tell the compiler that it would be
preferable if the compiler could generate this code inline. Until
compilers can do a better job than the programmer of judging when
inlining is necessary, it shoud take the "hint" into account. Maybe
some day, inline
will become irrelevant for this (like register
has
become), but we're far from there yet.
Having said that, I'm very surprised that g++ didn't inline in your
case. g++ is usually fairly aggressive about inlining, even when the
function isn't marked inline
. Maybe it just figured that since the
function wasn't in a loop, it wasn't worth the bother.
Upvotes: 0