Использование памяти LINQ и Visual Studio 2010 - PullRequest
1 голос
/ 20 сентября 2010

Я создал один запрос LINQ, который создает основную группу, а затем две вложенные группы.В последнем гнезде также есть простой OrderBy.Проблема, с которой я сталкиваюсь, - это когда я пишу запрос или пытаюсь отредактировать его, объем памяти, потребляемый Visual Studio, увеличивается до ~ 500 МБ и потребляет 50% моего процессора, что делает 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"))
               }
       };

Характеристики оборудования:

Dell Latitude с двухъядерным процессором 2,20 ГГц и оперативной памятью 4 ГБ

1 Ответ

2 голосов
/ 20 сентября 2010

Проблема с группами и заказами заключается в том, что им требуется знание всей коллекции для выполнения операций. То же самое с агрегатами, такими как min, max, sum, avg и т. Д. Некоторые из этих операций не могут запрашивать истинный тип передаваемого IEnumerable, или это не имеет значения, поскольку они являются «разрушительными» по своей природе, поэтому их приходится создавать рабочая копия. Когда вы объединяете эти вещи вместе, вы получаете как минимум две копии полностью перечислимого; один, созданный предыдущим методом, который повторяется текущим методом, и один, генерируемый текущим методом. Любая копия перечислимого объекта, имеющая ссылку вне запроса (например, перечисляемый источник), также остается в памяти, а перечислимые элементы, которые стали потерянными, остаются до тех пор, пока поток GC не успеет их утилизировать и завершить. Для большого перечисляемого источника все это может создать огромный спрос на кучу.

Кроме того, вложенные агрегаты в предложениях могут очень быстро сделать запрос дорогим. В отличие от СУБД, которая может разработать «план запроса», Linq не настолько умён. Min (), например, требует итерации всего перечисляемого, чтобы найти наименьшее значение указанной проекции. Когда это является критерием предложения Where, хорошая СУБД найдет это значение один раз для контекста, а затем при необходимости внесет его в последующие оценки. Linq просто запускает метод расширения каждый раз, когда он вызывается, и когда у вас есть условие, например enumerable.Where (x => x.Value == enumerable.Min (x2 => x2.Value)), это O (N ^ 2) - сложность операции только для оценки фильтра. Добавьте несколько уровней группировки, и Big-O может легко достичь высокой полиномиальной сложности.

Как правило, вы можете сократить время запроса, выполнив оптимизацию, чтобы СУБД дала тот же запрос. Если значение агрегата может быть известно по всей области запроса (например, result = source.Where(s=>s.Value == source.Min(x=>x.value))), оцените его как переменную с помощью предложения let (или внешнего запроса) и замените вызовы Min () псевдонимом. , Повторное перечисление дважды обычно дешевле, чем повторение N ^ 2 раза, особенно если перечисление остается в памяти между итерациями.

Кроме того, убедитесь, что ваш порядок запросов максимально сокращает пространство выборки и как можно дешевле перед началом группировки. Вы можете сделать обоснованные предположения об условиях, которые должны быть оценены дорого, например, где (s => s.Value <порог) .Where (s => s.Value == source.Min (x => x.Value) )) или более кратко, где (s => s.Value x.Value)) (второй работает в C # из-за отложенной оценки состояния, но не для всех языков оценивать лениво). Это сокращает количество оценок Min () до количества элементов, соответствующих первому критерию. Вы можете использовать существующие критерии, чтобы сделать то же самое, если критерии A и B достаточно независимы, чтобы A && B == B && A.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...