Часто задаваемые вопросы по LINQ

Я пытаюсь справиться с LINQ. Меня больше всего беспокоит то, что, даже если я понимаю синтаксис лучше, я не хочу невольно жертвовать производительностью для выразительности.

Являются ли они хорошими централизованными хранилищами информации или книг для "Эффективного LINQ"? В противном случае, какова ваша личная любимая высокопроизводительная технология LINQ?

В первую очередь я связан с LINQ to Objects, но все предложения по LINQ to SQL и LINQ to XML также приветствуются. Спасибо.

Ответ 1

Простое понимание того, что LINQ делает внутри, должно давать достаточную информацию, чтобы узнать, попадаете ли вы в хит производительности.

Вот простой пример, где LINQ помогает повысить производительность. Рассмотрим этот типичный подход старой школы:

List<Foo> foos = GetSomeFoos();
List<Foo> filteredFoos = new List<Foo>();
foreach(Foo foo in foos)
{
    if(foo.SomeProperty == "somevalue")
    {
        filteredFoos.Add(foo);
    }
}
myRepeater.DataSource = filteredFoos;
myRepeater.DataBind();

Таким образом, вышеприведенный код будет дважды итератировать и выделить второй контейнер для хранения отфильтрованных значений. Какая трата! Сравните с:

var foos = GetSomeFoos();
var filteredFoos = foos.Where(foo => foo.SomeProperty == "somevalue");
myRepeater.DataSource = filteredFoos;
myRepeater.DataBind();

Это повторяется только один раз (когда ретранслятор привязан); он только когда-либо использует оригинальный контейнер; filteredFoos является просто промежуточным перечислителем. И если по какой-то причине вы решили не связывать ретранслятор позже, ничего не пропадает. Вы даже не повторяете и не оцениваете один раз.

Когда вы попадаете в очень сложные манипуляции с последовательностями, вы можете выиграть много, используя LINQ для использования цепочки и ленивой оценки. Опять же, как и в любом случае, это просто вопрос понимания того, что он на самом деле делает.

Ответ 2

Linq, как встроенная технология, имеет преимущества и недостатки производительности. Кодекс, лежащий в основе методов расширения, привлек большое внимание, уделяемое ему командой .NET, а его способность обеспечивать ленивую оценку означает, что стоимость выполнения большинства манипуляций по набору объектов распространяется на более широкий алгоритм, требующий манипулируемого набора, Тем не менее, есть некоторые вещи, которые вам нужно знать, которые могут сделать или нарушить работу вашего кода.

В первую очередь, Linq не волшебным образом сохраняет вашу программу время или память, необходимые для выполнения операции; это просто может задержать эти операции до полной необходимости. OrderBy() выполняет QuickSort, который займет время nlogn точно так же, как если бы вы написали свой собственный QuickSorter или использовали List.Sort() в нужное время. Поэтому всегда помните о том, что вы просите Linq делать в серии при написании запросов; если манипуляция не нужна, попробуйте реструктурировать цепочку запросов или методов, чтобы избежать ее.

К тому же определенные операции (сортировка, группировка, агрегаты) требуют знания всего набора, на котором они действуют. Последний элемент в серии может быть первым, который операция должна вернуть из своего итератора. Кроме того, поскольку операции Linq не должны изменять исходный список, но многие из используемых им алгоритмов (т.е. На месте сортировки), эти операции заканчиваются не только оценкой, но и копированием всего перечислимого в конкретную конечную структуру, выполняя операцию и уступая ей. Итак, когда вы используете OrderBy() в инструкции, и вы запрашиваете элемент из конечного результата, все, что предоставленный ему IEnumerable может выдавать, оценивается, сохраняется в памяти как массив, сортируется, а затем возвращается один элемент в время. Мораль заключается в том, что любая операция, которая требует конечного набора вместо перечислимого, должна быть помещена как можно ближе к запросу, позволяя другим операциям, таким как Where() и Select(), уменьшить мощность и размер памяти исходного набора.

Наконец, методы Linq значительно увеличивают размер стека вызовов и объем памяти вашей системы. Каждая операция, которая должна знать весь набор, сохраняет весь источник в памяти до тех пор, пока последний элемент не будет итерирован, а оценка каждого элемента будет включать стек вызовов, по крайней мере, вдвое превышающий количество методов в вашей цепочке или предложениях в вашем встроенном заявлении (вызов каждого итератора MoveNext() или получение GetEnumerator, плюс по крайней мере один вызов для каждой лямбды на этом пути). Это просто приведет к большему, более медленному алгоритму, чем интеллектуально спроектированный встроенный алгоритм, который выполняет те же манипуляции. Основным преимуществом Linq является простота кода. Создание, а затем сортировка, словарь списков значений групп не очень легкий для понимания код (поверьте мне). Микрооптимизация может запутать его дальше. Если производительность является вашей основной задачей, то не используйте Linq; он добавит примерно 10% накладных расходов и в несколько раз превысит накладные расходы на управление списком на месте. Тем не менее, ремонтопригодность обычно является основной задачей разработчиков, и Linq DEFINITELY помогает там.

О производительности: если производительность вашего алгоритма является священным, непримиримым первым приоритетом, вы будете программировать на неуправляемом языке, таком как С++;.NET будет намного медленнее, поскольку он является управляемой средой выполнения, с встроенной компиляцией JIT, управляемой памятью и дополнительными системными потоками. Я бы принял философию, что она "достаточно хороша"; Linq может вводить замедление по своей природе, но если вы не можете отличить это, и ваш клиент не может отличить эту информацию, то для всех практических целей нет никакой разницы. "Преждевременная оптимизация - это корень всего зла"; Заставьте его работать, ТОГДА ищите возможности сделать его более совершенным, пока вы и ваш клиент не согласитесь на это достаточно хорошо. Он всегда может быть "лучше", но если вы не хотите быть машинным кодом для ручной упаковки, вы найдете недостаток, чем вы можете объявить победу и двигаться дальше.

Ответ 3

Существуют различные факторы, которые влияют на производительность.

Часто разработка решения с использованием LINQ будет предлагать довольно разумную производительность, поскольку система может построить дерево выражений для представления запроса без фактического выполнения запроса при его создании. Только при повторении результатов он использует это дерево выражений для генерации и запуска запроса.

С точки зрения абсолютной эффективности при работе с предопределенными хранимыми процедурами вы можете увидеть некоторый удар по производительности, но в целом подход к разработке заключается в разработке решения с использованием системы, предлагающей разумную производительность (например, LINQ), и не беспокоиться о несколько процентов потери производительности. Если запрос выполняется медленно, возможно, вы посмотрите на оптимизацию.

Реальность заключается в том, что большинство запросов не будет иметь ни малейшей проблемы с выполнением LINQ. Другой факт заключается в том, что если ваш запрос работает медленно, скорее всего, это проблемы с индексацией, структурой и т.д., Чем с самим запросом, поэтому даже если вы хотите оптимизировать вещи, вы часто не будете касаться LINQ, структура базы данных, с которой она работает.

Для обработки XML, если у вас есть документ, который загружается и анализируется в памяти (например, что-либо, основанное на модели DOM или XmlDocument или что-то еще), тогда вы получите больше использования памяти, чем системы, которые делают что-то вроде чтобы показать поиск начального или конечного тега, но не создать полную версию документа в памяти (например, SAX или XmlReader). Недостатком является то, что обработка на основе событий, как правило, является более сложной. Опять же, с большинством документов проблем не будет: в большинстве систем имеется несколько ГБ ОЗУ, поэтому рассмотрение нескольких МБ, представляющих один XML-документ, не является проблемой (и вы часто обрабатываете большой набор XML-документов, по крайней мере, несколько последовательно). Это только если у вас есть огромный XML файл, который займет до 100 МБ, что вы беспокоитесь о конкретном выборе.

Имейте в виду, что LINQ позволяет вам перебирать списки в памяти и т.д., поэтому в некоторых ситуациях (например, когда вы собираетесь использовать набор результатов снова и снова в функции), вы можете используйте .ToList или .ToArray, чтобы вернуть результаты. Иногда это может быть полезно, хотя обычно вы пытаетесь использовать запрос базы данных, а не в памяти.

Как для личных фаворитов - NHibernate LINQ - это инструмент объектно-реляционного сопоставления, который позволяет вам определять классы, определять детали отображения, а затем получать его для генерации базы данных из ваших классов, а не наоборот, а LINQ поддержка довольно хороша (конечно, лучше, чем у SubSonic).

Ответ 4

В linq to SQL вам не нужно заботиться о производительности. вы можете связать все ваши заявления так, как вы считаете, это наиболее читаемые. Linq просто переводит все ваши заявления в один оператор SQL в конце, который только вызывается/выполняется в конце (например, когда вы вызываете .ToList()

a var может содержать этот оператор без его выполнения, если вы хотите применять различные дополнительные инструкции в разных условиях. Выполнение в конце происходит только тогда, когда вы хотите перевести свои утверждения в результат как объект или список объектов.

Ответ 5

Там есть проект codeplex под названием i4o, который я использовал некоторое время назад, что может помочь улучшить производительность Linq для объектов в случаях, когда вы выполняете сравнения сравнений, например.

from p in People 
where p.Age == 21 
select p;

http://i4o.codeplex.com/ Я не тестировал его с .Net 4, поэтому не могу сказать, что он все равно будет работать, но стоит проверить. Чтобы заставить его работать над своей магией, вам в основном просто нужно украсить свой класс некоторыми атрибутами, чтобы указать, какое свойство следует индексировать. Когда я использовал его, прежде чем он работает только с сравнениями равенства.