5

I understand inlining as the following:

A separate function which is marked as "inline" (assuming the compiler really does inline) will be "merged" into the function it is called.

i.e. (pseudo code)

void main ()
{
  printf(b(2,3))  // will be printf(2*3*3);
}
inline function b(int x, int y)
{
  return x*y*y;
}

However, I had a discussion regarding inlining and why am I advised to not inline functions that are called exactly once in my code(Because they are initialization functions)?

For example:

void main(){
    initFunctionA();
    SomeType destinationA;
    SomeType destinationB;
    InitDescriptionType description;
    //initialize DescriptionType
    initFunctionB(&description, destinationA&);
    initFunctionC(&description, destinationB&);
}

inline void initFunctionA()
{
    //do stuff
}
inline void initFunctionB(InitDescriptionType* desc, SomeType** destination)
{
    // Take Description and put something into destination
}
inline void initFunction(InitDescriptionType* desc, SomeType** destination)
{
    // Take Description and put something into destination
}

Wouldn't that mean that my output code is marginally smaller (since no jumps to functions etc?)

€dit: The linked question doesn't answer my particular question. In my Case, the Initialization is a single function. My thought was "Hey, why don't I separate my Initizalization into multiple smaller functions for readability and make them inline?"

2

3 Answers 3

9

In C and C++, the inline keyword has effects that make inline possible, but also many other effects – such as suppressing the one-definition rule (ODR). This can obscure bugs.

Therefore, the inline keyword should only be used for functions and other objects that must be defined in a header anyway. In particular, any function definitions within a C++ class definition are already implicitly inline.

But if you merely want to make inlining optimizations possible for the compiler, give those functions internal linkage instead. This is possible through the static keyword, or since C++11 through anonymous namespaces.

Note that if a function call is only performed once per program execution, then optimizing that function call is not worth it. You will literally not be able to notice any difference in performance. Rather than optimizing initialization code, find the real bottlenecks:

  1. Determine your actual performance requirements.
  2. Profile your code to find hot spots and expensive operations.
  3. Find ways to avoid those expensive operations. In hot spots, micro-optimizations can sometimes pay off.
  4. Repeat steps 2 and 3 until your performance requirements are met.

See also:

2

If we're talking about forceinline techniques which do actually have a high probability of forcing the compiler to inline a function rather than inline in C or C++, I would echo the same advice to refrain from it even in functions called many times until after you fire up the profiler and compare results before and after. One of the problems if you get happy force-inlining functions in advance because they're small, or called only once, or very frequently, is that it tends to degrade the output given by profilers which can no longer tell you as precisely which non-inlined functions are actually making up your top hotspots.

Yet the main reason I wanted to jump in is that I got a sense that there's an implied assumption that inlining a function always called once might always make things run faster as a result of the marginally smaller code size and the elimination of function calling overhead, and while intuitive, that intuition can be dead wrong.

I've found plenty of cases over the years where hoisting a smaller function out of a larger function by refactoring it, and sometimes even forcefully preventing the compiler (and linker) from inlining them with noinline, produced measured gains in efficiency (sometimes quite substantial like 2-3x speed ups in operations). A simple rationale for why is that, at least without PGO, an optimizer can't tell what branches of code are more or less commonly taken since they're based on runtime inputs. There's only a very limited number of registers and things of this nature, so if we just inline every function called one time into the body of the calling function, it can result in less efficient instructions in the common-case branches and more stack spills, e.g.

It's something I think more devs should be aware of since I work in a fairly performance-critical field and encounter many devs zealously trying to force inline functions in advance before profiling, only to find measured improvements by actually removing those hints and occasionally even replacing them with noinline. It's something that will naturally come about though for anyone who leans on their profilers and is reluctant to try such things before measuring.

0

You don’t inline to save a few bytes of code. And what you do will create one huge function instead of several smaller ones, which tends to lead to less optimised and possibly larger, slower code.

Plus you have the maintenance problem. It’s unusual code, so the next maintainer will waste their time figuring out why the hell you were inlining initialisation code.

Your Answer

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