2

快速提问:

哪个更快?

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

或者

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

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

谢谢

4

6 回答 6

16

如果你的Collectionis a List<T>thenFindAll是通过创建一个新的List<T>并复制与谓词匹配的所有项目来实现的。这显然比仅枚举集合并决定谓词是否成立要慢。

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

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

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

于 2009-11-12T17:20:30.673 回答
7

下面的测试代码打印了迭代 1000 万个对象的系统滴答声(1 滴答声 = 100 纳秒)。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


      }
于 2009-11-12T17:42:33.390 回答
6

第一个会更快一些。

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

但是,您可以通过执行以下操作以与您的第一个选项相同的速度完成相同的事情:

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

这是因为Enumerable.Where使用流式传输返回一个IEnumerable<T>,它是在您迭代时生成的。没有复制。

于 2009-11-12T17:21:00.610 回答
3

在不考虑处理器数量等情况下将枚举并行化为多个线程的情况下,您可以获得的最快速度:

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

我会建议您始终使用 Linq 而不是编写forforeach循环,因为将来它会变得非常智能,以至于它实际上能够将工作分配给处理器并考虑到硬件特定的事情(请参阅 Plinq),并且最终会比您自己编写循环要快:声明式编程与命令式编程。

于 2009-11-12T17:20:43.797 回答
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);
}
于 2009-11-12T17:24:41.540 回答
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);
        }
    }
}

显示使用二分搜索的好处的示例 这张图片最初发布在一篇很棒的文章中:http: //letsalgorithm.blogspot.com/2012/02/intersecting-two-sorted-integer-arrays.html

希望这可以帮助!

于 2014-02-09T18:27:54.483 回答