【发布时间】:2010-09-24 21:07:23
【问题描述】:
我很困惑。今天在 CodeRage,Marco Cantu 说 CharInSet 很慢,我应该尝试使用 Case 语句。我在我的解析器中这样做了,然后用 AQTime 检查了加速是什么。我发现 Case 语句要慢得多。
4,894,539 次处决:
虽然不是 CharInSet (P^, [' ', #10,#13, #0]) 做 inc(P);
时间为 0.25 秒。
但执行次数相同:
而真正做到
案例 P^ 的
' ', #10, #13, #0: 休息;
其他公司(P);
结束;
“while True”需要 0.16 秒,第一种情况需要 0.80 秒,else 情况需要 0.13 秒,总计 1.09 秒,或超过 4 倍。
CharInSet 语句的汇编代码是:
添加edi,$02
mov edx,$0064b290
movzx eax,[edi]
调用 CharInSet
测试 a1,a1
jz $00649f18(回到add语句)
而案例逻辑就是这样:
movzx eax,[edi]
子斧头,$01
jb $00649ef0
子斧头,09 美元
jz $00649ef0
子斧,$03
jz $00649ef0
添加 edi,$02
jmp $00649ed6(回到 movzx 语句)
在我看来,案例逻辑使用了非常高效的汇编程序,而 CharInSet 语句实际上必须调用 CharInSet 函数,该函数位于 SysUtils 中,也很简单,即:
function CharInSet(C: AnsiChar; const CharSet: TSysCharSet): Boolean;
开始
结果 := CharSet 中的 C;
结束;
我认为这样做的唯一原因是因为 [' ', #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(和其他检测分析器)进行错误优化的可能性。知道这一点将有助于我重新做出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 在检测时确实会更改代码,这就是检测分析器的工作方式。
-
与大多数分析器不同,AQTime 以某种方式在不修改源代码的情况下完成其工作。我真的很喜欢这样,因为不用担心失败会破坏你的代码。它必须以某种方式分析目标代码并将其链接回源代码行以进行分析。但我还是不知道它是怎么做到的。
-
我想我知道它是如何做到的。见:stackoverflow.com/questions/322315/how-does-aqtime-do-it
标签: delphi delphi-2009 case-statement