Delphi优化:恒定循环

nul*_*ull 6 delphi optimization performance for-loop compiler-optimization

我刚刚在一个正在编写的程序中注意到了一些非常有趣的东西.我有一个简单的过程,用一个x类型的对象填充TStringlist.

我在跟踪一个问题时添加了一个断点,偶然发现这个问题,希望有人能够解释它为什么会发生这种情况,或者链接到相关文档,因为我找不到任何东西.

我的循环从0到11开始.我正在使用的指针在循环中初始化为nPtr:= 0但是当程序运行时,nPtr var从12变为1.然后我将var初始化为循环如代码片段所示,但同样的事情发生了.该变量在单元中没有其他地方使用.

我问过一位与我合作的人,他们说这是由于Delphi的优化,但我想知道为什么以及如何决定应该影响哪个循环.

谢谢你的帮助.

码:

procedure TUnit.ProcedureName;
var
    nPtr : Integer;
    obj : TObject;
begin
nPtr:=0;//added later
for nPtr := 0 to 11 do
    begin
    obj := TObject.Create(Self);
    slPeriodList.AddObject('X', obj);
    end;
end;
Run Code Online (Sandbox Code Playgroud)

Dav*_*nan 8

只有在循环体不引用循环变量时才可以进行优化.在这种情况下,如果循环的下限为零,则编译器将反转循环.

如果循环变量从未被循环体引用,那么编译器在实现循环时是合理的,但它很高兴.所需要做的就是按照循环边界的要求多次执行循环体.实际上,编译器在优化循环变量方面是完全合理的.

考虑这个程序:

{$APPTYPE CONSOLE}

procedure Test1;
var
  i: Integer;
begin
  for i := 0 to 11 do
    Writeln(0);
end;

procedure Test2;
var
  i: Integer;
begin
  for i := 0 to 11 do
    Writeln(i);
end;

begin
  Test1;
  Test2;
end.
Run Code Online (Sandbox Code Playgroud)

该主体Test1由XE7编译为此代码,32位Windows编译器,具有发布选项:

Project1.dpr.9: for i := 0 to 11 do
00405249 BB0C000000       mov ebx,$0000000c
Project1.dpr.10: Writeln(0);
0040524E A114784000       mov eax,[$00407814]
00405253 33D2             xor edx,edx
00405255 E8FAE4FFFF       call @Write0Long
0040525A E8D5E7FFFF       call @WriteLn
0040525F E800DBFFFF       call @_IOTest
Project1.dpr.9: for i := 0 to 11 do
00405264 4B               dec ebx
00405265 75E7             jnz $0040524e

编译器正在向下运行循环,可以通过使用来看到dec.请注意,循环终止测试是在jnz不需要a的情况下执行的cmp.那是因为dec执行与零的隐式比较.

文档dec说明如下:

受影响的旗帜

CF标志不受影响.根据结果​​设置OF,SF,ZF,AF和PF标志.

ZF当且仅当dec指令的结果为零时,才设置该标志.而ZF决定是否jnz分支的是什么.

发出的代码Test2是:

Project1.dpr.17: for i := 0 to 11 do
0040526D 33DB             xor ebx,ebx
Project1.dpr.18: Writeln(i);
0040526F A114784000       mov eax,[$00407814]
00405274 8BD3             mov edx,ebx
00405276 E8D9E4FFFF       call @Write0Long
0040527B E8B4E7FFFF       call @WriteLn
00405280 E8DFDAFFFF       call @_IOTest
00405285 43               inc ebx
Project1.dpr.17: for i := 0 to 11 do
00405286 83FB0C           cmp ebx,$0c
00405289 75E4             jnz $0040526f

请注意,循环变量正在增加,我们现在有一个额外的cmp指令,在每次循环迭代时执行.

值得注意的是,64位Windows编译器不包括此优化.因为Test1它产生了这个:

Project1.dpr.9: for i := 0 to 11 do
00000000004083A5 4833DB           xor rbx,rbx
Project1.dpr.10: Writeln(0);
00000000004083A8 488B0D01220000   mov rcx,[rel $00002201]
00000000004083AF 4833D2           xor rdx,rdx
00000000004083B2 E839C3FFFF       call @Write0Long
00000000004083B7 4889C1           mov rcx,rax
00000000004083BA E851C7FFFF       call @WriteLn
00000000004083BF E86CB4FFFF       call @_IOTest
00000000004083C4 83C301           add ebx,$01
Project1.dpr.9: for i := 0 to 11 do
00000000004083C7 83FB0C           cmp ebx,$0c
00000000004083CA 75DC             jnz Test1 + $8

我不确定为什么在64位编译器中没有实现这种优化.我的猜测是,优化在现实世界中的情况可以忽略不计,设计人员选择不花费精力为64位编译器实现它.