我创建了一个LINQ查询,创建一个主组,然后创建两个嵌套组。在最后一个嵌套中,还有一个简单的OrderBy。我遇到的问题是在编写查询或尝试编辑它时,视觉工作室内存消耗天空火箭到~500MB并且正在吃掉50%的CPU,这使得visual studio几秒钟没有响应。如果我注释掉查询,那么visual studio就可以了。所以我的问题是为什么visual studio在设计时为linq查询消耗了这么多内存,认为它有点复杂?
我使用的数据表是10732行长,21列
var results = from p in m_Scores.AsEnumerable()
group p by p.Field<string>("name") into x
select new
{
Name = x.Key,
Members = from z in x
group z by z.Field<string>("id") into zz
select new
{
Id = zz.Key,
Plots = from a in zz
group a by a.Field<string>("foo") into bb
select new
{
Foo = bb.Key,
Bars = bb
}.Bars.OrderBy(m => m.Field<string>("foo"))
}
};
硬件规格:
戴尔Latitude配备2.20GHz双核处理器和4GB内存答案 0 :(得分:2)
组和订单的问题在于,他们需要了解整个集合才能执行操作。与min,max,sum,avg等聚合相同的事情。其中一些操作无法询问传递的IEnumerable的真实类型,或者无关紧要,因为它们本质上是“破坏性的”,因此它们必须创建工作副本。当你把这些东西连在一起时,你最终会得到至少两个完整的可枚举副本;由当前方法迭代的前一方法产生的方法,以及由当前方法生成的方法。在查询之外具有引用的可枚举的任何副本(如源可枚举)也保留在内存中,并且已成为孤立的可用数据一直存在,直到GC线程有时间处理并完成它们。对于可枚举的大型源,所有这些都可以在堆上创建大量需求。
此外,在子句中嵌套聚合可以非常快速地使查询变得昂贵。与可以设计“查询计划”的DBMS不同,Linq并不那么聪明。例如,Min()需要迭代整个枚举,以找到指定投影的最小值。当这是Where子句的标准时,一个好的DBMS将在每个上下文中找到该值一次,然后根据需要在后续评估中内联该值。 Linq每次调用时都会运行扩展方法,当你有一个类似enumerable.Where(x =&gt; x.Value == enumerable.Min(x2 =&gt; x2.Value))的条件时,那就是O( N ^ 2) - 复杂度操作只是为了评估滤波器。添加多个级别的分组,Big-O可以轻松达到高多项式复杂度。
通常,您可以通过执行DBMS给出相同查询的优化来减少查询时间。如果可以为查询的整个范围(例如result = source.Where(s=>s.Value == source.Min(x=>x.value))
)知道聚合的值,请使用let
子句(或外部查询)将其计算为变量,并替换Min()使用别名调用。迭代可枚举两次通常比迭代N ^ 2次便宜,特别是如果可枚举在迭代之间保留在内存中。
此外,在开始分组之前,请确保您的查询顺序尽可能减少样本空间并尽可能便宜。您可能能够对必须昂贵评估的条件进行有根据的猜测,例如Where(s =&gt; s.Value&lt; threshold)。其中(s =&gt; s.Value == source.Min(x =&gt; ; x.Value))或更简洁,Where(s =&gt; s.Value&lt; threshold&amp;&amp; s.Value == source.Min(x =&gt; x.Value))(第二个在C#中工作)因为懒惰的条件评估,但并非所有语言都懒惰地评估)。这将Min()的评估数量减少到符合第一个标准的元素数量。您可以使用现有标准执行相同的操作,只要标准A和B足够独立,A&amp;&amp; amp; B == B&amp;&amp;甲