Delphi 优化:常量循环

Delphi optimisation : constant loop

我刚刚注意到我正在编写的程序中有一些非常有趣的东西。我有一个简单的过程,它用 x.

类型的对象填充 TStringlist

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

我的循环从 0 到 11。我正在使用的指针在循环中由 for 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;

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

如果循环变量从未被循环体引用,那么编译器可以随心所欲地实现循环。它所需要做的就是执行循环体,次数与循环边界规定的一样多。实际上,编译器完全有理由优化掉循环变量。

考虑这个程序:

{$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,[=25=]00000c
Project1.dpr.10: Writeln(0);
0040524E A114784000       mov eax,[[=25=]407814]
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 [=25=]40524e

编译器是运行向下循环,使用dec可以看出。请注意,循环终止测试是使用 jnz 执行的,不需要 cmp。这是因为 dec 对零执行隐式比较。

dec 的文档说明如下:

Flags Affected

The CF flag is not affected. The OF, SF, ZF, AF, and PF flags are set according to the result.

当且仅当 dec 指令的结果为零时,才会设置 ZF 标志。而 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,[[=26=]407814]
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,[=26=]c
00405289 75E4             jnz [=26=]40526f

请注意,循环变量在增加,我们现在有一个额外的 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 [=27=]002201]
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,
Project1.dpr.9: for i := 0 to 11 do
00000000004083C7 83FB0C           cmp ebx,[=27=]c
00000000004083CA 75DC             jnz Test1 + 

我不确定为什么这个优化没有在 64 位编译器中实现。我的猜测是优化在现实世界中的影响可以忽略不计并且设计者选择不花费精力为 64 位编译器实现它。