5

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?

13
  • 4
    You've shown that inline might influence the decision whether to inline, not that it's better in any way. Commented Sep 21, 2013 at 11:56
  • 3
    It does inline with either static declaration or -flto flag. Commented Sep 21, 2013 at 11:57
  • 2
    The highest rated answer to the question you quote is simply false. Ignore it. Commented Sep 21, 2013 at 11:58
  • 3
    The compiler cannot know whether you might want to use the function elsewhere, since you're only compiling one single, unlinked translation unit. Compiling a whole program (e.g. with -fwhole-program or -flto) changes that, as does giving the function internal linkage (anonymous namespace). In the original case, once the compiler has produced the external function body, it considers it more expedient to call that function rather than duplicate the code in the main function. Commented Sep 21, 2013 at 11:59
  • 2
    @JamesKanze: Avoid duplication when there's already a function definition around? Commented Sep 21, 2013 at 12:01

4 Answers 4

4

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...

Sign up to request clarification or add additional context in comments.

Comments

2

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.

3 Comments

I'm not sure what you mean by "the compiler generally does ignore this optimization hint". From a QoI point of view, it should only ignore it if it can do a better job than the programmer, and this isn't the case with the most common compilers. And as the question points out, g++ does not ignore it; from what I've seen, nor does VC++.
Re the sentence "most people have no clue how often to inline": I would hope that no one inlines anything until they have a performance problem. The clue comes from the actual performance of the program. (Otherwise, it's premature optimization.)
Re your third paragraph: in my experience, most of the functions I've declared inline for performance reasons are in the unnamed namespace. You want to avoid inline in a header, precisely because it breaks encapsulation, and introduces compiler dependencies.
1

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).

Comments

0

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.

13 Comments

Compilers typically are better at deciding when to inline. That does not mean there are no cases where a human experts aren't better at it, but the same is true of register allocation - just ask Mike Pall. And there's always compiler-specific "never inline" and "always inline if you can in any way" attributes.
Can you show us a case where the inline optimization hint does cause the compiler to inline something it wouldn't otherwise have inlined? I'm not aware of such a situation (as mentioned in my answer and in comments, static typically causes the compiler to inline as well, because it is not the hint that is significant, but the fact that it can avoid emitting code for the function definition. The hint, by itself, does not make a difference in any case I have seen. I'd love to see if you know of such a case.
@delnan That is simply false. Compilers can come close if they do whole program optimization based on profiler output; this is not the usual case, however. And neither g++ nor VC++ ignore the inline declaration. Inline is not like register.
I agree that inline is not like register, but for other reasons (semantically, one just restricts what you can do while the other allows you to write code that's more amendable to inlining). And yes, it's not completely ignored. But as I already wrote in my very first comment on the question, there is a difference between "you can force inlining with it" and "using it to force inlining is useful".
@jalf The question presented exactly such case.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.