Delphi何时会使用`inline`,何时不会?

5

我正在尝试优化一段代码,其中有这样的结构:

while (i > 0) do begin
  Dec(i);

这看起来很低效,所以我尝试了这个方法:
while (Dec(i) >= 0) do begin

那样做不起作用,因为Dec是一个过程而不是函数。

所以我重写成:

procedure Withloop;
var
  ....
  function Decr(var a: integer): integer; inline;
  begin
    Dec(a);
    Result:= a;
  end;

...
  while (Decr(i) >= 0) do begin

但是这将被编译为:

SDIMAIN.pas.448: while (Decr(i) >= 0) do begin
00468EE5 8BC4             mov eax,esp
00468EE7 E8D0FEFFFF       call Decr          <<--- A call??
00468EEC 85C0             test eax,eax
00468EEE 0F8D12FFFFFF     jnl $00468e06
00468EF4 EB01             jmp $00468ef7

然而,在程序的另一部分,它可以很好地内联一个函数。
有什么经验法则(或硬性规定)可以让我知道Delphi会遵守inline指令吗?


8
与你的说法相反,我认为你的第一段代码在这里是最有效的。 - Steve Mayne
5
你为什么要优化一个循环计数器呢?整数运算非常便宜,每次迭代只进行一次递减操作,所以如果循环有任何有趣的操作,那将占据绝大部分时间。而且这还没有触及到这个循环是否是优化的好候选对象的问题(它是热点代码还是仅占总运行时间的0.5%)。如果你了解一些机器代码的样子,你就会意识到前两个版本很容易编译成相同的代码。优化失败。 - user395760
1
@delnan,我有点讨厌人们默认你是个白痴。当然,循环是hot,否则我也不会费这个劲儿。测试做了更多的事情,但我将其简化到了本质。此外,我知道你不能内联asm函数,因此逻辑选择:function Decr(var a: integer): integer; inline; begin asm DEC EAX end; end;已经被排除了。 - Johan
2
我很难相信你会比第一个代码块做得更好。即使第二个代码块是有效的,为什么它会编译成比第一个代码块更快的东西呢?我认为delnan并没有认为你是个白痴。许多人对优化提出的问题都基于严重的误解。既然你没有声明这个特定的循环是代码中的热点,我们怎么能知道呢?现在,如果你在循环中做任何事情,那么while测试和dec将是微不足道的,所以也许delnan有一点道理。 - David Heffernan
8
你错了。for循环可以运行0次。请务必确保你的循环计数器是带符号整数。事实上,总是使用“Integer”。 - David Heffernan
显示剩余15条评论
2个回答

23

Delphi文档 枚举了内联发生或不发生的条件:

任何形式的后期绑定方法都不会进行内联。这包括虚拟、动态和消息方法。 包含汇编代码的程序不会被内联。 构造函数和析构函数不会被内联。 主程序块、单元初始化和单元终止块不能被内联。 在使用之前未定义的例程不能被内联。 带有开放数组参数的例程不能被内联。 可以在包中内联代码,但是内联从不跨越包边界发生。 不会在循环依赖的单元之间进行内联。这包括间接循环依赖,例如,单元A使用单元B,单元B使用单元C,而单元C又使用单元A。在此示例中,在编译单元A时,不会将单元B或单元C的代码内联到单元A中。 当一个单元处于循环依赖状态时,编译器可以内联代码,只要要内联的代码来自于循环关系之外的单元。在上面的示例中,如果单元A还使用了单元D,则可以在A中内联来自单元D的代码,因为它不涉及循环依赖。 如果在接口部分中定义的例程访问了在实现部分中定义的符号,则该例程无法内联。 如果标记为inline的例程使用其他单元中的外部符号,则必须在uses语句中列出所有这些单元,否则该例程无法内联。 在while-do和repeat-until语句的条件表达式中使用的过程和函数不能被展开成内联。 在一个单元内,在调用函数之前应定义内联函数的主体。否则,当编译器到达调用点时,它不知道函数的主体,因此无法将其展开成内联形式。
在您的情况下,请检查此条件:

在while-do和repeat-until语句中使用的条件表达式中使用的过程和函数无法进行内联扩展。


10

由于某些原因,编译器不会内联while循环控制表达式。Hallvard Vassbotn之前曾经讨论过这个问题(请阅读文章结尾部分)。


3
文章最后部分的简要概述:将 while True do begin if Decr(i) < 0 then Break; 中的 Decr 函数内联化。 - Sertac Akyuz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接