从性能的角度来看,您应该使用“嵌套 foreach”还是“lambda/linq 查询”?
5 回答
尽可能编写最清晰的代码,然后进行基准测试和分析以发现任何性能问题。如果您确实有性能问题,您可以尝试使用不同的代码来确定它是否更快(始终使用尽可能真实的数据进行测量),然后判断性能的提高是否值得可读性打。
在许多情况下,直接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 独有......
如果你这样做
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 实现的。
在这方面更复杂。最终,大部分 LINQ-to-Objects 是(在幕后)一个foreach
循环,但增加了一些抽象/迭代器块等的开销。但是,除非您在两个版本中做非常不同的事情(foreach vs LINQ) ,它们都应该是 O(N)。
真正的问题是:是否有更好的方法来编写您的特定算法,这意味着foreach
效率低下?LINQ 可以为您做到吗?
例如,LINQ 使散列/分组/排序数据变得容易。
以前说过,但值得重复。
开发人员在运行性能测试之前永远不知道性能瓶颈在哪里。
将技术 A 与技术 B 进行比较也是如此。除非有显着差异,否则您只需对其进行测试。如果您有 O(n) 与 O(n^x) 的场景,这可能很明显,但由于 LINQ 的东西主要是编译器巫术,因此值得进行分析。
此外,除非您的项目正在生产中并且您已经对代码进行了分析并发现该循环会减慢您的执行速度,否则请将其保留为您对可读性和维护的偏好。过早的优化是魔鬼。
一个很大的好处是,使用 Linq-To-Objects 查询使您能够轻松地将查询转到 PLinq 并让系统自动在当前系统的正确线程数上执行操作。
如果您在大型数据集上使用此技术,那么这很容易成为一个大胜利,而且麻烦很少。