Again inline optimalization

by Radek Červinka 25. September 2010 00:12

So small comment for inline optimalization. This time inspired by Allen Bauer and I never know about this before..

For example this "good" code:

    1program InlineTest;
    2{$APPTYPE CONSOLE}
    3{$O+}
    4uses
    5  SysUtils;
    6
    7var
    8  s : string;
    9begin
   10  s:= 'Test';
   11  writeln(s);
   12  writeln(Length(s));
   13end.

Internally compiler for length call "magic" function, in Delphi 5 _LStrLen from system.pas, in Delphi 2010 or XE _UStrLen from system.pas too.

Delphi 5 example:

    1function        _LStrLen{str: AnsiString}: Longint;
    2asm
    3        { ->    EAX str }
    4
    5        TEST    EAX,EAX
    6        JE      @@done
    7        MOV     EAX,[EAX-skew].StrRec.length;
    8@@done:
    9end;

For Delphi XE:

    1function _UStrLen(const S: UnicodeString): Integer;
    2{$IF defined(CPUX64)}
    3begin
    4  Result := 0;
    5  if Pointer(S) <> nil then                // PStrRec should be used here, but
    6    Result := PLongInt(NativeInt(S) - 4)^; // a private symbol can't be inlined
    7end;
    8{$ELSE}
    9begin
   10  Result := Longint(S);
   11  if Result <> 0 then                // PStrRec should be used here, but
   12    Result := PLongint(Result - 4)^; // a private symbol can't be inlined
   13end;
   14{$IFEND}}

Main difference is this: in Delphi 5 procedure is in assembler, but in Delphi XE in Pascalu. At first I think that because 64bit.

OK, so Delphi 2007 code (e.g. nonunicode version with inline)

    1function _LStrLen(const S: AnsiString): Longint;
    2begin
    3  Result := Longint(S);
    4  if Result <> 0 then                // PStrRec should be used here, but
    5    Result := PLongint(Result - 4)^; // a private symbol can't be inlined
    6end;

Starting with Delphi 2007 are appropriate function marketed as inline. This mean that compiler (linker?) can skip call of subroutine and inline code directly.

Delphi 5
004086F8 E8BFAEFFFF       call @Write0LString
004086FD E8C5BEFFFF       call @WriteLn
00408702 E8F99EFFFF       call @_IOTest
Project1.dpr.11: writeln(Length(s));
00408707 A100A64000       mov eax,[s]
0040870C E85FADFFFF       call @LStrLen
00408711 8BD0             mov edx,eax
00408713 A1EC924000       mov eax,[$004092ec]
00408718 E8BFA0FFFF       call @Write0Long
0040871D E8A5BEFFFF       call @WriteLn
00408722 E8D99EFFFF       call @_IOTest
Delphi XE without inline
004111E0 E87B33FFFF       call @Write0UString
004111E5 E8EE33FFFF       call @WriteLn
004111EA E8012BFFFF       call @_IOTest
004111EF A1787E4100       mov eax,[$00417e78]
004111F4 E8774DFFFF       call @UStrLen
004111F9 8BD0             mov edx,eax
004111FB A1EC2C4100       mov eax,[$00412cec]
00411200 E80B33FFFF       call @Write0Long
00411205 E8CE33FFFF       call @WriteLn
0041120A E8E12AFFFF       call @_IOTest
Delphi XE with inline
004111E1 E87A33FFFF       call @Write0UString
004111E6 E8ED33FFFF       call @WriteLn
004111EB E8002BFFFF       call @_IOTest
004111F0 8B1D787E4100     mov ebx,[$00417e78]
004111F6 85DB             test ebx,ebx
004111F8 7405             jz $004111ff
004111FA 83EB04           sub ebx,$04
004111FD 8B1B             mov ebx,[ebx]
004111FF A1EC2C4100       mov eax,[$00412cec]
00411204 8BD3             mov edx,ebx
00411206 E80533FFFF       call @Write0Long
0041120B E8C833FFFF       call @WriteLn
00411210 E8DB2AFFFF       call @_IOTest

If we write inline function in Object Pascal, we allow the compiler use any available register instead of programmer's. If we write inline function in assembler, compiler must probably manipulate registers (or ignore inline directive) and this is not optimal.

Conclusion: If you have a really big reason for the assembler, so do not do it :-)

Tags: , ,

Optimalization

Comments are closed