我很困惑。今天在 CodeRage,Marco Cantu 说 CharInSet 很慢,我应该尝试使用 Case 语句。我在我的解析器中这样做了,然后用 AQTime 检查了加速是什么。我发现 Case 语句要慢得多。
4,894,539 次处决:
而不是 CharInSet (P^, [' ', #10,#13, #0]) 做 inc(P);
时间为 0.25 秒。
但相同数量的执行:
而 True do
case P^ of
' ', #10, #13, #0: break;
其他公司(P);
结尾;
“while True”需要 0.16 秒,第一种情况需要 0.80 秒,else 情况需要 0.13 秒,总共需要 1.09 秒,或超过 4 倍。
CharInSet 语句的汇编代码是:
add edi,$02
mov edx,$0064b290
movzx eax,[edi]
call CharInSet
test a1,a1
jz $00649f18(回到add语句)
而案例逻辑就是这样:
movzx eax,[edi]
sub ax,$01
jb $00649ef0
sub ax,$09
jz $00649ef0
sub ax,$03
jz $00649ef0
add edi,$02
jmp $00649ed6(回到 movzx 语句)
在我看来,案例逻辑使用了非常高效的汇编程序,而 CharInSet 语句实际上必须调用 CharInSet 函数,该函数位于 SysUtils 中,也很简单,即:
函数 CharInSet(C: AnsiChar; const CharSet: TSysCharSet): 布尔值;
开始
Result := C in CharSet;
结尾;
我认为这样做的唯一原因是因为 [' ', #10, #13, #0] 中的 P^ 在 Delphi 2009 中不再允许,因此调用会转换类型以允许它。
尽管如此,我对此感到非常惊讶,但仍然不相信我的结果。
AQTime 测量是否有问题,我是否在比较中遗漏了什么,或者 CharInSet 真的是一个值得使用的高效函数?
结论:
我想你明白了,巴里。感谢您抽出宝贵时间做详细的示例。我在我的机器上测试了你的代码,得到了 0.171、0.066 和 0.052 秒(我猜我的台式机比你的笔记本电脑快一点)。
在 AQTime 中测试该代码,它给出了:三个测试的 0.79、1.57 和 1.46 秒。在那里,您可以看到仪器的大量开销。但真正让我吃惊的是,这种开销将明显的“最佳”结果更改为实际上是最差的 CharInSet 函数。
所以 Marcu 是正确的,而 CharInSet 更慢。但是您无意中(或者可能是故意)通过提取 CharInSet 对 Set 方法中的 AnsiChar(P^) 所做的事情,给了我一个更好的方法。除了与 case 方法相比速度稍有优势之外,它还比使用 case 方法更少的代码和更易于理解。
您还让我意识到使用 AQTime(和其他检测分析器)进行错误优化的可能性。知道这一点将有助于我对 Delphi 的 Profiler 和内存分析工具做出决定,这也是对我的问题AQTime 如何做到这一点的另一个答案?. 当然,AQTime 在检测时不会更改代码,因此它必须使用其他魔法来完成。
所以答案是 AQTime 显示的结果会导致错误的结论。
跟进:我留下了这个问题,“指责”AQTime 结果可能具有误导性。但公平地说,我应该指导您阅读这个问题:Delphi 是否有快速 GetToken 例程?它开始认为 AQTime 给出了误导性的结果,并得出结论认为它没有。