問題描述
求和的三種不同實現 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 中的聚合與總和性能的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!