В .NET, какой цикл работает быстрее, «for» или «foreach»?

В C # / VB.NET / .NET, какой цикл работает быстрее, for или foreach ?

С тех пор, как я прочитал, что цикл for работает быстрее, чем цикл foreach я предположил, что это верно для всех коллекций, общих коллекций, всех массивов и т. Д.

Я просмотрел Google и нашел несколько статей, но большинство из них неубедительны (читайте комментарии к статьям) и открытые.

Было бы идеально, чтобы каждый сценарий был указан и наилучшим решением для него.

Например (просто пример того, как это должно быть):

  1. для итерации массива из 1000 строк – for лучше, чем foreach
  2. для итерации по строкам IList (non generic) – foreach лучше, чем for

Несколько ссылок, найденных в Интернете для них:

  1. Оригинальная великая старая статья Эммануэля Санцера
  2. CodeProject FOREACH Vs. ДЛЯ
  3. Блог. Чтобы foreach или не foreach , это вопрос
  4. Форум ASP.NET – NET 1.1 C # for vs foreach

[Редактировать]

Помимо аспекта читабельности, я действительно интересуюсь фактами и цифрами. Есть приложения, где последняя миля оптимизации производительности сжимается.

Патрик Смаккия написал об этом в прошлом месяце со следующими выводами:

  • для циклов в списке немного больше, чем в 2 раза дешевле, чем петли foreach в списке.
  • Looping on array примерно в 2 раза дешевле, чем цикл в List.
  • Как следствие, цикл с использованием массива в 5 раз дешевле, чем цикл в List с использованием foreach (который, как я считаю, является тем, что мы все делаем).

foreach петли демонстрируют более конкретный смысл, чем for циклов .

Использование цикла foreach демонстрирует всем, кто использует ваш код, который вы планируете сделать для каждого члена коллекции независимо от его места в коллекции. Он также показывает, что вы не изменяете исходную коллекцию (и вы пытаетесь сделать исключение).

Другим преимуществом foreach является то, что он работает на любом IEnumerable , где, поскольку это имеет смысл только для IList , где каждый элемент имеет индекс.

Однако, если вам нужно использовать индекс элемента, то, конечно, вам должно быть разрешено использовать цикл for . Но если вам не нужно использовать индекс, то один из них просто загромождает ваш код.

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

Во-первых, встречный иск к ответу Дмитрия . Для массивов компилятор C # испускает в основном тот же код для foreach что и для эквивалента цикла. Это объясняет, почему для этого теста результаты в основном одинаковы:

 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 в приведенном выше, и вы получите радикально разные результаты. В целом это не только значительно медленнее, но foreach становится значительно медленнее, чем доступ по индексу. Сказав это, я все равно почти всегда предпочитаю foreach для цикла for, где он делает код более простым, потому что читаемость почти всегда важна, тогда как микро-оптимизация редко бывает.

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

Используйте class 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 более выразителен, и предлагает LINQ и т. Д. В общем, придерживайтесь foreach .

Кроме того, foreach может быть оптимизирован в некоторых сценариях. Например, связанный список может быть ужасным для индексатора, но он может быть быстрым foreach . Фактически, стандартный LinkedList даже не предлагает индексатора по этой причине.

Я предполагаю, что это, вероятно, не будет значительным в 99% случаев, так почему бы вам выбрать быстрее, чем наиболее подходящий (как проще всего понять / поддерживать)?

Вряд ли будет огромная разница в производительности между ними. Как всегда, когда сталкиваешься с «тем быстрее»? вопрос, вы всегда должны думать: «Я могу измерить это».

Напишите две петли, которые выполняют ту же самую вещь в теле цикла, выполняют и время их обоих, и посмотрите, какая разница в скорости. Сделайте это как с почти пустым телом, так и с телом, похожим на то, что вы на самом деле делаете. Попробуйте также использовать тип коллекции, который вы используете, поскольку различные типы коллекций могут иметь разные характеристики производительности.

Есть очень веские причины, чтобы предпочесть петли foreach for петель. Если вы можете использовать цикл foreach , ваш босс прав, что вам нужно.

Однако не каждая итерация просто перебирает список поочередно. Если он запрещает , да, это неправильно.

Если бы я был вами, я бы превратил все ваши естественные циклы в рекурсию . Это научит его, и это тоже хорошее умственное упражнение для вас.

Джеффри Рихтер на TechEd 2005:

«Я пришел, чтобы узнать, на протяжении многих лет компилятор C # в основном лжец для меня». .. «Это связано с многими вещами». .. «Например, когда вы делаете цикл foreach …» … «… это одна маленькая строка кода, которую вы пишете, но то, что компилятор C # вырывает, чтобы сделать это феноменально. try / finally, там, внутри блока finally, он переводит вашу переменную в IDisposable-интерфейс, и, если листинг преуспевает, он вызывает метод Dispose на нем, внутри цикла он вызывает свойство Current и метод MoveNext многократно внутри цикла, объекты создаются под обложками. Многие люди используют foreach, потому что это очень простое кодирование, очень легко сделать .. «..» foreach не очень хорош с точки зрения производительности, если вы повторили сборку, используя квадрат но только с индексом, это намного быстрее, и он не создает никаких объектов в куче … ”

Веб-трансляция по запросу: http://msevents.microsoft.com/CUI/WebCastEventDetails.aspx?EventID=1032292286&EventCategory=3&culture=en-US&CountryCode=US

Это нелепо. Нет никаких веских оснований для запрета for-loop, производительности или другого.

Смотрите блог Jon Skeet для теста производительности и других аргументов.

В случаях, когда вы работаете с коллекцией объектов, foreach лучше, но если вы увеличиваете число, лучше использовать цикл for .

Обратите внимание: в последнем случае вы можете сделать что-то вроде:

 foreach (int i in Enumerable.Range(1, 10))... 

Но он, конечно, не работает лучше, он на самом деле хуже производительность по сравнению с.

Это должно спасти вас:

 public IEnumerator 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 – и foreach -loop крошечные, когда вы перебираете общие структуры, такие как массивы, списки и т. Д., И выполнение запроса LINQ по коллекции почти всегда немного медленнее, хотя лучше писать! Как говорили другие плакаты, выходите за выразительность, а не за миллисекунду дополнительной производительности.

До сих пор не было сказано, что когда цикл foreach скомпилирован, он оптимизируется компилятором на основе коллекции, которую он итерирует. Это означает, что, когда вы не знаете, какой цикл использовать, вы должны использовать цикл foreach – он будет генерировать лучший цикл для вас, когда он будет скомпилирован. Это более читаемо.

Другим ключевым преимуществом цикла foreach является то, что если ваша реализация коллекции изменяется (например, от array int до List ), то ваш цикл foreach не требует каких-либо изменений кода:

 foreach (int i in myCollection) 

Вышеуказанное одно и то же, независимо от того, какой тип вашей коллекции есть, тогда как в цикле for цикл не будет создан, если вы изменили myCollection из array в List :

 for (int i = 0; i < myCollection.Length, i++) 

«Есть ли какие-либо аргументы, которые я мог бы использовать, чтобы помочь мне убедить его, что цикл for приемлемо использовать?»

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

Вероятно, это зависит от типа собираемой вами коллекции и реализации ее индексатора. В общем, хотя использование foreach , вероятно, будет лучшим подходом.

Кроме того, он будет работать с любым IEnumerable – не просто с индексаторами.

Каждая языковая конструкция имеет подходящее время и место для использования. Существует причина, по которой язык C # имеет четыре отдельных оператора итерации – каждый из них существует для определенной цели и имеет соответствующее применение.

Я рекомендую сесть с вашим боссом и попытаться рационально объяснить, почему for цикла for есть цель. Бывают случаи, когда блок for iteration более четко описывает алгоритм, чем итерация foreach . Если это верно, целесообразно использовать их.

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

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

У этого есть те же два ответа, что и большинство вопросов «что быстрее»:

1) Если вы не измеряете, вы не знаете.

2) (Потому что …) Это зависит.

Это зависит от того, насколько дорогим является метод MoveNext (), относительно того, насколько дорогим является этот метод [int index] для типа (или типов) IEnumerable, который вы будете итерировать.

Ключевое слово «foreach» является сокращением для серии операций – он вызывает GetEnumerator () один раз в IEnumerable, он вызывает MoveNext () один раз на итерацию, выполняет некоторую проверку типов и т. Д. Вещь, которая, скорее всего, повлияет на измерения производительности, – это стоимость MoveNext (), так как она вызывается O (N) раз. Может быть, это дешево, но, возможно, это не так.

Ключевое слово «для» выглядит более предсказуемым, но внутри большинства циклов «для» вы найдете что-то вроде «collection [index]». Это выглядит как простая операция индексирования массива, но на самом деле это вызов метода, стоимость которого полностью зависит от характера коллекции, которую вы повторяете. Наверное, это дешево, но, возможно, это не так.

Если базовая структура коллекции по существу является связанным списком, MoveNext является грязным, но индекс может иметь стоимость O (N), что делает истинную стоимость цикла «for» O (N * N).

Это то, что вы делаете внутри цикла, который влияет на производительность, а не на фактическую конструкцию цикла (при условии, что ваше дело нетривиально).

Ибо это быстрее, чем foreach , действительно, помимо этого. Я серьезно сомневаюсь, что выбор одного над другим существенно повлияет на вашу работу.

Лучший способ оптимизировать ваше приложение – это профилирование фактического кода. Это будет определять методы, которые отвечают наибольшему количеству работы / времени. Сначала оптимизируйте их. Если производительность по-прежнему неприемлема, повторите процедуру.

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

Оба будут работать почти точно так же. Напишите код для использования обоих, затем покажите ему IL. Он должен показывать сопоставимые вычисления, что означает отсутствие различий в производительности.

Я нашел цикл 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 быстрее, чем следующий,

  • Цикл Foreach с массивом.
  • Для цикла со списком.
  • Цикл Foreach со списком.

См. Мои результаты и код теста ниже,

введите описание изображения здесь

 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); } 

для более простой логики для реализации, поэтому она быстрее, чем foreach.

Если вы не выполняете определенный процесс оптимизации скорости, я бы сказал, что использование любого метода дает самый легкий для чтения и поддержания кода.

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

Джеффри Рихтер рассказал о разнице в производительности между for и foreach в недавнем подкасте: http://pixel8.infragistics.com/shows/everything.aspx#Episode:9317

В большинстве случаев нет никакой разницы.

Обычно вам всегда нужно использовать foreach, когда у вас нет явного числового индекса, и вам всегда нужно использовать, когда у вас фактически нет итерируемой коллекции (например, итерации по двумерной решетке массива в верхнем треугольнике) , Есть некоторые случаи, когда у вас есть выбор.

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

На самом деле винт с головой и пойти на IQueryable. Перед закрытием вместо этого:

myList.ForEach (c => Console.WriteLine (c.ToString ());

лол

Я бы не ожидал, что кто-нибудь найдет «огромную» разницу в производительности между этими двумя.

Я думаю, ответ зависит от того, имеет ли коллекция, к которой вы пытаетесь получить доступ, более быструю реализацию доступа индексатора или более быструю реализацию доступа IEnumerator. Since IEnumerator often uses the indexer and just holds a copy of the current index position, I would expect enumerator access to be at least as slow or slower than direct index access, but not by much.

Of course this answer doesn’t account for any optimizations the compiler may implement.

Keep in mind that the for-loop and foreach-loop are not always equivalent. List enumerators will throw an exception if the list changes, but you won’t always get that warning with a normal for loop. You might even get a different exception if the list changes at just the wrong time.

It seems a bit strange to totally forbid the use of something like a for loop.

There’s an interesting article here that covers a lot of the performance differences between the two loops.

I would say personally I find foreach a bit more readable over for loops but you should use the best for the job at hand and not have to write extra long code to include a foreach loop if a for loop is more appropriate.

I did test it a while ago, with the result that a for loop is much faster than a foreach loop. The cause is simple, the foreach loop first needs to instantiate an IEnumerator for the collection.

  • Декодирование длины пробега в MATLAB
  • Микропроцессорность DateTime.DayOfWeek
  • Какие счетчики perfmon полезны для выявления узких мест ASP.NET?
  • Почему в отдельных циклах стигментные добавления намного быстрее, чем в комбинированном цикле?
  • Как изменить язык приложений iPhone во время выполнения?
  • HttpWebRequest очень медленный!
  • Android - предотrotation белого экрана при запуске
  • Почему изменение 0.1f to 0 замедляет производительность на 10x?
  • Почему медленная инструкция цикла? Не удалось ли Intel эффективно внедрить его?
  • Зачем использовать AJAX, когда доступны WebSockets?
  • Анатомия «утечки памяти»
  • Interesting Posts

    Найти устройство в Windows на основе идентификаторов объектов физического устройства (PDO)

    размер растрового изображения превышает бюджетную ошибку Vm

    Как сбросить пароль для Windows 7 с помощью флеш-накопителя?

    Как установить загрузку имени файла в веб-API ASP.NET

    Excel: последнее совпадение символов / строк в строке

    Малиновый PI и экран для ноутбука

    Как исправить черные линии на ЖК-дисплее ноутбука, которые становятся длиннее?

    jQuery getJSON работает локально, но не перекрестный домен

    Что значит !! (двойной восклицательный знак) означает?

    pinpointing “условный переход или перемещение зависит от неинициализированного значения (значений)” valgrind message

    Ошибка создания bean-компонента с именем «entityManagerFactory», определенным в ресурсе пути classа: вызов метода init

    Существуют ли инструменты для обрезки полей PDF?

    rails от многих до многих

    Ключевые события: ProcessCmdKey

    Как показать условие «если» на диаграмме последовательности?

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