rulururu

post Inlining

February 10th, 2008

Filed under: C++ — Kai @ 5:34 pm

Consider the following call to function myPow():

void func()
{
   int a = 1;
   int b = 2;
   double c = myPow(x, y);
}
 
double myPow(int a, int b)
{
    return pow(a,b);
}

Assuming a typical C++ implementation that has registers and a stack, the registers and parameters get written to the stack just before the call to myPow(), then the parameters get read from the stack inside myPow() and read again to restore the registers while MyPow() returns to func(). But that’s a lot of unnecessary reading and writing, especially in cases when the compiler is able to use registers for variables a and b: each variable could get written twice (as a register and also as a parameter) and read twice (when used within myPow() and to restore the registers during the return to func()).

If the compiler inline-expands the call to myPow(), all those memory operations could vanish. The registers wouldn’t need to get written or read since there wouldn’t be a function call, and the parameters wouldn’t need to get written or read since the optimizer would know they’re already in registers.

Inlining is one of the easiest optimizations to use in C++ and it can result in the most dramatic improvements in execution speed. The main thing to know is when you should inline a method and when you shouldn’t inline.
There is always a trade-off between code size and execution speed when inlining. In general, small methods (for example, accessors) should be inlined and large methods should not be inlined.

The compiler preprocessor can be used to implement conditional inlining. This is useful so that during testing the code is easier to debug. But for compiling production code, there are no changes to be made to the source code. This is implemented by using a preprocessor macro called INLINE. Inlined code is defined within #ifdef INLINE ... #endif code blocks. Similarly, non-inlined code is defined within #ifndef INLINE ... #endif code blocks. Then to compile using inlined code, you tell the compiler to treat INLINE as defined. (-DINLINE with G++)

GCC implements three different semantics of declaring a function inline. One is available with -std=gnu89, another when -std=c99 or -std=gnu99, and the third is used when compiling C++ (usage of G++).

To declare a function inline, use the inline keyword in its declaration, like this:

static inline int inc(int* a)
{
 (*a)++;
}

When a function is both inline and static, if all calls to the function are integrated into the caller, and the function’s address is never used, then the function’s own assembler code is never referenced. In this case, GCC does not actually output assembler code for the function, unless you specify the option -fkeep-inline-functions.

As required by ISO C++, GCC considers member functions defined within the body of a class to be marked inline even if they are not explicitly declared with the inline keyword. You can override this with -fno-default-inline.

Before putting great effort into ‘inline’ you’d know that it’s ignored by many C and C++ compilers, as the compiler can generally make a better decision about what to inline and what not to inline than the programmer.

Finally, as systems got better, compilers got into the act, and started doing enough analysis to make good decisions on their own without ‘inline’ keyword.
Inline was always defined as a hint, not a requirement. If you are damn sure that you have a function that needs to be inlined VC++ (V6.0) provides a makro that forces inlining. The __forceinline keyword overrides the cost/benefit analysis and relies on the judgment of the programmer instead.

1 Comment »

  1. […] In a previous post I described inlining in detail: Inlining - on February 10th, 2008 […]

    Pingback by Kai’s Blog » Improve C++ Compile-Time and Run-Time Performance — February 21, 2008 @ 3:53 pm

RSS feed for comments on this post. TrackBack URI

Leave a comment

ruldrurd
Powered by WordPress, Content and Design by Kai Bellmann
Entries (RSS) and Comments (RSS)