Disadvantage of using 'inline'

M

Method Man

If I don't care about the size of my executable or compile time, is there
any reason why I wouldn't want to inline every function in my code to make
the program run more efficient?
 
D

Dan Pop

In said:
If I don't care about the size of my executable or compile time, is there
any reason why I wouldn't want to inline every function in my code to make
the program run more efficient?

1. inline is not a portable feature of C.

2. There is no guarantee that inlining everything is going to speed up
your code. Larger executable means less efficient usage of the
processor cache.

Dan
 
A

Andrey Tarasevich

Method said:
If I don't care about the size of my executable or compile time, is there
any reason why I wouldn't want to inline every function in my code to make
the program run more efficient?

Thoughtless inlining might easily cause code bloat, which will make your
program run _less_ efficiently.
 
K

Keith Thompson

kyle york said:
Greetings,


Odd, I must be horribly misreading 6.7.4: Function specifiers

His point is that "inline" is a new feature in C99, not supported by
C90, and that C99 compilers are not yet widespread enough for code
that depends on C99-specific features to be considered portable.

On the other hand, many compilers that don't support all of C99 do
support inline (whether they do so in a way that's 100% compatible
with the C99 specification is another question).
 
D

Derrick Coetzee

Method said:
If I don't care about the size of my executable or compile time, is there
any reason why I wouldn't want to inline every function in my code to make
the program run more efficient?

Disclaimer: Parts of this response may be platform-specific.

Actually, many compilers *do* aggressively inline small functions within
a module at high optimization levels, even if you don't explicitly ask
them to. For example, gcc does this at -O3 (which includes the flag
-finline-functions, the primary difference from -O2).

No good compiler will inline a large function, though, even if you ask
it to, not only because of the large space overhead in doing so but
because the purpose of inlining is to eliminate function call overhead,
and this is simply relatively insigificant if enough time is spent
inside the function called (even a small function with a long loop
doesn't benefit from inlining).

Placing the inline keyword on all static (module-local) functions is an
okay thing to do, if you trust your compiler; it'll figure out when not
to do it, although you can't take an inline function's address to call
it indirectly, and you'll look a bit silly. Trouble starts when you
attempt to export an inline function, though, and use it in multiple
modules. Such an inline function has to include its definition in every
module using it, usually via a header. If the compiler ends up not
inlining it, for whatever reason, this may result in a copy of its code
being placed in every module that uses it. Thus you should only export
small inline functions.
 
A

Andrey Tarasevich

Derrick said:
...
Placing the inline keyword on all static (module-local) functions is an
okay thing to do, if you trust your compiler; it'll figure out when not
to do it, although you can't take an inline function's address to call
it indirectly, and you'll look a bit silly.

While it is true that an indirect call cannot be inlined (for obvious
reasons), there is absolutely no problem with taking an address of an
inline function. You seem to be mistaking two different things: inline
functions and inlined calls to functions.
Trouble starts when you
attempt to export an inline function, though, and use it in multiple
modules. Such an inline function has to include its definition in every
module using it, usually via a header. If the compiler ends up not
inlining it, for whatever reason, this may result in a copy of its code
being placed in every module that uses it. Thus you should only export
small inline functions.

That would be true in C++, but that's not necessarily true in C (C99).
An inline function with external linkage in C can have external
definition in addition to inline definition. The external definition can
be reached from other translation units (by non-inline calls, of course).
 
E

E. Robert Tisdale

Method said:
If I don't care about the size of my executable or compile time,
is there any reason why I wouldn't want to inline every function in my code
to make the program run more efficient?

No.
You may find that you want to define than as "inline static"
in a header file so that you don't get "multiply defined references".

The idea behind inline functions is to encourage programmers
to decompose functions into smaller functions
and let the compiler "inline" them automatically
instead of inlining them manually.
This should result in code
that is easier to read, understand and maintain
while maximizing performance and efficiency.
 
C

CBFalconer

E. Robert Tisdale said:
No.
You may find that you want to define than as "inline static"
in a header file so that you don't get "multiply defined references".

If the function is static there is no point in mentioning it in
any header file. Headers should be used solely to export things
from a compilation unit. Static functions are not accessible
outside the compilation unit.
The idea behind inline functions is to encourage programmers to
decompose functions into smaller functions and let the compiler
"inline" them automatically instead of inlining them manually.
This should result in code that is easier to read, understand and
maintain while maximizing performance and efficiency.

This is accurate.
 
M

Mark F. Haigh

1. inline is not a portable feature of C.

2. There is no guarantee that inlining everything is going to speed up
your code. Larger executable means less efficient usage of the
processor cache.

A larger executable may mean less efficient usage of the processor
cache, but it may not. As an obvious example, judicious loop
unrolling (by the compiler) often causes larger code, but faster code.
Of course, everything in moderation: if you unroll too much you will
cause icache (or trace cache) thrashing because your working sets keep
pushing each other out. That's really the essence of what Dan was
getting at.

As usual, there's a bunch of competing factors. Inlining small,
frequently *called* functions may be beneficial because it eliminates
function call overhead and allows CSE and other optimizations to be
performed within the context of the caller. This could translate to
fewer branches, which reduces the likelihood of mispredicted branches,
which can be costly.

Now I just said small, frequently *called* functions, but I did not
mean frequently *used* functions, unless they're so small that
function call overhead is more than, or a significant percentage of,
their execution time. By frequently used functions, I mean the ones
you find sprinkled all over the code, but not typically in performance
sensitive areas. These are what you do not want to inline, because
the code bloat is not worth it.

One big problem is that your inline function looks like a function to
you, and in the source code, but not to the processor. Inline
functions will not cause a hot spot in the cache. They decrease
locality. If your program calls a frequently used inline function
twice reasonably near each other, for example, the second call will
*not* find the function already sitting in cache, ready to go.
Granted, hardware prefetch may cause this to be a moot point in some
cases, but then again, the hardware prefetch would then be fetching
something that should already be in the cache, and displacing
something else that may be beneficial.

So what can you inline? Well, the best functions to inline are the
ones that are used exactly once and are static. It's hard to go wrong
with that. With anything else, you need to profile your code and be
familiar with the relative costs your architecture imposes on you. As
always, when in doubt, check your compiler's assembly output. You may
be pleasantly surprised to find that your compiler is good at figuring
out which functions to inline for you, under certain optimization
levels. And if you religiously use static functions, as you should,
the compiler has a much easier time at doing just that.

If you're serious about code performance, you need to be familiar with
the applicable profiling tools and with the increasingly popular
profile driven optimization. Profile driven optimization gives the
compiler a much better idea of which branches are taken and which are
not, so that it can output the best possible assembly. Now when you
combine that with profiling and subsequent inlining and tweaking, you
have the possibility of some very well performing code. But you're
not going to get that by simply inlining everything, that's for sure.


Mark F. Haigh
(e-mail address removed)
 
C

Chris Barts

While it is true that an indirect call cannot be inlined (for obvious
reasons), there is absolutely no problem with taking an address of an
inline function. You seem to be mistaking two different things: inline
functions and inlined calls to functions.

This seems odd to me, too: If the function `should' be inlined (and,
therefore, have no independent existence), what is the rationale for
allowing programmers to take its address? Wouldn't that pretty much defeat
the purpose, by forcing the compiler to generate code for the function at
a specific location?

My point of reference isn't C++, but the C `register' keyword. It turns
out that C compilers are under no obligation to listen to the programmer
about which variables to try and place in registers (and on plenty of
machines, there are so few registers that tying one up is stupid), but the
compiler is obligated to act as if it had by disallowing the taking of the
address of a register-qualified variable.
 
A

Andrey Tarasevich

Chris said:
This seems odd to me, too: If the function `should' be inlined (and,
therefore, have no independent existence), what is the rationale for
allowing programmers to take its address?

Declaring a function as 'inline' never meant that is has no "independent
existence".

The decision to inline a call is made by the compiler on a per-call
basis. Nothing in the language specification says that a function should
be either always inlined or never inlined. This has never been the
intention with inline functions. The compiler is completely free to
choose which concrete calls to inline and which not to inline.

It is obvious that in general case indirect calls cannot be inlined.
This, however, doesn't in any way prevent direct calls from being inlined.
Wouldn't that pretty much defeat
the purpose, by forcing the compiler to generate code for the function at
a specific location?

No. Why? Direct calls that have access to the inline definition of the
function can still be perfectly inlined. No problems here. Other calls
(indirect calls or calls that have no access to the inline definition)
are made in the "traditional" way. It is up to you to design and
organize your program the way that maximizes inlining (if that's what
you wish to achieve).
My point of reference isn't C++, but the C `register' keyword. It turns
out that C compilers are under no obligation to listen to the programmer
about which variables to try and place in registers (and on plenty of
machines, there are so few registers that tying one up is stupid), but the
compiler is obligated to act as if it had by disallowing the taking of the
address of a register-qualified variable.

It is a bad analogy. The fundamental difference between function and
variables that makes this a bad analogy is that functions are "frozen",
they don't change. If some entity is "frozen", there's no problem in
keeping and using several copies of that entity - no one would ever
notice and no one would ever know which copy is being used in each
particular case.

With functions the property of being 'inline' is a property of the
function itself, while the property of being actually _inlined_ is a
property of a concrete function call. Different calls to the same
function can have different properties (i.e. they can be inlined or they
can be directed to a "regular" function body), they don't conflict with
each other. It is a very natural separation, it requires relatively
little effort from the compiler and imposes no performance penalties.

Variables, on the other hand, can change their values. In order to keep
several copies of a variable (a 'register' copy and a normal copy in
memory, to allow address taking) the program will have to make sure that
values stored in these copies are carefully synchronized. This is very
difficult, if at all possible (frankly, I don't think it is). And in any
case this will impose significant run-time performance penalty.

Now, with _const-qualified objects declared with 'register' keyword
address-taking would be easy to implement, just because constants are
similar to functions - they don't change.
 
A

Andrey Tarasevich

Declaring a function as 'inline' never meant that is has no "independent
existence".

The decision to inline a call is made by the compiler on a per-call
basis. Nothing in the language specification says that a function should
be either always inlined or never inlined. This has never been the
intention with inline functions. The compiler is completely free to
choose which concrete calls to inline and which not to inline.

It is obvious that in general case indirect calls cannot be inlined.
This, however, doesn't in any way prevent direct calls from being inlined.



No. Why? Direct calls that have access to the inline definition of the
function can still be perfectly inlined. No problems here. Other calls
(indirect calls or calls that have no access to the inline definition)
are made in the "traditional" way. It is up to you to design and
organize your program the way that maximizes inlining (if that's what
you wish to achieve).



It is a bad analogy. The fundamental difference between function and
variables that makes this a bad analogy is that functions are "frozen",
they don't change. If some entity is "frozen", there's no problem in
keeping and using several copies of that entity - no one would ever
notice and no one would ever know which copy is being used in each
particular case.

With functions the property of being 'inline' is a property of the
function itself, while the property of being actually _inlined_ is a
property of a concrete function call. Different calls to the same
function can have different properties (i.e. they can be inlined or they
can be directed to a "regular" function body), they don't conflict with
each other. It is a very natural separation, it requires relatively
little effort from the compiler and imposes no performance penalties.

Variables, on the other hand, can change their values. In order to keep
several copies of a variable (a 'register' copy and a normal copy in
memory, to allow address taking) the program will have to make sure that
values stored in these copies are carefully synchronized. This is very
difficult, if at all possible (frankly, I don't think it is). And in any
case this will impose significant run-time performance penalty.

Now, with _const-qualified objects declared with 'register' keyword
address-taking would be easy to implement, just because constants are
similar to functions - they don't change.
 
E

E. Robert Tisdale

CBFalconer said:
If the function is static,
there is no point in mentioning it in any header file.
Headers should be used solely to export things from a compilation unit.
Static functions are not accessible outside the compilation unit.
> cat file.h
#ifndef GUARD_FILE_H
#define GUARD_FILE_H 1

inline
double f(double x) {
return x*(x + 2.0) + 1.0;
}

double g(double x);

#endif//GUARD_FILE_H
> cat file.c
#include "file.h"

double g(double x) {
return f(x);
}
> cat main.c
#include <stdio.h>
#include "file.h"

int main(int argc, char* argv[]) {
fprintf(stdout, "f(13.0) = %f\n", f(13.0));
fprintf(stdout, "g(13.0) = %f\n", g(13.0));
return 0;
}
> gcc -Wall -std=c99 -pedantic -o main main.c file.c
/tmp/ccCBkYgj.o(.text+0x0): In function `f':
: multiple definition of `f'
/tmp/ccqaborv.o(.text+0x0): first defined here
collect2: ld returned 1 exit status

The definition of f(double)
must appear in both translation units if it is to be inlined
but the link editor will see multiple definitions
unless it is also static.
 
C

CBFalconer

E. Robert Tisdale said:
#ifndef GUARD_FILE_H
#define GUARD_FILE_H 1

inline
double f(double x) {
return x*(x + 2.0) + 1.0;
}
double g(double x);
#endif//GUARD_FILE_H .... snip ...

The definition of f(double) must appear in both translation
units if it is to be inlined but the link editor will see
multiple definitions unless it is also static.

You have a point. However I consider any code so organized an
abortion.
 
S

Stephen Sprunk

CBFalconer said:
You have a point. However I consider any code so organized an
abortion.

Why?

I'd considered inline static functions in a header file to be a reasonable
replacement for many macros, such as replacing

#define ADD(x,y) ((x)+(y))

with

inline static int add(int x, int y) { return x+y; }

Obviously the latter version isn't type-flexible, but that may be an
advantage in many cases.

S
 
C

CBFalconer

Stephen said:
Why?

I'd considered inline static functions in a header file to be a
reasonable replacement for many macros, such as replacing

#define ADD(x,y) ((x)+(y))

with

inline static int add(int x, int y) { return x+y; }

Obviously the latter version isn't type-flexible, but that may
be an advantage in many cases.

You too have a point. Yet I would still consider it unclean, and
would prefer to put that definition directly in the file in
question. Similarly, I would not like to see such a #define in a
header file. My principle remains that header files are intended
to export connections to a compilation unit. That way I remain
free to alter that unit without having to worry about effects on
(possibly myriad) other units, as long as I do not modify the
header(s).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top