Оптимизация Delphi: постоянный цикл

Я только что заметил кое-что интересное в программе, которую я пишу. У меня есть простая процедура, которая заполняет TStringlist объектами типа x.

Я добавил точку останова, когда я отслеживал проблему, и наткнулся на этот ans, надеясь, что кто-то сможет объяснить, почему это происходит, или ссылку на соответствующий документ, поскольку я ничего не мог найти.

мой цикл идет от 0 - 11. Указатель, который я использую, был инициализирован в цикле для nPtr: = 0, но когда программа была запущена, nPtr var шел от 12 до 1. Тогда я запустить ВАР вне цикла, как показано в фрагменте кода, но произошло то же самое. Переменная больше нигде не используется.

Я спросил одного из парней, с которыми я работал, который сказал, что это связано с оптимизацией 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 ответов


оптимизация возможна только в том случае, если тело цикла не ссылается на переменную цикла. В этом случае, если нижняя граница цикла равна нулю, компилятор обратит цикл.

если переменная цикла никогда не ссылается на тело цикла, то компилятор оправдан в реализации цикла, как ему заблагорассудится. Все, что требуется сделать, это выполнить тело цикла столько раз, сколько требуется границами цикла. Действительно, компилятор был бы совершенно оправдан в оптимизация переменной цикла.

считайте эту программу:

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

компилятор запускает цикл вниз, как видно из использования 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,[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,c
00405289 75E4             jnz 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 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,c
00000000004083CA 75DC             jnz Test1 + 

я не уверен, почему эта оптимизация не была реализована в 64-битный компилятор. Я предполагаю, что оптимизация имеет незначительный эффект в реальном мире cases и дизайнеры решили не тратить усилий на его реализацию для 64-битного компилятора.