F# インラインによりパフォーマンスが 11 倍向上するのはなぜですか? 質問する

F# インラインによりパフォーマンスが 11 倍向上するのはなぜですか? 質問する

私はCPUに負荷のかかる問題に取り組んでいます。キーワードを使用するとパフォーマンスが大幅に向上しますinline。標準の.netライブラリから辞書を作成し、カスタムキーComparerを渡します。コードとタイミングの結果は以下を参照してください。

出典: github.com

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# コードはなぜこんなに遅いのでしょうか?

おすすめ記事