В какой производительности вы должны использовать «Вложенные foreach» или «лямбда-linq-запросы»?«Вложенные foreach» против производительности «lambda/linq query» (LINQ-to-Objects)
ответ
Напишите самый чистый код, который вы можете, а затем сравнительный анализ и профиль, чтобы обнаружить любые проблемы с производительностью. Если у вас есть do, у вас есть проблемы с производительностью, вы можете поэкспериментировать с другим кодом, чтобы решить, быстрее или не работает (все время измеряйте как можно более реалистичные данные), а затем выносите суждение о том, стоит ли повышение производительности читаемость.
Прямой 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.
EDIT: Для того, чтобы ответить о «вложенном Еогеаспе» петля ... обычно те, которые представлены с SelectMany
или вторым from
пунктом:
var query = from item in firstSequence
from nestedItem in item.NestedItems
select item.BaseCount + nestedItem.NestedCount;
Здесь мы только добавление одного дополнительного итератором, потому что мы d уже использует дополнительный итератор для каждого элемента в первой последовательности из-за вложенного цикла foreach
. Все еще немного накладных расходов, в том числе накладные расходы на выполнение проекции в делегате вместо «встроенного» (что-то, о чем я не упоминал ранее), но он все равно не будет сильно отличаться от производительности вложенных-foreach.
Это не значит, что вы не можете стрелять в ногу с помощью LINQ, конечно. Вы можете писать безупречно неэффективные запросы, если сначала не занимаетесь своим мозгом, но это далеко не уникально для LINQ ...
Это более сложный вопрос. В конечном счете, большая часть LINQ-to-Objects (за кулисами) имеет цикл foreach
, но с дополнительными издержками небольших блоков абстракции/итератора/и т. Д. Однако, если вы не делаете очень разные вещи в своих двух версиях (foreach vs LINQ), они должны быть как O (N).
Реальный вопрос: есть ли лучший способ написать ваш конкретный алгоритм, что означает, что foreach
будет неэффективным? И может ли LINQ сделать это за вас?
Например, LINQ позволяет легко хешировать/группировать/сортировать данные.
Если вы
foreach(Customer c in Customer)
{
foreach(Order o in Orders)
{
//do something with c and o
}
}
Вы выполнить Customer.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.
отличный ответ с хорошим объяснением –
Это потому, что вы используете два разных алгоритма; вы сравниваете яблоки с апельсинами. Я не вижу, как это имеет отношение к поставленному вопросу. – mquander
Ваш вложенный foreach на самом деле эквивалентен SelectMany, а не Join - i.e. from c в Customer from o в Orders ... (нет соединения) –
Это было сказано ранее, но это заслуживает повторения.
Разработчики никогда не знают, где узкое место производительности до тех пор, пока они не проведут тесты производительности.
То же самое верно для сравнения техники A с техникой B. Если нет существенной разницы, вам просто нужно ее протестировать. Это может быть очевидно, если у вас есть сценарий O (n) vs O (n^x), но поскольку материал LINQ в основном является компилятором, он заслуживает профилирования.
Кроме того, если ваш проект не находится в производстве, и вы профилировали код и обнаружили, что этот цикл замедляет выполнение, оставьте его в зависимости от вашего предпочтения для удобства чтения и обслуживания. Преждевременная оптимизация - дьявол.
Хотя верно, что вы не можете по-настоящему предвидеть узкие места в производительности, также верно, что большинство проблем с производительностью разработаны, они находятся очень поздно в жизненном цикле разработки и поэтому трудно кодировать. Существует много всего, что нужно сказать, чтобы всегда иметь представление о последствиях решений решений по дизайну и реализации, которые вы делаете, а не беспечно кодировать, надеясь, что все будет в порядке. – Mike
Большое преимущество заключается в том, что использование запросов Linq-To-Objects дает возможность легко перевести запрос на PLinq и система автоматически выполнит его работу с правильным количеством потоков для текущей системы.
Если вы используете эту технику на больших наборах данных, это легко станет большой победой для очень маленьких неприятностей.
Правда, но есть и параллельные эквиваленты, предложенные для foreach. http://www.danielmoth.com/Blog/2009/01/parallelising-loops-in-net-4.html – jpierson
Первое предложение должно быть распечатано как баннер и помещено в каждый отдел программирования. –
Джон ... Ты потрясающий ... Сердечно спасибо! –