IEnumerable.Any比休息的for循环快吗?

时间:2011-06-15 14:13:23

标签: c# performance loops ienumerable

我们在打开表单的代码中经历了一些缓慢,这可能是由于for循环带有break需要很长时间才能执行。我将其切换为IEnumerable.Any()并快速打开表单。我现在试图弄清楚单独进行此更改是否会提高性能,还是更有效地访问ProductIDs属性。这种实施应该更快,如果是这样,为什么?

原始实施:

public bool ContainsProduct(int productID) {
    bool containsProduct = false;
    for (int i = 0; i < this.ProductIDs.Length; i++) {
        if (productID == this.ProductIDs[i]) {
            containsProduct = true;
            break;
        }
    }
    return containsProduct;
}

新实施:

public bool ContainsProduct(int productID) {
    return this.ProductIDs.Any(t => productID == t);
}

6 个答案:

答案 0 :(得分:9)

称之为有根据的猜测:

this.ProductIDs.Length

这可能是缓慢所在的地方。如果在每次迭代中从数据库(例如)中检索ProductIDs的列表以获得Length,那么它确实会非常慢。您可以通过分析您的申请来确认这一点。

如果不是这种情况(假设ProductIDs在内存中并且Length被缓存),则两者的运行时间应该几乎相同。

答案 1 :(得分:8)

首次实现稍快(枚举稍微慢于for循环)。第二个是更具可读性


更新

Oded的答案可能是正确的,并且很好地发现了它。第一个在这里较慢,因为它涉及数据库往返。否则,就像我说的那样稍微快一些。


更新2 - 证明

这是一个简单的代码,显示为什么第一个更快:

    public static void Main()
    {

        int[] values = Enumerable.Range(0, 1000000).ToArray();

        int dummy = 0;
        Stopwatch stopwatch = new Stopwatch();

        stopwatch.Start();
        for (int i = 0; i < values.Length; i++)
        {
            dummy *= i;
        }
        stopwatch.Stop();
        Console.WriteLine("Loop took {0}", stopwatch.ElapsedTicks);

        dummy = 0;
        stopwatch.Reset();
        stopwatch.Start();
        foreach (var value in values)
        {
            dummy *= value;         
        }
        stopwatch.Stop();
        Console.WriteLine("Iteration took {0}", stopwatch.ElapsedTicks);

        Console.Read();
    }

这是输出:

  

循环花了12198

     

迭代花了20922

所以循环是迭代/枚举的两倍。

答案 2 :(得分:4)

我认为它们或多或少相同。我通常会参考Jon Skeet的Reimplementing LINQ to Objects博客系列来了解扩展方法的工作原理。以下是Any()All()

the post

以下是该帖子的Any()实施的核心部分

public static bool Any<TSource>( 
    this IEnumerable<TSource> source, 
    Func<TSource, bool> predicate) 
{ 
   ...

    foreach (TSource item in source) 
    { 
        if (predicate(item)) 
        { 
            return true; 
        } 
    } 
    return false; 
} 

答案 3 :(得分:1)

此帖子假定ProductIDsList<T>或数组。所以我在谈论Linq-to-objects。

Linq通常比传统的基于循环的代码更慢但更短/更易读。取决于你正在做的事情是2-3的因素。

您可以重构代码以使this.ProductIDs成为HashSet<T>吗?或者至少对数组进行排序,以便您可以使用二进制搜索。您的问题是您正在执行线性搜索,如果有许多产品,则搜索速度很慢。

答案 4 :(得分:0)

我认为下面的实现会比相应的linq实现快一点,但非常小虽然

public bool ContainsProduct(int productID) {
    var length = this.ProductIDs.Length;

    for (int i = 0; i < length; i++) {
        if (productID == this.ProductIDs[i]) {
            return true;
        }
    }

    return false;
}

答案 5 :(得分:0)

差异通常在于内存使用情况,然后是速度。

但是通常你应该使用for循环当你知道你将使用数组的所有元素时,你应该尝试使用while或do while。

我认为此解决方案使用最少的资源

int i = this.ProductIDs.Length - 1;

while(i >= 0) {
 if(this.ProductIDs[i--] == productId) {
   return true;
 }
}

return false;