Domanda

In C#/VB.NET/.NET, che il loop viene eseguito più velocemente, for o foreach?

Sin da quando ho letto che una for loop è più veloce di una foreach il ciclo molto tempo fa Ho pensato che stava vera per tutti gli insiemi, insiemi generici, tutte le matrici, etc.

Ho setacciato Google e ho trovato un paio di articoli, ma la maggior parte di loro sono inconcludenti (leggere i commenti su articoli) e indeterminato.

Che cosa sarebbe l'ideale è quello di avere ogni scenario quotate e la migliore soluzione per le stesse.

Per esempio (solo un esempio di come dovrebbe essere):

  1. per la ripetizione di un array di 1000+ corde - for è meglio foreach
  2. per iterare su IList (non generico) stringhe - foreach è meglio di for

Un paio di riferimenti che si trovano sul web per le stesse:

  1. Originale grande vecchio articolo di Emanuele Schanzer
  2. CodeProject FOREACH Vs.PER
  3. Blog - Di foreach o non foreach, che è la domanda
  4. ASP.NET forum - NET 1.1 C# for vs foreach

[Modifica]

A parte la leggibilità aspetto, io sono veramente interessato a fatti e cifre.Ci sono applicazioni in cui l'ultimo miglio di ottimizzazione delle prestazioni spremuto contano.

È stato utile?

Soluzione

Patrick Smacchia bloggato su questo il mese scorso, con le seguenti conclusioni:

  
      
  • per i loop su List sono un po 'più di 2 volte in meno rispetto foreach   loop sulla lista.
  •   
  • Looping sulla matrice è circa 2 volte in meno rispetto loop sulla lista.
  •   
  • Di conseguenza, loop su array usando per è 5 volte più conveniente   di loop sulla lista utilizzando foreach   (Che a mio avviso, è quello che facciamo tutti).
  •   

Altri suggerimenti

foreach loop dimostrano intento più specifico di for loops .

Utilizzando un ciclo IEnumerable dimostra a chiunque di utilizzare il codice che si sta progettando di fare qualcosa per ogni membro di una collezione a prescindere dal suo posto nella collezione. Essa mostra anche che non sta modificando la collezione originale (e genera un'eccezione se si tenta di).

L'altro vantaggio di IList è che funziona su qualsiasi <=>, dove come <=> senso solo per <=>, in cui ogni elemento ha effettivamente un indice.

Tuttavia, se è necessario utilizzare l'indice di un elemento, poi, naturalmente, si dovrebbe essere consentito di utilizzare un <=> ciclo. Ma se non è necessario utilizzare un indice, avere uno è appena ingombrare il vostro codice.

Non ci sono significative implicazioni di prestazioni per quanto ne sono a conoscenza. Ad un certo punto, in futuro potrebbe essere più facile adattare il codice utilizzando <=> di girare su più core, ma che non è qualcosa di cui preoccuparsi in questo momento.

In primo luogo, una domanda riconvenzionale per di Dmitry risposta . Per array, il C # compilatore emette sostanzialmente lo stesso codice per foreach come sarebbe per una spira equivalente for. Questo spiega il motivo per cui per questo benchmark, i risultati sono sostanzialmente gli stessi:

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

Risultati:

For loop: 16638
Foreach loop: 16529

Avanti, convalida che il punto di Greg sul tipo di raccolta essendo importante - cambiare l'array a un List<double> in quanto sopra, e si ottiene i risultati radicalmente diversi. Non solo è significativamente più lento in generale, ma foreach diventa notevolmente più lento di accesso in base all'indice. Detto questo, vorrei ancora quasi sempre preferiscono foreach per un ciclo for in cui rende il codice più semplice - perché la leggibilità è quasi sempre importante, mentre micro-ottimizzazione raramente è

.

Ogni volta che c'è argomenti oltre le prestazioni, è solo bisogno di scrivere un piccolo test in modo da poter utilizzare i risultati quantitativi per sostenere il vostro caso.

Utilizzare la classe cronometro e ripetere qualcosa di un paio di milioni di volte, per la precisione. (Questo potrebbe essere difficile senza un ciclo 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

Dita incrociate i risultati di questa mostra che la differenza è trascurabile, e si potrebbe anche solo fare quello che determina il codice più mantenibile

Sarà sempre vicino.Per un array, a volte for è leggermente più veloce, ma foreach è più espressivo, e offre LINQ, etc.In generale, il bastone con foreach.

Inoltre, foreach possono essere ottimizzati in alcuni scenari.Per esempio, un elenco collegato potrebbe essere terribile da indicizzatore, ma potrebbe essere più veloce di foreach.In realtà, la norma LinkedList<T> non offrono anche un indicizzatore per questo motivo.

La mia ipotesi è che probabilmente non sarà significativo nel 99% dei casi, quindi perché si dovrebbe scegliere il più veloce al posto del più appropriato (come nel più facile da capire / mantenere)?

Non è improbabile che sia una grande differenza di prestazioni tra i due. Come sempre, di fronte a una "che è più veloce?" domanda, si deve sempre pensare "posso misurare questo".

Scrivi due anelli che fanno la stessa cosa al corpo del ciclo, eseguire e ora tutti e due, e vedere che la differenza di velocità è. Fare questo sia un corpo quasi-vuoto, e un corpo del ciclo simile a quello che ci troveremo a fare. Anche provare con il tipo di raccolta che si sta utilizzando, poiché diversi tipi di collezioni possono avere diverse caratteristiche di prestazioni.

Ci sono ottime ragioni per preferire foreach loop sopra for loop. Se è possibile utilizzare un <=> ciclo, il tuo capo è giusto che si dovrebbe.

Tuttavia, non ogni iterazione è semplicemente sta attraversando una lista in ordine ad uno ad uno. Se è proibendo per, sì che è sbagliato.

Se fossi in te, quello che vorrei fare è trasformare tutto il vostro naturale per loop in ricorsione . Sarebbe insegnargli, ed è anche un buon esercizio mentale per voi.

Jeffrey Richter su TechEd 2005:

  

"Sono venuto ad imparare nel corso degli anni il compilatore C # è fondamentalmente un bugiardo a me." .. "Si trova su molte cose." .. "Come quando si esegue un ciclo foreach ..." .." ... che è una piccola linea di codice che si scrive, ma ciò che il compilatore C # sputa fuori per farlo è fenomenale. Si mette fuori un try / finally bloccare in là, all'interno del blocco finally getta la variabile a un'interfaccia IDisposable, e se il cast suceeds chiama il metodo Dispose su di essa, all'interno del ciclo si chiama la proprietà corrente e il metodo MoveNext più volte all'interno del ciclo, gli oggetti vengono creati sotto le coperte. un sacco di gente usa foreach perché è molto semplice codifica, molto facile da fare .. ".." foreach non è molto buona in termini di prestazioni, se ripetuti su una raccolta invece utilizzando piazza staffa notazione, solo facendo indice, che è solo molto più veloce, e non crea alcun oggetto nel mucchio ... "

Webcast su richiesta: http://msevents.microsoft.com/CUI /WebCastEventDetails.aspx?EventID=1032292286&EventCategory=3&culture=en-US&CountryCode=US

Questo è ridicolo. Non c'è alcuna ragione per vietare il ciclo for, prestazioni-saggio o altro.

Jon Skeet per un benchmark delle prestazioni e di altri argomenti.

Nel caso in cui si lavora con una collezione di oggetti, foreach è meglio, ma se si incrementa un numero, una for ciclo è meglio.

Si noti che in quest'ultimo caso, si potrebbe fare qualcosa di simile:

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

Ma certamente non esegue meglio, è in realtà peggiore performance rispetto ad un <=>.

Questo dovrebbe risparmiare:

public IEnumerator<int> For(int start, int end, int step) {
    int n = start;
    while (n <= end) {
        yield n;
        n += step;
    }
}

Usa:

foreach (int n in For(1, 200, 4)) {
    Console.WriteLine(n);
}

Per una maggiore vittoria, si può prendere tre delegati come parametri.

Le differenze di velocità in un for - e foreach - ciclo sono minuscoli quando si sta scorrendo strutture comuni come gli array, liste, ecc, e facendo una query LINQ sulla raccolta è quasi sempre leggermente più lento, anche se è più bello di scrivere! Come gli altri manifesti detto, andare per espressività piuttosto che un millisecondo di prestazioni.

Ciò che non è stato detto finora è che quando un array ciclo viene compilato, viene ottimizzato dal compilatore basato sulla raccolta viene iterazione di. Ciò significa che quando non sei sicuro di quale ciclo da utilizzare, è necessario utilizzare il List<int> loop - che genererà il miglior circuito per voi quando viene compilato. E 'più leggibile anche.

Un altro vantaggio chiave con la myCollection ciclo è che se le modifiche di implementazione della raccolta (da un int List ad un <=> per esempio) e poi il ciclo <=> saranno non richiede alcuna modifica del codice:

foreach (int i in myCollection)

Quanto sopra è la stessa, non importa quale tipo la vostra collezione è, mentre nel vostro <=> ciclo, il seguente non costruire se è stata modificata da un <=> <=> ad un <=>:

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

"Ci sono argomenti ho potuto usare per aiutarmi a convincere il ciclo for è accettabile da usare?"

No, se il vostro capo è micromanaging al livello di dirvi ciò che il linguaggio di programmazione costruisce da usare, non c'è davvero nulla si può dire. Siamo spiacenti.

Probabilmente dipende dal tipo di raccolta che si sta enumerazione e l'attuazione del suo indicizzatore. In generale, comunque, utilizzando foreach è probabile che sia un approccio migliore.

Inoltre, funzionerà con qualsiasi IEnumerable - non solo le cose con gli indicizzatori

.

Questo ha le stesse due risposte come la maggior parte "che è più veloce" domande:

1) Se non si misura, non si sa.

2) (Perché ...) Dipende.

Dipende da come costoso il metodo del "MoveNext ()" è, rispetto a quanto costoso il "questo [int index]" metodo è, per il tipo (o tipi) di IEnumerable che si sarà l'iterazione.

La parola "foreach" è un'abbreviazione per una serie di operazioni - chiama GetEnumerator () una volta sul IEnumerable, chiama MoveNext () una volta per iterazione, fa qualche tipo di controllo, e così via. La cosa più probabile che l'impatto misurazioni delle prestazioni è il costo di MoveNext () dal momento che i tempi ottiene invocato O (N). Forse è a buon mercato, ma forse non lo è.

Il "per" parola chiave sembra più prevedibile, ma dentro la maggior parte "per" loop troverete qualcosa come "collezione [index]". Questo appare come una semplice operazione di allineamento di indicizzazione, ma in realtà è una chiamata di metodo, il cui costo dipende interamente dalla natura della collezione che si sta scorrendo sopra. Probabilmente è a buon mercato, ma forse non lo è.

Se la struttura di base della collezione è essenzialmente una lista collegata, MoveNext è a buon prezzo, ma l'indicizzatore potrebbe avere O (N) costo, rendendo il vero costo di un ciclo "for" O (N * N).

Ogni lingua costrutto ha un tempo e il luogo adatto per l'uso. C'è un motivo per il linguaggio C # è un quattro -. ognuno è lì per uno scopo specifico, e ha un uso appropriato

Vi consiglio di sedersi con il vostro capo e cercando di spiegare razionalmente perché un for ciclo ha uno scopo. Ci sono momenti in cui un blocco foreach iterazione descrive più chiaramente un algoritmo di un'iterazione <=>. Quando questo è vero, è opportuno usarli.

Vorrei anche sottolineare al tuo capo - La prestazione non è, e non dovrebbe essere un problema in alcun modo pratico - è più una questione di espressione l'algoritmo in un significativo maniera succinta, mantenibile. Micro-ottimizzazioni come questo perdere il punto di ottimizzazione delle prestazioni del tutto, dal momento che nessun beneficio reale delle prestazioni verrà da riprogettazione algoritmico e refactoring, senza anello di ristrutturazione.

Se, dopo una discussione razionale, c'è ancora questa visione autoritaria, spetta a voi come a come procedere. Personalmente, non sarei felice di lavorare in un ambiente dove è sconsigliato il pensiero razionale, e sarebbe considerare lo spostamento in un'altra posizione sotto un altro datore di lavoro. Tuttavia, vi consiglio vivamente di discussione prima di arrabbiarsi -. Ci può essere solo un semplice malinteso in atto

E 'quello che si fa all'interno il ciclo che influisce sulle prestazioni, non il costrutto iterativo attuale (supponendo che il caso non è banale).

Sia for è più veloce di foreach è davvero oltre il punto. Dubito seriamente che la scelta di uno sopra l'altro avrà un impatto significativo sulle prestazioni.

Il modo migliore per ottimizzare l'applicazione è attraverso profiling del codice vero e proprio. Che individuare i metodi che rappresentano la maggior parte del lavoro / tempo. Ottimizzare quelli prima. Se le prestazioni sono ancora non è accettabile, ripetere la procedura.

Come regola generale mi sento di raccomandare di stare lontano da micro ottimizzazioni in quanto saranno raramente produrrà alcun vantaggio significativo. Unica eccezione è quando ottimizzando identificato percorsi calde (cioè se il profiling identifica alcuni metodi molto utilizzati, può essere utile per ottimizzare questi ampiamente).

si può leggere su di esso in profonda .NET - parte 1 Iterazione

è coprire i risultati (senza la prima inizializzazione) dal codice sorgente NET fino allo smontaggio.

per esempio - Array Iterazione con un ciclo foreach: entrare image description qui

e - Lista di iterazione con ciclo foreach: entrare image description qui

e il risultato finale: entrare image description qui

 entrare descrizione dell'immagine qui

I due verrà eseguito quasi esattamente nello stesso modo. Scrivere del codice per utilizzare entrambi, poi mostrargli l'IL. Dovrebbe mostrare calcoli comparabili, il che significa nessuna differenza in termini di prestazioni.

per una logica più semplice da implementare in modo che sia più veloce di foreach.

A meno che non sei in uno specifico processo di ottimizzazione della velocità, direi uso a seconda di quale metodo produce il più facile da leggere e conservare il codice.

Se un iteratore è già messa a punto, come con una delle classi di raccolta, poi il foreach è una buona scelta facile. E se si tratta di un campo intero si sta iterazione, poi per è probabilmente più pulito.

Jeffrey Richter ha parlato della differenza di prestazioni tra e foreach su un recente podcast di: http://pixel8.infragistics.com/shows/everything.aspx#Episode:9317

Nella maggior parte dei casi non c'è davvero nessuna differenza.

In genere bisogna sempre utilizzare foreach, quando non si dispone di un indice numerico esplicito, e si deve sempre usare per quando in realtà non hanno una collezione iterabile (ad esempio, l'iterazione su una griglia di matrice bidimensionale in un triangolo superiore). Ci sono alcuni casi in cui si ha una scelta.

Si potrebbe sostenere che per i loop può essere un po 'più difficile da mantenere se i numeri magici iniziano a comparire nel codice. Si dovrebbe essere diritto di essere infastidito per non essere in grado di utilizzare un ciclo for e devono costruire una collezione o utilizzare un lambda per costruire una sottoraccolta invece solo perché per i loop sono stati vietati.

Davvero vite con la testa e andare per la chiusura di un IQueryable .foreach invece:

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

LOL

Ho trovato la foreach ciclo che l'iterazione attraverso un List più veloce . Vedere i miei risultati dei test qui sotto. Nel seguente codice I iterare un array di dimensione 100, 10000 e 100000 separatamente utilizzando for e <=> ciclo per misurare il tempo.

entrare descrizione dell'immagine qui

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

AGGIORNAMENTO

Dopo suggerimento @jgauffin Ho usato @johnskeet codice e ha scoperto che il ciclo con <=> <=> è più veloce di seguito,

  • ciclo Foreach con matrice.
  • Per ciclo con la lista.
  • ciclo foreach con la lista.

Guarda i miei risultati dei test e codice qui sotto,

entrare descrizione dell'immagine qui

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

Non mi aspetto a chiunque di trovare un "enorme" differenza di prestazioni tra i due.

Credo che la risposta dipende dal fatto che la raccolta si sta tentando di accedere dispone di un'implementazione accesso indicizzatore più veloce o un'implementazione accesso IEnumerator più veloce. Dal momento che IEnumerator utilizza spesso l'indicizzatore e solo detiene una copia della posizione di indice corrente, mi aspetterei l'accesso enumeratore di essere almeno altrettanto lento o più lento di accesso dell'indice diretta, ma non di molto.

Naturalmente questa risposta non tiene conto di eventuali ottimizzazioni del compilatore può implementare.

Tieni presente che il ciclo for e foreach loop non sono sempre equivalenti. Elenco enumeratori sarà un'eccezione se i cambiamenti della lista, ma non sarà sempre ottenere che avviso con un normale ciclo for. Si potrebbe anche ottenere un'eccezione diversa se la lista cambia proprio al momento sbagliato.

Mi sembra un po 'strano per vietare del tutto l'uso di qualcosa di simile a un ciclo for.

C'è un articolo interessante qui che copre molte delle differenze di prestazioni tra i due cicli.

Direi che personalmente trovo foreach un po 'più leggibile su cicli for, ma si consiglia di utilizzare il meglio per il lavoro a portata di mano e non dover scrivere codice extra lungo per includere un ciclo foreach se un ciclo è più appropriato.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top