2010-12-03 7 views
7
for (var keyValue = 0; keyValue < dwhSessionDto.KeyValues.Count; keyValue++) 
{...} 


var count = dwhSessionDto.KeyValues.Count; 
for (var keyValue = 0; keyValue < count; keyValue++) 
{...} 

Ich weiß, es gibt einen Unterschied zwischen den beiden, aber ist einer von ihnen schneller als der andere? Ich denke, der zweite ist schneller.Ist eine dieser For-Schleifen schneller als die andere?

+7

Sieht wie ein perfekter Mikro-Benchmark-Kandidat aus. – Oded

+1

Sie haben nicht angegeben, um welche Typen es sich handelt oder was Sie in der Schleife tun. –

+1

@ Jon Skeet: Vielleicht kann das Teil der Antwort sein. –

Antwort

39

Ja, die erste Version ist viel langsamer. Schließlich habe ich nehme an, Sie mit Typen wie dies zu tun hat:

public class SlowCountProvider 
{ 
    public int Count 
    { 
     get 
     { 
      Thread.Sleep(1000); 
      return 10; 
     } 
    } 
} 

public class KeyValuesWithSlowCountProvider 
{ 
    public SlowCountProvider KeyValues 
    { 
     get { return new SlowCountProvider(); } 
    } 
} 

hier Ihre erste Schleife ~ 10 Sekunden in Anspruch nehmen wird, während Ihre zweite Schleife ~ 1 Sekunde dauern.

Natürlich könnten Sie argumentieren, dass die Annahme, dass Sie diesen Code verwenden, ungerechtfertigt ist - aber mein Punkt ist, dass die richtige Antwort von den beteiligten Typen abhängt, und die Frage nicht angibt, was diese Typen sind.

Nun, wenn Sie tatsächlich mit einer Art zu tun, wo KeyValues und Count Zugriff ist billig (was sehr wahrscheinlich ist) Ich würde nicht erwarten, dass viel Unterschied.Wohlgemerkt, ich würde fast immer lieber foreach verwenden, wo möglich:

foreach (var pair in dwhSessionDto.KeyValues) 
{ 
    // Use pair here 
} 

diese Weise können Sie nie die Zählung benötigen. Aber dann hast du auch nicht gesagt, was du in der Schleife machen willst. (Tipp: Um mehr nützliche Antworten zu erhalten, provide more information.)

+6

;-) Schöne, Jon, ich werde versuchen, das nächste Mal genauer zu sein. Andererseits denke ich, dass Sie schlau genug sind zu wissen, dass ich mich nicht mit solchen Typen beschäftige. –

+0

Danke für Ihr Update! –

+1

@Lieven: In der Tat, aber ich weiß nicht, mit welchem ​​Typ * du es zu tun hast, und das kann einen großen Unterschied machen. Auch der Unterschied zwischen 'List ' und einem Array kann in manchen Fällen sehr wichtig sein. –

0

Nein. Es gibt keinen Leistungsunterschied zwischen diesen beiden Schleifen. Mit JIT und Code Optimization macht es keinen Unterschied.

+0

Und was ist, wenn der Körper der Schleife das KeyValues-Mitglied ändert? –

1

Siehe Kommentare für Gründe, warum diese Antwort falsch ist.

Wenn es einen Unterschied gibt, ist es umgekehrt: In der Tat, die erste man könnte schneller sein. Das liegt daran, dass der Compiler erkennt, dass Sie von 0 bis zum Ende des Arrays iterieren, und er kann daher innerhalb der Schleife Begrenzungsüberprüfungen durchführen (d. H. Wenn Sie auf dwhSessionDTo.KeyValues[i] zugreifen).

Ich glaube jedoch, dass der Compiler diese Optimierung nur auf Arrays anwendet, also wird es hier wahrscheinlich keinen Unterschied geben.

+3

Ich glaube nicht, dass die Überprüfung der Grenzen hier vermieden werden kann, weil es für den Körper der Schleife möglich ist, das KeyValues-Mitglied zu ändern – JaredPar

+1

@ Jared: wird der Compiler das nicht erkennen? Ich meine, das gleiche gilt grundsätzlich für * jede * Schleife über Arrays und trotzdem kann der Compiler * diese * Fälle erkennen. –

+2

Ich glaube, die JIT kann für eine lokale, die ein Array ist. In diesem Fall ist jedoch ein lokaler, der ein Mitglied hat, das ein Array ist. Dieser Member kann jederzeit geändert werden (zB Threads) und daher kann der JIT keine Annahmen darüber machen – JaredPar

6

es hängt davon ab, wie schwierig es ist, dwhSessionDto.KeyValues.Count zu berechnen, wenn es nur ein Zeiger auf eine int dann die Geschwindigkeit jeder Version wird gleich sein. Wenn jedoch der Count Wert Bedarf berechnet wird, dann wird es jedes Mal berechnet werden, und daher perfomance behindern.

EDIT - here einig Code zeigen, dass die Bedingung immer

public class Temp 
{ 
    public int Count { get; set; } 
} 

static void Main(string[] args) 
{ 
    var t = new Temp() {Count = 5}; 
    for (int i = 0; i < t.Count; i++) 
    { 
     Console.WriteLine(i); 
     t.Count--; 
    } 
    Console.ReadLine(); 
} 
-bewertet re

Der Ausgang 0, 1, 2 - nur!

+0

Ich glaube nicht, dass es neu berechnet wird. Hast du eine Referenz? – Kugel

+0

Ich denke ich bin richtig - siehe meine Bearbeitung –

+2

@Dean: na und? Sie ändern das zugrunde liegende int, die Schleife verweist auf den zugrunde liegenden int. Das ist nur eine einfache 'cmp/jne' in Assembly, unabhängig davon, ob 'Count' eine Konstante oder eine Variable ist. "Neu berechnen" impliziert etwas Kostspieliges. Die einzige "kostspielige" Sache hier ist das Laden eines Wertes aus dem Cache in ein CPU-Register. –

0

Es gibt keinen Unterschied ist aber, warum dieser Unterschied thereis, können Sie bitte Ihre Ergebnisse veröffentlichen?

, wenn Sie sehen die Umsetzung von Einsatz Artikel in Dictionary Reflektor

private void Insert(TKey key, TValue value, bool add) 
{ 
int freeList; 
if (key == null) 
{ 
    ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); 
} 
if (this.buckets == null) 
{ 
    this.Initialize(0); 
} 
int num = this.comparer.GetHashCode(key) & 0x7fffffff; 
int index = num % this.buckets.Length; 
for (int i = this.buckets[index]; i >= 0; i = this.entries[i].next) 
{ 
    if ((this.entries[i].hashCode == num) && this.comparer.Equals(this.entries[i].key, key)) 
    { 
     if (add) 
     { 
      ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AddingDuplicate); 
     } 
     this.entries[i].value = value; 
     this.version++; 
     return; 
    } 
} 
if (this.freeCount > 0) 
{ 
    freeList = this.freeList; 
    this.freeList = this.entries[freeList].next; 
    this.freeCount--; 
} 
else 
{ 
    if (this.count == this.entries.Length) 
    { 
     this.Resize(); 
     index = num % this.buckets.Length; 
    } 
    freeList = this.count; 
    this.count++; 
} 
this.entries[freeList].hashCode = num; 
this.entries[freeList].next = this.buckets[index]; 
this.entries[freeList].key = key; 
this.entries[freeList].value = value; 
this.buckets[index] = freeList; 
this.version++; 

}

Count ist ein inneres Element dieser Klasse, die jedes Element erhöht wird Sie ein Element in Wörterbuch einfügen

also glaube ich, dass es überhaupt keinen Unterschied gibt.

0

Die zweite Version kann manchmal schneller sein. Der Punkt ist, dass die Bedingung nach jeder Iteration neu bewertet wird, wenn also z. der Getter von "Count" zählt tatsächlich die Elemente in einem IEnumerable oder interagiert eine Datenbank/etc, dies verlangsamt die Dinge.

Also würde ich sagen, dass, wenn Sie nicht den Wert von "Count" in der "für" beeinflussen, die zweite Version sicherer ist.

1

Es ist unmöglich zu sagen, ohne die Implementierung von dwhSessionDto.KeyValues.Count und den Schleifenkörper zu kennen.

Angenommen eine globale Variable bool foo = false; und dann folgende Implementierungen:

/* Loop body... */ 
{ 
    if(foo) Thread.Sleep(1000); 
} 

/* ... */ 
public int Count 
{ 
    get 
    { 
     foo = !foo;    
     return 10; 
    } 
} 
/* ... */ 

Nun wird die erste Schleife doppelt so schnell in etwa durchführt, wie die zweiten, D

jedoch unter der Annahme, nicht-moronic Implementierung des der zweite ist wahrscheinlicher schneller.

Verwandte Themen