Какой цикл работает быстрее в C# / VB.NET / .NET, for или foreach?
С тех пор, как я прочитал, что цикл for работает быстрее, чем цикл foreach a давным-давно, я предположил, что он верен для всех коллекций, общих коллекций, всех массивов и т. д.
Я поискал в Google и нашел несколько статей, но большинство из них неубедительны (читайте комментарии к статьям) и не имеют окончательного ответа.
Было бы идеально, если бы каждый сценарий был указан в списке и было бы лучшее решение для него.
Например (просто пример того, как должно быть):
for лучше, чем foreachIList (не универсальных) - лучше foreach
чем forНесколько ссылок на то же самое в Интернете:
foreach или не к foreach, вот в чем вопросfor против foreach[Редактировать]
Помимо аспекта удобочитаемости, меня действительно интересуют факты и цифры. Есть приложения, для которых важна последняя миля оптимизации производительности.
Я вижу три решения: (1) Напишите компилятор, который перечисляет в случайном порядке (2) переименуйте «foreach» в «ForEachInStrictOrder» (3) Переименуйте себя в ~ Ian, возможно, Iain?
Без контекста я не могу точно знать, что вы делаете, но что происходит, когда вы сталкиваетесь с частично заполненным массивом?
Кстати, 2 миллиона просмотров в месяц - это ничего страшного. В среднем это меньше, чем попаданий в секунду.
См. Также stackoverflow.com/questions/1723855/…
Из публикации своей темы (которая оказалась для меня очень полезной) я узнал, что вы должны использовать foreach, когда вам не нужно ссылаться на индекс в коллекции. В противном случае используйте для. Это одно хорошее решение
foreach вместо for в C#. Если вы видите здесь ответы, которые не имеют никакого смысла, то вот почему. Винить модератора, а не неудачных ответов.
В общем, for будет быстрее, чем foreach. Однако, если вам не все равно, у вас, вероятно, есть «другие проблемы», более важные ...
@ТЕД. О, мне было интересно, откуда берутся все комментарии «твой босс - идиот», спасибо
Связанные Разница в производительности управляющих структур for и foreach в C#





Я предполагаю, что это, вероятно, не будет иметь значения в 99% случаев, так почему бы вам выбрать более быстрый вместо наиболее подходящего (как в самом простом для понимания / обслуживания)?
@klew, если вы действительно профилируете свой код, вам не придется угадывать, какие 20% должны быть как можно быстрее. Вы, вероятно, также обнаружите, что фактическое количество петель, которые должны быть быстрыми, намного меньше. Более того, действительно ли вы говорите, что действие цикла - это то, на что вы проводите свое время, а не то, что вы на самом деле делаете в этом цикле?
Всегда будет рядом. Для массива иногдаfor немного быстрее, но foreach более выразителен и предлагает LINQ и т. д. В общем, придерживайтесь foreach.
Кроме того, foreach можно оптимизировать в некоторых сценариях. Например, связанный список может быть ужасен для индексатора, но может быть быстрым для foreach. Собственно, стандартный LinkedList<T> по этой причине даже не предлагает индексатор.
Так вы говорите, что LinkedList<T> компактнее, чем List<T>? И если я всегда буду использовать foreach (вместо for), мне лучше использовать LinkedList<T>?
@JohnB - не стройнее; просто другой. Например, каждый узел в связанном списке имеет дополнительные ссылки, которые не нужны для плоского массива (который также поддерживает List<T>). Тем более что дешевле вставлять / Удалить.
Некоторое время назад я тестировал его, и в результате выяснилось, что петля for намного быстрее, чем петля foreach. Причина проста: цикл foreach сначала должен создать экземпляр IEnumerator для коллекции.
Не с массивом, это не так. Скомпилируйте его и посмотрите на IL :) (Это также зависит от коллекции. IIRC, List <T> использует тип значения для перечислителя.)
Почему одно размещение может быть дорогим? В управляемой .NET распределения практически бесплатны, не так ли, поскольку все, что делается, - это инкремент указателя в управляемой куче, в большинстве случаев фрагментация практически отсутствует.
не только одно выделение, но и все накладные расходы на вызов методов для MoveNext () и get_Current () для каждой итерации.
У for есть более простая логика для реализации, поэтому он быстрее, чем foreach.
Патрик Смаччиа писал об этом в блоге в прошлом месяце со следующими выводами:
- for loops on List are a bit more than 2 times cheaper than foreach loops on List.
- Looping on array is around 2 times cheaper than looping on List.
- As a consequence, looping on array using for is 5 times cheaper than looping on List using foreach (which I believe, is what we all do).
Однако никогда не забывайте: «Преждевременная оптимизация - корень всех зол».
@Hardwareguy: Если вы знаете, что for работает почти неприемлемо быстрее, почему бы вам не начать использовать его вообще? Это не займет лишнего времени.
@devinb, использовать "for" сложнее, чем использовать "foreach", поскольку он добавляет код, другую переменную, условие, которое необходимо проверить, и т. д. Сколько раз вы видели постепенную ошибку в цикле "foreach" ?
@Hardwareguy, дай мне посмотреть, правильно ли я понял. Перебор списка с foreach занимает в 5 раз больше времени, чем перебор массива с for, и вы называете это несущественным? Такая разница в производительности может иметь значение для вашего приложения, а может и нет, но я бы не стал сразу отказываться от нее.
Читая сообщение в блоге, похоже, что тесты проводились в отладке, а не в выпуске, так что это может иметь значение. Кроме того, разница заключается только в накладных расходах цикла. Это вообще не влияет на время выполнения тела цикла, которое в большинстве случаев намного дольше, чем время, необходимое для перехода к следующему элементу списка. Полезно знать, когда вы определили, что проблема явно существует, и вы измерили разницу в конкретном приложении, и есть заметное улучшение, но определенно не общий совет по удалению всех foreach.
foreach может быть дешевле, чем for loop с LinkedListизображения в этой ссылке не работают, вот архивная копия web.archive.org/web/20140827024150/http://codebetter.com/…
@ Davy8 Я проверял текущие результаты в режиме выпуска с помощью секундомера. Результаты изменились с тех пор, как была написана эта статья. 1. Цикл List <int> почти в 7 раз быстрее цикла foreach. Было выполнено 100 000 000 ^ 2 итераций. Ибо с оптимизированным подсчетом - самый быстрый, но почти нет разницы с примером без оптимизации (без оптимизации: 326; для оптимизированного подсчета: 296; foreach 2356). 2. Но что интереснее. В некоторых случаях для int [] foreach немного быстрее, чем for (без оптимизации: 294; для оптимизированного числа: 294; foreach 267, меньше!). 10 ^ 8 ^ 2 оперов тоже.
@gdoron Я знаю, что вы имели в виду включение поиска по индексу, но цикл for (n = l.First; n != null; n = n.Next) находится примерно на полпути между циклом foreach и соответствующим циклом for на основе массива.
Вы не можете заменить более дешевое на более быстрое (если вы это имеете в виду). было бы яснее
@ Ян Нельсон: В статье Джексонданстан ForEach - Назначение не выполнено. Я изменил код и выполнил, как вы упомянули, результаты не были изменены ... Спасибо: -0)
Стоит ли добавлять уточнение к этому ответу; то есть переключение с foreach в списке на for в массиве (в большинстве случаев) не улучшит производительность вашего кода в 5 раз, как подразумевается. Если бы все, что вы делали, было зацикливанием, это было бы правдой, но тогда вы могли бы улучшить производительность бесконечно, полностью пропустив цикл. Если у вас есть код внутри цикла, он все равно будет обрабатываться столько же времени и будет обрабатываться один раз за итерацию, что обычно делает преимущество цикла for довольно незначительным по сравнению.
Различия в скорости в циклах for и foreach крошечные, когда вы просматриваете общие структуры, такие как массивы, списки и т. д., А выполнение запроса LINQ по коллекции почти всегда немного медленнее, хотя писать лучше! Как сказано на других плакатах, стремитесь к выразительности, а не к миллисекунде дополнительной производительности.
До сих пор не было сказано, что когда цикл foreach компилируется, он оптимизируется компилятором на основе коллекции, которую он повторяет. Это означает, что если вы не уверены, какой цикл использовать, вам следует использовать цикл foreach - он сгенерирует для вас лучший цикл при компиляции. Это тоже более читабельно.
Еще одно ключевое преимущество цикла foreach заключается в том, что если ваша реализация коллекции изменится (например, с int array на List<int>), то ваш цикл foreach не потребует каких-либо изменений кода:
foreach (int i in myCollection)
Вышеупомянутое одинаково независимо от типа вашей коллекции, тогда как в вашем цикле for следующее не будет построено, если вы изменили myCollection с array на List:
for (int i = 0; i < myCollection.Length, i++)
Вероятно, это зависит от типа перечисляемой коллекции и реализации ее индексатора. В целом, использование foreach, вероятно, будет лучшим подходом.
Кроме того, он будет работать с любым IEnumerable, а не только с индексаторами.
Маловероятно, что между ними будет огромная разница в производительности. Как всегда, когда сталкиваешься с вопросом "что быстрее?" вопрос, вы всегда должны думать: «Я могу это измерить».
Напишите два цикла, которые делают одно и то же, в теле цикла, выполните и синхронизируйте их оба, и посмотрите, какова разница в скорости. Сделайте это как с почти пустым телом, так и с телом цикла, аналогичным тому, что вы на самом деле будете делать. Также попробуйте это с типом коллекции, которую вы используете, потому что разные типы коллекций могут иметь разные характеристики производительности.
Во-первых, встречный иск к Ответ Дмитрия (сейчас удален). Для массивов компилятор C# генерирует в основном тот же код для foreach, что и для эквивалентного цикла for. Это объясняет, почему для этого теста результаты в основном такие же:
using System;
using System.Diagnostics;
using System.Linq;
class Test
{
const int Size = 1000000;
const int Iterations = 10000;
static void Main()
{
double[] data = new double[Size];
Random rng = new Random();
for (int i=0; i < data.Length; i++)
{
data[i] = rng.NextDouble();
}
double correctSum = data.Sum();
Stopwatch sw = Stopwatch.StartNew();
for (int i=0; i < Iterations; i++)
{
double sum = 0;
for (int j=0; j < data.Length; j++)
{
sum += data[j];
}
if (Math.Abs(sum-correctSum) > 0.1)
{
Console.WriteLine("Summation failed");
return;
}
}
sw.Stop();
Console.WriteLine("For loop: {0}", sw.ElapsedMilliseconds);
sw = Stopwatch.StartNew();
for (int i=0; i < Iterations; i++)
{
double sum = 0;
foreach (double d in data)
{
sum += d;
}
if (Math.Abs(sum-correctSum) > 0.1)
{
Console.WriteLine("Summation failed");
return;
}
}
sw.Stop();
Console.WriteLine("Foreach loop: {0}", sw.ElapsedMilliseconds);
}
}
Полученные результаты:
For loop: 16638
Foreach loop: 16529
Затем проверка того, что точка Грега о важности типа коллекции - измените массив на List<double> в приведенном выше примере, и вы получите радикально другие результаты. Это не только значительно медленнее в целом, но и foreach становится значительно медленнее, чем доступ по индексу. Сказав это, я бы все же предпочел почти всегда foreach циклу for, где он упрощает код, потому что удобочитаемость почти всегда важна, а микрооптимизация - редко.
"измените массив на List <double> в приведенном выше примере, и вы получите радикально другие результаты" Очень интересно, я не думал об этом
Учитывая странные различия в результатах между моими тестами и тестами других людей, я думаю, что это заслуживает публикации в блоге ...
Что вы предпочитаете почти всегда между массивами и List<T>? В этом случае удобочитаемость важнее микрооптимизации?
@JohnB: Ага, я почти всегда предпочитаю List<T> массивам. Исключение составляют char[] и byte[], которые чаще рассматриваются как «фрагменты» данных, а не как обычные коллекции.
Удивительно, но на моей машине разница становится еще более заметной, почти 10% в пользу foreach на простых массивах. Я дико предполагаю, что все это происходит из-за дрожания, не нужно беспокоиться о дополнительной переменной, связанных проверках и т. д. Было бы очень интересно, если бы у кого-то есть подробное объяснение этого.
Я бы посоветовал прочитать это для конкретного ответа. Вывод статьи состоит в том, что использование цикла for обычно лучше и быстрее, чем цикл foreach.
Этот вывод был написан в 2004 году. Дела пошли дальше. См. Мой тест.
Если вы не участвуете в каком-либо конкретном процессе оптимизации скорости, я бы сказал, что используйте тот метод, который дает самый простой для чтения и поддержки код.
Если итератор уже настроен, например, с одним из классов коллекции, то foreach - хороший простой вариант. И если вы повторяете целочисленный диапазон, то for, вероятно, будет чище.
Джеффри Рихтер рассказал о разнице в производительности между for и foreach в недавнем подкасте: http://pixel8.infragistics.com/shows/everything.aspx#Episode:9317
Я не ожидал, что кто-то обнаружит «огромную» разницу в производительности между ними.
Я предполагаю, что ответ зависит от того, имеет ли коллекция, к которой вы пытаетесь получить доступ, более быструю реализацию доступа к индексатору или более быструю реализацию доступа к IEnumerator. Поскольку IEnumerator часто использует индексатор и просто хранит копию текущей позиции индекса, я ожидаю, что доступ к перечислителю будет как минимум таким же медленным или более медленным, чем прямой доступ к индексу, но не намного.
Конечно, этот ответ не учитывает какие-либо оптимизации, которые может реализовать компилятор.
Компилятор C# выполняет очень небольшую оптимизацию, он действительно оставляет это на усмотрение JITter.
Ну, JITter - это компилятор ... Верно?
Имейте в виду, что цикл for и цикл foreach не всегда эквивалентны. Перечислители списков вызовут исключение, если список изменится, но вы не всегда получите это предупреждение с помощью обычного цикла for. Вы можете даже получить другое исключение, если список изменится не в то время.
Если список меняется из-под вас, вы не можете полагаться на перечислитель, поддерживающий цикл foreach, чтобы сообщить вам об этом. Он не будет проверять снова после того, как вернет вам значение, что приведет к гонке.
Джеффри Рихтер на TechEd 2005:
"I have come to learn over the years the C# compiler is basically a liar to me." .. "It lies about many things." .. "Like when you do a foreach loop..." .. "...that is one little line of code that you write, but what the C# compiler spits out in order to do that it's phenomenal. It puts out a try/finally block in there, inside the finally block it casts your variable to an IDisposable interface, and if the cast suceeds it calls the Dispose method on it, inside the loop it calls the Current property and the MoveNext method repeatedly inside the loop, objects are being created underneath the covers. A lot of people use foreach because it's very easy coding, very easy to do.." .. "foreach is not very good in terms of performance, if you iterated over a collection instead by using square bracket notation, just doing index, that's just much faster, and it doesn't create any objects on the heap..."
Интернет-трансляция по запросу: http://msevents.microsoft.com/CUI/WebCastEventDetails.aspx?EventID=1032292286&EventCategory=3&culture=en-US&CountryCode=US
Петли foreach демонстрируют более конкретное намерение, чем петли for.
Использование цикла foreach демонстрирует любому, кто использует ваш код, что вы планируете что-то сделать с каждым членом коллекции, независимо от его места в коллекции. Он также показывает, что вы не изменяете исходную коллекцию (и выдает исключение, если вы попытаетесь это сделать).
Другим преимуществом foreach является то, что он работает с любым IEnumerable, тогда как for имеет смысл только для IList, где каждый элемент фактически имеет индекс.
Однако, если вам нужно использовать индекс элемента, тогда, конечно, вам должно быть разрешено использовать цикл for. Но если вам не нужно использовать индекс, его наличие просто загромождает ваш код.
Насколько мне известно, нет значительных последствий для производительности. На каком-то этапе в будущем может быть проще адаптировать код, использующий foreach, для работы на нескольких ядрах, но сейчас об этом не стоит беспокоиться.
Это очень полезно при итерации по списку настраиваемых объектов или списку данных, и вам не нужно беспокоиться обо всей номенклатуре индексов при обработке данных.
Я не вижу, как foreach помогает многопоточности. Я думаю, вы имеете в виду такие вещи, как Task Parallel Library, но на самом деле это не волшебство foreach.
@Mehrdad Компилятор может свободно перебирать список с помощью foreach. Например, он может перебирать четные элементы на одном ядре и нечетные элементы на другом. foreach ближе к функциональному стилю программирования.
ctford: Нет, это не так. Компилятор, конечно, не может переупорядочивать элементы в foreach. foreach вообще не имеет отношения к функциональному программированию. Это полностью - императивная парадигма программирования. Вы неправильно приписываете foreach то, что происходит в TPL и PLINQ.
@ Mehrdad, ctford: Я думаю, это зависит от того, есть ли у foreach гарантия порядка. Если у foreach нет гарантии порядка, он может быть оптимизирован будущей версией компилятора для использования ядер, если есть что-то вроде атрибута, чтобы пометить код в цикле foreach как потокобезопасный?
@BlueTrin: это, безусловно, гарантирует упорядочение (раздел спецификации C# 8.8.4 формально определяет foreach как эквивалент цикла while). Думаю, я знаю, что @ctford имеет в виду. Библиотека параллельных задач позволяет основная коллекция предоставлять элементы в произвольном порядке (путем вызова .AsParallel для перечисляемого). foreach здесь ничего не делает, и тело цикла выполняется на одиночный поток. Единственное, что распараллеливается, - это генерация последовательности.
@Mehrdad: Кроме того, использование foreach помогает избежать связывания кода обработки элементов с какой-либо концепцией упорядочивания, что упрощает его распараллеливание в будущем. Я преувеличил свою точку зрения - foreach - это не карта.
foreach на самом деле работает с любым IEnumerable- "подобным" объектом (не столько поправкой к вашему сообщению, сколько интересным примечанием о утином наборе текста): blogs.msdn.com/kcwalina/archive/2007/07/18/DuckNotation.aspxEnumerable.Select имеет перегрузку, которая позволяет получить индекс элемента, поэтому даже необходимость в индексе не требует использования for. См. msdn.microsoft.com/en-us/library/bb534869.aspx
ForEach удобен для удобства чтения и экономии ввода. Однако стоимость имеет значение; изменение 2 или 3 циклов for в пользовательском интерфейсе дизайнера документов, которое я создал, с (для каждого объекта в списке) на (для i = 0 в list.count-1) сократило время отклика с 2-3 секунд на редактирование до примерно. 5 секунд на редактирование небольшого документа, просто перебирая несколько сотен объектов. Теперь даже для огромных документов время на зацикливание всего не увеличивается. Понятия не имею, как это произошло. Что я точно знаю, так это то, что альтернативой была сложная схема, позволяющая зацикливать только подмножество объектов. Я возьму 5-минутную смену в любой день! - Микрооптимизация нет.
@FastAl Разница между производительностью foreach и for для обычных списков составляет доли секунды для итерации по миллионам элементов, поэтому ваша проблема определенно не связана напрямую с производительностью foreach, по крайней мере, для нескольких сотен объектов. Похоже на неработающую реализацию перечислителя в любом списке, который вы использовали.
В случаях, когда вы работаете с коллекцией объектов, лучше использовать foreach, но если вы увеличиваете число, лучше использовать цикл for.
Обратите внимание, что в последнем случае вы можете сделать что-то вроде:
foreach (int i in Enumerable.Range(1, 10))...
Но он, конечно, не работает лучше, он на самом деле имеет худшую производительность по сравнению с for.
«Лучше» можно спорить: он медленнее, и отладчик dnspy не взламывает C# foreach (хотя отладчик VS2017 будет). Иногда более читабельный, но если вы поддерживаете языки без него, это может раздражать.
Оба будут работать почти одинаково. Напишите какой-нибудь код, чтобы использовать оба, а затем покажите ему IL. Он должен показать сопоставимые вычисления, что означает отсутствие разницы в производительности.
Компилятор распознает циклы foreach, используемые для массивов / списков IL и т. д., И меняет их на циклы for.
Покажите ему строчки непонятного доказательство, что это нормально, и попросите его доказательства, что это не нормально.
Каждый раз, когда возникают споры по поводу производительности, вам просто нужно написать небольшой тест, чтобы вы могли использовать количественные результаты в поддержку своего дела.
Используйте класс StopWatch и для точности повторите что-нибудь несколько миллионов раз. (Это может быть сложно без цикла for):
using System.Diagnostics;
//...
Stopwatch sw = new Stopwatch()
sw.Start()
for(int i = 0; i < 1000000;i ++)
{
//do whatever it is you need to time
}
sw.Stop();
//print out sw.ElapsedMilliseconds
Скрестив пальцы, результаты этого шоу показывают, что разница незначительна, и вы можете просто делать любые результаты в наиболее удобном для сопровождения коде.
Но вы не можете сравнивать производительность for и foreach. Их предполагается использовать в разных обстоятельствах.
Я согласен с вами, Майкл, вам не следует выбирать, какой из них использовать, исходя из производительности - вы должны выбрать тот, который имеет наибольшее значение! Но если ваш босс говорит: «Не используйте for, потому что он медленнее, чем foreach», то это единственный способ убедить его, что разница незначительна.
«(Это может быть сложно без цикла for)» Или вы можете использовать цикл while.
Это нелепо. Нет веских причин для запрета цикла for, с точки зрения производительности или чего-то еще.
См. Блог Джона Скита для теста производительности и других аргументов.
Обновленная ссылка: codeblog.jonskeet.uk/2009/01/29/…
Конструкция цикла, которая работает быстрее, зависит от того, что вам нужно перебрать. Еще один блог, в котором сравнивается несколько итераций с несколькими типами объектов., например DataRows и настраиваемые объекты. Он также включает производительность конструкции цикла While, а не только конструкции цикла for и foreach.
По крайней мере, я не видел, чтобы кто-либо из моих коллег или более высокопоставленных лиц говорил это, это просто смешно, учитывая тот факт, что нет значительной разницы в скорости между for и foreach. То же самое применимо, если он просит использовать его во всех случаях!
Кажется немного странным полностью запретить использование чего-то вроде цикла for.
Есть интересная статья здесь, в которой рассказывается о многих различиях в производительности между двумя циклами.
Я бы сказал, что лично я нахожу foreach более читабельным по сравнению с циклами for, но вы должны использовать лучшее для выполняемой работы и не писать слишком длинный код для включения цикла foreach, если цикл for более уместен.
Существенная цитата из статьи, на которую вы ссылаетесь: «... если вы планируете писать высокопроизводительный код, не предназначенный для коллекций, используйте цикл for. Даже для коллекций foreach может показаться удобным при использовании, но не настолько эффективным».
В большинстве случаев разницы действительно нет.
Обычно вам всегда нужно использовать foreach, когда у вас нет явного числового индекса, и вам всегда нужно использовать, когда у вас фактически нет итерируемой коллекции (например, итерация по двумерной сетке массива в верхнем треугольнике) . В некоторых случаях у вас есть выбор.
Можно возразить, что поддерживать циклы for будет немного сложнее, если в коде начнут появляться магические числа. Вы должны быть правы, когда раздражаетесь из-за невозможности использовать цикл for и должны создавать коллекцию или использовать лямбда-выражение для создания вложенной коллекции только потому, что циклы for запрещены.
Есть очень веские причины для предпочитатьforeach зацикливания по петлям for. Если вы можете использовать петлю foreach, ваш босс прав в том, что вы должны.
Однако не каждая итерация просто просматривает список по порядку. Если он запрещающий для, да, это неправильно.
На вашем месте я бы сделал превратить все ваши естественные циклы for в рекурсию. Это его научит, а также для вас это хорошее умственное упражнение.
Как рекурсия сравнивается с циклами for и циклами foreach с точки зрения производительности?
По-разному. Если вы используете хвостовую рекурсию и ваш компилятор достаточно умен, чтобы заметить, он может быть идентичным. OTOH: Если этого не произойдет, и вы сделаете что-то глупое, например, передадите много несекретных (неизменяемых) данных в качестве параметров или объявите большие структуры в стеке как локальные, это может быть очень медленным (или даже не хватить RAM).
Аааа. Я понимаю, почему вы спросили об этом сейчас. Этот ответ направлен на совершенно другой вопрос. По какой-то причудливой причине Джонатан Сэмпсон вчера объединил их. Он действительно не должен был этого делать. Объединенные ответы здесь не будут иметь никакого смысла.
Неважно, будет ли for быстрее, чем foreach. Я серьезно сомневаюсь, что выбор одного из них существенно повлияет на вашу производительность.
Лучший способ оптимизировать ваше приложение - профилировать фактический код. Это определит методы, на которые приходится больше всего работы / времени. Оптимизируйте их в первую очередь. Если производительность по-прежнему не приемлема, повторите процедуру.
Как правило, я бы рекомендовал держаться подальше от микрооптимизаций, так как они редко приносят значительный выигрыш. Единственное исключение - оптимизация выявленных горячих путей (т.е. если при профилировании выявляются несколько часто используемых методов, может иметь смысл их обширная оптимизация).
Если бы единственный вид оптимизации, который мне нужно было делать в проектах, над которыми я работаю, - это микро-оптимизации, я был бы счастливым туристом. К сожалению, это не так.
for немного быстрее, чем foreach. Я бы серьезно возражал против этого утверждения. Это полностью зависит от базовой коллекции. Если класс связанного списка предоставляет индексатор с целочисленным параметром, я бы ожидал, что использование цикла for для него будет O (n ^ 2), в то время как foreach, как ожидается, будет O (n).
@Merhdad: На самом деле это хороший момент. Я как раз думал о обычном случае индексации списка (т.е. массива). Я перефразирую, чтобы отразить это. Спасибо.
@Mehrdad Afshari: Индексирование коллекции по целому числу может быть на много медленнее, чем перечисление по ней. Но на самом деле вы сравниваете использование forа также для поиска в индексаторе с использованием самого foreach. Я думаю, что ответ @Brian Rasmussen верен, что, помимо любого использования с коллекцией, for всегда будет немного быстрее, чем foreach. Однако for плюс поиск в коллекции всегда будет медленнее, чем foreach сам по себе.
@Daniel: Либо у вас есть простой массив, для которого оба будут генерировать идентичный код, либо при использовании оператора for задействован индексатор. Обычный цикл for с целочисленной управляющей переменной несопоставим с foreach, так что его нет. Я понимаю, что имеет в виду @Brian, и, как вы говорите, это правильно, но ответ может ввести в заблуждение. Re: последнее замечание: нет, на самом деле for по сравнению с List<T> все еще быстрее, чем foreach.
@ Mehrdad - Конечно, с LinkedList вы бы написали что-то вроде for (var node = list.Head; node! = Null; node = node.Next), если вы хотите использовать цикл for, сделав его O (n).
@RichardOD: класс связанного списка, который я представлял себе, абстрагирует от вас узлы, и вы можете получить доступ к элементам с номером или с foreach;) В любом случае, моя точка зрения, строго говоря, заключается в том, что я не знаю базовой коллекции и того, как она работает , нельзя сказать, что один быстрее другого. Это все равно, что сказать, что метод A() быстрее, чем B(), не имея представления о том, что они делают.
Вы действительно можете накрутить его голову и вместо этого использовать закрытие IQueryable .foreach:
myList.ForEach(c => Console.WriteLine(c.ToString());
Я бы заменил вашу строку кода на myList.ForEach(Console.WriteLine).
«Есть ли какие-нибудь аргументы, которые я мог бы использовать, чтобы убедить его, что цикл for приемлем для использования?»
Нет, если ваш босс занимается микроменеджментом до уровня, говорящего вам, какие конструкции языка программирования использовать, вы действительно ничего не можете сказать. Простите.
Я думаю, что for в большинстве случаев немного быстрее, чем foreach, но это действительно упускает суть. Одна вещь, о которой я не упоминал, заключается в том, что в сценарии, о котором вы говорите (то есть в веб-приложении большого объема), разница в производительности между for и foreach не будет иметь никакого отношения к производительности сайта. Вы будете ограничены временем запроса / ответа и временем БД, а не v. Foreach.
Тем не менее, я не понимаю вашего отвращения к foreach. На мой взгляд, foreach обычно более понятен в ситуации, когда можно использовать любой из них. Я обычно прибегаю к ситуациям, когда мне нужно пройти по коллекции каким-то уродливым, нестандартным способом.
Мне нравится управлять вещами по индексу. В цикле foreach вы должны вместо этого установить переменную и увеличивать ее внутри foreach, если вам нужен счетчик. Я думаю, если вам не нужен индекс, тогда меня устраивает foreach.
Это действительно так. Я просто считаю, что в большинстве сценариев я хочу «что-то сделать» с каждым элементом. Если вам нужно знать, где вы находитесь в коллекции, это определенно способ.
Это то, что вы делаете внутри - цикл, который влияет на производительность, а не фактическую конструкцию цикла (при условии, что ваш случай нетривиален).
У каждой языковой конструкции есть подходящее время и место для использования. Есть причина, по которой в языке C# есть четыре отдельных операторы итерации - каждый существует для определенной цели и имеет соответствующее применение.
Я рекомендую сесть со своим начальником и попытаться рационально объяснить, почему петля for имеет цель. Бывают случаи, когда итерационный блок for более четко описывает алгоритм, чем итерация foreach. Когда это правда, их уместно использовать.
Я также хотел бы указать вашему боссу - производительность не является и не должна быть проблемой с практической точки зрения - это скорее вопрос выражения алгоритма в краткой, содержательной, поддерживаемой манере. Подобные микрооптимизации полностью упускают из виду оптимизацию производительности, поскольку любое реальное повышение производительности будет происходить от алгоритмической модернизации и рефакторинга, а не от реструктуризации цикла.
Если после рационального обсуждения эта авторитарная точка зрения все еще сохраняется, решать вам, как действовать дальше. Лично я не был бы счастлив работать в среде, где рациональное мышление не поощряется, и подумал бы о переходе на другую должность у другого работодателя. Тем не менее, я настоятельно рекомендую обсудить это, прежде чем расстраиваться - возможно, это просто недоразумение.
Это должно вас спасти:
public IEnumerator<int> For(int start, int end, int step) {
int n = start;
while (n <= end) {
yield n;
n += step;
}
}
Использовать:
foreach (int n in For(1, 200, 4)) {
Console.WriteLine(n);
}
Для большего выигрыша вы можете взять в качестве параметров трех делегатов.
Одно крошечное отличие состоит в том, что цикл for обычно пишется так, чтобы исключить конец диапазона (например, 0 <= i < 10). Parallel.For также делает это, чтобы его можно было легко заменить обычным шлейфом for.
В моем проекте в Windows Mobile я использовал цикл for для контрольной коллекции. На 20 элементов управления ушло 100 ms! Цикл foreach использовал только 4 ms. Это была проблема с производительностью ...
Начиная с .NET Framework 4, вы также можете использовать Parallel.For и Parallel.ForEach, как описано здесь: Многопоточный цикл C# с Parallel.For или Parallel.ForEach
Обратите внимание, что ответы только по ссылке не приветствуются, ответы SO должны быть конечной точкой поиска решения (по сравнению с еще одной остановкой ссылок, которые со временем устаревают). Пожалуйста, рассмотрите возможность добавления здесь отдельного синопсиса, сохранив ссылку в качестве справочной.
Я нашел цикл foreach, который повторяется через ListБыстрее. См. Результаты моих тестов ниже. В приведенном ниже коде я повторяю array размером 100, 10000 и 100000 отдельно, используя цикл for и foreach для измерения времени.

private static void MeasureTime()
{
var array = new int[10000];
var list = array.ToList();
Console.WriteLine("Array size: {0}", array.Length);
Console.WriteLine("Array For loop ......");
var stopWatch = Stopwatch.StartNew();
for (int i = 0; i < array.Length; i++)
{
Thread.Sleep(1);
}
stopWatch.Stop();
Console.WriteLine("Time take to run the for loop is {0} millisecond", stopWatch.ElapsedMilliseconds);
Console.WriteLine(" ");
Console.WriteLine("Array Foreach loop ......");
var stopWatch1 = Stopwatch.StartNew();
foreach (var item in array)
{
Thread.Sleep(1);
}
stopWatch1.Stop();
Console.WriteLine("Time take to run the foreach loop is {0} millisecond", stopWatch1.ElapsedMilliseconds);
Console.WriteLine(" ");
Console.WriteLine("List For loop ......");
var stopWatch2 = Stopwatch.StartNew();
for (int i = 0; i < list.Count; i++)
{
Thread.Sleep(1);
}
stopWatch2.Stop();
Console.WriteLine("Time take to run the for loop is {0} millisecond", stopWatch2.ElapsedMilliseconds);
Console.WriteLine(" ");
Console.WriteLine("List Foreach loop ......");
var stopWatch3 = Stopwatch.StartNew();
foreach (var item in list)
{
Thread.Sleep(1);
}
stopWatch3.Stop();
Console.WriteLine("Time take to run the foreach loop is {0} millisecond", stopWatch3.ElapsedMilliseconds);
}
После предложения @jgauffin я использовал код @johnskeet и обнаружил, что цикл for с array быстрее, чем следующий,
См. Результаты моих тестов и код ниже,

private static void MeasureNewTime()
{
var data = new double[Size];
var rng = new Random();
for (int i = 0; i < data.Length; i++)
{
data[i] = rng.NextDouble();
}
Console.WriteLine("Lenght of array: {0}", data.Length);
Console.WriteLine("No. of iteration: {0}", Iterations);
Console.WriteLine(" ");
double correctSum = data.Sum();
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < Iterations; i++)
{
double sum = 0;
for (int j = 0; j < data.Length; j++)
{
sum += data[j];
}
if (Math.Abs(sum - correctSum) > 0.1)
{
Console.WriteLine("Summation failed");
return;
}
}
sw.Stop();
Console.WriteLine("For loop with Array: {0}", sw.ElapsedMilliseconds);
sw = Stopwatch.StartNew();
for (var i = 0; i < Iterations; i++)
{
double sum = 0;
foreach (double d in data)
{
sum += d;
}
if (Math.Abs(sum - correctSum) > 0.1)
{
Console.WriteLine("Summation failed");
return;
}
}
sw.Stop();
Console.WriteLine("Foreach loop with Array: {0}", sw.ElapsedMilliseconds);
Console.WriteLine(" ");
var dataList = data.ToList();
sw = Stopwatch.StartNew();
for (int i = 0; i < Iterations; i++)
{
double sum = 0;
for (int j = 0; j < dataList.Count; j++)
{
sum += data[j];
}
if (Math.Abs(sum - correctSum) > 0.1)
{
Console.WriteLine("Summation failed");
return;
}
}
sw.Stop();
Console.WriteLine("For loop with List: {0}", sw.ElapsedMilliseconds);
sw = Stopwatch.StartNew();
for (int i = 0; i < Iterations; i++)
{
double sum = 0;
foreach (double d in dataList)
{
sum += d;
}
if (Math.Abs(sum - correctSum) > 0.1)
{
Console.WriteLine("Summation failed");
return;
}
}
sw.Stop();
Console.WriteLine("Foreach loop with List: {0}", sw.ElapsedMilliseconds);
}
Это очень плохой тест. а) вы делаете слишком мало итераций, чтобы получить окончательный ответ. б) Этот Thread.Sleep на самом деле не будет ждать одну миллисекунду. Используйте тот же метод, что и Джон Скит в своем ответе.
99,99% времени определенно тратится в thread.sleep (что не дает никаких гарантий относительно того, насколько быстро он вернется, за исключением того, что он не вернется по крайней мере до этого времени). Зацикливание происходит очень быстро, а сон очень медленный, вы не используете последнее для проверки первого.
Мне нужно было выполнить синтаксический анализ некоторых больших данных с помощью трех вложенных циклов (на List<MyCustomType>). Я подумал, используя сообщение Роба Фонсека-Энсора выше, было бы интересно время и сравнить разницу, используя для и для каждого.
Разница была: Foreach (три вложенных foreach, например, foreach {foreach {forech {}}} выполняли задание за 171,441 секунд, тогда как as for (for {for {for {}}}) выполняли его за 158,616 секунд.
Теперь 13 секунд - это сокращение 13% времени на это, что в некоторой степени важно для меня. Однако foreach определенно более читабелен, чем использование трех индексированных for ...
Здесь есть те же два ответа, что и на большинство вопросов "что быстрее":
1) Если вы не измеряете, вы не знаете.
2) (Потому что ...) Это зависит от обстоятельств.
Это зависит от того, насколько дорогостоящий метод MoveNext () по сравнению с тем, насколько дорог метод this [int index] для типа (или типов) IEnumerable, который вы будете повторять.
Ключевое слово foreach является сокращением для серии операций: оно вызывает GetEnumerator () один раз в IEnumerable, вызывает MoveNext () один раз за итерацию, выполняет некоторую проверку типов и так далее. Скорее всего, на измерения производительности повлияет стоимость MoveNext (), поскольку она вызывается O (N) раз. Может это дешево, а может и нет.
Ключевое слово for выглядит более предсказуемым, но внутри большинства циклов for вы найдете что-то вроде collection [index]. Это похоже на простую операцию индексации массива, но на самом деле это вызов метода, стоимость которого полностью зависит от характера коллекции, которую вы повторяете. Наверное, дешево, а может и нет.
Если базовая структура коллекции - это, по сути, связанный список, MoveNext очень дешевый, но индексатор может иметь стоимость O (N), что делает истинную стоимость цикла «for» O (N * N).
Бен Уотсон, автор книги «Написание высокопроизводительного кода .NET»:
"Will these optimization matter to your program? Only if your program is CPU bound and collection iteration is a core part of your processing. As you'll see, there are ways you can hurt your performance if you're not careful, but that only matters if it was a significant part of your program to begin with. My philosophy is this: most people never need to know this, but if you do, then understanding every layer of the system is important so that you can make intelligent choices".
Наиболее исчерпывающее объяснение можно найти здесь: http://www.codeproject.com/Articles/844781/Digging-Into-NET-Loop-Performance-Bounds-checking
Я упомянул об этих деталях, основываясь на скорости сбора в for и foreach.
List -For Loop немного быстрее, чем Foreach Loop
ArrayList - For Loop примерно в 2 раза быстрее, чем Foreach Loop.
Массив - оба работают с одинаковой скоростью, но цикл по каждому элементу кажется немного быстрее.
Я столкнулся со случаем, когда foreach намного быстрее, чем For
почему foreach быстрее, чем цикл for при чтении строк richtextbox
У меня был случай, похожий на OP в этом вопросе.
Текстовое поле, читающее около 72 КБ строк, и я обращался к свойству Lines (которое на самом деле является методом получения). (И, по-видимому, часто в winforms есть методы получения, которые не являются O (1). Я полагаю, что это O (n), поэтому чем больше текстовое поле, тем больше времени требуется для получения значения из этого `` свойства ''. А в for У меня был цикл, поскольку у OP был for(int i=0;i<textBox1.lines.length;i++) str=textBox1.Lines[i], и он действительно был довольно медленным, поскольку он читал все текстовое поле каждый раз, когда читал строку, плюс он читал все текстовое поле каждый раз, когда проверял условие.
Джон Скит показывает, что вы можете сделать это, обращаясь к свойству Lines только один раз (даже не один раз за итерацию, только один раз). Скорее, чем дважды на каждой итерации (а это тонна раз). Сделайте строку [] strarrlines = textBox1.Lines; и пропустите строчки.
Но, безусловно, цикл for в довольно интуитивно понятной форме и доступ к свойству Lines очень неэффективен.
for (int i = 0; i < richTextBox.Lines.Length; i++)
{
s = richTextBox.Lines[i];
}
для текстового поля или расширенного текстового поля это очень медленно.
OP, тестируя этот цикл на расширенном текстовом поле, обнаружил, что «с 15000 строками. Для цикла for потребовалось 8 минут, чтобы просто выполнить цикл до 15000 строк. Тогда как foreach потребовалось доли секунды для его перечисления».
OP по этой ссылке обнаружил, что этот foreach намного более эффективен, чем его (тот же OP) цикл for, упомянутый выше. Как и я.
String s=String.Empty;
foreach(string str in txtText.Lines)
{
s=str;
}
вы можете прочитать об этом в Deep .NET - часть 1, итерация
он охватывает результаты (без первой инициализации) от исходного кода .NET до разборки.
например - Итерация массива с циклом foreach:

internal static void Test()
{
int LOOP_LENGTH = 10000000;
Random random = new Random((int)DateTime.Now.ToFileTime());
{
Dictionary<int, int> dict = new Dictionary<int, int>();
long first_memory = GC.GetTotalMemory(true);
var stopWatch = Stopwatch.StartNew();
for (int i = 0; i < 64; i++)
{
dict.Add(i, i);
}
for (int i = 0; i < LOOP_LENGTH; i++)
{
for (int k = 0; k < dict.Count; k++)
{
if (dict[k] > 1000000) Console.WriteLine("Test");
}
}
stopWatch.Stop();
var last_memory = GC.GetTotalMemory(true);
Console.WriteLine($"Dictionary for T:{stopWatch.Elapsed.TotalSeconds}s\t M:{last_memory - first_memory}");
GC.Collect();
}
{
Dictionary<int, int> dict = new Dictionary<int, int>();
long first_memory = GC.GetTotalMemory(true);
var stopWatch = Stopwatch.StartNew();
for (int i = 0; i < 64; i++)
{
dict.Add(i, i);
}
for (int i = 0; i < LOOP_LENGTH; i++)
{
foreach (var item in dict)
{
if (item.Value > 1000000) Console.WriteLine("Test");
}
}
stopWatch.Stop();
var last_memory = GC.GetTotalMemory(true);
Console.WriteLine($"Dictionary foreach T:{stopWatch.Elapsed.TotalSeconds}s\t M:{last_memory - first_memory}");
GC.Collect();
}
{
Dictionary<int, int> dict = new Dictionary<int, int>();
long first_memory = GC.GetTotalMemory(true);
var stopWatch = Stopwatch.StartNew();
for (int i = 0; i < 64; i++)
{
dict.Add(i, i);
}
for (int i = 0; i < LOOP_LENGTH; i++)
{
foreach (var item in dict.Values)
{
if (item > 1000000) Console.WriteLine("Test");
}
}
stopWatch.Stop();
var last_memory = GC.GetTotalMemory(true);
Console.WriteLine($"Dictionary foreach values T:{stopWatch.Elapsed.TotalSeconds}s\t M:{last_memory - first_memory}");
GC.Collect();
}
{
List<int> dict = new List<int>();
long first_memory = GC.GetTotalMemory(true);
var stopWatch = Stopwatch.StartNew();
for (int i = 0; i < 64; i++)
{
dict.Add(i);
}
for (int i = 0; i < LOOP_LENGTH; i++)
{
for (int k = 0; k < dict.Count; k++)
{
if (dict[k] > 1000000) Console.WriteLine("Test");
}
}
stopWatch.Stop();
var last_memory = GC.GetTotalMemory(true);
Console.WriteLine($"list for T:{stopWatch.Elapsed.TotalSeconds}s\t M:{last_memory - first_memory}");
GC.Collect();
}
{
List<int> dict = new List<int>();
long first_memory = GC.GetTotalMemory(true);
var stopWatch = Stopwatch.StartNew();
for (int i = 0; i < 64; i++)
{
dict.Add(i);
}
for (int i = 0; i < LOOP_LENGTH; i++)
{
foreach (var item in dict)
{
if (item > 1000000) Console.WriteLine("Test");
}
}
stopWatch.Stop();
var last_memory = GC.GetTotalMemory(true);
Console.WriteLine($"list foreach T:{stopWatch.Elapsed.TotalSeconds}s\t M:{last_memory - first_memory}");
GC.Collect();
}
}
Словарь для T: 10.1957728s M: 2080
Словарь foreach T: 10.5900586s M: 1952
Словарь для каждого значения T: 3.8294776s M: 2088
список для T: 3.7981471s M: 320
list foreach T: 4.4861377s M: 648
Разница все еще существует. В частности, массивы должны быть такими же быстрыми при использовании foreach, но для всего остального простые циклы быстрее. Конечно, в большинстве случаев это не имеет значения, и, конечно же, умный JIT-компилятор теоретически может устранить разницу.