what is/are the purpose(s) of inline?

2019-01-06 20:53发布

问题:

I had a discussion with Johannes Schaub regarding the keyword inline. The code there was this:

namespace ... {
    static void someFunction() {
        MYCLASS::GetInstance()->someFunction();
    }
};

He stated that:

Putting this as an inline function may save code size in the executable

But according to my findings here and here it wouldn't be needed, since:

  • [Inline] only occurs if the compiler's cost/benefit analysis show it to be profitable
  • Mainstream C++ compilers like Microsoft Visual C++ and GCC support an option that lets the compilers automatically inline any suitable function, even those not marked as inline functions.

Johannes however states that there are other benefits of explicitly specifying it. Unfortunately I do not understand them. For instance, he stated that And "inline" allows you to define the function multiple times in the program., which I am having a hard time understanding (and finding references to).

So

  1. Is inline just a recommendation for the compiler?
  2. Should it be explicitly stated when you have a small function (I guess 1-4 instructions?)
  3. What other benefits are there with writing inline?
  4. is it needed to state inline in order to reduce the executable file size, even though the compiler (according to wikipedia [I know, bad reference]) should find such functions itself?

Is there anything else I am missing?

回答1:

Is inline just a recommendation for the compiler?

Yes.

7.1.2 Function specifiers

2 A function declaration (8.3.5, 9.3, 11.4) with an inline specifier declares an inline function. The inline specifier indicates to the implementation that inline substitution of the function body at the point of call is to be preferred to the usual function call mechanism. An implementation is not required to perform this inline substitution at the point of call; however, even if this inline substitution is omitted, the other rules for inline functions defined by 7.1.2 shall still be respected.

For example from MSDN:

The compiler treats the inline expansion options and keywords as suggestions. There is no guarantee that functions will be inlined. You cannot force the compiler to inline a particular function, even with the __forceinline keyword. When compiling with /clr, the compiler will not inline a function if there are security attributes applied to the function.

Note though:

3.2 One definition rule

3 [...]An inline function shall be defined in every translation unit in which it is used.

4 An inline function shall be defined in every translation unit in which it is used and shall have exactly the same definition in every case (3.2). [ Note: a call to the inline function may be encountered before its definition appears in the translation unit. —end note ] If the definition of a function appears in a translation unit before its first declaration as inline, the program is ill-formed. If a function with external linkage is declared inline in one translation unit, it shall be declared inline in all translation units in which it appears; no diagnostic is required. An inline function with external linkage shall have the same address in all translation units. A static local variable in an extern inline function always refers to the same object. A string literal in the body of an extern inline function is the same object in different translation units. [ Note: A string literal appearing in a default argument expression is not in the body of an inline function merely because the expression is used in a function call from that inline function. —end note ] A type defined within the body of an extern inline function is the same type in every translation unit.

[Note: Emphasis mine]

A TU is basically a set of headers plus an implementation file (.cpp) which leads to an object file.

Should it be explicitly stated when you have a small function (I guess 1-4 instructions?)

Absolutely. Why not help the compiler help you generate less code? Usually, if the prolog/epilog part incurs more cost than having it inline force the compiler to generate them? But you must, absolutely must go through this GOTW article before getting started with inlining: GotW #33: Inline

What other benefits are there with writing inline?

  • namespaces can be inline too. Note that member functions defined in the class body itself are inline by default. So are implicitly generated special member functions.

  • Function templates cannot be defined in an implementation file (see FAQ 35.12) unless of course you provide a explicit instantiations (for all types for which the template is used -- generally a PITA IMO). See the DDJ article on Moving Templates Out of Header Files (If you are feeling weird read on this other article on the export keyword which was dropped from the standard.)

Is it needed to state inline in order to reduce the executable file size, even though the compiler (according to wikipedia [I know, bad reference]) should find such functions itself?

Again, as I said, as a good programmer, you should, when you can, help the compiler. But here's what the C++ FAQ has to offer about inline. So be wary. Not all compilers do this sort of analysis so you should read the documentation on their optimization switches. E.g: GCC does something similar:

You can also direct GCC to try to integrate all “simple enough” functions into their callers with the option -finline-functions.

Most compilers allow you to override the compiler's cost/benefit ratio analysis to some extent. The MSDN and GCC documentation is worth reading.



回答2:

To restate what I said in those little comment boxes. In particular, I was never talking about inlin-ing:

// foo.h:
static void f() {
  // code that can't be inlined
}

// TU1 calls f
// TU2 calls f

Now, both TU1 and TU2 have their own copy of f - the code of f is in the executable two times.

// foo.h:
inline void f() {
  // code that can't be inlined
}

// TU1 calls f
// TU2 calls f

Both TUs will emit specially marked versions of f that are effectively merged by the linker by discarding all but one of them. The code of f only exists one time in the executable.

Thus we have saved space in the executable.



回答3:

Is inline just a recommendation for the compiler?

Yes. But the linker needs it if there are multiple definitions of the function (see below)

Should it be explicitly stated when you have a small function (I guess 1-4 instructions?)

On functions that are defined in header files it is (usually) needed. It does not hurt to add it to small functions (but I don't bother). Note class members defined within the class declaration are automatically declared inline.

What other benefits are there with writing inline?

It will stop linker errors if used correctly.

is it needed to state inline in order to reduce the executable file size, even though the compiler (according to wikipedia [I know, bad reference]) should find such functions itself?

No. The compiler makes a cost/benefit comparison of inlining each function call and makes an appropriate choice. Thus calls to a function may be inlined in curtain situations and not inlined in other (depending on how the compilers algorithm works).

Speed/Space are two competing forces and it depends what the compiler is optimizing for which will determine weather functions are inlined and weather the executable will grow or shrink.

Also note if excessively aggressive inlining is used causing the program to expand too much, then locality of reference is lost and this can actually slow the program down (as more executable pages need to be brought into memory).

Multiple definition:

File: head.h

// Without inline the linker will choke.
/*inline*/       int  add(int x, int y) { return x + y; }
extern void test()

File: main.cpp

#include "head.h"
#include <iostream>

int main()
{
    std::cout << add(2,3) << std::endl;
    test();
}

File: test.cpp

#include "head.h"
#include <iostream>

void test()
{
    std::cout << add(2,3) << std::endl;
}

Here we have two definitions of add(). One in main.o and one in test.o



回答4:

  1. Yes. It's nothing more.
  2. No.
  3. You hint the compiler that it's a function that gets called a lot, where the jump-to-the-function part takes a lot of the execution time. The compiler might decide to put the function code right where it gets called instead where normal functions are. However, if a function is inlined in x places, you need x times the space of a normal function.
  4. Always trust your compiler to be much smarter than yourself on the subject of premature micro-optimization.


回答5:

Actually, inline function may increase executable size, because inline function code is duplicated in every place where this function is called. With modern C++ compilers, inline mostly allows to programmer to believe, that he writes high-performance code. Compiler decides itself whether to make function inline or not. So, writing inline just allows us to feel better...



回答6:

With regards to this:

And "inline" allows you to define the function multiple times in the program.

I can think of one instance where this is useful: Making copy protection code harder to crack. If you have a program that takes user information and verifies it against a registration key, inlining the function that does the verification will make it harder for a cracker to find all duplicates of that function.

As to other points:

  1. inline is just a recommendation to compiler, but there are #pragma directives that can force inlining of any function.
  2. Since it's just a recommendation, it's probably safe to explicitly ask for it and let the compiler override your recommendation. But it's probably better to omit it altogether and let the compiler decide.
  3. The obfuscation mentioned above is one possible benefit of inlining.
  4. As others have mentioned, inline would actually increase the size of the compiled code.


回答7:

  1. Yes, it will readily ignore it when it thinks the function is too large or uses incompatible features (exception handling perhaps). Furthermore, there is usually a compiler setting to let it automatically inline functions that it deems worthy (/Ob2 in MSVC).

  2. It should be explicitly stated if you put the definition of the function in the header file. Which is usually necessary to ensure that multiple translation units can take advantage of it. And to avoid multiple definition errors. Furthermore, inline functions are put in the COMDAT section. Which tells the linker that it can pick just one of the multiple definitions. Equivalent to __declspec(selectany) in MSVC.

  3. Inlined functions don't usually make the executable smaller. Since the call opcode is typically smaller than the inlined machined code, except for very small property accessor style functions. It depends but bigger is not an uncommon outcome.



回答8:

Another benefit of in-lining (note that actual inlining is sometimes orthogonal to use of the "inline" directive) occurs when a function uses reference parameters. Passing two variables to a non-inline function to add its first operand to the second would require pushing the value of the first operand and the address of the second and then calling a function which would have to pop the first operand and address of the second, and then add the former value indirectly to the popped address. If the function were expanded inline, the compiler could simply add one variable to the other directly.



回答9:

Actually inlining leads to bigger executables, not smaller ones. It's to reduce one level of indirection, by pasting the function code.

http://www.parashift.com/c++-faq-lite/inline-functions.html