Ma question est donc la suivante: pourquoi le résultat de l'appel de Vector2.Normalize (v) passe-t-il de <0,9750545, -0,22196561> à <0,97505456, -0,22196563> après l'avoir appelé 34 fois?
Alors d'abord - pourquoi le changement se produit. La modification est observée car le code qui calcule ces valeurs change également.
Si nous entrons dans WinDbg au début des premières exécutions du code et descendons un peu dans le code qui calcule le Normalize
vecteur ed, nous pourrions voir l'assemblage suivant (plus ou moins - j'ai coupé certaines parties):
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]
et après ~ 30 exécutions (plus sur ce nombre plus tard), ce serait le 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
Différents opcodes, différentes extensions - SSE vs AVX et, je suppose, avec différents opcodes, nous obtenons une précision différente des calculs.
Alors maintenant, plus sur le pourquoi? .NET Core (pas sûr de la version - en supposant 3.0 - mais il a été testé en 2.1) a quelque chose qui s'appelle "compilation JIT à plusieurs niveaux". Ce qu'il fait, c'est qu'au début, il produit du code qui est généré rapidement, mais qui n'est peut-être pas super optimal. Ce n'est que plus tard, lorsque le runtime détecte que le code est très utilisé, qu'il passe du temps supplémentaire à générer du nouveau code, plus optimisé. Il s'agit d'une nouveauté dans .NET Core, de sorte qu'un tel comportement pourrait ne pas être observé plus tôt.
Aussi pourquoi 34 appels? C'est un peu étrange car je m'attendrais à ce que cela se produise autour de 30 exécutions car c'est le seuil auquel la compilation à plusieurs niveaux entre en jeu. La constante peut être vue dans le code source de coreclr . Il y a peut-être une certaine variabilité supplémentaire au moment où il entre en jeu.
Juste pour confirmer que c'est le cas, vous pouvez désactiver la compilation à plusieurs niveaux en définissant la variable d'environnement en émettant set COMPlus_TieredCompilation=0
et en vérifiant à nouveau l'exécution. L'effet étrange a disparu.
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>
Est-ce prévu ou s'agit-il d'un bogue dans la langue / le runtime?
Un bogue a déjà été signalé à ce sujet - Problème 1119