This is the mail archive of the
mailing list for the GCC project.
On inlining in C++
- From: Gabriel Dos Reis <gdr at integrable-solutions dot net>
- To: gcc at gcc dot gnu dot org
- Date: 04 Aug 2003 15:06:43 +0200
- Subject: On inlining in C++
- Organization: Integrable Solutions
This text I should have sent a while ago.
Give C++ "inline" its original and obvious meaning
Gabriel Dos Reis <gdr@integrable-solutions>
"Inlining was considered important for the utility of classes"
-- B. Stroustrup in "The Design an Evolution of C++"
This note is about handling of "inline" by C++ implementations.
It has grown up after various misfortunate experience with
different C++ implementations, reading reports and complaints from C++
users; it is also a reaction to the more or less recent semantics
shift implementors are trying to put in the "inline" keyword. While
this paper uses examples based on GCC/g++ behaviour to illustrate some
points, I believe that most of the issues are not unique to GCC.
A cost-free functional abstraction
One of the principal uses of inline functions is to offer safe,
cost-free functional access to small, simple expressions. Consider:
inline int min(int a, int b)
return a > b ? b : a;
Call to abstractions like min() should not incur function call overhead.
A function like min() is just a convenient notation for the simple
expression it contains.
Is there an alternative way to express that notation? Often, people
in fear, doubt and uncertainty of their compilers not substituting an
inline function body at call sites resort to macros:
#define MIN(A, B) ((A) > (B) ? (B) : (A))
However, that is only a very rough approximation: The macro MIN does
suffer from several serious problems:
* It does break scope rules and it cannot be passed around.
* It does break function call semantics - it evaluates its arguments
more than once.
In other words, that macro looses both semantics and syntax of a function.
That loss is a show-stopper as far as programming in C++ goes.
A compiler targetting a given language should implement the semantics
and support usage patterns of that language. For example, a C++
compiler should inline a call like min(2003-20, 49 * 36) yielding something
(2003-20) > (49 * 36)? (49 * 36) : (2003-20)
which a front-end or back-end optimization should reduce to plain
1764. Even the first C++ compiler, Cfront, could do that in 1985.
Interestingly, the GCC manual (as of this writing) describes inline
functions as follows:
An Inline Function is As Fast as a Macro
By declaring a function inline, you can direct GCC to integrate
that function's code into the code for its callers. This makes
execution faster by eliminating the function-call overhead; in
addition, if any of the actual argument values are constant, their
known values may permit simplifications at compile time so that not
all of the inline function's code needs to be included.
The interesting point about that documentation is that it is close to
the documented meaning of "inline" found in any official C++ manual
Crossing protection barrier ought to be free
Another important use of inline function is for providing controled
access to protected data. In fact, such uses were the decisive factors
for introducing inline function in C++:
In particular, [Stroustrup, 1982b] observes that people had made
data members public to avoid the function call overhead incurred by
a constructor for simple classes where only one or two assignments
are needed for initialization. The immediate cause for the
inclusion of inline functions into C with Classes was a project
that couldn't afford function call overhead for some classes
involved in real-time processing. For classes to be useful in that
applications, crossing the protection barrier had to be free.
-- B. Stroustrup in "The Design and Evolution of C++"
Those words were written more than 20 years ago, but they do sound so
familiar, so contemporary. Access control is a compile-time notion.
A controled access to protected data ought to be free.
It is crucial to realize that "inline" in C++ does not mean "leave
this to the back-end". It has a language specific meaning. Inline is
in C++ exactly because the programmer cannot rely on a compiler
reliably inline predictably and reasonably according to the
programmer's needs for a particular program. Even if one compiler does
it right, reliance on a compiler's inlining heuristics will not lead
to portable behavior vis a vis the performance. Inlining was
introduced as a language feature into C++ exactly to give the
programmer control, a lever to state his preference for an alternate
function call mechanism, namely <it>substitution</it>. An advantage of
substitution is that once inlined, the function body can expose many
optimization opportunities that otherwise would necessitate more
More than twenty years after its introduction in C++, do major and
common C++ compilers get "inline" right? I fear the answer is
<strong>no</strong> for many of them. Failures to implement the
meaning of "inline" and to support usage patterns has been incitative
for otherwise talented programmers to deploy all sorts of
workarounds, going against good software practice, defeating the
intended semantics of common abstractions. Simple abstractions like
inline const T& min(const T& a, const T& b)
return a > b ? b : a;
or std::string::end() should be efficiently handled by C++ compilers.
Simple inline functions should be cost-free. Functions that provide
controled access to data should be as efficient as accessing the data
directly. Whatever strategy C++ implementors use to implement "inline",
the delivered behaviour should meet the documented obvious and
original meaning of "inline".
Is "inline" just like "register"?
It is not uncommon to see the argument that "inline" is just a
<em>hint</em>, like "register", and as such the compiler should treat
them equally, i.e. ignore the C++ programmer preference because the
compiler knows best.
Such an argument is confusing. There is no dispute that "inline" is a
hint. But it is a hint only because, quoting again , p. 34:
This is a logical necessity because one can write inline functions
that cannot at compile time be proven not to cause infinite
recursions; trying to inline one of those would lead to infinite
compilations. Leaving "inline" a hint is also a practical
advantage because it allows the compiler writer to handle
"pathological" by simply not inlining.
Another way Stroustrup used to put the logical necessity is: "we don't
want to require the compiler to solve the halting problem". Of course,
"pathological" is not clearly defined, but it is matter of fact that no
simple, short expression is pathogolical. In the very early days,
a loop in a function called in a way that required use of its return
value to be used was considered pathological, but most compilers can
do better today and this case can be very important because inlining
in this case opens possibilities for optimization.
Furthermore, it is not true that in real code the compiler often knows
better than the programmer what to inline. Compiler heuristics don't
serve all people well and "inline" was introduced into C++ exactly to
give the programmer control. The current state of compiler technology
and development is not at the same level of sophistication as that of
automatic register allocation. Given the control offered by a simple
and straightforward implementation of "inline", good programmers
consistently produce well performing code. Furthermore, compiler
technology is still far from the maturity level where a language
independent meaning of "inline" could be productively substituted for
the language specific meaning it has in C++. When compilers are as
good at inlining as they are at register allocation, "inline" can go
the way of "register". However, we are nowhere near that level of
sophistication yet, so we should - in the best C and C++ tradition -
trust the programmer.
Failure to meet behaviour that have been documented for more than two
decades will only increase dialect proliferations, because the
fundamental need will still be there, and people will continue to
reinvent "inline" with vendor lock-in syntaxes. Compiler middle ends
and back ends are not the right places to change an ISO standardized
Should an Inline Function returning a structure make a difference?
No, an inline function returning a structure or class should not make
any difference. For that, it is crucial to understand the language
specific meaning of inlining in C++: It is substitution of the
function body for the usual call. In other words, it is an alternate
implementation of function call mechanism. Compiler back-ends that
insist on using a language-neutral meaning for C++ inline fail to
understand that feature. Dare I say, they fail to support C++.
Even more so, it is essential that an inline function returning a
structure that contains a scalar be as efficiently supported as an
inline function returning just that scalar. The reason is that
liberal use of types is central for programming in C++, and most of
the types involved in most C++ programs are user-defined types,
i.e. structures or classes.
The writing of this paper benefited from support, inputs and
improvements from friends I would like to thank. Bjarne Stroustrup
patiently corrected errors and confusing formulations contained
in the drafts of this note. He also provided assistance for history.
Paolo Carlini made careful reading, asking for better clarifications
and explanations. Benjamin Kosnik provided data through his continual
exercising, improvement and testing for a "-Winline"-clean GNU
implementation of the C++ standard library. Any remaining error,
inaccuracy are my responsability.
[Stroustrup, 1982b] Bjarne Stroustrup: "Adding Classes to C: An
Exercise in Language Evolution". Bell Laboratories Computer Science
internal document. April 1982. Software: Practice & Experience,
Vol 13, 1983.
 GCC 3.3 Manual
 Bjarne Stroustrup, private communication.
 See patches included in the message
 Bjarne Stroustrup: "The Design and Evolution of C++", 1994 Addison-Wesly.