【问题标题】:Is IEnumerable.Any faster than a for loop with a break?IEnumerable.Any 是否比带中断的 for 循环快?
【发布时间】:2011-09-15 14:09:37
【问题描述】:

我们的代码在打开表单时遇到了一些缓慢,这可能是由于带有breakfor 循环需要很长时间才能执行。我将其切换为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);
}

【问题讨论】:

  • 您是否分析过您的代码并确保缓慢来自 for 循环?
  • 除非您重复数百万次,否则“更快”毫无意义
  • 什么是 ProductID?不可变数组?
  • 你有多少产品?百万?
  • Products 属性的访问速度极慢,因为它正在执行繁重的处理(预先存在的代码)。那么该属性在 for 循环中调用的次数是否比在 .Any 枚举中调用的次数多?

标签: c# performance loops ienumerable


【解决方案1】:

将此称为有根据的猜测:

this.ProductIDs.Length

这可能就是慢的地方。如果ProductIDs 的列表在每次迭代时都从数据库(例如)中检索出来以获取Length,那么它确实会非常慢。您可以通过分析您的应用程序来确认这一点。

如果不是这种情况(比如ProductIDs 在内存中,而Length 被缓存),那么两者的运行时间应该几乎相同。

【讨论】:

  • 因此,证明这一点的建议是访问 ProductIDs.Length 一次并将其存储在本地变量中,然后将该变量放入您的 for 循环中。
  • @Adam - 是的,如果这就是缓慢的原因。
  • @Oded 是的,我只是在评论,所以 OP 可以看到一种可能的方式来实现你的想法。
  • 这是唯一可能的解决方案,imo。我更有可能怀疑这不是所做的唯一更改或某些外部因素适用。
  • 这实际上是我的问题,尽管 ProductIDs 不是来自数据库。有很多与该属性相关的可怕代码。我将 Aliostad 标记为答案,因为它回答了更一般的问题 will a .Any 提供比 for-break 更好的性能。在 for 循环中访问 ProductID 的次数会比 .Any 多吗?
【解决方案2】:

第一个实现略快(枚举比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

所以循环两次比迭代/枚举快。

【讨论】:

  • 我认为您所指的 slightly 优化起来会很荒谬。
  • 我只提供事实,仅此而已。我个人更喜欢第二个。
  • 然而,两者相差甚远。请参阅在每次循环迭代中发生的对 this.ProductIDs.Length 的调用,而不是在 LINQ 查询中。
  • 我认为foreach 和枚举实际上为编译器提供了更多的优化空间来使用......至少这是我在 .NET 1.1 天阅读 Effective C# 时所记得的。
  • @Aliostad,我并不反对,只是为后续读者发表评论以了解速度差异的范围。 ;)
【解决方案3】:

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

这是该帖子中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; 
} 

【讨论】:

    【解决方案4】:

    本文假定ProductIDsList&lt;T&gt; 或数组。所以我说的是 Linq-to-objects。

    Linq 通常比传统的基于循环的代码更慢但更短/更易读。 2-3 的系数取决于您正在做什么是典型的。

    您能否重构您的代码以使this.ProductIDs 成为HashSet&lt;T&gt;?或者至少对数组进行排序,以便您可以使用二进制搜索。您的问题是您正在执行线性搜索,如果产品很多,这会很慢。

    【讨论】:

      【解决方案5】:

      我认为下面的实现会比相应的 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;
      }
      

      【讨论】:

        【解决方案6】:

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

        但一般来说,当你知道你将使用数组的所有元素时,你应该使用 for 循环,而在其他情况下你应该尝试使用 while 或 do while。

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

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

        【讨论】:

          猜你喜欢
          • 2013-06-28
          • 2017-11-29
          • 2013-11-13
          • 2021-10-01
          • 2019-04-15
          • 1970-01-01
          • 2011-03-10
          • 2011-01-29
          • 2015-10-30
          相关资源
          最近更新 更多