困惑しています。今日のCodeRageで、Marco Cantuは、CharInSetが遅いので、代わりにCaseステートメントを試す必要があると述べました。パーサーでこれを行ってから、AQTimeでスピードアップを確認しました。Caseステートメントの方がはるかに遅いことがわかりました。
4,894,539の実行:
CharInSet(P ^、[''、#10、#13、#0])ではありませんがinc(P);を実行します。
0.25秒で計時されました。
しかし、同じ数の実行:
Trueは、 ''、#10、#13、#0の
ケースP ^を実行します:ブレーク; else inc(P); 終わり;
「whileTrue」の場合は.16秒、最初の場合は.80秒、その他の場合は.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ステートメントは実際にはSysUtilsにあり、次のような単純なCharInSet関数を呼び出す必要があります。
関数CharInSet(C:AnsiChar; const CharSet:TSysCharSet):ブール値;
結果の開始
:=CharSetのC;
終わり;
これが行われる唯一の理由は、[''、#10、#13、#0]のP ^がDelphi2009で許可されなくなったため、呼び出しが型の変換を行って許可するためだと思います。
それにもかかわらず、私はこれに非常に驚いており、それでも私の結果を信頼していません。
AQTimeは何か間違ったものを測定していますか、この比較で何かが欠けていますか、それともCharInSetは本当に使用する価値のある効率的な関数ですか?
結論:
バリー、わかったと思う。時間を割いて詳細な例を示していただきありがとうございます。私は自分のマシンでコードをテストし、.171、.066、および.052秒を取得しました(私のデスクトップはラップトップよりも少し速いと思います)。
そのコードをAQTimeでテストすると、3つのテストで0.79、1.57、1.46秒になります。そこでは、計装からの大きなオーバーヘッドを見ることができます。しかし、私が本当に驚いたのは、このオーバーヘッドによって、見かけの「最良の」結果が、実際には最悪のCharInSet関数に変わることです。
したがって、Marcuは正しく、CharInSetは低速です。しかし、あなたはうっかりして(または意図的に)、CharInSetがSetメソッドのAnsiChar(P ^)で何をしているのかを引き出すことによって私にもっと良い方法を与えてくれました。ケース方式に比べて速度がわずかに優れていることを除けば、ケースを使用するよりもコードが少なく、理解しやすいです。
また、AQTime(および他のインストルメンテーションプロファイラー)を使用した誤った最適化の可能性についても認識しました。これを知っていると、Delphi用のプロファイラーとメモリ分析ツールに関する私の決定に役立ちます。また、 AQTimeはどのようにそれを行うのかという私の質問に対する別の答えでもあります。。もちろん、AQTimeはインストルメント時にコードを変更しないため、他の魔法を使用して変更する必要があります。
したがって、答えは、AQTimeが誤った結論につながる結果を示しているということです。
フォローアップ:AQTimeの結果が誤解を招く可能性があるという「告発」をこの質問に残しました。しかし、公平を期すために、この質問を読むように指示する必要があります。Delphiの高速GetTokenルーチンはありますか?これは、AQTimeが誤解を招く結果をもたらすと考え始めたものであり、そうではないと結論付けています。