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的结果可能是误导的“指责”。但是,为了公平起见,我应该引导你仔细阅读这个问题:Is There A Fast GetToken Routine For Delphi?开始考虑AQTime给出了误导的结果,并得出结论。
解决方法
在任何情况下,这是另一个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