注:私のコンピューターは.Net 4.5 RCを実行しているため、結果がこれによって影響を受ける可能性があります。
メソッドの実行にかかる時間を1回だけ測定することは、通常、あまり役に立ちません。これは、実際のコードの実際のボトルネックではないJITコンパイルなどによって簡単に支配される可能性があります。このため、各メソッドの実行を100回測定しました(デバッガーを接続しないリリースモードで)。私の結果は次のとおりです。
Aggregate()
:9ミリ秒
Sum(lambda)
:12ミリ秒
Sum()
:6ミリ秒
最速であるという事実Sum()
は驚くべきことではありません。デリゲート呼び出しのない単純なループが含まれているため、非常に高速です。との違いはSum(lambda)
、Aggregate()
測定したものほど顕著ではありませんが、それでもそこにあります。その理由は何でしょうか?2つのメソッドの逆コンパイルされたコードを見てみましょう。
public static TAccumulate Aggregate<TSource, TAccumulate>(this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func)
{
if (source == null)
throw Error.ArgumentNull("source");
if (func == null)
throw Error.ArgumentNull("func");
TAccumulate local = seed;
foreach (TSource local2 in source)
local = func(local, local2);
return local;
}
public static int Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, int> selector)
{
return source.Select<TSource, int>(selector).Sum();
}
ご覧のとおりAggregate()
、ループを使用しますが、をSum(lambda)
使用しSelect()
、次にイテレータを使用します。また、イテレータを使用するということは、いくらかのオーバーヘッドがあることを意味します。イテレータオブジェクトを作成し、(おそらくもっと重要なことですが)各アイテムに対してもう1つのメソッド呼び出しを作成します。
Select()
を使用することが実際の理由であることを確認しましょう。1回はフレームワークと同じように動作するSum(lambda)
を使用し、もう1回は使用せずに2回記述します。Select()
Sum(lambda)
Select()
public static int SlowSum<T>(this IEnumerable<T> source, Func<T, int> selector)
{
return source.Select(selector).Sum();
}
public static int FastSum<T>(this IEnumerable<T> source, Func<T, int> selector)
{
if (source == null)
throw new ArgumentNullException("source");
if (selector == null)
throw new ArgumentNullException("selector");
int num = 0;
foreach (T item in source)
num += selector(item);
return num;
}
私の測定は私が思ったことを確認します:
SlowSum(lambda)
:12ミリ秒
FastSum(lambda)
:9ミリ秒