Entonces mi pregunta es, ¿por qué el resultado de llamar a Vector2.Normalize (v) cambia de <0.9750545, -0.22196561> a <0.97505456, -0.22196563> después de llamarlo 34 veces?
Primero, por qué ocurre el cambio. El cambio se observa porque el código que calcula esos valores también cambia.
Si entramos en WinDbg al principio de las primeras ejecuciones del código y profundizamos un poco en el código que calcula el Normalize
vector ed, podríamos ver el siguiente ensamblaje (más o menos, he cortado algunas partes):
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]
y después de ~ 30 ejecuciones (más sobre este número más adelante) este sería el código:
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
Diferentes códigos de operación, diferentes extensiones: SSE vs AVX y, supongo, con diferentes códigos de operación obtenemos una precisión diferente de los cálculos.
¿Entonces ahora más sobre el por qué? .NET Core (no estoy seguro de la versión, suponiendo 3.0, pero se probó en 2.1) tiene algo que se llama "compilación JIT por niveles". Lo que hace es que al principio produce código que se genera rápidamente, pero que podría no ser súper óptimo. Solo más tarde, cuando el tiempo de ejecución detecte que el código es altamente utilizado, pasará un tiempo adicional para generar un código nuevo y más optimizado. Esto es algo nuevo en .NET Core, por lo que tal comportamiento podría no haberse observado antes.
¿También por qué 34 llamadas? Esto es un poco extraño, ya que esperaría que esto suceda alrededor de 30 ejecuciones, ya que este es el umbral en el que entra en juego la compilación por niveles. La constante se puede ver en el código fuente de coreclr . Tal vez hay alguna variabilidad adicional cuando se activa.
Solo para confirmar que este es el caso, puede deshabilitar la compilación escalonada configurando la variable de entorno emitiendo set COMPlus_TieredCompilation=0
y comprobando la ejecución nuevamente. El extraño efecto se ha ido.
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>
¿Es esto esperado o es un error en el lenguaje / tiempo de ejecución?
Ya hay un error reportado para esto - Problema 1119