Linq to objects 比普通 C# 慢 20 倍。有没有办法加快速度?
Posted
技术标签:
【中文标题】Linq to objects 比普通 C# 慢 20 倍。有没有办法加快速度?【英文标题】:Linq to objects is 20 times slower than plain C#. Is there a way to speed it up? 【发布时间】:2020-04-01 09:44:36 【问题描述】:如果我只需要数组的最大值 [或 3 个最大的项],我使用 myArray.OrderBy(...).First() [或 myArray.OrderBy(...).Take( 3)],它比调用 myArray.Max() 慢 20 倍。有没有办法编写更快的 linq 查询?这是我的示例:
using System;
using System.Linq;
namespace ConsoleApp1
class Program
static void Main(string[] args)
var array = new int[1000000];
for (int i = 0; i < array.Length; i++)
array[i] = i;
var maxResults = new int[10];
var linqResults = new int[10];
var start = DateTime.Now;
for (int i = 0; i < maxResults.Length; i++)
maxResults[i] = array.Max();
var maxEnd = DateTime.Now;
for (int i = 0; i < maxResults.Length; i++)
linqResults[i] = array.OrderByDescending(it => it).First();
var linqEnd = DateTime.Now;
// 00:00:00.0748281
// 00:00:01.5321276
Console.WriteLine(maxEnd - start);
Console.WriteLine(linqEnd - maxEnd);
Console.ReadKey();
【问题讨论】:
var linqResults = array.OrderByDescending(it => it).Take(10).ToArray();
获得最大值是 O(n),而排序可能是 O(n log(n))。因此,不要排序以获得最大值。
为什么当它是array.Max()时你认为你没有使用Linq。您在两者中都使用了 Linq,只是以更长的方式执行其中一个。
请阅读 Speed Rant:ericlippert.com/2012/12/17/performance-rant
Linq-to-Sql 我想优化为不对整个表进行排序,然后只从中取出一项。这就是为什么我认为 Linq-to-objects 足够聪明,不会对整个数组进行排序。
【参考方案1】:
您在循环中对初始数组10
次进行排序:
for (int i = 0; i < maxResults.Length; i++)
linqResults[i] = array.OrderByDescending(it => it).First();
让我们这样做一次:
// 10 top item of the array
var linqResults = array
.OrderByDescending(it => it)
.Take(10)
.ToArray();
请注意
for (int i = 0; i < maxResults.Length; i++)
maxResults[i] = array.Max();
只需重复 相同 Max
值 10
次(它不会返回 10
***项目)
【讨论】:
不过,获得最大值 10 可以在 O(n) 中完成。猜猜挑战是 linq 要求。 我已经做了 10 次,只是为了更准确地测量时间。我只想做一次。 从技术上讲,我们可以在 O(N) 中完成,例如我们可以使用Min Heap
:我们循环array
,同时将项目添加到堆中;当它的大小达到11
时,我们从堆中取出(并丢弃)顶部(最小)项。
.Net 中也没有内置 MinHeap(或 PriorityQueue)。【参考方案2】:
最大方法耗时为O(n),最佳时间排序为O(n log(n)) 您的代码的第一个错误是您订购了 10 次,这是最糟糕的情况。您可以像 Dmitry 回答的那样订购一次并拿走 10 个。 而且,调用 Max 方法 10 次不会给你 10 个最大值,只是 10 次的最大值。
但是 Max 方法只迭代一次列表并将 Max 值保存在单独的变量中。您可以重写此方法来迭代您的数组并在您的 maxResults 中保留 10 个最大值,这是您获得结果的最快方式。
【讨论】:
令人印象深刻的是除了主题之外还有多少人在谈论。我叫它10次只是为了更准确地测量时间。我一直在寻找的东西,但在 linq 中,在我的答案中 (morelinq)。 所以,您的答案是 Max 和 OrderBy 方法的时间消耗。最大算法时间为 O(n),排序时间为 O(n log n)。没有办法以 Max 方法更快的方式使用 OrderBy 或 OrderByDescending。【参考方案3】:似乎其他人已经填补了微软在 linq-to-objects 中留下的效率差距: https://morelinq.github.io/3.1/ref/api/html/M_MoreLinq_MoreEnumerable_PartialSort__1_3.htm
【讨论】:
以上是关于Linq to objects 比普通 C# 慢 20 倍。有没有办法加快速度?的主要内容,如果未能解决你的问题,请参考以下文章
Linq之旅:Linq入门详解(Linq to Objects)