43

从性能的角度来看,您应该使用“嵌套 foreach”还是“lambda/linq 查询”?

4

5 回答 5

64

尽可能编写最清晰的代码,然后进行基准测试和分析以发现任何性能问题。如果您确实有性能问题,您可以尝试使用不同的代码来确定它是否更快(始终使用尽可能真实的数据进行测量),然后判断性能的提高是否值得可读性打。

在许多情况下,直接foreach方法比 LINQ 更快。例如,考虑:

var query = from element in list
            where element.X > 2
            where element.Y < 2
            select element.X + element.Y;

foreach (var value in query)
{
    Console.WriteLine(value);
}

现在有两个where子句和一个select子句,所以每个最终的项目都必须通过三个迭代器。(显然,在这种情况下,两个 where 子句可以结合使用,但我是在提出一般性观点。)

现在将其与直接代码进行比较:

foreach (var element in list)
{
    if (element.X > 2 && element.Y < 2)
    {
        Console.WriteLine(element.X + element.Y);
    }
}

那会跑得更快,因为它有更少的箍要跑。不过,控制台输出可能会使迭代器成本相形见绌,我当然更喜欢 LINQ 查询。

编辑:要回答“嵌套的 foreach”循环......通常用SelectMany或第二个from子句表示:

var query = from item in firstSequence
            from nestedItem in item.NestedItems
            select item.BaseCount + nestedItem.NestedCount;

这里我们只添加了一个额外的迭代器,因为由于嵌套foreach循环,我们已经在第一个序列中为每个项目使用了一个额外的迭代器。仍然有一些开销,包括在委托中进行投影而不是“内联”(我之前没有提到)的开销,但它仍然不会与嵌套的 foreach 性能有很大不同。

当然,这并不是说你不能用 LINQ 打自己的脚。如果你不先动脑筋,你可以写出效率极低的查询——但这远非 LINQ 独有......

于 2009-06-25T14:27:28.453 回答
24

如果你这样做

foreach(Customer c in Customer)
{
  foreach(Order o in Orders)
  {
    //do something with c and o
  }
}

您将执行 Customer.Count * Order.Count 迭代


如果你这样做

var query =
  from c in Customer
  join o in Orders on c.CustomerID equals o.CustomerID
  select new {c, o}

foreach(var x in query)
{
  //do something with x.c and x.o
}

您将执行 Customer.Count + Order.Count 迭代,因为 Enumerable.Join 是作为 HashJoin 实现的。

于 2009-06-25T14:31:09.197 回答
13

在这方面更复杂。最终,大部分 LINQ-to-Objects 是(在幕后)一个foreach循环,但增加了一些抽象/迭代器块等的开销。但是,除非您在两个版本中做非常不同的事情(foreach vs LINQ) ,它们都应该是 O(N)。

真正的问题是:是否有更好的方法来编写您的特定算法,这意味着foreach效率低下?LINQ 可以为您做到吗?

例如,LINQ 使散列/分组/排序数据变得容易。

于 2009-06-25T14:29:01.790 回答
3

以前说过,但值得重复。

开发人员在运行性能测试之前永远不知道性能瓶颈在哪里。

将技术 A 与技术 B 进行比较也是如此。除非有显着差异,否则您只需对其进行测试。如果您有 O(n) 与 O(n^x) 的场景,这可能很明显,但由于 LINQ 的东西主要是编译器巫术,因此值得进行分析。

此外,除非您的项目正在生产中并且您已经对代码进行了分析并发现该循环会减慢您的执行速度,否则请将其保留为您对可读性和维护的偏好。过早的优化是魔鬼。

于 2009-06-25T14:43:23.647 回答
2

一个很大的好处是,使用 Linq-To-Objects 查询使您能够轻松地将查询转到 PLinq 并让系统自动在当前系统的正确线程数上执行操作。

如果您在大型数据集上使用此技术,那么这很容易成为一个大胜利,而且麻烦很少。

于 2009-06-25T14:44:00.777 回答