Warum bekomme ich unterschiedliche Ergebnisse mit einer virtuellen oder nicht virtuellen Eigenschaft?

9

Der folgende Code erzeugt beim Ausführen einer Release-Konfiguration unter .NET 4.5 die folgende Ausgabe ...

%Vor%

(Wenn beide Debug-Versionen ausgeführt werden, geben Sie 0.333333343267441 als Ergebnis an.)

Ich kann sehen, dass das Teilen eines Floats durch einen kurzen und das Zurückgeben eines Double in einen Doppelpunkt nach einem bestimmten Punkt wahrscheinlich Müll erzeugt.

Meine Frage ist: Kann jemand erklären, warum die Ergebnisse unterschiedlich sind, wenn die Eigenschaft, die den Kurzschluss im Nenner liefert, virtuell oder nicht virtuell ist?

%Vor%     
IanR 19.08.2014, 10:24
quelle

3 Antworten

1

Ich glaube, James 'Vermutung ist richtig und das ist eine JIT-Optimierung. Das JIT führt eine weniger genaue Teilung durch, wenn es möglich ist, was zu dem Unterschied führt. Im folgenden Codebeispiel werden Ihre Ergebnisse dupliziert, wenn sie im Freigabemodus mit dem x64-Ziel kompiliert und direkt von einer Eingabeaufforderung ausgeführt werden. Ich verwende Visual Studio 2008 mit NET 3.5.

%Vor%

Die Ergebnisse lauten wie folgt:

%Vor%

Die IL ist ziemlich einfach:

%Vor%

Die ersten beiden Fälle sind nahezu identisch. Die IL drückt zuerst die 1 als 32-Bit-Gleitkommazahl auf den Stapel, erhält 3 von einer der beiden Methoden, konvertiert die 3 in eine 32-Bit-Gleitkommazahl, führt die Division durch und konvertiert das Ergebnis anschließend in eine 64-Bit-Gleitkommazahl (doppelt). Die Tatsache, dass (fast) identische IL - der einzige Unterschied ist der Befehl callvirt vs. call - verursacht unterschiedliche Ergebnispunkte direkt am JIT.

Im dritten Fall hat der Compiler die Aufteilung in eine Konstante bereits durchgeführt. Der Befehl div IL wird für diesen Fall nicht ausgeführt.

Im letzten Fall verwende ich eine Parse -Operation, um die Wahrscheinlichkeit zu minimieren, dass die Anweisung optimiert wird (ich würde "prevent" sagen, aber ich weiß nicht genug darüber, was der Compiler macht). Das Ergebnis für diesen Fall entspricht dem Ergebnis des Aufrufs virtual . Es scheint, dass das JIT die nicht-virtuelle Methode entweder optimiert oder die Division auf andere Weise durchführt.

Interessanterweise ist das Ergebnis, wenn Sie die Variable parsedThree eliminieren und einfach den folgenden für den vierten Fall, resultParsed = 1.0f / short.Parse("3") , aufrufen, das gleiche wie im ersten Fall. Auch hier scheint der JIT die Division anders auszuführen, wenn es möglich ist.

    
Mike Cowan 07.11.2014 04:58
quelle
0

Ich habe Ihren Code unter .Net 4.5 getestet Ich bekomme immer die gleichen Ergebnisse beim Ausführen in Visual Studio 2012:
0.3333333333333333 wenn in rel / dbg 32 bit ausgeführt wird 0.3333333343267441 beim Ausführen in Rel / Dbg 64 Bit

Ich erhalte die Ergebnisse, wenn ich die EXE ausführe, ohne sie von der Eingabeaufforderung ohne Visual Studio zu starten, und nur wenn der Code lautet:

  • laufe im 64bit-Modus (ich laufe in Any CPU und der Code wurde ohne die Prefer 32-bit-Prüfung kompiliert)
  • in Version

Die Option Code optimieren macht keinen Unterschied.

Das Einzige, worüber ich nachdenken kann, ist, dass die Verwendung von virtual eine spätere Auswertung des double-Typs erzwingt, so dass die Laufzeit ein 1/3 mit floats macht und dann das Ergebnis verdoppelt, während es bei Nichtverwendung der virtuellen property die Operanden fördert direkt vor der Operation zu verdoppeln

    
Terenzio Berni 21.08.2014 12:35
quelle
0

Es könnte eher eine JITter-Optimierung als eine Compiler-Optimierung sein. Es gibt hier nicht viel, was der Compiler optimieren könnte, aber der JITter könnte die nicht-virtuelle Version leicht inline machen und mit (double) 1.0f / 3 anstatt (double) (1.0f / 3) enden. Sie können sich nicht darauf verlassen, dass Gleitkomma-Ergebnisse genau das sind, was Sie ohnehin erwarten.

    
James 06.11.2014 22:51
quelle

Tags und Links