找到 IEnumerable<的总和的三种不同实现方式INT>下面给出了源以及源具有10,000个整数所花费的时间。
source.Aggregate(0, (result, element) => result + element);
需要3毫秒
source.Sum(c => c);
需要12毫秒
source.Sum();
需要1毫秒
我想知道为什么第二次实施比第一次实施贵四倍。不应该与第三个实现相同。
答案 0 :(得分:78)
注意:我的电脑正在运行.Net 4.5 RC,因此我的结果可能会受此影响。
测量一次执行方法所花费的时间通常不是很有用。它很容易被像JIT编译这样的东西所支配,而这些东西并不是实际代码中的实际瓶颈。因此,我测量了每个方法执行100×(在没有附带调试器的发布模式下)。我的结果是:
Aggregate()
:9 ms Sum(lambda)
:12 ms Sum()
:6 ms Sum()
最快的事实并不令人惊讶:它包含一个没有任何委托调用的简单循环,这非常快。 Sum(lambda)
和Aggregate()
之间的差异并不像您测量的那么明显,但它仍然存在。可能是什么原因呢?让我们看看这两种方法的反编译代码:
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()
,而Select()
又使用迭代器。使用迭代器意味着有一些开销:创建迭代器对象,并且(可能更重要的是)为每个项创建一个方法调用。
让我们验证使用Sum(lambda)
实际上是使用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)
我的测量证实了我的想法:
FastSum(lambda)
:12 ms