Which loop is faster for or foreach in C #?

7

I've read articles from some programming languages that the for loop is faster than the foreach , and was wondering if C # has performance differences? >     

asked by anonymous 12.10.2016 / 23:21

2 answers

12

In terms of performance, the for is faster, but the foreach is more readable.

Ididtestsiterating10,000timesandathousandtimeswithdifferentdatatypes,justtogetanidea.InallcasesForwon.

Iusedthefollowingcode

usingSystem;usingSystem.Collections;usingSystem.Collections.Generic;usingSystem.Diagnostics;usingSystem.Linq;usingSystem.Threading.Tasks;namespaceBanchMark.ForAndForeach{classProgram{privatestaticList<int>lista;privatestaticint[]array;privatestaticHashSet<int>hashSet;staticvoidMain(string[]args){intqtdRegistros=99999;intiterar=10000;CriarCasos(qtdRegistros);ForeachVsFor(iterar);qtdRegistros=99999;iterar=1000;CriarCasos(qtdRegistros);ForeachVsFor(iterar);Console.ReadLine();}privatestaticvoidForeachVsFor(intrepetir){inttest=0;Stopwatchwatch=Stopwatch.StartNew();for(inti=0;i<repetir;i++){foreach(varoinarray){test=i+i;}}watch.Stop();Console.WriteLine($"Foreach/Array {repetir} vezes: {watch.ElapsedMilliseconds}ms");

        watch = Stopwatch.StartNew();
        for (int i = 0; i < repetir ;i++)
        {
            for (int j = 0; j < array.Length;j++)
            {
                test = i + i;
            }
        }
        watch.Stop();
        Console.WriteLine($"For/Array {repetir} vezes: {watch.ElapsedMilliseconds}ms");

        watch = Stopwatch.StartNew();
        for (int i = 0; i < repetir; i++)
        {
            foreach (var o in lista)
            {
                test = i + i;
            }
        }
        watch.Stop();
        Console.WriteLine($"Foreach/Lista {repetir} vezes: {watch.ElapsedMilliseconds}ms");

        watch = Stopwatch.StartNew();
        for (int i = 0; i < repetir; i++)
        {
            for (int j = 0; j < lista.Count; j++)
            {
                test = i + i;
            }
        }
        watch.Stop();
        Console.WriteLine($"For/Lista {repetir} vezes: {watch.ElapsedMilliseconds}ms");

        watch = Stopwatch.StartNew();
        for (int i = 0; i < repetir; i++)
        {
            foreach (var o in hashSet)
            {
                test = i + i;
            }
        }
        watch.Stop();
        Console.WriteLine($"Foreach/HashSet {repetir} vezes: {watch.ElapsedMilliseconds}ms");

        watch = Stopwatch.StartNew();
        for (int i = 0; i < repetir; i++)
        {
            for (int j = 0; j < hashSet.Count; j++)
            {
                test = i + i;
            }
        }
        watch.Stop();
        Console.WriteLine($"For/HashSet {repetir} vezes: {watch.ElapsedMilliseconds}ms");

    }

    private static void CriarCasos(int tamanho)
    {
        Console.WriteLine("***************************************");

        lista = new List<int>(tamanho);
        Random random = new Random();
        Parallel.For(1, tamanho, i =>
        {
            lista.Add(random.Next(tamanho));
        });
        array = lista.ToArray();
        hashSet = new HashSet<int>(lista);
      }
   }
}
    
13.10.2016 / 00:34
11

Before responding, I think it is very important to note that for 99.99% of all applications developed in C #, performance differences between for and foreach should not even be taken into account. It's a huge loss of time to worry about this level of micro-optimization before it actually proves necessary. The premature optimization is the root of all evil in systems engineering, our supreme poet Donald Knuth would say.

That said, answering this question is an interesting exercise and the end result may surprise some. However, we first have to stick to some .NET inherent details before we start testing the performance of the two constructs for iterations:

  • .NET performs optimizations only in RELEASE build
  • .NET performs run-time compilation (JIT)
  • The number of iterations should be large enough
  • You must access the items in the loop's internal code
  • Item 1 is extremely important. If tests run in DEBUG mode, .NET will not apply compiler optimizations, and this can completely test results. In an article about performance of replacing white space in strings test results have turned upside down when run as RELEASE vs DEBUG.

    Item 2 is important as it may adversely affect the first test, or the first runs of each test. The compile time of the code at runtime can be a considerable part of the measurement.

    Item 3 should never be underestimated because with few iterations you can be measuring the time of each statement executed within the loop more than the performance differences between each language construct.

    Item 4 is self-explanatory, but it is important to note that if items are not accessed from the collections being swept inside the loop, the test will not be fair between for and foreach because it always accesses the item in question

    Let's get to the results then! And then for 1,000,000 (one million) iterations (%% of% of% was not executed since the only way to get an item by index is through the for extension method of LINQ and takes a lot time, since sets are not indexed internally by a positional index):

    WecanseethattoscananarraythereisalmostnodifferencebetweenHashSetandElementAt.Thisisbecause.NEToptimizesforeachforarrayssuchthatitisfasterthanfor,sinceweendupneedingtoaccessthearrayitembytheindexatthebeginningoftheloopandintheforeachitalreadycomesdirectlyavailableinlanguageconstruction.Mostpeoplewillbesurprisedbythisresult,especiallyifitcomesfromotherprogramminglanguageswhereforisoneofthefastestbuildsandalmostunbeatabletoimplementiterations.

    Incaseofiterationoftheitemsinalist(foreach)fortook60%ofList<Data>timetoperformthesameoperation.Thisparticularlysurprisedme,asthereisnotmuchexplanationforthistimedifference.Iwillanalyzethefor(machinecode)generatedforeachofthemethodstotrytounderstandwhereallthisdifferenceiscomingfrom(andwillupdatetheanswerifnecessary/interesting).

    However,thisdifferenceisnegligible(forthevastmajorityofapplications)ifweconsiderthat,in1millioniterations,only10mswereaddedtotheruntime.

    Theforeach,assaidbefore,cannotbetestedwithILbecauseforthisamountofiterationsitwouldtakealotoftime(thingofseveralminutesforeachtestcycle).

    Thecompletecodeofthetestprogramfollows:

    classProgram{constintMAX_TEST_DATA_LENGTH=1000000;//1000000;constintMAX_FOR_HASHSET=10000;staticinttestDataLength=MAX_TEST_DATA_LENGTH;staticvoidMain(string[]args){if(args.Length>0)testDataLength=Convert.ToInt32(args[0]);Console.WriteLine("For vs ForEach Peformance\r\n");
            Console.Write(" ■ Inicializando dados de teste com {0} registros", testDataLength);
            watch.Start();
            baseData = new Data[testDataLength];
            for (int i = 0; i < testDataLength; i++) {
                baseData[i] = new Data(i);
            }
            listData = new List<Data>(baseData);
            arrayData = listData.ToArray();
            hashsetData = new HashSet<Data>(baseData);
            DisplayElapsed();
    
            Restart(" ■ Forçando JIT (compilação em runtime)");
            ForArray(); ForEachArray(); ForList(); ForEachList(); ForHashset(); ForEachHashset();
            DisplayElapsed();
    
            for (int i = 1; i <= 5; i++) {
    
                Console.WriteLine("\r\n*** Ciclo {0} de teste:\r\n", i);
    
                // array
                Restart(" ■ For array");
                ForArray(testDataLength);
                DisplayElapsed();
                Restart(" ■ ForEach array");
                ForEachArray(testDataLength);
                DisplayElapsed();
                // List
                Restart(" ■ For List");
                ForList(testDataLength);
                DisplayElapsed();
                Restart(" ■ ForEach List");
                ForEachList(testDataLength);
                DisplayElapsed();
                // Hashset
                Restart(" ■ For HashSet");
                if (testDataLength > MAX_FOR_HASHSET) {
                    Console.Write(" >>> inviável acima de {0} iterações", MAX_FOR_HASHSET);
                } else {
                    ForHashset(testDataLength);
                }
                DisplayElapsed();
                Restart(" ■ ForEach HashSet");
                ForEachHashset(testDataLength);
                DisplayElapsed();
    
            }
    
            Console.WriteLine("\r\nTecle algo para encerrar");
            Console.ReadKey(true);
        }
    
        static Stopwatch watch = new Stopwatch();
    
        static Data[] baseData;
        static Data[] arrayData;
        static List<Data> listData;
        static HashSet<Data> hashsetData;
    
        static void Restart(string msg) {
            Console.Write(msg);
            watch.Restart();
        }
    
        static void DisplayElapsed() {
            var elapsed = watch.Elapsed;
            Console.WriteLine(" >>> duração: {0}ms", elapsed.TotalMilliseconds);
        }
    
        static void ForArray(int iterations = 10) {
            for (int i = 0; i < iterations; i++) {
                var item = arrayData[i];
                if (item.TheInt > MAX_TEST_DATA_LENGTH)
                    throw new InvalidOperationException("Tamanho inválido de iterações");
            }
        }
        static void ForEachArray(int iterations = 10) {
            foreach(var item in arrayData) {
                if (item.TheInt > MAX_TEST_DATA_LENGTH)
                    throw new InvalidOperationException("Tamanho inválido de iterações");
            }
        }
        static void ForList(int iterations = 10) {
            for (int i = 0; i < iterations; i++) {
                var item = listData[i];
                if (item.TheInt > MAX_TEST_DATA_LENGTH)
                    throw new InvalidOperationException("Tamanho inválido de iterações");
            }
        }
        static void ForEachList(int iterations = 10) {
            foreach (var item in listData) {
                if (item.TheInt > MAX_TEST_DATA_LENGTH)
                    throw new InvalidOperationException("Tamanho inválido de iterações");
            }
        }
        static void ForHashset(int iterations = 10) {
            for (int i = 0; i < iterations; i++) {
                var item = hashsetData.ElementAt(i);
                if (item.TheInt > MAX_TEST_DATA_LENGTH)
                    throw new InvalidOperationException("Tamanho inválido de iterações");
            }
        }
        static void ForEachHashset(int iterations = 10) {
            foreach (var item in hashsetData) {
                if (item.TheInt > MAX_TEST_DATA_LENGTH)
                    throw new InvalidOperationException("Tamanho inválido de iterações");
            }
        }
    
        class Data
        {
            public Data(int seed) {
                TheGuid = Guid.NewGuid();
                TheString = string.Format("{0}: {1}", seed, TheGuid);
                TheInt = seed;
                TheByteArray = new byte[1024];
            }
            public Guid TheGuid { get; set; }
            public string TheString { get; set; }
            public int TheInt { get; set; }
            public double TheDouble { get; set; }
            public byte[] TheByteArray { get; set; }
        }
    
    }
    
    Note that the test within each loop is only performed to ensure that the .NET optimizer does not resolve to simply "disappear" with the body of the loop if it determines that nothing is being done inside it.

    / em>

    Conclusion? I believe it depends on each one. I particularly use HashSet whenever I do not need to know the current index of an iteration within the loop code. If you see me doing things like for in the middle of a loop I transform it, no matter, into foreach . Otherwise I forget until var idx = myList.IndexOf(item) exists! : -)

        
    13.10.2016 / 20:30