“嵌套的foreach”vs“lambda / linq查询”性能(LINQ-to-Objects)

从性能的angular度来看,你应该使用“嵌套的foreach”或“lambda / linq查询”?

编写最清晰的代码,然后进行基准testing和configuration文件发现任何性能问题。 如果确实存在性能问题,可以尝试使用不同的代码来确定是否更快(使用尽可能真实的数据来测量所有时间),然后判断性能改进是否值得可读性击中。

在很多情况下,直接的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子句,所以每个最终的项目都必须经过三个迭代器。 (很明显,在这种情况下,可以将两个子句结合起来,但是我要说明一点。)

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

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

这将运行得更快,因为它有更less的篮球运行。 尽pipe如此,控制台输出可能会使迭代器的成本变得更低,我当然更喜欢LINQ查询。

编辑:要回答有关“嵌套的foreach”循环…通常这些代表SelectMany或第二个from子句:

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

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

当然,这并不是说你不能用自己的脚来打自己的脚。 如果你不先把自己的大脑搞得一团糟,那么你可以写出一些效率低下的查询 – 但这与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 xc and xo } 

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

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

真正的问题是:有没有更好的方式来编写你的特定algorithm,这意味着foreach将是低效的? LINQ可以为你做吗?

例如,LINQ可以很容易地散列/分组/sorting数据。

之前已经说过了,但值得重复。

在运行性能testing之前,开发人员不知道性能瓶颈在哪里。

比较技术A和技术B的情况也是如此。除非有显着差异,否则你只需要testing它。 如果你有一个O(n)和O(n ^ x)的场景,这可能是显而易见的,但是由于LINQ的东西大部分是编译器巫术,所以值得分析。

此外,除非您的项目正在生产中,并且您已经对代码进行了剖析,并发现该循环正在减慢执行速度,请将其作为您可读性和维护的首选项。 不成熟的优化是恶魔。

一个很大的好处是,使用Linq-To-Objects查询可以轻松地将查询转换为PLinq,并让系统自动对当前系统的正确线程数进行操作。

如果你在大数据集上使用这种技术,那么很容易成为一个很小的麻烦。