IEnumerable问题:最佳表现?

时间:2009-11-12 17:14:12

标签: c# ienumerable predicate

快速提问:

哪一个更快?

foreach (Object obj in Collection)
{
     if(obj.Mandatory){ ... }
}

foreach (Object obj in Collection.FindAll(o => o.Mandatory))
{
...
}

如果你知道更快的建议,我很高兴知道。

谢谢

6 个答案:

答案 0 :(得分:16)

如果您的CollectionList<T>,则FindAll通过创建新List<T>并复制与谓词匹配的所有项来实现。如果谓词成立,这显然比枚举集合和决定每个项目要慢。

如果您使用的是.NET 3.5,则可以使用LINQ,它不会创建副本,并且与您的第一个示例类似:

foreach (object obj in someCollection.Where(o => o.Mandatory))
{
    ...
}

请注意,这不一定是最快的解决方案。很容易看出,分配内存枚举集合的方法比枚举集合的方法慢。如果绩效至关重要:衡量它。

答案 1 :(得分:7)

以下测试代码打印系统节拍(1 tick = 100纳秒),用于迭代1000万个对象。 FindAll是最慢的,for循环按预期最快。

但即使在最坏的情况下,迭代的开销也是以每个项目纳秒来衡量的。如果你在循环中做了一些重要的事情(例如每个项目需要一微秒的东西),那么迭代的速度差异完全无关紧要

因此,对于图灵的爱,现在不要在编码指南中禁止foreach。它没有任何实际区别,LINQ语句肯定更容易阅读。

   public class Test
   {
      public bool Bool { get; set; }
   }

   class Program
   {

      static void Main(string[] args)
      {
         // fill test list
         var list = new List<Test>();
         for (int i=0; i<1e7; i++)
         {
            list.Add(new Test() { Bool = (i % 2 == 0) });
         }

         // warm-up
         int counter = 0;
         DateTime start = DateTime.Now;
         for (int i = 0; i < list.Count; i++)
         {
            if (list[i].Bool)
            {
               counter++;
            }
         }

         // List.FindAll
         counter = 0;
         start = DateTime.Now;
         foreach (var test in list.FindAll(x => x.Bool))
         {
            counter++;
         }
         Console.WriteLine(DateTime.Now.Ticks - start.Ticks); // prints 7969158

         // IEnumerable.Where
         counter = 0;
          start = DateTime.Now;
         foreach (var test in list.Where(x => x.Bool))
         {
            counter++;
         }
         Console.WriteLine(DateTime.Now.Ticks - start.Ticks); // prints 5156514

         // for loop
         counter = 0;
         start = DateTime.Now;
         for (int i = 0; i < list.Count; i++)
         {
            if (list[i].Bool)
            {
               counter++;
            }
         }
         Console.WriteLine(DateTime.Now.Ticks - start.Ticks); // prints 2968902


      }

答案 2 :(得分:6)

第一个会更快一些。

在第二种情况下,您使用List<T>.FindAll创建符合条件的临时列表。这会复制列表,然后迭代它。

然而,您可以通过以下方式完成同样的事情,速度与第一个选项相同:

foreach (Object obj in Collection.Where(o => o.Mandatory))
{
}

这是因为Enumerable.Where使用流返回IEnumerable<T>,这是在您迭代时生成的。没有副本。

答案 3 :(得分:3)

如果没有将枚举并行化为多个线程并考虑处理器数量等,那么你可以获得最快的速度:

for (int i = 0; i < Collection.Count; i++)
{
    var item = Collection[i];
    if (item.Mandatory) { ... }
}

我建议你总是使用Linq而不是编写forforeach循环,因为将来它会变得非常聪明,它实际上能够通过处理器分配工作并采取考虑到硬件特定的东西(参见PLinq),它最终会比你自己编写循环更快:声明性和命令式编程。

答案 4 :(得分:1)

FindAll只是语法糖。例如:

    List<string> myStrings = new List<string>();
    foreach (string str in myStrings.FindAll(o => o.Length > 0))
    {

    }

编译为:

List<string> list = new List<string>();
if (CS$<>9__CachedAnonymousMethodDelegate1 == null)
{
    CS$<>9__CachedAnonymousMethodDelegate1 = new Predicate<string>(MyClass.<RunSnippet>b__0);
}
using (List<string>.Enumerator enumerator = list.FindAll(CS$<>9__CachedAnonymousMethodDelegate1).GetEnumerator())
{
    while (enumerator.MoveNext())
    {
        string current = enumerator.Current;
    }
}

public List<T> FindAll(Predicate<T> match)
{
    if (match == null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
    }
    List<T> list = new List<T>();
    for (int i = 0; i < this._size; i++)
    {
        if (match(this._items[i]))
        {
            list.Add(this._items[i]);
        }
    }
    return list;
}

private static bool <RunSnippet>b__0(string o)
{
    return (o.Length > 0);
}

答案 5 :(得分:0)

如果性能有问题,这可能不是瓶颈,但是,您是否考虑过使用并行库或PLINQ?见下文:

Parallel.ForEach(Collection, obj =>
{
    if (obj.Mandatory)
    {
        DoWork();
    }
});

http://msdn.microsoft.com/en-us/library/dd460688(v=vs.110).aspx

此外,虽然可能稍微不相关,但似乎性能会让您感到好奇,如果您处理非常大的数据集,二进制搜索可能会有用。在我的情况下,我有两个单独的数据列表。我必须处理数百万条记录的列表,这实际上节省了我每次执行的指数。唯一的缺点是它仅对非常大的集合有用,并且需要事先进行排序。您还会注意到,这会使用ConcurrentDictionary类,这会产生很大的开销,但它是线程安全的,并且由于我要异步管理的要求和线程数而需要它。

private ConcurrentDictionary<string, string> items;
private List<string> HashedListSource { get; set; }
private List<string> HashedListTarget { get; set; }

this.HashedListTarget.Sort();
this.items.OrderBy(x => x.Value);

private void SetDifferences()
{
    for (int i = 0; i < this.HashedListSource.Count; i++)
    {
        if (this.HashedListTarget.BinarySearch(this.HashedListSource[i]) < 0)
        {
            this.Mismatch.Add(items.ElementAt(i).Key);
        }
    }
}

Example displaying the benefits of using Binary Search 此图片最初发布在一篇很棒的文章中:http://letsalgorithm.blogspot.com/2012/02/intersecting-two-sorted-integer-arrays.html

希望这有帮助!