Hier ist ein einfaches C # .NET Core 3.1-Programm, das System.Numerics.Vector2.Normalize()
in einer Schleife (mit identischer Eingabe bei jedem Aufruf) aufruft und den resultierenden normalisierten Vektor ausgibt:
using System;
using System.Numerics;
using System.Threading;
namespace NormalizeTest
{
class Program
{
static void Main()
{
Vector2 v = new Vector2(9.856331f, -2.2437377f);
for(int i = 0; ; i++)
{
Test(v, i);
Thread.Sleep(100);
}
}
static void Test(Vector2 v, int i)
{
v = Vector2.Normalize(v);
Console.WriteLine($"{i:0000}: {v}");
}
}
}
Und hier ist die Ausgabe des Ausführens dieses Programms auf meinem Computer (der Kürze halber abgeschnitten):
0000: <0.9750545, -0.22196561>
0001: <0.9750545, -0.22196561>
0002: <0.9750545, -0.22196561>
...
0031: <0.9750545, -0.22196561>
0032: <0.9750545, -0.22196561>
0033: <0.9750545, -0.22196561>
0034: <0.97505456, -0.22196563>
0035: <0.97505456, -0.22196563>
0036: <0.97505456, -0.22196563>
...
Meine Frage ist also, warum sich das Ergebnis des Aufrufs Vector2.Normalize(v)
von <0.9750545, -0.22196561>
nach <0.97505456, -0.22196563>
nach 34-maligem Aufruf von auf ändert . Wird dies erwartet oder ist dies ein Fehler in der Sprache / Laufzeit?
Antworten:
Also zuerst - warum die Änderung auftritt. Die Änderung wird beobachtet, weil sich auch der Code ändert, der diese Werte berechnet.
Wenn wir früh in den ersten Ausführungen des Codes in WinDbg einbrechen und ein wenig in den Code eintauchen, der den
Normalize
ed-Vektor berechnet , können wir die folgende Assembly sehen (mehr oder weniger - ich habe einige Teile gekürzt):und nach ~ 30 Hinrichtungen (mehr zu dieser Nummer später) wäre dies der Code:
Unterschiedliche Opcodes, unterschiedliche Erweiterungen - SSE vs AVX und ich denke, mit unterschiedlichen Opcodes erhalten wir unterschiedliche Genauigkeit der Berechnungen.
Also jetzt mehr über das Warum? .NET Core (nicht sicher über die Version - vorausgesetzt 3.0 - aber es wurde in 2.1 getestet) hat etwas, das als "Tiered JIT Compilation" bezeichnet wird. Am Anfang wird Code generiert, der schnell generiert wird, aber möglicherweise nicht optimal ist. Erst später, wenn die Laufzeit erkennt, dass der Code stark ausgelastet ist, wird zusätzliche Zeit aufgewendet, um neuen, optimierten Code zu generieren. Dies ist eine neue Sache in .NET Core, sodass ein solches Verhalten möglicherweise nicht früher beobachtet wird.
Auch warum 34 Anrufe? Dies ist etwas seltsam, da ich davon ausgehen würde, dass dies bei etwa 30 Ausführungen geschieht, da dies der Schwellenwert ist, ab dem die gestufte Kompilierung einsetzt. Die Konstante ist im Quellcode von coreclr zu sehen . Vielleicht gibt es eine zusätzliche Variabilität, wenn es losgeht.
Um zu bestätigen, dass dies der Fall ist, können Sie die gestufte Kompilierung deaktivieren, indem Sie die Umgebungsvariable festlegen,
set COMPlus_TieredCompilation=0
indem Sie die Ausführung erneut ausgeben und überprüfen. Der seltsame Effekt ist weg.Hierfür wurde bereits ein Fehler gemeldet - Ausgabe 1119
quelle