Какой фрагмент кода даст лучшую производительность? Приведенные ниже сегменты кода были написаны на C #.
1.
for(int counter=0; counter<list.Count; counter++)
{
list[counter].DoSomething();
}
2.
foreach(MyType current in list)
{
current.DoSomething();
}
c#
performance
for-loop
foreach
Ктевар
источник
источник
list
действительно не имеетcount
члена вместоCount
.Ответы:
Ну, отчасти это зависит от конкретного типа
list
. Это также будет зависеть от конкретной среды CLR, которую вы используете.Будет ли это каким-либо образом значительным или нет, будет зависеть от того, выполняете ли вы какую-либо реальную работу в цикле. Практически во всех случаях разница в производительности не будет значительной, но разница в удобочитаемости способствует
foreach
циклу.Я бы лично использовал LINQ, чтобы избежать «если»:
РЕДАКТИРОВАТЬ: Для тех из вас, кто утверждает, что повторение с помощью
List<T>
withforeach
дает тот же код, что иfor
цикл, вот свидетельство того, что это не так:Производит ИЖ из:
Компилятор обрабатывает массивы по- разному, преобразовывая
foreach
цикл в основном вfor
цикл, но не в этом случаеList<T>
. Вот эквивалентный код для массива:Интересно, что я нигде не могу найти это задокументировано в спецификации C # 3 ...
источник
List<T>
.foreach
над массивом эквивалентно вfor
любом случае. Всегда сначала кодируйте для удобочитаемости, а затем выполняйте микрооптимизацию только тогда, когда у вас есть доказательства того, что это дает ощутимое преимущество в производительности.for
Цикл компилируется в код примерно эквивалентно следующему:Где как
foreach
цикл компилируется в код, примерно эквивалентный этому:Итак, как видите, все будет зависеть от того, как реализован перечислитель, и как реализован индексатор списков. Как оказалось, перечислитель для типов на основе массивов обычно записывается примерно так:
Итак, как вы можете видеть, в этом случае это не будет иметь большого значения, однако перечислитель для связанного списка, вероятно, будет выглядеть примерно так:
В .NET вы обнаружите, что класс LinkedList <T> даже не имеет индексатора, поэтому вы не сможете выполнить цикл for в связанном списке; но если бы вы могли, индексатор должен был быть написан так:
Как видите, вызов этого несколько раз в цикле будет намного медленнее, чем использование перечислителя, который может запоминать, где он находится в списке.
источник
Легкий тест для полуутверждения. Я сделал небольшой тест, просто чтобы увидеть. Вот код:
А вот секция foreach:
Когда я заменил на с Еогеаспом - в цикле , была 20 миллисекунд быстрее - последовательно . Значение for было 135–139 мс, а значение foreach - 113–119 мс. Я несколько раз менял местами, чтобы убедиться, что это не какой-то процесс, который просто запустился.
Однако, когда я удалил операторы foo и if, for было быстрее на 30 мс (foreach было 88 мсек, а for было 59 мсек). Оба были пустыми снарядами. Я предполагаю, что foreach действительно передал переменную, в то время как for просто увеличивал переменную. Если бы я добавил
Затем for становится медленным примерно на 30 мс. Я предполагаю, что это было связано с созданием foo, захватом переменной в массиве и присвоением ее foo. Если вы просто обращаетесь к intList [i], вы не получаете этого штрафа.
Честно говоря .. Я ожидал, что foreach будет немного медленнее при любых обстоятельствах, но этого недостаточно, чтобы иметь значение для большинства приложений.
edit: вот новый код с использованием предложений Джонса (134217728 - это самый большой int, который вы можете иметь до того, как будет выбрано исключение System.OutOfMemory):
И вот результаты:
Создание данных. Расчет для цикла: 2458 мс Расчет для каждого цикла: 2005 мс
Их перестановка местами, чтобы увидеть, имеет ли он дело с порядком вещей, дает те же результаты (почти).
источник
Примечание: этот ответ больше относится к Java, чем к C #, поскольку в C # нет индексатора
LinkedLists
, но я думаю, что общая точка все еще сохраняется.Если
list
вы работаете с aLinkedList
, производительность кода индексатора ( доступ в стиле массива ) намного хуже, чем при использованииIEnumerator
from theforeach
для больших списков.Когда вы обращаетесь к элементу 10.000 в a
LinkedList
с использованием синтаксиса индексатора:,list[10000]
связанный список начнется с головного узла и пройдет поNext
указателю десять тысяч раз, пока не достигнет нужного объекта. Очевидно, если вы сделаете это в цикле, вы получите:Когда вы вызываете
GetEnumerator
(неявно используяforach
-syntax), вы получаетеIEnumerator
объект, который имеет указатель на головной узел. Каждый раз, когда вы вызываетеMoveNext
, этот указатель перемещается на следующий узел, например:Как вы можете видеть, в случае
LinkedList
s метод индексатора массива становится все медленнее и медленнее, чем дольше вы выполняете цикл (он должен проходить через один и тот же указатель заголовка снова и снова). АIEnumerable
просто работает в постоянное время.Конечно, как сказал Джон, это действительно зависит от типа
list
, еслиlist
это неLinkedList
массив, а массив, поведение будет совершенно другим.источник
LinkedList<T>
документацию на MSDN, и у нее довольно приличный API. Самое главное, что у него нетget(int index)
метода, как у Java. Тем не менее, я полагаю, что это все еще актуально для любой другой структуры данных в виде списка, которая предоставляет индексатор, который работает медленнее, чем конкретныйIEnumerator
.Как уже упоминали другие люди, хотя производительность на самом деле не имеет большого значения, foreach всегда будет немного медленнее из-за использования
IEnumerable
/IEnumerator
в цикле. Компилятор преобразует конструкцию в вызовы этого интерфейса, и для каждого шага в конструкции foreach вызываются функция + свойство.Это эквивалентное расширение конструкции в C #. Вы можете себе представить, как влияние на производительность может варьироваться в зависимости от реализации MoveNext и Current. В то время как при доступе к массиву у вас нет этих зависимостей.
источник
List<T>
здесь, значит, все еще есть хит (возможно, встроенный) вызова индексатора. Это не похоже на доступ к голому металлическому массиву.Прочитав достаточно аргументов в пользу того, что «цикл foreach должен быть предпочтительнее для удобочитаемости», я могу сказать, что моей первой реакцией было «что»? Читаемость, как правило, субъективна, а в данном случае даже больше. Для кого-то с опытом программирования (практически на всех языках до Java) циклы for читать намного легче, чем циклы foreach. Вдобавок те же люди, утверждающие, что циклы foreach более читабельны, также являются сторонниками linq и других «функций», затрудняющих чтение и поддержку кода, что подтверждает вышесказанное.
О влиянии на производительность см. Ответ на этот вопрос.
РЕДАКТИРОВАТЬ: в C # есть коллекции (например, HashSet), у которых нет индексатора. В этих коллекциях, Еогеасп это единственный способ итерации , и это единственный случай , я думаю , что он должен быть использован в течение для .
источник
Есть еще один интересный факт, который можно легко упустить при тестировании скорости обоих циклов: использование режима отладки не позволяет компилятору оптимизировать код с использованием настроек по умолчанию.
Это привело меня к интересному результату: foreach работает быстрее, чем в режиме отладки. В то время как for ist быстрее, чем foreach в режиме выпуска. Очевидно, что у компилятора есть лучшие способы оптимизации цикла for, чем у цикла foreach, который нарушает несколько вызовов методов. Между прочим, цикл for настолько фундаментален, что вполне возможно, что он даже оптимизирован самим процессором.
источник
В приведенном вами примере определенно лучше использовать
foreach
цикл вместоfor
цикла.Стандартная
foreach
конструкция может быть быстрее (1,5 цикла на шаг), чем простаяfor-loop
(2 цикла на шаг), если цикл не был развернут (1,0 цикла на шаг).Так что для повседневного кода, производительность не является основанием для использования более сложных
for
,while
илиdo-while
конструкции.Посмотрите эту ссылку: http://www.codeproject.com/Articles/146797/Fast-and-Less-Fast-Loops-in-C
источник
вы можете прочитать об этом в Deep .NET - часть 1, итерация
он охватывает результаты (без первой инициализации) от исходного кода .NET до разборки.
например - Итерация массива с циклом foreach:
и - итерация списка с циклом foreach:
и конечные результаты:
источник