Meine Frage ist also, warum sich das Ergebnis des Aufrufs von Vector2.Normalize (v) nach 34 Aufrufen von <0.9750545, -0.22196561> auf <0.97505456, -0.22196563> ändert.
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):
movss xmm0,dword ptr [rax]
movss xmm1,dword ptr [rax+4]
lea rax,[rsp+40h]
movss xmm2,dword ptr [rax]
movss xmm3,dword ptr [rax+4]
mulss xmm0,xmm2
mulss xmm1,xmm3
addss xmm0,xmm1
sqrtss xmm0,xmm0
lea rax,[rsp+40h]
movss xmm1,dword ptr [rax]
movss xmm2,dword ptr [rax+4]
xorps xmm3,xmm3
movss dword ptr [rsp+28h],xmm3
movss dword ptr [rsp+2Ch],xmm3
divss xmm1,xmm0
movss dword ptr [rsp+28h],xmm1
divss xmm2,xmm0
movss dword ptr [rsp+2Ch],xmm2
mov rax,qword ptr [rsp+28h]
und nach ~ 30 Hinrichtungen (mehr zu dieser Nummer später) wäre dies der Code:
vmovsd xmm0,qword ptr [rsp+70h]
vmovsd qword ptr [rsp+48h],xmm0
vmovsd xmm0,qword ptr [rsp+48h]
vmovsd xmm1,qword ptr [rsp+48h]
vdpps xmm0,xmm0,xmm1,0F1h
vsqrtss xmm0,xmm0,xmm0
vinsertps xmm0,xmm0,xmm0,0Eh
vshufps xmm0,xmm0,xmm0,50h
vmovsd qword ptr [rsp+40h],xmm0
vmovsd xmm0,qword ptr [rsp+48h]
vmovsd xmm1,qword ptr [rsp+40h]
vdivps xmm0,xmm0,xmm1
vpslldq xmm0,xmm0,8
vpsrldq xmm0,xmm0,8
vmovq rcx,xmm0
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.
C:\Users\lukas\source\repos\FloatMultiple\FloatMultiple\bin\Release\netcoreapp3.1
λ FloatMultiple.exe
0000: <0,9750545 -0,22196561>
0001: <0,9750545 -0,22196561>
0002: <0,9750545 -0,22196561>
...
0032: <0,9750545 -0,22196561>
0033: <0,9750545 -0,22196561>
0034: <0,9750545 -0,22196561>
0035: <0,97505456 -0,22196563>
0036: <0,97505456 -0,22196563>
^C
C:\Users\lukas\source\repos\FloatMultiple\FloatMultiple\bin\Release\netcoreapp3.1
λ set COMPlus_TieredCompilation=0
C:\Users\lukas\source\repos\FloatMultiple\FloatMultiple\bin\Release\netcoreapp3.1
λ FloatMultiple.exe
0000: <0,97505456 -0,22196563>
0001: <0,97505456 -0,22196563>
0002: <0,97505456 -0,22196563>
...
0032: <0,97505456 -0,22196563>
0033: <0,97505456 -0,22196563>
0034: <0,97505456 -0,22196563>
0035: <0,97505456 -0,22196563>
0036: <0,97505456 -0,22196563>
Wird dies erwartet oder ist dies ein Fehler in der Sprache / Laufzeit?
Hierfür wurde bereits ein Fehler gemeldet - Ausgabe 1119