Quindi la mia domanda è: perché il risultato della chiamata Vector2.Normalize (v) cambia da <0.9750545, -0.22196561> a <0.97505456, -0.22196563> dopo averlo chiamato 34 volte?
Quindi, innanzitutto: perché si verifica il cambiamento. La modifica viene osservata perché cambia anche il codice che calcola quei valori.
Se entriamo in WinDbg nelle prime esecuzioni del codice e andiamo un po 'giù nel codice che calcola il Normalize
vettore ed, potremmo vedere il seguente assembly (più o meno - ho tagliato alcune parti):
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]
e dopo ~ 30 esecuzioni (più su questo numero in seguito) questo sarebbe il codice:
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
Codici operativi diversi, estensioni diverse - SSE vs AVX e, immagino, con codici operativi diversi otteniamo una precisione dei calcoli diversa.
Quindi ora di più sul perché? .NET Core (non sono sicuro della versione - presupponendo 3.0 - ma è stato testato in 2.1) ha qualcosa che si chiama "compilazione JIT a livelli". Quello che fa è all'inizio produce codice che viene generato velocemente, ma potrebbe non essere super ottimale. Solo più tardi, quando il runtime rileva che il codice è molto utilizzato, impiegherà del tempo aggiuntivo per generare un nuovo codice più ottimizzato. Questa è una novità in .NET Core, pertanto tale comportamento potrebbe non essere osservato in precedenza.
Anche perché 34 chiamate? Questo è un po 'strano poiché mi aspetto che ciò accada intorno alle 30 esecuzioni poiché questa è la soglia alla quale entra in gioco la compilazione a livelli. La costante può essere vista nel codice sorgente di coreclr . Forse c'è qualche ulteriore variabilità rispetto a quando entra in gioco.
Solo per confermare che questo è il caso, è possibile disabilitare la compilazione a livelli impostando la variabile ambientale emettendo set COMPlus_TieredCompilation=0
e controllando di nuovo l'esecuzione. Lo strano effetto è sparito.
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>
È previsto o è un bug nella lingua / runtime?
È già stato segnalato un bug per questo - Numero 1119