Tuesday, April 17, 2007

DN4DP#7: Inlined routines

This post continues the series of The Delphi Language Chapter teasers from Jon Shemitz’ .NET 2.0 for Delphi Programmers book. Last time we covered the new for in loop and the pattern for introducing enumeration support to your own classes. This post includes the section on the inlining support of the compiler.

Note that I do not get any royalties from the book and I highly recommend that you get your own copy – for instance at Amazon.

"Inlined routines

From Chapter 4 we know that the .NET Just-In-Time (JIT) compiler will automatically perform optimizations, including inlining small and simple methods at call sites. In addition to this JIT inlining, Delphi now supports explicit inlining of non-virtual routines, both in .NET and Win32.

function InlineMeToo(const Value: integer): integer; inline;
begin
Result := Value * 200 div 300;
end;

The inline directive is just a hint to the Delphi compiler that it should try to expand the code inline whenever the routine is called, at compile time. The exact rules of what and when it can be inlined differ slightly between the two platforms.

In .NET, Delphi inlining occurs at the IL level. This means that the IL code generated at the call site must obey CLR limitations and rules regarding member visibility. This limitation is mitigated by the less constrained JIT inlining that occurs at runtime.

The Win32 inlining support was made more aggressive in Delphi 2006 - now even methods that access private members can be inlined. Assembly (BASM) code cannot be inlined.

The rest of the inlining restrictions are common for both platforms and the most important ones are


  • no inlining across package boundaries
  • the inlined routine cannot access implementation section identifiers
  • the call site must have access to all identifiers used in the inlined routine


Note The last point means that unless the call site unit uses the units required by the routine, the routine cannot be inlined. When this happens, the compiler emits a hint like this

[Pascal Hint] InlinedRoutinesU.pas(14): H2443 Inline function 'InlineMe' has not been expanded because unit 'RequiredUnit' is not specified in USES list

To resolve the issue, add the missing unit name to the call site's uses clause.


The {$INLINE ON/AUTO/OFF} compiler directive can be used both at the definition and the call site. The OFF mode turns off all inlining. The default ON setting tries to inline routines explicitly marked inline. AUTO additionally tries to inline all small routines (consisting of less than 32 bytes of machine or IL code).




Caution Be careful with inlining too much code, the potential code size increase may actually reduce performance.




"


Update [May 12th 2007]:


In addition to the inline restrictions mentioned above I should have included the issue of implementation order.


If the inlined method is called from the same unit, the implementation of the inline method must have been "seen" by the compiler. In other words, the inlined implementation should preceed the call.

For example:

type
Foo = class
procedure A; inline;
procedure B;
end;

procedure TFoo.B;
begin
A; // note: Call to A is *not* inlined here
end;

procedure TFoo.A;
begin
// code
end;

Change this to:

type
Foo = class
procedure A; inline;
procedure B;
end;

procedure TFoo.A;
begin
// code
end;

procedure TFoo.B;
begin
A; // note: Call to A *is* inlined here
end;

2 comments:

Anonymous said...

Any idea why class methods don't get inlined when calles from non-class methods? (They do seem to get inlined in other class methods, at least that's what I see in Delphi 2006 win32 personality). Cheers, Patrick.

gabr said...

Be also extra careful when inlining as inliner is not as mature as the rest of the compiler.

I recently found some code that doesn't inline properly in BDS2006. The problem has been fixed in Delphi 2007, though.



Copyright © 2004-2007 by Hallvard Vassbotn