«Вложенные foreach» против производительности «lambda / linq query» (LINQ-to-Objects)

С точки зрения производительности, что вы должны использовать «Nested foreach» или «lambda / linq queries»?

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

Прямой подход foreach будет быстрее, чем LINQ во многих случаях. Например, рассмотрим:

 var query = from element in list where element.X > 2 where element.Y < 2 select element.X + element.Y; foreach (var value in query) { Console.WriteLine(value); } 

Теперь есть два предложения where и предложение select , поэтому каждый конечный элемент должен пройти через три iteratorа. (Очевидно, что два предложения, которые могут быть объединены в этом случае, но я делаю общий вывод.)

Теперь сравните его с прямым кодом:

 foreach (var element in list) { if (element.X > 2 && element.Y < 2) { Console.WriteLine(element.X + element.Y); } } 

Это будет работать быстрее, потому что у него меньше ходов. Скорее всего, консольный вывод будет затмевать стоимость iteratorа, и я бы предпочел запрос LINQ.

РЕДАКТИРОВАТЬ. Чтобы ответить на петли «вложенные foreach» ... обычно те, которые представлены с помощью SelectMany или second from :

 var query = from item in firstSequence from nestedItem in item.NestedItems select item.BaseCount + nestedItem.NestedCount; 

Здесь мы добавляем только один дополнительный iterator, потому что мы уже будем использовать дополнительный iterator для каждого элемента в первой последовательности из-за вложенного цикла foreach . Все еще немного накладных расходов, в том числе накладные расходы на выполнение проекции в делегате вместо «встроенного» (что-то, о чем я не упоминал ранее), но он все равно не будет сильно отличаться от производительности вложенных-foreach.

Это не значит, что вы не можете стрелять в ногу с помощью LINQ, конечно. Вы можете писать безупречно неэффективные запросы, если сначала не занимаетесь своим мозгом, но это далеко не единственно для LINQ ...

Если вы это сделаете

 foreach(Customer c in Customer) { foreach(Order o in Orders) { //do something with c and o } } 

Вы выполните итерации Customer.Count * Order.Count


Если вы это сделаете

 var query = from c in Customer join o in Orders on c.CustomerID equals o.CustomerID select new {c, o} foreach(var x in query) { //do something with xc and xo } 

Вы будете выполнять итерации Customer.Count + Order.Count, потому что Enumerable.Join реализован как HashJoin.

Это более сложно. В конечном счете, большая часть LINQ-to-Objects является (за кулисами) циклом foreach , но с добавленными издержками небольших блоков абстракции / iteratorа / и т. Д. Однако, если вы не делаете очень разные вещи в своих двух версиях (foreach vs LINQ ), они должны быть как O (N).

Реальный вопрос: есть ли лучший способ написания вашего конкретного алгоритма, который означает, что foreach будет неэффективным? И может ли LINQ сделать это за вас?

Например, LINQ упрощает хеширование / группировку / сортировку данных.

Это было сказано ранее, но это заслуживает повторения.

Разработчики никогда не знают, где узкое место производительности до тех пор, пока они не проведут тесты производительности.

То же самое верно для сравнения метода A с техникой B. Если не существует драматической разницы, вам просто нужно ее протестировать. Это может быть очевидно, если у вас есть сценарий O (n) vs O (n ^ x), но поскольку материал LINQ в основном является компилятором, он заслуживает профилирования.

Кроме того, если ваш проект не находится в производстве, и вы профилировали код и обнаружили, что этот цикл замедляет выполнение, оставьте его в зависимости от вашего предпочтения для удобства чтения и обслуживания. Преждевременная оптимизация – дьявол.

Большое преимущество в том, что использование запросов Linq-To-Objects дает вам возможность легко перевести запрос на PLinq и система автоматически выполняет его работу с правильным количеством streamов для текущей системы.

Если вы используете эту технику на больших наборах данных, это легко станет большой победой для очень маленьких неприятностей.

Давайте будем гением компьютера.