Function inlining
published: Tue, 14-Sep-2004 | updated: Thu, 27-Oct-2005
Yes, I'm at BorCon again. (Sheesh: can't he keep away?) Three years ago I was working for TurboPower and attended; two years ago I was working for Aristocrat Technologies (TurboPower's parent) and attended, hitching a ride in TurboPower's booth as it were; a year ago I was working for Microsoft, and just about making the decision to leave them; and now I'm here as part of the Falafel team on an exhibitor pass.
So last night, I carefully turned my pass back to front and smuggled myself into the Diamondback preview session. This turned out to be quite easy since there were so many people and the ladies at the door were swamped.
It was a rather rushed presentation (details can be found on any
number of blogs, including
Nick's), but there was one item in the chat
about language changes that made me shake my head in puzzlement. Danny
Thorpe showed off three new language changes, and so I presume they
were the most important (or maybe there were only three changes in total). The
first was multi-unit namespaces, the second the for..in syntax, and
the last was the inline
keyword being applied to routines and methods.
Now I'd known about the first two (indeed, I've rabbited on at length in the past about namespaces being broken in Delphi 8), but the last was new to me.
Now, throughout the conference so far Borlanders have been talking
about being responsive to customer requests. So someone, or presumably
a large group of people, has requested this feature. Essentially (and
for more details read
Joe White's blog entry),
you can mark a
procedure, function or method with the inline
keyword.
function Foo(someValue : integer) : integer; inline; begin // do some work end;
This keyword tells the compiler that, instead of calling the routine, it should emit code that includes the routine at the call site. This would remove the need for a call and thereby save a little bit of execution time, especially for those routines that don't do very much anyway but are called a lot. The downside is that there is a possibility of a some code bloat. Sounds good, eh?
Well, on further digging, no, not really. First, the inline
keyword is
merely a hint not a command. The number of exceptions to the rule of
the compiler blindly applying it are large and the scenarios where
it's used are pretty much unknowable to the developer in advance. For
example, if the routine is too "large" it won't be inlined. If there
would be too much register "pressure" it won't be inlined.
Second? Well let's see my objection through an analogy.
Imagine that Delphi R&D adds a new keyword called energize
that can be
applied to a routine. Adding this keyword causes the compiler to emit
energized machine code or IL, but only if the foobar metric for the
code is between 2.1718 and 3.1416. It's the compiler that calculates
the foobar metric: it's hard to ascertain this in advance since the
surrounding code's gravitational attraction can affect its value. If
the foobar metric is not in range, the compiler will ignore the
energize
keyword.
Given this, do you:
-
1. Ignore the
energize
keyword altogether, because you don't understand what the foobar metric is? - 2. Only apply it to some routines, those routines being chosen because your intuition or the tea-leaves in your mug tell you that they'd benefit?
- 3. Apply it to every routine you write and let the compiler sort it out?
I know what I'd do: option 1 then option 3, measure the difference in
performance and then go with the winner. In other words, I'd expect a
compiler option that either switched energize
on for everything or
turned it completely off. I won't (and don't) have the time to check
on a routine by routine basis.
And that is exactly the argument I have against the inline
keyword.
We're getting a language syntax change for something that the compiler
should be doing as a matter of course. The compiler should be made
more intelligent to make this decision for me, not the language made
more complex so that I have yet another thing to worry about. Machine
code generation and its run-time execution performance is up to the
compiler, and the compiler writer, not the developer. The developer is
in charge of the algorithms and structures that his application
require; he shouldn't be worried about the machine code representation
of this business logic.
(Having said that, of course the developer should be aware of some of the consequences of implementing his code in a certain way: boxing, string copying, when to use var parameters, etc. But, in general, he must trust the compiler writers to do the best thing in translating our code into machine code or IL. He must also trust the Jitter writers to do the best thing when IL is executed. After all, we don't have to worry about register usage, optimization, and caching, so why should we have to worry about when it's best to inline routines or not.)
I wonder what was left out because inline
went in?
Update
It seems that there is a compiler switch to turn inlining on.
Nick gave me the hint at the
end of the Borland conference, but I was dashing off somewhere and didn't
catch what he was saying (I thought he was talking about $AUTOBOX
). Use
{$INLINE AUTO}
. So my advice is to ignore the inline
keyword
altogether and use this switch instead, but again please test either way
to understand the performance vs. space characteristics.