Function inlining

published: Tue, 14-Sep-2004   |   updated: Thu, 27-Oct-2005
Swan

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.