tags:

views:

846

answers:

12

Some people love using inline keyword in C, and put big functions in headers. When do you consider this to be ineffective? I consider it sometime even annoying, because it is unusual.

My principle is that inline should be used for small functions accessed very frequently, or in order to have real type checking. Anyhow, my taste guide me, but I am not sure how to explain best the reasons why inline is not so useful for big functions.

In this question people suggest that the compiler can do a better job at guessing the right thing to do. That was also my assumption. When I try to use this argument, people reply it does not work with functions coming from different objects. Well, I don't know (for example, using GCC).

Thanks for your answers!

+18  A: 

inline does two things:

  1. gives you an exemption from the "one definition rule" (see below). This always applies.
  2. Gives the compiler a hint to avoid a function call. The compiler is free to ignore this.

#1 Can be very useful (e.g. put definition in header if short) even if #2 is disabled.

In practice compilers often do a better job of working out what to inline themselves (especially if profile guided optimisation is available).


[EDIT: Full References and relevant text]

The two points above both follow from the ISO/ANSI standard (ISO/IEC 9899:1999(E), commonly known as "C99").

In §6.9 "External Definition", paragraph 5:

An external definition is an external declaration that is also a definition of a function (other than an inline definition) or an object. If an identifier declared with external linkage is used in an expression (other than as part of the operand of a sizeof operator whose result is an integer constant), somewhere in the entire program there shall be exactly one external definition for the identifier; otherwise, there shall be no more than one.

While the equalivalent definition in C++ is explictly named the One Definition Rule (ODR) it serves the same purpose. Externals (i.e. not "static", and thus local to a single Translation Unit -- typically a single source file) can only be defined once only unless it is a function and inline.

In §6.7.4, "Function Specifiers", the inline keyword is defined:

Making a function an inline function suggests that calls to the function be as fast as possible.[118] The extent to which such suggestions are effective is implementation-defined.

And footnote (non-XXXX, but provides clarification:

By using, for example, an alternative to the usual function call mechanism, such as ‘‘inline substitution’’. Inline substitution is not textual substitution, nor does it create a new function. Therefore, for example, the expansion of a macro used within the body of the function uses the definition it had at the point the function body appears, and not where the function is called; and identifiers refer to the declarations in scope where the body occurs. Likewise, the function has a single address, regardless of the number of inline definitions that occur in addition to the external definition.

Summary: what most users of C and C++ expect from inline is not what they get. Its apparent primary purpose, to avoid functional call overhead, is completely optional. But to allow separate compilation, a relaxation of single definition is required.

(All emphasis in the quotes from the standard.)


EDIT 2: A few notes:

  • There are various restrictions on external inline functions. You cannot have a static variable in the function, and you cannot reference static TU scope objects/functions.
  • Just seen this on VC++'s "whole program optimisation", which is an example of a compiler doing its own inline thing, rather than the author.
Richard
I didn't even think about #1, but you're right - very useful! Thanks for the tip.
Mike
-1: #1 isn't true - at leat for gcc, you have to add `static` as well!
Christoph
@Christoph: that's just gcc. Just checked in C99 standard document.
Richard
well, c does not have a one-definition-rule as far as i know. it's not so clear in C as in C++ (which does have that ODR), which says an inline function definition actually can occur in the header and be included in every compilation unit and used.
Johannes Schaub - litb
for example, in C99 it states that if you have an inline function definition in one TU, then you *must* still have a external function definition in another TU. and if you call the function, it's unspecified what version (whether the external or the inline function definition) is used.
Johannes Schaub - litb
and of course, the inline definitions still are separate functions and have all their own set of static objects. in C++, instead, all inline definitions (it does not even have this strange term iirc) of functions share the same static objects.
Johannes Schaub - litb
@Richard: you're right - "An inline definition does not provide an external definition for the function, and does not forbid an external definition in another translation unit."; added an upvote instead, but it still might be a good idea to add a note about compiler support..
Christoph
i agree you should write something about it in the answer. for example: note that the term "external definition" is different from "external linkage". you should still make the function definition static to be really safe (give internal linkage). i don't see a real reason not to put static.
Johannes Schaub - litb
Need to expand and detail, but need to find the bit of thw C standard that defines how definitions work from one translation unit to another (i.e. how C gets along without the ODR).
Richard
Richard, i think 6.9 "external definitions" of TC2 (i got n1124 as of 2005 here) is appropriate. for inline, 6.7.4 is appropriate. i like the ODR of C++ because it summarizes all the stuff in one place :) C seems to have this defined in their respective sections only.
Johannes Schaub - litb
Now expanded with the two relevant sections from C99.
Richard
For 2. is that also valid for "external definition" (if I understand correctly, it is what I call "objects" during link)? Could you update your answer so I can validate it? thanks
elmarco
@elmarco: correct, functions can be both inline and external. Would take a very long answer, really beyond SO. If you need to know, the standard is (or at least was) available for USD18.
Richard
+1  A: 
  1. inline acts as a hint only.
  2. Added only very recently. So works with only the latest standard compliant compilers.
dirkgently
Added very recently? I'm pretty sure inline has been around for at least a dozen years (since I started coding in C)
Eric Petroelje
inline has been in C++ for a long time. In C it's been in the standard since C99 (but not too many compilers fully support C99), but has been in various compilers as an extension for a while. So for C programs, using inline can be a bit of a portability headache.
Michael Burr
10yrs. Yet get MS to implement a decent C only compiler. Anyway, that is recent in C-time;) Added in C99. See: http://en.wikipedia.org/wiki/C99
dirkgently
Inline has been in C since (the very end of) 1999 - C99.
Jonathan Leffler
+3  A: 

Inline is ineffective when you use the pointer to function.

Xolve
sure, that's not the case in my project however. thanks
elmarco
+6  A: 

The important thing about an inline declaration is that it doesn't necessarily do anything. A compiler is free to decide to, in many cases, to inline a function not declared so, and to link functions which are declared inline.

TokenMacGuy
even if the inline function you link with is from a different .o object?
elmarco
the inline modifier is just a hint. If the compiler chooses, it may decide that a function cannot be inlined, but the linker could then decide to rewrite the function call as an inline anyway. There's just no behavior guarantee with inline.
TokenMacGuy
+2  A: 

That's right. Using inline for big functions increases compile time, and brings little extra performance to the application. Inline functions are used to tell the compiler that a function is to be included without a call, and such should be small code repeated many times. In other words: for big functions, the cost of making the call compared to the cost of the own function implementation is negligible.

Diego Sevilla
As has been mentioned earlier, inline is used to _suggest_ to the compiler that the function should be included without a call. There's no guarantee that it actually will do so.
Peter
+2  A: 

Inline can be used for small and frequently used functions such as getter or setter method. For big functions it is not advisable to use inline as it increases the exe size. Also for recursive functions, even if you make inline, the compiler will ignore it.

chappar
+2  A: 

I mainly use inline functions as typesafe macros. There's been talk about adding support for link-time optimizations to GCC for quite some time, especially since LLVM came along. I don't know how much of it actually has been implemented yet, though.

Christoph
+3  A: 

Inline is effective in one case: when you've got a performance problem, ran your profiler with real data, and found the function call overhead for some small functions to be significant.

Outside of that, I can't imagine why you'd use it.

Ken
Dang. I duped your answer. Well, that fact alone clearly shows you were imparting great insight, so upvote. :-)
T.E.D.
I'd say sometimes you don't need to run the profiler to know that overhead is significant. For example, it's fairly obvious to me that an atomic increment function should be inline, and it'll decrease code size too.
Dietrich Epp
+2  A: 

Personally I don't think you should ever inline, unless you have first run a profiler on your code and have proven that there is a significant bottleneck on that routine that can be partially alleviated by inlining.

This is yet another case of the Premature Optimization Knuth warned about.

T.E.D.
+2  A: 

An example to illustrate the benefits of inline. sinCos.h :

int16 sinLUT[ TWO_PI ]; 

static inline int16_t cos_LUT( int16_t x ) {
    return sin_LUT( x + PI_OVER_TWO )
}

static inline int16_t sin_LUT( int16_t x ) {
    return sinLUT[(uint16_t)x];
}

When doing some heavy number crunching and you want to avoid wasting cycles on computing sin/cos you replace sin/cos with a LUT.

When you compile without inline the compiler will not optimize the loop and the output .asm will show something along the lines of :

;*----------------------------------------------------------------------------*
;*   SOFTWARE PIPELINE INFORMATION
;*      Disqualified loop: Loop contains a call
;*----------------------------------------------------------------------------*

When you compile with inline the compiler has knowledge about what happens in the loop and will optimize because it knows exactly what is happening.

The output .asm will have an optimized "pipelined" loop ( i.e. it will try to fully utilize all the processor's ALUs and try to keep the processor's pipeline full without NOPS).


In this specific case, I was able to increase my performance by about 2X or 4X which got me within what I needed for my real time deadline.


p.s. I was working on a fixed point processor... and any floating point operations like sin/cos killed my performance.

Trevor Boyd Smith
+5  A: 

Another reason why you shouldn't use inline for large functions, is in the case of libraries. Every time you change the inline functions, you might loose ABI compatibility because the application compiled against an older header, has still inlined the old version of the function. If inline functions are used as a typesafe macro, chances are great that the function never needs to be changed in the life cycle of the library. But for big functions this is hard to guarantee.

Of course, this argument only applies if the function is part of your public API.

quinmars
+1  A: 

Inline functions should be approximately 10 lines or less, give or take, depending on your compiler of choice.

You can tell your compiler that you want something inlined .. its up to the compiler to do so. There is no -force-inline option that I know of which the compiler can't ignore. That is why you should look at the assembler output and see if your compiler actually did inline the function, if not, why not? Many compilers just silently say 'screw you!' in that respect.

so if:

static inline unsigned int foo(const char *bar)

.. does not improve things over static int foo() its time to revisit your optimizations (and likely loops) or argue with your compiler. Take special care to argue with your compiler first, not the people who develop it.. or your just in store for lots of unpleasant reading when you open your inbox the next day.

Meanwhile, when making something (or attempting to make something) inline, does doing so actually justify the bloat? Do you really want that function expanded every time its called? Is the jump so costly?, your compiler is usually correct 9/10 times, check the intermediate output (or asm dumps).

Tim Post