最近、予期しないコードの最適化に遭遇したので、観察した内容の解釈が正しいかどうかを確認したいと思いました。以下は、状況の非常に単純化された例です。
let demo =
let swap fst snd i =
if i = fst then snd else
if i = snd then fst else
i
[ for i in 1 .. 10000 -> swap 1 i i ]
let demo2 =
let swap (fst: int) snd i =
if i = fst then snd else
if i = snd then fst else
i
[ for i in 1 .. 10000 -> swap 1 i i ]
2 つのコード ブロックの唯一の違いは、2 番目のケースでは swap の引数を明示的に整数として宣言していることです。それでも、#time を指定して fsi で 2 つのスニペットを実行すると、次のようになります。
ケース 1 実数: 00:00:00.011、CPU: 00:00:00.000、GC gen0: 0、gen1: 0、gen2: 0
ケース 2 実数: 00:00:00.004、CPU: 00:00:00.015、GC gen0 : 0、gen1: 0、gen2: 0
つまり、2 番目のスニペットは最初のスニペットよりも 3 倍速く実行されます。ここでの絶対的なパフォーマンスの違いは明らかに問題ではありませんが、swap 関数を頻繁に使用すると、それが積み重なってしまいます。
私の推測では、パフォーマンス ヒットの理由は、最初のケースでは swap が汎用的で「等価性が必要」であり、int がそれをサポートしているかどうかをチェックするのに対し、2 番目のケースでは何もチェックする必要がないためです。これがこれが起こっている理由ですか、それとも何か他のものを見逃していますか? もっと一般的に言えば、自動汎化は諸刃の剣、つまり、パフォーマンスに予期しない影響を与える可能性のある優れた機能であると考えるべきでしょうか?