在今天的节目讨论中,我和朋友有点困惑。作为一个例子,我们创建了一个虚构的问题,即具有List<int>
n个随机整数(通常为1.000.000)并且想要创建一个函数,该函数返回有多个整数的整数集。很简单的东西。我们创建了一个LINQ语句来解决这个问题,以及一个基于普通插入排序的算法。
现在,当我们测试代码运行的速度时(使用System.Diagnostics.StopWatch
),结果令人困惑。 LINQ代码不仅优于简单排序,而且比单个 foreach
/ for
运行得更快,只执行了一个循环列表,并且没有内部的操作(在侧面轨道上,我认为编译器应该发现并完全删除)。
如果我们在程序的同一执行中生成了新的List<int>
随机数并再次运行LINQ代码,则性能将提高数量级(通常为千倍)。空循环的性能当然是相同的。
那么,这里发生了什么? LINQ使用并行性是否优于正常循环?这些结果怎么可能呢? LINQ使用以n * log(n)运行的quicksort,根据定义,它已经慢于n。
第二次运行中的性能飞跃发生了什么?
我们对这些结果感到困惑和兴趣,并希望从社区中获得一些澄清的见解,只是为了满足我们自己的好奇心。
答案 0 :(得分:13)
毫无疑问,您实际上没有执行过查询,您只是定义了它。 LINQ构造一个表达式树,在您执行需要迭代枚举的操作之前,该表达式树不会被实际评估。尝试向LINQ查询添加ToList()
或Count()
操作以强制评估查询。
根据您的评论,我希望这与您所做的类似。注意:我没有花时间搞清楚查询是否尽可能高效;我只想要一些查询来说明如何构建代码。
var dataset = ...
var watch = Stopwatch.StartNew();
var query = dataset.Where( d => dataset.Count( i => i == d ) > 1 );
watch.Stop(); // timer stops here
foreach (var item in query) // query is actually evaluated here
{
... print out the item...
}
答案 1 :(得分:1)
我建议当你的算法不完美时(或者你的代码中有问题),LINQ只比'正常循环'快。因此,如果你没有编写有效的排序算法等,LINQ的排序速度会比你快。
LINQ通常“正常”或“足够接近”正常循环的速度,并且可以更快(更简单)地进行编码/调试/读取。这是它的好处 - 而不是执行速度。
如果它的执行速度比空循环快,那么你做错了。最有可能的是,正如评论中所建议的那样,您没有考虑延迟执行,并且LINQ语句实际上并未执行。
答案 2 :(得分:1)
如果您没有启用“优化代码”编译,您可能会看到此行为。 (这肯定会解释为什么没有删除空循环。)
然而,LINQ底层代码是已编译代码的一部分,它肯定已经过优化(由JIT,NGen或类似代码)。