2012-12-05 56 views
1

如果我連鎖條款,如Linq。鏈接Select/Where子句較慢?

var results = elements 
    .Where(n => n > 3) 
    .Where(n => n % 2 == 0); 

這是慢只是

var results = elements.Where(n => n > 3 && n % 2 == 0); 

解釋爲什麼或者爲什麼不呢?

編輯:似乎共識是即使POCO對象迭代兩次。如果是這種情況,有人可以解釋爲什麼微軟不會合並這些謂詞。我偶然發現了Enumerable.CombinePredicates,我認爲是這樣做的。有人可以請解釋一下這是什麼。

+0

另請參見[適當的Linq where子句](http://stackoverflow.com/questions/6359980)。 –

回答

2

編輯: 我看起來更接近一點。由Where擴展方法返回的WhereEnumerableIterator實際上覆蓋Where方法,並將謂詞組合爲單個回調。

public override IEnumerable<TSource> Where(Func<TSource, bool> predicate) { 
    return new Enumerable.WhereEnumerableIterator<TSource>(
     this.source, 
     Enumerable.CombinePredicates<TSource>(this.predicate, predicate)); 
} 

private static Func<TSource, bool> CombinePredicates<TSource>(
    Func<TSource, bool> predicate1, Func<TSource, bool> predicate2 
    ) { 
    return (TSource x) => predicate1(x) && predicate2(x); 
} 

所以,我在我的機器上看到的速度差異可能應該歸因於別的。

第一個例子將環比 elements收集一旦找到滿足條件的 item > 3,並再次找到滿足條件的 item % 2 == 0項目的項目。

第二個示例將遍歷elements集合一次,以查找滿足條件item > 3 && item % 2 == 0的條目。

在提供的示例中,第二個很可能總是比第一個更快,因爲它只循環超過 elements一次。

下面是一些相當一致的結果我得到我的機器上(.NET 3.5)的例子:

var stopwatch = new System.Diagnostics.Stopwatch(); 
    var elements = Enumerable.Range(1, 100000000); 
    var results = default(List<int>); 
    stopwatch.Start(); 
    results = elements.Where(n => n > 3).Where(n => n % 2 == 0).ToList(); 
    stopwatch.Stop(); 
    Console.WriteLine(stopwatch.Elapsed); 
    stopwatch.Reset(); 
    stopwatch.Start(); 
    results = elements.Where(n => n > 3 && n % 2 == 0).ToList(); 
    stopwatch.Stop(); 
    Console.WriteLine(stopwatch.Elapsed); 
    Console.WriteLine("Done"); 
    Console.ReadLine(); 

結果:

00:00:03.0932811 
00:00:02.3854886 
Done 

編輯
@Rawling是正確的,因爲我的解釋僅適用於在POCO對象集合上使用的LINQ。當用作LINQ到SQL,NHibernate,EF等的接口時,您的結果將更依賴於實現。

+0

第一個例子不會遍歷'elements'兩次;它循環一次,但是通過兩個迭代器傳遞值而不是第二個例子中的單個迭代器。 – Rawling

+0

@Rawling - 在第二個集合的任何迭代器之前,第一個集合的所有迭代器都不會調用「MoveNext」方法嗎? – ken

+0

第一個'Where'對輸入集合調用'foreach',而第二個'Where'對第一個'Where'的結果調用'foreach'。輸入集合只被迭代一次,但還有其他迭代器可以同時設置和使用。 – Rawling

2

如果你正在談論LINQ到對象,每個Where包括設置一個新的迭代器狀態機,這是昂貴的,所以是的,它比把這兩個條件放在一起要慢。

如果你正在談論LINQ到別的東西,那要看它;一個額外的Where可能只涉及額外的字符串連接。它仍然可能更昂貴,但實際的差異取決於LINQ提供商。