【问题标题】:SortedSet / SortedList with better LINQ performance?SortedSet / SortedList 具有更好的 LINQ 性能?
【发布时间】:2013-01-18 11:10:59
【问题描述】:

假设我们有一个已排序的集合,例如 SortedSetSortedList,其中包含许多 (10M+) 个元素。正在发生大量查询,因此性能很重要。从运行时比较来看,我的印象是 LINQ to Objects 没有利用排序,因此没有利用潜在的性能提升。

第一个例子 - 计算一个范围内的元素:

        var mySortedSet1 = new SortedSet<int>();
        // populate ...
        int rangeCount = (from n in mySortedSet1
                          where ((n >= 1000000000) && (n <= 2000000000))
                          select n).Count();

不完全确定 LINQ to Objects 在内部做了什么,最坏的情况是检查每个 O(n) 的元素。通过利用 O(log n) 中的下限和上限进行二分搜索的排序,可以更快地完成。

第二个例子 - SelectMany 在集合列表中:

        var myListOfSortedSets = new List<SortedSet<int>>();
        // populate...

        var q = myListOfSortedSets.SelectMany(s => s).OrderBy(s => s);
        foreach (var n in q)
        {
            Console.WriteLine(n);
        }

如果 LINQ to SQL 对象要利用排序,它可以在 O(n) 中有效地将所有排序集压缩合并到一个大的排序列表中。结果上的 .OrderBy 然后可以被忽略,因为列表已经排序。

相反,SelectMany 将所有已排序的集合连接到一个大的(现在未排序的)列表中,这将需要另一个 O(n log n) 排序。这可以通过删除 .OrderBy 并观察元素写入控制台的顺序来轻松验证。

我的问题是:是否已经有另一种更有效的 LINQ to SortedSet/SortedList 实现?

i4o 看起来很有趣,但似乎需要二级索引集合来提高对原始集合的查询性能。我只是希望通过利用排序来更快地对排序集合进行查询。

【问题讨论】:

  • Linq 中的 Where 方法对每个元素进行对象检查。所以在这种情况下使用 SortedList 并没有性能提升。
  • 您到底想做什么查询?因为断言“LINQ 很愚蠢”并没有真正的用处……
  • 上面的例子对于我想要运行的查询来说是非常典型的。我敢肯定还有更多的实例,LINQ to Objects 可以使用排序但不使用,例如找到最小值/最大值,所以我的问题是是否已经有一个用于排序集合的通用 LINQ 提供程序。
  • 好问题!我想知道如何实现这样的提供者。我会调查的。
  • 这并没有回答上述问题,但是既然您使用的是SortedSet,为什么不明确使用它提供的方法而不是依靠 LINQ 来尝试做最好的事情呢?例如,对于您的第一个示例,int rangeCount = mySortedSet1.GetViewBetween(1000000000, 2000000000).Count; 总是会更快,无论 LINQ 多么优化。

标签: .net linq linq-to-objects sortedlist sortedset


【解决方案1】:

LINQ 的问题是它无法知道排序集的排序方式与查询预期的方式完全相同。由于可以使用IComparer / IComparable / Comparison&lt;T&gt; 创建任何有序集合,因此不知道&gt; 500000 实际上是有意义的。也许您在比较器上有一个自定义方法,首先按奇数/偶数排序,然后按数字排序。在这种情况下,订单将完全混乱,并且在所有情况下都需要 O(n)。

所以为了安全起见,LINQ 需要遍历集合中的所有元素,即使它以某种方式排序。默认的.Where 实现不包含对有序集合的优化。

也许可以创建一个优化版本,在迭代时牢记现有的顺序,但很难做到并使其在所有情况下都能正常工作。

您可以创建一个Between 方法,该方法使用SortedSetGetViewBetween 方法返回一个新的预购集合。或者添加标准的.Where,就像您通常对任何非预排序集所做的那样。

Linq-to-SQL 和 Entity Framework 使用 IQueryable 并将您的 Linq 查询实际转换为 SQL 并让服务器处理索引、排序、过滤等。

【讨论】:

  • 对,我的意思是“LINQ to Objects”,而不是“LINQ to SQL”。
  • 即使IComparer 问题可以解决,这也意味着LINQ to objects 需要Expressions,而不仅仅是Func 代表。
  • 只要您能够检测到 which IComparer 正在被使用,它就不会。这至少可以解决“默认”排序问题。任何自定义排序都会使整个主题变得一团糟;)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-02-25
  • 2010-09-21
  • 2011-07-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多