В .NET какой цикл выполняется быстрее, «для» или «foreach»? - PullRequest
311 голосов
/ 13 декабря 2008

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

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

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

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

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

  1. для итерации массива 1000+ строки - for лучше, чем foreach
  2. для итерации по IList (не универсальным) строкам - foreach лучше чем for

В Интернете найдено несколько ссылок на одно и то же:

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

[Изменить]

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

Ответы [ 40 ]

311 голосов
/ 13 декабря 2008

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

  • для циклов в Списке чуть более чем в 2 раза дешевле, чем foreach петли в списке.
  • Зацикливание в массиве примерно в 2 раза дешевле, чем зацикливание в List.
  • Как следствие, зацикливание на использовании массива в 5 раз дешевле чем зацикливание на List с использованием foreach (что я считаю, это то, что мы все делаем).
156 голосов
/ 22 декабря 2009

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

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

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

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

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

152 голосов
/ 23 января 2009

Сначала встречный иск к ответ Дмитрия . Для массивов компилятор 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, где он упрощает код - потому что читаемость почти всегда важна, тогда как микрооптимизация редко бывает.

50 голосов
/ 22 декабря 2009

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

Используйте класс 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

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

49 голосов
/ 13 декабря 2008

Это всегда будет близко. Для массива иногда for немного быстрее, но foreach более выразителен и предлагает LINQ и т. Д.

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

33 голосов
/ 13 декабря 2008

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

30 голосов
/ 23 января 2009

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

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

30 голосов
/ 22 декабря 2009

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

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

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

16 голосов
/ 03 декабря 2009

Джеффри Рихтер на 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

12 голосов
/ 22 декабря 2009

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

* * * * * * * * * * * * * * * * * * * * * * *.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...