foreach vs für: Bitte erklären Sie die Assembly Code-Differenz

8

Ich habe kürzlich die Leistung der for-Schleife gegen die foreach-Schleife in C # getestet, und ich habe festgestellt, dass die foreach-Schleife tatsächlich schneller auskommt, wenn man ein Array von Ints zu einem long summiert. Hier ist das vollständige Testprogramm , ich habe Visual Studio 2012, x86, den Veröffentlichungsmodus, Optimierungen verwendet.

Hier ist der Assemblercode für beide Schleifen. Die foreach:

%Vor%

Und das für:

%Vor%

Wie Sie sehen können, besteht die Hauptschleife aus sieben Anweisungen für "foreach" und neun Anweisungen für "for". Dies bedeutet in meinen Benchmarks einen Leistungsunterschied von ca. 10%.

Ich bin jedoch nicht sehr gut darin, Assemblercode zu lesen, und ich verstehe nicht, warum die for-Schleife nicht mindestens so effizient ist wie die foreach. Was ist hier los?

    
Asik 10.01.2013, 16:24
quelle

3 Antworten

8

Da das Array so groß ist, ist der einzige relevante Teil eindeutig der innerhalb der Schleife, dieser:

%Vor%

Da die Summe ein langer int ist, wird sie in zwei verschiedenen Registern gespeichert, nämlich ebx enthält die am wenigsten signifikanten vier Bytes und edi die höchstwertigen vier. Sie unterscheiden sich darin, wie collection [i] (implizit) von int nach long geworfen wird:

%Vor%

Eine weitere wichtige Sache ist, dass die For-Loop-Version die Summe in umgekehrter Reihenfolge ausführt:

%Vor%

Ich kann Ihnen nicht sagen, warum der Compiler diesen Weg anstelle von sum + = temp vorgezogen hat (@EricLippert könnte uns das vielleicht sagen :)), aber ich vermute, dass er mit einigen möglichen Instruktionsabhängigkeiten zusammenhängt.

>     
BlackBear 10.01.2013, 17:26
quelle
5

OK, also hier ist eine kommentierte Version des Assembler-Codes, da Sie sehen werden, dass die Anweisung in der Schleife sehr nahe ist.

%Vor%     
Peter Wooster 10.01.2013 16:50
quelle
-1

Gemäß der C # -Sprachspezifikation 4.0 , eine foreach -Schleife wird vom Compiler folgendermaßen zerlegt:

  

foreach-Anweisung :

     

foreach ( lokaler Variablentyp Bezeichner in Ausdruck ) eingebettete Anweisung

%Vor%

Dies ist nach der folgenden Verarbeitung (wieder aus den Spezifikationen):

  

Wenn der Typ X von Ausdruck ein Array-Typ ist, gibt es eine implizite Referenzkonvertierung von X zur Schnittstelle System.Collections.IEnumerable (da System.Array dies implementiert) Schnittstelle). Der Auflistungstyp ist die System.Collections.IEnumerable -Schnittstelle, der Aufzählungstyp ist die System.Collections.IEnumerator -Schnittstelle und der Elementtyp ist der Elementtyp des Array-Typs X.

Wahrscheinlich ein guter Grund, warum Sie nicht den gleichen Assemblercode vom Compiler sehen.

    
NominSim 10.01.2013 16:32
quelle

Tags und Links