【问题标题】:Why doesn't the optimizer eliminate High in a loop?为什么优化器不循环消除 High?
【发布时间】:2013-03-01 11:42:29
【问题描述】:

人们说 Delphi 在整数运算上产生了相当不错的优化代码。我在 Delphi 2007 中尝试以下示例,并查看编译器生成的汇编代码。

program p1000;
{$APPTYPE CONSOLE}

procedure test;
var
  arr: array of integer;
  i: integer;

begin
  SetLength(arr, 100);
  for i := 0 to High(arr) do
  begin
    if (i = High(arr)) then
    begin
      arr[i] := -9;
    end;
  end;
end;

begin
  test;
  readln;
end.

当构建配置设置为DEBUG时,我可以设置一个断点并使用快捷键Ctrl+Alt+D来查看它的汇编代码,像这样:

Project3.dpr.11: for i := 0 to High(arr) do
004045A1 8B45FC           mov eax,[ebp-$04]
004045A4 E8F7FAFFFF       call @DynArrayHigh
004045A9 8BF0             mov esi,eax
004045AB 85F6             test esi,esi
004045AD 7C1D             jl $004045cc
004045AF 46               inc esi
004045B0 33DB             xor ebx,ebx
Project3.dpr.13: if (i = High(arr)) then
004045B2 8B45FC           mov eax,[ebp-$04]
004045B5 E8E6FAFFFF       **call @DynArrayHigh**
004045BA 3BD8             cmp ebx,eax
004045BC 750A             jnz $004045c8
Project3.dpr.15: arr[i] := -9;
004045BE 8B45FC           mov eax,[ebp-$04]
004045C1 C70498F7FFFFFF   mov [eax+ebx*4],$fffffff7
Project3.dpr.17: end;
004045C8 43               inc ebx
Project3.dpr.11: for i := 0 to High(arr) do
004045C9 4E               dec esi
004045CA 75E6             jnz $004045b2

据我了解,它在循环中一次又一次地调用High()函数:

Project3.dpr.13: if (i = High(arr)) then
    004045B2 8B45FC           mov eax,[ebp-$04]
    004045B5 E8E6FAFFFF       **call @DynArrayHigh**
    004045BA 3BD8             cmp ebx,eax

当构建配置设置为RELEASE时,断点不可用,所以我按F8/F7进入循环。

00404589 6A64             push $64
0040458B 8D45FC           lea eax,[ebp-$04]
0040458E B901000000       mov ecx,$00000001
00404593 8B1554454000     mov edx,[$00404554]
00404599 E8B6FCFFFF       call @DynArraySetLength
0040459E 83C404           add esp,$04
004045A1 8B45FC           mov eax,[ebp-$04]
004045A4 E8F7FAFFFF       call @DynArrayHigh
004045A9 8BF0             mov esi,eax
004045AB 85F6             test esi,esi
004045AD 7C1D             jl $004045cc
004045AF 46               inc esi
004045B0 33DB             xor ebx,ebx
004045B2 8B45FC           mov eax,[ebp-$04]
004045B5 E8E6FAFFFF       call @DynArrayHigh
004045BA 3BD8             cmp ebx,eax
004045BC 750A             jnz $004045c8
004045BE 8B45FC           mov eax,[ebp-$04]
004045C1 C70498F7FFFFFF   mov [eax+ebx*4],$fffffff7
004045C8 43               inc ebx
004045C9 4E               dec esi
004045CA 75E6             jnz $004045b2
004045CC 33C0             xor eax,eax
004045BC 750A             jnz $004045c8

再次生成相同的call @DynArrayHigh...
所以我的问题是,为什么编译器不能优化这个?只需将 High() 值保存在本地寄存器/变量中,因为数组大小不会改变。

【问题讨论】:

  • 它在 this 线程中没有改变,但是编译器无法知道它是否是从另一个线程改变的——我想在安全优化之前它只能检查这么多。
  • 但是'arr'是一个本地数组,所以其他线程不可能改变它。
  • @roger,这个数组是一个局部变量,所以应该很容易识别。
  • @TLama,我认为这不实用。考虑调用另一个通过引用传递数组的函数,这会改变数组长度。或更改数组长度而不使用显式SetLenth,或重新初始化数组。事实上,如果编译器确实“优化”了这个,我会感到惊讶。
  • @Lurd,您定义的过程接收一个开放数组,而不是动态数组。我很确定开放数组的长度不能被修改。开放数组的长度始终作为第二个参数传递;这在 XE3 中并不新鲜。由于它不是动态数组,所以很清楚为什么编译器不为它调用@DynArrayHigh。相反,编译器读取参数的值,这是存储该打开数组的High 值的唯一位置。

标签: delphi compiler-construction compiler-optimization


【解决方案1】:

这不是一个答案,而是一个(自毁)评论:)

在我看来,编译器不得尝试对此进行优化。

为什么编译器应该尝试优化(非确定性)High 函数而不是其他函数? (如Length

动态 数组长度可以在循环内通过SetLenth 或通过其他方式进行更改。数组可能会在运行时重新初始化,您的代码可能依赖于此:

for i := 0 to High(arr) do
begin
  if (i = High(arr)) then
    arr[i] := -9
  else
    if foo() then
      arr := nil; // or SetLength(arr, 0);

  if High(arr) = -1 then Exit; // arr is nil  
end;

您认为应该如何优化?编译器是否应该尝试对此进行优化? 我没有看到任何关于High functionm 的特殊,即使编译器将其转换为 @DynArrayHigh

如果你想优化你的代码,你自己优化它。例如:

var
  arrHigh: Integer;

  arrHigh := High(arr);
  for i := 0 to arrHigh do
    if i = arrHigh then...

【讨论】:

  • 谢谢.. 我同意。只是想知道编译器是否足够聪明,可以弄清楚。 :)
猜你喜欢
  • 2018-11-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-09
  • 2016-10-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多