私はCPUに負荷のかかる問題に取り組んでいます。キーワードを使用するとパフォーマンスが大幅に向上しますinline
。標準の.netライブラリから辞書を作成し、カスタムキーComparerを渡します。コードとタイミングの結果は以下を参照してください。
Eq_cmp のインラインキーワードなし
> perf_run 10000000 ;;
Real: 00:00:11.039, CPU: 00:00:11.029, GC gen0: 771, gen1: 3, gen2: 1
val it : unit = ()
Eq_cmp でインラインキーワードを使用する
perf_run 10000000 ;;
Real: 00:00:01.319, CPU: 00:00:01.388, GC gen0: 1, gen1: 1, gen2: 1
val it : unit = ()
>
また、インライン コードと非インライン コードでは Gen 0 GC の量に大きな違いがあることにも気付きました。
なぜこれほど大きな違いがあるのか、誰か説明してもらえませんか?
ベストアンサー1
キーワードを追加した後、私のマシンでも動作を再現でき、パフォーマンスが 3 倍向上しましたinline
。
2つのバージョンを並べて逆コンパイルすると、ILSpyほぼ同じ C# コードが生成されます。注目すべき違いは、次の 2 つの等価性テストにあります。
// Version without inline
bool IEqualityComparer<Program.Pair<a>>.System-Collections-Generic-IEqualityComparer(Program.Pair<a> x, Program.Pair<a> y)
{
a v@ = x.v@;
a v@2 = y.v@;
if (LanguagePrimitives.HashCompare.GenericEqualityIntrinsic<a>(v@, v@2))
{
a w@ = x.w@;
a w@2 = y.w@;
return LanguagePrimitives.HashCompare.GenericEqualityIntrinsic<a>(w@, w@2);
}
return false;
}
// Version with inline
bool IEqualityComparer<Program.Pair<int>>.System-Collections-Generic-IEqualityComparer(Program.Pair<int> x, Program.Pair<int> y)
{
int v@ = x.v@;
int v@2 = y.v@;
if (v@ == v@2)
{
int w@ = x.w@;
int w@2 = y.w@;
return w@ == w@2;
}
return false;
}
汎用的な等価性は、特殊化されたバージョンよりも効率がはるかに低くなります。
また、インライン コードと非インライン コードでは Gen 0 GC の量に大きな違いがあることにも気付きました。
なぜこれほど大きな違いがあるのか、誰か説明してもらえませんか?
GenericEqualityIntrinsic
機能を見てみるとF# ソースコード:
let rec GenericEqualityIntrinsic (x : 'T) (y : 'T) : bool =
fsEqualityComparer.Equals((box x), (box y))
引数に対してボックス化が行われるため、最初の例で大量のガベージが生成されます。GC が頻繁に実行されると、計算速度が大幅に低下します。2 番目の例 ( を使用inline
) では、 が構造体である場合、ほとんどガベージが生成されませんPair
。
とはいえ、inline
呼び出しサイトで特殊バージョンが使用される場合、これはキーワードの予想される動作です。私の提案は、常に同じベンチマークでコードを最適化して測定することです。
非常に似たスレッドに興味があるかもしれませんこの F# コードはなぜこんなに遅いのでしょうか?。