2010-09-20 2 views
1

У меня есть один запрос LINQ, который создает основную группу, а затем две вложенные группы. В пределах последнего гнезда есть также простой OrderBy. Проблема, с которой я сталкиваюсь, заключается в написании запроса или попытке отредактировать его ракеты с потреблением небоскребов визуальной студии до ~ 500 МБ и есть 50% моего процессора, что делает визуальную студию безответной в течение нескольких минут. Если я закомментирую запрос, тогда визуальная студия будет работать отлично. Поэтому мой вопрос заключается в том, почему визуальная студия потребляет столько памяти во время разработки для запроса linq, что это довольно сложно?Использование памяти LINQ и Visual Studio 2010

Объект DataTable Я использую длинный 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.20GHz Dual Core и 4 Гб оперативной памяти

+3

, пожалуйста, разместите свой запрос LINQ для всех, а также приблизительные размеры коллекций, в которых он работает. – Dave

+0

Тройная вложенная «группа» может быть проблемой здесь, хотя я никогда не могу понять «естественный язык» LINQ. –

+0

Вы должны, вероятно, показать нам некоторые аппаратные спецификации также ... –

ответ

2

Проблема с групп и заказов - это то, что они требуют знания всей коллекции для выполнения операций. То же самое с агрегатами, такими как 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 раза, особенно если перечислимое остается в памяти между итерациями.

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

+0

Даже в дизайне и времени выполнения это происходит. Я понимаю, как это может повлиять на производительность во время выполнения, но не на время разработки. – Nathan

+0

Во время разработки во время написания текста происходит много за кулисами. Функции «check-as-you-go» VS/ReSharper выполняют различные анализы кода; например, ReSharper проверяет, являются ли какие-либо условия всегда истинными или всегда ложными. Для некоторых из них требуется анализ пути выполнения, который, по крайней мере, будет несколько зависеть от сложности самого алгоритма. – KeithS

+0

А теперь это имеет смысл. Спасибо за урок! – Nathan

Смежные вопросы