3

私の開発者の友人は、デリゲートを使用するとループがはるかに高速になると言っています。それをベンチマークしたいのですが、それがどのように機能するかについて点をつなぐのに問題があります。

次のバランス計算機を考えてみましょう。これは基本的にアカウントのリストを取得し、存在する場合は初期値(開始残高)を合計クレジット値に追加し、各アカウントの合計借方値を減算します。

    private static IDictionary<string, decimal> CalculateBalances(
        IDictionary<string, decimal> initialValue, 
        IDictionary<string, decimal> credits, 
        IDictionary<string, decimal> debits)
    {
        var r = new Dictionary<string, decimal>();

        foreach (var key in initialValue.Select(k => k.Key)
            .Concat(credits.Select(k => k.Key))
            .Concat(debits.Select(k => k.Key))
            .Distinct())
        {
            r.Add(key,
                (initialValue.ContainsKey(key) ? initialValue[key] : 0M)
                + (credits.ContainsKey(key) ? credits[key] : 0M)
                - (debits.ContainsKey(key) ? debits[key] : 0M)
                );
        }

        return r;
    }

これは、小規模から中規模のアカウントリストではかなりパフォーマンスが高くなりますが、デリゲートを使用する方が高速でしょうか。そして率直に言って、デリゲートロジックは私の思考プロセスに対して直角に機能しているようです。なぜなら、これを書く方法さえ頭を悩ませているからです。

デリゲートを使用してこれを書き直す方法を誰かが提供できますか?

4

3 に答える 3

5

あなたの友達がクラスのForEachメソッドのようなものを参照していると思います。List<T>あなたの質問に対する簡単な答えはノーです。

同等の構文は次のようになります。

initialValue.Select(k => k.Key)
            .Concat(credits.Select(k => k.Key))
            .Concat(debits.Select(k => k.Key))
            .Distinct()
            .ToList()
            .ForEach(var => r.Add(key,
                (initialValue.ContainsKey(key) ? initialValue[key] : 0M)
                + (credits.ContainsKey(key) ? credits[key] : 0M)
                - (debits.ContainsKey(key) ? debits[key] : 0M)
                ));

これは、上記の方法よりも優れているわけではありません。それは遅く、読むのがより困難です。デリゲートの呼び出しは、通常のメソッドの呼び出しよりも遅くなります。上記の構文は、より高速で読みやすくなっています。

于 2012-04-27T03:31:40.630 に答える
2

デリゲートを使用してこれを書き直す方法を誰かが提供できますか?

しかし、あなたすでにデリゲートを使用しています!これがラムダが変換されるものです。シーケンスの各アイテムを生成するためだけに非常に多くのデリゲート呼び出しが使用されている場合、パフォーマンス上の理由でループ本体にデリゲートを使用するかどうかについての質問は少し奇妙です。

List.ForEachとにかく、Adam Robinsonは、リストの各項目に副作用を実行する方法と、関連する可読性とパフォーマンスへの影響についてはすでに説明しているので、これについては説明しません。

しかし、LINQとデリゲート呼び出しのわずかなオーバーヘッドが決定的な要因ではなかった場合、私があなたのメソッドをどのように書くかを次に示します。

return initialValue
       .Concat(credits)
       .Concat(debits.Select(kvp => new KeyValuePair<string, decimal>(kvp.Key, -kvp.Value)))
       .GroupBy(kvp => kvp.Key, kvp => kvp.Value)
       .ToDictionary(group => group.Key, group => group.Sum());

今でははるかに読みやすくなっています。

于 2012-04-27T03:45:48.187 に答える
0

foreachを使用して新しい辞書を作成する場合は、この構成が適切ですDictionary.Add。いつでも既存の辞書から選択して新しい辞書を作成できますが、それは遅くなります。

しかし、読みやすさに関しては、これははるかに簡単ではありませんか?

private static decimal GetOrZero(this IDictionary<string,decimal> dict, string key)
{
    decimal value = 0;
    dict.TryGetValue(key, out value);
    return value;
}

private static IDictionary<string, decimal> CalculateBalances(
    IDictionary<string, decimal> initialValue, 
    IDictionary<string, decimal> credits, 
    IDictionary<string, decimal> debits)
{   
    var r = new Dictionary<string, decimal>();
    var accounts = initialValue.Keys.Union(debits.Keys).Union(credits.Keys);

    foreach (var accounts in accounts)
    {
        r.Add(initialValue.GetOrZero(key) + credits.GetOrZero(key) - debits.GetOrZero(key));
    }

    return r;
}
于 2012-04-27T03:51:44.137 に答える