问题描述
求和的三种不同实现 IEnumerable <下面给出了 int> source 以及 source 有 10,000 个整数所用的时间.
Three different implementations of finding the sum of an IEnumerable < int> source are given below along with the time taken when the source has 10,000 integers.
source.Aggregate(0, (result, element) => result + element);
需要 3 毫秒
source.Sum(c => c);
需要 12 毫秒
source.Sum();
需要 1 毫秒
我想知道为什么第二个实现比第一个贵四倍.不应该和第三个实现一样吗?
I am wondering why the second implementation is four times more expensive than the first one. Shouldn't it be same as the third implementation.
推荐答案
注意:我的电脑运行的是 .Net 4.5 RC,所以我的结果可能会受此影响.
Note: My computer is running .Net 4.5 RC, so it's possible that my results are affected by this.
测量只执行一次方法所花费的时间通常不是很有用.它很容易被 JIT 编译之类的东西所支配,而这些东西在实际代码中并不是真正的瓶颈.因此,我测量了每个方法的执行次数为 100 次(在没有附加调试器的发布模式下).我的结果是:
Measuring the time it takes to execute a method just once is usually not very useful. It can be easily dominated by things like JIT compilation, which are not actual bottlenecks in real code. Because of this, I measured executing each method 100× (in Release mode without debugger attached). My results are:
Aggregate()
:9 毫秒Sum(lambda)
:12 毫秒Sum()
:6 毫秒
Aggregate()
: 9 msSum(lambda)
: 12 msSum()
: 6 ms
Sum()
最快这一事实并不令人惊讶:它包含一个没有任何委托调用的简单循环,这非常快.Sum(lambda)
和 Aggregate()
之间的差异并不像您测量的那样突出,但它仍然存在.可能是什么原因?我们来看看这两种方法的反编译代码:
The fact that Sum()
is the fastest is not surprising: it contains a simple loop without any delegate invocations, which is really fast. The difference between Sum(lambda)
and Aggregate()
is not nearly as prominent as what you measured, but it's still there. What could be the reason for it? Let's look at decompiled code for the two methods:
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()
,后者又使用迭代器.使用迭代器意味着有一些开销:创建迭代器对象和(可能更重要的是)为每个项目多调用一次方法.
As you can see, Aggregate()
uses a loop but Sum(lambda)
uses Select()
, which in turn uses an iterator. And using an iterator means there is some overhead: creating the iterator object and (probably more importantly) one more method invocation for each item.
让我们通过编写我们自己的 Sum(lambda)
两次来验证使用 Select()
实际上是原因,一次使用 Select()
,它的行为应该与框架中的 Sum(lambda)
相同,并且一次不使用 Select()
:
Let's verify that using Select()
is actually the reason by writing our own Sum(lambda)
twice, once using Select()
, which should behave the same as Sum(lambda)
from the framework, and once without using 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;
}
我的测量结果证实了我的想法:
My measurements confirm what I thought:
SlowSum(lambda)
:12 毫秒FastSum(lambda)
:9 毫秒
SlowSum(lambda)
: 12 msFastSum(lambda)
: 9 ms
这篇关于LINQ 中的聚合与总和性能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!