Is the compiler more likely to inline a function in the class declaration with the inline keyword specified?
Asked Answered
I

3

6

I was reviewing a colleague's code recently and noticed he had put the "inline" keyword in in front of a bunch of Getter functions that were defined in a class declaration.

eg

class Foo
{
public:
    inline bool GetBar() const { return m_Bar; }
private:
    bool m_Bar;
};

I suggested in the code review that he remove the inline keywords, as I've read in a number of different places that defining a function in the class declaration is interpreted by the compiler (MSVC in this case, but apparently part of the C++ standard) as an indication that the author wants the functions inlined. My feeling is that if the extra text doesn't serve any purpose, it's just unnecessary clutter and should be removed.

His response was as follows:

  1. The inline keyword makes it clear to other programmers interacting with this code that these functions are/should be inlined.

  2. Many compilers still take the inline keyword into account in this case and use it to affect (read: increase) some kind of weighting that is used to decide whether or not said function would in fact be inlined.

  3. Having the inline keyword there means that "warn if not inlined" warnings will be triggered (if enabled) if said functions are not inlined for whatever reason.

Personally, I disagree with the first reason altogether. To me, having the functions defined in the class declaration is enough to show the intention.

I'm skeptical about the last two reasons. I can't find any information that either confirms or denies the the point about the inline keyword affecting some sort of weighting. I'm also having trouble triggering the "warn if not inlined" warning for a function defined in a class declaration.

If you've read this far, I was wondering if you might have any insight into any of the above points? Additionally, if you could point me to any relevant articles/documentation, I'd really appreciate it.

Thanks!

Inmate answered 11/11, 2015 at 6:38 Comment(9)
"Is the compiler more likely to inline a function in the class declaration with the inline keyword specified?" – no. The sole purpose of inline in C++ is to prevent ODR violations resulting from functions being defined in a header file. (while in C, it has exactly zero useful purpose. I know you didn't ask about C, but I thought to mention this fact as clarification.)Terrilyn
@TheParamagneticCroissant I thought this might be the case, but I'd really like to find out for sure. Do you know for sure that none of the major compilers (lets say MSVC, GCC, and Clang) take the inline keyword into account in any way in this case?Inmate
Both #31722973 and #17326411 might help. And, personally, I'm far more about indenting violations or using variables like i and j for array indicesRatfink
@Inmate I know about GCC not ignoring the keyword entirely; although I see that your colleague wants to be sure the function is inlined. If that is the case, he should rather use __attribute__((always_inline)), because inline does not guarantee inlining. Better yet, he should just probably leave the decision to the compiler; perhaps after enabling whole-module optimization (using -flto).Terrilyn
@GregorMcGregor Forgive me if my wording wasn't particularly clear -- I'm aware that the function is already implicitly inlined, my question was concerning whether or not the inline keyword has any affect whatsoever on the compiler's decision to inline the function.Inmate
@TheParamagneticCroissant Sometimes you want the reverse. I've seen gcc inline something [that has no inline on it (in C)] even with two -fno-inline-* command line options. I had to use __attribute__((never_inline))Ratfink
@CraigEstey Yes, exactly – but in this post OP writes that the author of the code wants to ensure inlining. He could have wanted the opposite, of course – to which the removal of inline wouldn't have been the solution, and your suggestion regarding these attributes would have applied.Terrilyn
Thanks for your comments so far guys! The question is not really about how to force the functions to be inlined, and I'm aware that the inline keyword is only a suggestion. I'm interested in finding out if there's any validity to his response (the 3 numbered points), because I can't find any evidence either way online.Inmate
@Inmate inline may be valid. Suppose you've got a big member func that compiler won't inline [and doesn't have inline keyword]. But, author determines, despite compiler choice, that func should/must be inlined for use case. That's valid. I've seen 200 liners with always_inline because they're used in 1-2 places and author wanted a cleaner alternative to a cpp macro. Or, if member func is lifted outside class definition, the auto/implicit inline no longer applies. the inline would show intent, be useful there. IMO, reviewing comments more useful--show intent, etc?Ratfink
B
3

For the particular example you mentioned, GetBar is implicitly inlined, which makes the inline keyword redundant. Section 7.1.2 P3 from the C++ standard says:

A function defined within a class definition is an inline function.

Also the VC++ documentation states the same:

A class's member functions can be declared inline either by using the inline keyword or by placing the function definition within the class definition.

In general, you don't need to use the inline keyword. A C/C++ expert once said back in 2002:

the inline keyword allows the programmer to tell the compiler something it might have a hard time figuring out automatically. However, in the future, compilers may be able to do a better job of making inline decisions than programmers. When that happens, the inline keyword might be regarded as a quaint reminder of when programmers were forced to worry about details of code generation.

You can find the complete article here. You should read the whole article to understand why the keyword was added to C99. Also the article discusses extern inline functions.

Now we are in the future and indeed, modern compilers are very sophisticated and don't require this keyword anymore. The only exception is when using extern inline functions.

Does the inline keyword have any affect whatsoever on the compiler's decision to inline the function?

In MSVC, the inline keyword might affect the compiler's decision, although the compiler may choose to ignore this hint if inlining would be a net loss.

The inline keyword makes it clear to other programmers interacting with this code that these functions are/should be inlined.

This is an invalid reason. The programmer is a human being. It's generally very hard for a human to make a better decision than MSVC regarding whether to inline a function or not. Second, what is a programmer supposed to do when you tell him/her that a function should be inlined? The compiler is the one that is doing the inlining. The warning by itself does not tell you whether you have to do anything about it and, in if that was the case, what to do.

Many compilers still take the inline keyword into account in this case and use it to affect (read: increase) some kind of weighting that is used to decide whether or not said function would in fact be inlined.

This is true in MSVC. But the modern MSVC doesn't need this hint anymore. If it's beneficial to inline the function, the compiler will know and will inline it. If it's not, it will ignore the inline keyword which was inserted by a human.

Having the inline keyword there means that "warn if not inlined" warnings will be triggered (if enabled) if said functions are not inlined for whatever reason.

The MSVC compiler issues warning C4710 when a function that is selected for inlining was not inlined. This warning is disabled by default because most developers don't (and shouldn't) care about this warning. You can enable this warning, however. Unless you're a compiler optimizations researcher and want to understand the inlining algorithm used by MSVC, you shouldn't enable these warnings.

Benevolence answered 14/11, 2015 at 14:12 Comment(5)
Thanks for the informative answer. It's interesting to know that MSVC does take the inline keyword into account (although I agree that it's a pretty weak reason to include the keyword). MSVC is the primary compiler that we use, so I'm marking this as the answer. Thanks again!Inmate
Keyword inline has effect in most compilers, provided their optimization settings are configured for "attempt to inline only explicitly inline functions". GCC, for example, belongs to this category in -O1 mode. However, in none of them adding explicit inline to an implicitly inline function has any effect. This is true for MSVC as well.Sparke
@AnT What do you mean by an implicitly inline function? You mean specifying the optimization option that enables inlining? Anyway, the inline keyword always has an effect in MSVC irrespective of whether optimizations are enabled or not.Benevolence
What I mean is that a function defined inside class definition is always inline by language rules. There's no need to add an explicit inline keyword to such definition - it is already inline. Someone apparently believed that adding inline to an in-class definition makes that function "even more inline than it already is". But I'm not aware of any compilers where it would work that way.Sparke
@AnT Thanks! You're right. I forgot about that. A member function defined inside a class definition is implicitly inline and has the same effect as a function defined outside with the inline keyword.Benevolence
V
5

Edit 1: Added LLVM (in other words "clang") inlining code

Edit 2: Add clarification as to how to "resolve" this.

The actual correct

Point 1 is of course self-explanatory.

Point 2 is nonsense - all modern compilers (at least MS, GCC and Clang [aka XCode]) completely ignore inline keywords and decides purely based on frequency/size critera (determining "code-bloat factor" based on size * number times, so small functions or functions called only a few times are more likely to be inlined - of course a getter would be a perfect choice for inlining always by the compiler, since it will be only two or three instructions, and most likely shorter than loading up this, then calling the getter function.

The inline keyword doesn't make ANY difference at all [and the C++ standard states that a definition inside the class is inline anyway].

Point 3 is another plausible scenario, but I would have thought that the fact that it is implicitly inline by it's defintion should give the same result. There was discussion about the inline keyword and its meaning on Clang mailing list a while back, with the conclusion that "the compiler usually knows best".

It is also typically completely useless to use inline together with virtual functions, as they will nearly always be called through a vtable entry, and can't be inlined.

Edit 1:

Code taken from LLVM's "InlineCost.cpp":

InlineCost InlineCostAnalysis::getInlineCost(CallSite CS, Function *Callee,
                                             int Threshold) {
  // Cannot inline indirect calls.
  if (!Callee)
    return llvm::InlineCost::getNever();

  // Calls to functions with always-inline attributes should be inlined
  // whenever possible.
  if (CS.hasFnAttr(Attribute::AlwaysInline)) {
    if (isInlineViable(*Callee))
      return llvm::InlineCost::getAlways();
    return llvm::InlineCost::getNever();
  }

  // Never inline functions with conflicting attributes (unless callee has
  // always-inline attribute).
  if (!functionsHaveCompatibleAttributes(CS.getCaller(), Callee,
                                         TTIWP->getTTI(*Callee)))
    return llvm::InlineCost::getNever();

  // Don't inline this call if the caller has the optnone attribute.
  if (CS.getCaller()->hasFnAttribute(Attribute::OptimizeNone))
    return llvm::InlineCost::getNever();

  // Don't inline functions which can be redefined at link-time to mean
  // something else.  Don't inline functions marked noinline or call sites
  // marked noinline.
  if (Callee->mayBeOverridden() ||
      Callee->hasFnAttribute(Attribute::NoInline) || CS.isNoInline())
    return llvm::InlineCost::getNever();

  DEBUG(llvm::dbgs() << "      Analyzing call of " << Callee->getName()
        << "...\n");

  CallAnalyzer CA(TTIWP->getTTI(*Callee), ACT, *Callee, Threshold, CS);
  bool ShouldInline = CA.analyzeCall(CS);

  DEBUG(CA.dump());

  // Check if there was a reason to force inlining or no inlining.
  if (!ShouldInline && CA.getCost() < CA.getThreshold())
    return InlineCost::getNever();
  if (ShouldInline && CA.getCost() >= CA.getThreshold())
    return InlineCost::getAlways();

  return llvm::InlineCost::get(CA.getCost(), CA.getThreshold());
}

As can be seen (with some digging around in the rest of the code), there is only checks for the "always" and "never" inline options. None for the inline keyword itself.

[Note that this is the inliner code for clang and clang++ - clang itself does not do anything particularly clever when it generates code, it's "just" (upsetting hundreds of programmers who have spent hundreds of manyears on that project!) a parser for C and C++ that translates to LLVM IR, all the good, clever stuff is done at the LLVM layer - this is really a good way to provide a "multilanguage" compiler framework. I have written a Pascal compiler, and despite being a complete novice on compiler work, my compiler (which uses LLVM to generate actual machine code) is better in benchmarks (of the generated code) than Free Pascal - all thanks to LLVM, nearly none of that is my work - except for some code to inline a few commonly called functions in one particular benchmark]

I don't have access sources for MS compiler (doh!), and I can't be bothered to download gcc just to check this. I know, from experience, that all three will inline functions without the inline keyword, and gcc will aggressively inline functions that it can determine has only one caller (e.g. large static helper functions)

Edit 2:

The right way to resolve this sort of issue is to have a coding standard that says, clearly, when and where inline [and functions defined inside the class] should be used, and when this shouldn't be done. If currently, none of the other small getter functions in other classes have inline on them, then it would be strange and stand out that this function does. If all except some have inline, that probably should be fixed too.

Another anectdote: I personally like to write if-statements as

if (some stuff goes here)

(space between if and parenthesis, but not around the thing inside) but the coding standard at work says:

if( some stuff goes here )

(no space between if and parenthesis, but space around the thing inside)

I had accidentally got something like this:

if ( some stuff goes here )

in some code that was up for review. I fixed that line, but decided to ALSO fix the 175 other if-statements with a space after if - in total there were less than 350 if-statements in that file, so MORE than half of them were incorrect...

Vladimar answered 11/11, 2015 at 8:57 Comment(5)
Last time I did a careful investigation of inlining behavior of ICC was a few versions ago, but I think still a "modern compiler". The documentation and actual behavior had virtually nothing in common with each other, but both indicated that the inline keyword did influence the inlining behavior within a narrow boundary range of large function size. The original example was too tiny for the keyword to matter in any sane compiler. But that doesn't mean the keyword never influences the inlining decision.Spice
@GregorMcGregor: I have added code from LLVM (as of Friday last week, give or take). I don't have sources on my machine at present for any other compiler projects, so can't provide evidence for that.Vladimar
The only important difference I think between using the keyword inline or not, is that explicity marked inline functions must be defined in the same translation unit (even if the compiler decides that that function must not be inlined), and functions without the inline keyword haven't to. Of course, if a non-inline function isn't defined in the same translation unit, the compiler cannot mark it as inline, because it doesn't know its body.Chishima
@Peregring-lk: But here we are talking about functions declared and defined within the class body itself. These are automatically inline even if you don't say so in the code, and can not be anything else.Vladimar
Post from 2018: blog.tartanllama.xyz/inline-hints - presence of inline can certainly affect compiler's decision to inline (though it's not the sole factor by any margin).Alliteration
B
3

For the particular example you mentioned, GetBar is implicitly inlined, which makes the inline keyword redundant. Section 7.1.2 P3 from the C++ standard says:

A function defined within a class definition is an inline function.

Also the VC++ documentation states the same:

A class's member functions can be declared inline either by using the inline keyword or by placing the function definition within the class definition.

In general, you don't need to use the inline keyword. A C/C++ expert once said back in 2002:

the inline keyword allows the programmer to tell the compiler something it might have a hard time figuring out automatically. However, in the future, compilers may be able to do a better job of making inline decisions than programmers. When that happens, the inline keyword might be regarded as a quaint reminder of when programmers were forced to worry about details of code generation.

You can find the complete article here. You should read the whole article to understand why the keyword was added to C99. Also the article discusses extern inline functions.

Now we are in the future and indeed, modern compilers are very sophisticated and don't require this keyword anymore. The only exception is when using extern inline functions.

Does the inline keyword have any affect whatsoever on the compiler's decision to inline the function?

In MSVC, the inline keyword might affect the compiler's decision, although the compiler may choose to ignore this hint if inlining would be a net loss.

The inline keyword makes it clear to other programmers interacting with this code that these functions are/should be inlined.

This is an invalid reason. The programmer is a human being. It's generally very hard for a human to make a better decision than MSVC regarding whether to inline a function or not. Second, what is a programmer supposed to do when you tell him/her that a function should be inlined? The compiler is the one that is doing the inlining. The warning by itself does not tell you whether you have to do anything about it and, in if that was the case, what to do.

Many compilers still take the inline keyword into account in this case and use it to affect (read: increase) some kind of weighting that is used to decide whether or not said function would in fact be inlined.

This is true in MSVC. But the modern MSVC doesn't need this hint anymore. If it's beneficial to inline the function, the compiler will know and will inline it. If it's not, it will ignore the inline keyword which was inserted by a human.

Having the inline keyword there means that "warn if not inlined" warnings will be triggered (if enabled) if said functions are not inlined for whatever reason.

The MSVC compiler issues warning C4710 when a function that is selected for inlining was not inlined. This warning is disabled by default because most developers don't (and shouldn't) care about this warning. You can enable this warning, however. Unless you're a compiler optimizations researcher and want to understand the inlining algorithm used by MSVC, you shouldn't enable these warnings.

Benevolence answered 14/11, 2015 at 14:12 Comment(5)
Thanks for the informative answer. It's interesting to know that MSVC does take the inline keyword into account (although I agree that it's a pretty weak reason to include the keyword). MSVC is the primary compiler that we use, so I'm marking this as the answer. Thanks again!Inmate
Keyword inline has effect in most compilers, provided their optimization settings are configured for "attempt to inline only explicitly inline functions". GCC, for example, belongs to this category in -O1 mode. However, in none of them adding explicit inline to an implicitly inline function has any effect. This is true for MSVC as well.Sparke
@AnT What do you mean by an implicitly inline function? You mean specifying the optimization option that enables inlining? Anyway, the inline keyword always has an effect in MSVC irrespective of whether optimizations are enabled or not.Benevolence
What I mean is that a function defined inside class definition is always inline by language rules. There's no need to add an explicit inline keyword to such definition - it is already inline. Someone apparently believed that adding inline to an in-class definition makes that function "even more inline than it already is". But I'm not aware of any compilers where it would work that way.Sparke
@AnT Thanks! You're right. I forgot about that. A member function defined inside a class definition is implicitly inline and has the same effect as a function defined outside with the inline keyword.Benevolence
C
2

I'm ignoring the C++-specific part of the question and focusing just on ordinary inline functions (C or C++) per your #2 bullet:

I'm skeptical about the last two reasons. I can't find any information that either confirms or denies the the point about the inline keyword affecting some sort of weighting.

https://blog.tartanllama.xyz/inline-hints/ provides some detail, dated January 2018. In short: Clang+LLVM and GCC both take the keyword into account when deciding whether or not to inline a function. It isn't the only consideration, but it does affect the decision.

Contravene answered 16/2, 2019 at 22:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.