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

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

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

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

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

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

  • для итерации массива 1000+ строки - for лучше, чем foreach
  • для итерации более IList (не общих) строк - foreach лучше чем for

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

[изменить]

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

Ответ 1

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

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

Ответ 2

Циклы

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

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

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

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

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

Ответ 3

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

Ответ 4

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

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

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

Ответ 5

Он всегда будет близок. Для массива иногда for немного быстрее, но foreach более выразителен и предлагает LINQ и т.д. В общем, придерживайтесь foreach.

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

Ответ 6

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

Ответ 7

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

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

Ответ 8

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

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

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

Ответ 9

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

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

Ответ 10

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

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

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

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

Ответ 11

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

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

Ответ 12

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

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

Для большей победы вы можете взять трех делегатов в качестве параметров.

Ответ 13

Различия в скорости в for - и a foreach -loop крошечные, когда вы перебираете общие структуры, такие как массивы, списки и т.д., и выполнение запроса 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++)

Ответ 14

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

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

Ответ 15

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

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

Ответ 16

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

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

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

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

Ответ 17

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

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

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

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

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

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

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

Ответ 18

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

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

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

Ответ 19

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

Ответ 20

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

Ответ 21

Я нашел цикл foreach, который повторяется через List быстрее. См. Мои результаты испытаний ниже. В приведенном ниже коде я повторяю a array размером 100, 10000 и 100000 отдельно, используя цикл for и foreach для измерения времени.

enter image description here

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 со списком.

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

enter image description here

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

Ответ 22

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

Ответ 23

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

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

Ответ 25

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

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

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

Ответ 26

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

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

LOL

Ответ 27

Я тестировал это некоторое время назад, в результате цикл for намного быстрее, чем цикл foreach. Причина проста, цикл foreach сначала должен создать экземпляр IEnumerator для коллекции.

Ответ 28

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

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

Конечно, этот ответ не учитывает какие-либо оптимизации, которые может реализовать компилятор.

Ответ 29

Имейте в виду, что петли for-loop и foreach не всегда эквивалентны. Перечислители списков будут генерировать исключение, если список изменится, но вы не всегда получите это предупреждение с нормальным циклом. Возможно, вы даже получите другое исключение, если список изменится в самое неподходящее время.

Ответ 30

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

Здесь есть интересная статья которая покрывает множество различий в производительности между двумя циклами.

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