Sungmin
Sungmin

Reputation: 2631

C++ `inline` keyword and compiler optimization

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

Answers (4)

Philipp Cla&#223;en
Philipp Cla&#223;en

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

Mats Petersson
Mats Petersson

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

Stack Overflow is garbage
Stack Overflow is garbage

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

James Kanze
James Kanze

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

Related Questions