2016-12-02 71 views
1

简短问题:为什么LINQ比通过foreach循环聚合操作要慢?LINQ是否比聚合操作的foreach循环慢?

描述 -

我试图优化一些旧代码,并同时LINQ查询整个代码中广泛使用的,琐碎的操作,比如在Enumerable数字总结使用循环完成。

因此,我跑了一些测试来比较两种方法的性能。 以下是使用LINQ的.Sum()方法和使用foreach循环计算总计1000个数字的总和并且手动总结每个发生的代码。

List<Double> numbers = new List<Double>(); 
Double[] sums1 = new Double[1000]; 
Double[] sums2 = new Double[1000]; 

for (int i = 0; i < 1000; i++) 
{ 
    numbers.Add(i * i); 
} 

Int64 startTime1 = Stopwatch.GetTimestamp(); 
for (int i = 0; i < 1000; i++) 
{ 
    Double sum = 0; 
    sum = numbers.Sum(); 
    sums1[i] = sum; 
} 
Int64 endTime1 = Stopwatch.GetTimestamp(); 

Int64 startTime2 = Stopwatch.GetTimestamp(); 
for (int i = 0; i < 1000; i++) 
{ 
    Double sum = 0; 
    foreach (Double number in numbers) 
    { 
     sum += number; 
    } 
    sums2[i] = sum; 
} 
Int64 endTime2 = Stopwatch.GetTimestamp(); 

Console.WriteLine("LINQ. Start = {0}, End = {1}: Diff = {2}", startTime1, endTime1, endTime1 - startTime1); 
Console.WriteLine("ForEach. Start = {0}, End = {1}: Diff = {2}", startTime2, endTime2, endTime2 - startTime2); 

我跑这个测试一对夫妇(TEN)次,结果是:

LINQ. Start = 117385428996, End = 117385462197: Diff = 33201 
Foreach. Start = 117385462203, End = 117385476329: Diff = 14126 
LINQ. Start = 117385478555, End = 117385499802: Diff = 21247 
Foreach. Start = 117385499808, End = 117385520756: Diff = 20948 
LINQ. Start = 117385521426, End = 117385546256: Diff = 24830 
Foreach. Start = 117385546260, End = 117385567052: Diff = 20792 
LINQ. Start = 117385572791, End = 117385602149: Diff = 29358 
Foreach. Start = 117385602156, End = 117385622367: Diff = 20211 
LINQ. Start = 117385623153, End = 117385652563: Diff = 29410 
Foreach. Start = 117385652568, End = 117385673733: Diff = 21165 
LINQ. Start = 117385674403, End = 117385705028: Diff = 30625 
Foreach. Start = 117385705035, End = 117385725552: Diff = 20517 
LINQ. Start = 117385726094, End = 117385753161: Diff = 27067 
Foreach. Start = 117385753166, End = 117385771824: Diff = 18658 
LINQ. Start = 117385772341, End = 117385793726: Diff = 21385 
Foreach. Start = 117385793733, End = 117385811332: Diff = 17599 
LINQ. Start = 117385811768, End = 117385837204: Diff = 25436 
Foreach. Start = 117385837209, End = 117385852670: Diff = 15461 
LINQ. Start = 117385853003, End = 117385874410: Diff = 21407 
Foreach. Start = 117385874416, End = 117385891874: Diff = 17458 

注意,foreach循环总是表现得更好。这可能是什么原因?

编辑:answer对此question有很多关于为什么性能可能会比常规内联操作更糟糕的很好的信息。但是我无法看到它在这里的确切关系。

+1

[LINQ性能FAQ](http://stackoverflow.com/questions/4044400/linq-performance-faq) –

+1

我加入'对(INT X = 0一看可能的复制; X <号码.Count; x ++)',它在LINQ和foreach之间 – mayu

+2

顺便说一句。你编写日志'ForEach',结果包含'Foreach'。 Magic;) – mayu

回答

3

这似乎是关于你在迭代。我稍微改变了你的代码。首先它遍历IEnumerable(你说linq),然后迭代List(你说的foreach)。我得到了与你相同的结果。

请检查方法withIEnumerablewithList。除签名不同之外,它们完全一样。 LINQ扩展方法获取IEnumerable作为参数。

编辑:Performance between Iterating through IEnumerable<T> and List<T>给出了一个很好的解释,为什么list列举得更快。

class Program 
{ 
    static void Main(string[] args) 
    { 
     for (int i = 0; i < 10; i++) 
     { 
      calculate(); 
     } 

    } 

    private static void calculate() 
    { 
     List<Double> numbers = new List<Double>(); 
     Double[] sums1 = new Double[1000]; 
     Double[] sums2 = new Double[1000]; 

     for (int i = 0; i < 1000; i++) 
     { 
      numbers.Add(i * i); 
     } 

     Int64 startTime1 = Stopwatch.GetTimestamp(); 
     for (int i = 0; i < 1000; i++) 
     { 
      sums1[i] = withIEnumerable(numbers); 
     } 
     Int64 endTime1 = Stopwatch.GetTimestamp(); 

     Int64 startTime2 = Stopwatch.GetTimestamp(); 
     for (int i = 0; i < 1000; i++) 
     { 
      sums2[i] = withList(numbers); 
     } 
     Int64 endTime2 = Stopwatch.GetTimestamp(); 


     Console.WriteLine("withIEnumerable. Start = {0}, End = {1}: Diff = {2}", startTime1, endTime1, endTime1 - startTime1); 
     Console.WriteLine("withList. Start = {0}, End = {1}: Diff = {2}", startTime2, endTime2, endTime2 - startTime2); 
    } 

    private static double withIEnumerable(IEnumerable<double> numbers) 
    { 
     double sum = 0; 
     foreach (Double number in numbers) 
     { 
      sum += number; 
     } 

     return sum; 
    } 

    private static double withList(List<double> numbers) 
    { 
     double sum = 0; 
     foreach (Double number in numbers) 
     { 
      sum += number; 
     } 

     return sum; 
    } 
} 
0

这不是正确的答案,但我将在此处将它留给 引用而不是删除它。请阅读serdar的回答(接受 )。如果这违背了社区的工作方式,请让我知道或编辑。

调用方法有一个额外的非常突出的时间开销。 从我发现的情况来看,这是在这种特殊情况下性能下降的最大原因。

我添加了一个扩展方法来总结与相同(更快)的代码,但在扩展方法内的列表。

public static class MyExtensionMethods 
{ 
    public static Double SumX(this IEnumerable<Double> list) 
    { 
     Double sum = 0; 
     foreach (Double number in list) 
     { 
      sum += number; 
     } 
     return sum; 
    } 
} 

,然后使用我刚创建的扩展方法,

... 
Int64 startTime2 = Stopwatch.GetTimestamp(); 
for (int i = 0; i < 1000; i++) 
{ 
    Double sum = numbers.SumX(); 
    sums2[i] = sum; 
} 
Int64 endTime2 = Stopwatch.GetTimestamp(); 
... 

结果为:

LINQ. Start = 129087370675, End = 129087395309: Diff = 24634 
Foreach. Start = 129087395312, End = 129087420320: Diff = 25008 
LINQ. Start = 129087422887, End = 129087447541: Diff = 24654 
Foreach. Start = 129087447547, End = 129087465859: Diff = 18312 
LINQ. Start = 129087466278, End = 129087484777: Diff = 18499 
Foreach. Start = 129087484784, End = 129087505378: Diff = 20594 
LINQ. Start = 129087506425, End = 129087526134: Diff = 19709 
Foreach. Start = 129087526141, End = 129087552013: Diff = 25872 
LINQ. Start = 129087552500, End = 129087578445: Diff = 25945 
Foreach. Start = 129087578451, End = 129087601858: Diff = 23407 
LINQ. Start = 129087602371, End = 129087630873: Diff = 28502 
Foreach. Start = 129087630880, End = 129087674495: Diff = 43615 
LINQ. Start = 129087675028, End = 129087702841: Diff = 27813 
Foreach. Start = 129087702849, End = 129087732360: Diff = 29511 
LINQ. Start = 129087732974, End = 129087760529: Diff = 27555 
Foreach. Start = 129087760536, End = 129087785590: Diff = 25054 
LINQ. Start = 129087786096, End = 129087813331: Diff = 27235 
Foreach. Start = 129087813336, End = 129087842947: Diff = 29611 
LINQ. Start = 129087843471, End = 129087870633: Diff = 27162 
Foreach. Start = 129087870639, End = 129087896678: Diff = 26039 

底线:LINQ是在这种情况下,由于速度较慢增加了扩展方法的开销。我也尝试使用简单的静态方法,而不是扩展方法,并且它的结果与扩展方法的结果没有任何不同。

+0

你的静态方法的输入参数是什么? IEnumerable 或列表?我认为这是列表。试试其他请。 – serdar

+0

你说得对。我已经接受你的答案。 :-) –

+0

现在,任何想法为什么IEnumerable列表迭代比列表慢? –