С точки зрения производительности, что вы должны использовать "Nested foreach" или "lambda/linq queries"?
"Вложенные foreach" против производительности "lambda/linq query" (LINQ-to-Objects)
Ответ 1
Напишите самый четкий код, который вы можете, а затем сравнительный анализ и профиль, чтобы обнаружить любые проблемы с производительностью. Если у вас есть проблемы с производительностью, вы можете поэкспериментировать с другим кодом, чтобы выяснить, быстрее или нет это (все время измеряя как можно более реалистичные данные), а затем вынести решение о том, стоит ли повысить производительность удар.
Прямой подход 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
, поэтому каждый конечный элемент должен пройти через три итератора. (Очевидно, что два предложения, которые могут быть объединены в этом случае, но я делаю общий вывод.)
Теперь сравните его с прямым кодом:
foreach (var element in list)
{
if (element.X > 2 && element.Y < 2)
{
Console.WriteLine(element.X + element.Y);
}
}
Это будет работать быстрее, потому что у него меньше обручей. Скорее всего, вывод на консоль будет затмевать стоимость итератора, и я бы предпочел бы запрос LINQ.
РЕДАКТИРОВАТЬ: Чтобы ответить на петли "вложенные foreach"... обычно те, которые представлены с помощью SelectMany
или второго предложения from
:
var query = from item in firstSequence
from nestedItem in item.NestedItems
select item.BaseCount + nestedItem.NestedCount;
Здесь мы добавляем только один дополнительный итератор, потому что мы уже будем использовать дополнительный итератор для каждого элемента в первой последовательности из-за вложенного цикла foreach
. Там все еще немного накладных расходов, в том числе накладные расходы на выполнение проекции в делегате вместо "встроенного" (что-то, о чем я не упоминал ранее), но он все равно не будет сильно отличаться от производительности вложенного foreach.
Это не значит, что вы не можете стрелять в ногу с помощью LINQ, конечно. Вы можете писать безупречно неэффективные запросы, если сначала не занимаетесь своим мозгом, но это далеко не уникально для LINQ...
Ответ 2
Если вы делаете
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 x.c and x.o
}
Вы будете выполнять итерации Customer.Count + Order.Count, потому что Enumerable.Join реализован как HashJoin.
Ответ 3
Это сложнее. В конечном счете, большая часть LINQ-to-Objects является (за кулисами) циклом foreach
, но с добавленными накладными расходами на небольшие блоки абстракции/итератора/и т.д. Однако, если вы не делаете очень разные вещи в своих двух версиях (foreach vs LINQ), они должны быть как O (N).
Реальный вопрос: есть ли лучший способ написать ваш специфический алгоритм, что означает, что foreach
будет неэффективным? И может ли LINQ сделать это за вас?
Например, LINQ упрощает хеширование/группировку/сортировку данных.
Ответ 4
Это было сказано ранее, но это заслуживает повторения.
Разработчики никогда не знают, где узкое место в производительности, пока не будут выполнены тесты производительности.
То же самое верно для сравнения техники A с техникой B. Если не существует драматической разницы, вам просто нужно ее протестировать. Это может быть очевидно, если у вас есть сценарий O (n) vs O (n ^ x), но поскольку материал LINQ в основном является компилятором, он заслуживает профилирования.
Кроме того, если ваш проект не находится в производстве, и вы профилировали код и обнаружили, что этот цикл замедляет выполнение, оставьте его в зависимости от вашего предпочтения для удобства чтения и обслуживания. Преждевременная оптимизация - дьявол.
Ответ 5
Большое преимущество в том, что использование запросов Linq-To-Objects дает вам возможность легко перевести запрос на PLinq и система автоматически выполнит его работу с правильным количеством потоков для текущей системы.
Если вы используете эту технику на больших наборах данных, это легко станет большой победой для очень маленьких неприятностей.