Ještě malá poznámka k inline optimalizacím. Tentokrát na ni upozornil přímo Allen Bauer a dokud na ni neupozornil on, tak jsem si tuto souvislost neuvědomil.
Mějme následující kvalitní kód:
1program InlineTest;
2
3
4uses
5 SysUtils;
6
7var
8 s : string;
9begin
10 s:= 'Test';
11 writeln(s);
12 writeln(Length(s));
13end.
Interně kompilátor pro length volá "magic" funkci, v případě Delphi 5 je to _LStrLen v system.pas, v případě Delphi 2010 nebo XE (ale i předchozích) je to _UStrLen také v system.pas.
Pro Delphi 5 je to
1function _LStrLen: Longint;
2asm
3
4
5 TEST EAX,EAX
6 JE @@done
7 MOV EAX,[EAX-skew].StrRec.length;
8@@done:
9end;
Pro Delphi XE je to
1function _UStrLen(const S: UnicodeString): Integer;
2
3begin
4 Result := 0;
5 if Pointer(S) <> nil then
6 Result := PLongInt(NativeInt(S) - 4)^;
7end;
8
9begin
10 Result := Longint(S);
11 if Result <> 0 then
12 Result := PLongint(Result - 4)^;
13end;
14}
Hlavní rozdíl je v tom, že v Delphi 5 je to v assembleru, kdežto v Delphi XE je to v Pascalu. Myslel jsem si původně, že je to k vůli 64bit.
OK, takže implementace v Delphi 2007 (tj. neunicode verze s inline)
1function _LStrLen(const S: AnsiString): Longint;
2begin
3 Result := Longint(S);
4 if Result <> 0 then
5 Result := PLongint(Result - 4)^;
6end;
Od Delphi 2007 jsou patřičné funkce označeny jako inline, tj. kompilátor může vynechat volání a kód začlenit přímo.
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 bez 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 s 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
Tím, že inlinovaná funkce je psána přímo v Object Pascalu, je umožněno kompilátoru při inline optimalizaci používat i jiné registry, než by původně programátor zamýšlel. V případě, že by inline funkce byla psaná v assembleru (jako v Delphi 5), musel by kompilátor před voláním šachovat s registry, což určitě není optimální.
Závěr: pokud opravdu nemáte velký důvod pro assembler, tak to nedělejte :-)