delphi – 为什么CharInSet比Case语句更快?

前端之家收集整理的这篇文章主要介绍了delphi – 为什么CharInSet比Case语句更快?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我很困惑在CodeRage今天,Marco Cantu说CharInSet很慢,我应该尝试一个Case语句。我在我的解析器中这样做,然后用AQTime检查加速是什么。我发现Case语句要慢得多。

4,894,539处决:

while not CharInSet (P^,[‘ ‘,#10,#13,#0]) do inc(P);

时间为0.25秒。

但同样数量的处决:

while True do
case P^ of
‘ ‘,#0: break;
else inc(P);
end;

对于“while True”为.16秒,第一种情况为.80秒,else为0.13秒,总共为1.09秒或超过4倍。

CharInSet语句的汇编代码是:

add edi,$02
mov edx,$0064b290
movzx eax,[edi]
call CharInSet
test a1,a1
jz $00649f18 (back to the add statement)

而案例逻辑就是这样的:

movzx eax,[edi]
sub ax,$01
jb $00649ef0
sub ax,$09
jz $00649ef0
sub ax,$03
jz $00649ef0
add edi,$02
jmp $00649ed6 (back to the movzx statement)

case逻辑看起来我使用非常高效的汇编器,而CharInSet语句实际上必须调用CharInSet函数,它在SysUtils中也很简单,它是:

function CharInSet(C: AnsiChar; const CharSet: TSysCharSet): Boolean;
begin
Result := C in CharSet;
end;

我认为这样做的唯一原因是因为Delphi 2009中不再允许[”,#10,#13,#0]中的P ^,所以调用类型的转换是允许的。

无论如何,我非常惊讶,仍然不信任我的结果。

AQTime测量出错了,我在这个比较中缺少一些东西,还是CharInSet真正有价值的功能呢?

结论:

我想你得到了,巴里。感谢您抽出时间并做详细的例子。我在我的机器上测试了你的代码,并获得了.171,.066和.052秒(我猜我的桌面比你的笔记本电脑要快一些)。

在AQTime中测试代码,它给出了三个测试的0.79,1.57和1.46秒。在那里,您可以看到仪器的大量开销。但真正令我惊奇的是,这种开销将显而易见的“最佳”结果改变为CharInSet函数,这实际上是最差的。

所以Marcu是正确的,而CharInSet更慢。但是,在Set方法中,通过使用AnsiChar(P ^)提取CharInSet正在做的事情,无意中(或者也可能是故意的)给了我更好的方法。除了比例方法的次要速度优势之外,它也比使用案例更少的代码和更容易理解。

您也让我意识到使用AQTime(和其他仪器分析器)不正确优化的可能性。知道这将有助于我的决定Profiler and Memory Analysis Tools for Delphi,它也是我的问题How Does AQTime Do It?的另一个答案。当然,AQTime不会更改代码,当它的工具,所以它必须使用一些其他的魔法来做到这一点。

所以答案是AQTime显示导致错误结论的结果。

跟进:我把这个问题留给了AQTime的结果可能是误导的“指责”。但是,为了公平起见,我应该引导你仔细阅读这个问题:Is There A Fast GetToken Routine For Delphi?开始考虑AQTime给出了误导的结果,并得出结论。

解决方法

AQTime是一个仪器分析器。仪表分析器通常不适合测量代码时间,特别是在像您这样的微型基准测试中,因为仪器的成本往往超过被测量的成本。另一方面,仪器仪表仪表能够分析内存和其他资源的使用情况。

定期检查cpu位置的采样分析器通常更适合测量代码时间。

在任何情况下,这是另一个microbenchmark,它确实表明一个case语句比CharInSet快。但是,请注意,set检查仍然可以与typecast一起使用以消除截断警告(实际上这是CharInSet存在的唯一原因):

{$apptype console}

uses Windows,SysUtils;

const
  SampleString = 'foo bar baz blah de;blah de blah.';

procedure P1;
var
  cp: PChar;
begin
  cp := PChar(SampleString);
  while not CharInSet(cp^,[#0,';','.']) do
    Inc(cp);
end;

procedure P2;
var
  cp: PChar;
begin
  cp := PChar(SampleString);
  while True do
    case cp^ of
      '.',#0,';':
        Break;
    else
      Inc(cp);
    end;
end;

procedure P3;
var
  cp: PChar;
begin
  cp := PChar(SampleString);
  while not (AnsiChar(cp^) in [#0,'.']) do
    Inc(cp);
end;

procedure Time(const Title: string; Proc: TProc);
var
  i: Integer;
  start,finish,freq: Int64;
begin
  QueryPerformanceCounter(start);
  for i := 1 to 1000000 do
    Proc;
  QueryPerformanceCounter(finish);
  QueryPerformanceFrequency(freq);
  Writeln(Format('%20s: %.3f seconds',[Title,(finish - start) / freq]));
end;

begin
  Time('CharInSet',P1);
  Time('case stmt',P2);
  Time('set test',P3);
end.

它在我笔记本电脑上的输出是:

CharInSet: 0.261 seconds
case stmt: 0.077 seconds
 set test: 0.060 seconds

猜你在找的Delphi相关文章