Delphi优化:恒定循环

时间:2015-02-02 14:43:31

标签: performance delphi for-loop optimization 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;

1 个答案:

答案 0 :(得分:9)

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

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

考虑这个程序:

{$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.

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执行的,不需要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位编译器实现它。