Więc moje pytanie brzmi: dlaczego wynik wywołania Vector2.Normalize (v) zmienia się z <0,9750545, -0,22196561> na <0,97505456, -0,22196563> po wywołaniu go 34 razy?
Po pierwsze - dlaczego taka zmiana występuje. Zmieniono to, ponieważ zmienia się również kod obliczający te wartości.
Jeśli włamiemy się do WinDbg wcześnie w pierwszych wykonaniach kodu i przejdziemy nieco do kodu, który oblicza Normalize
wektor ed, możemy zobaczyć następujący zestaw (mniej więcej - wyciąłem niektóre części):
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]
a po ~ 30 wykonaniach (więcej o tym numerze później) będzie to kod:
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
Różne kody, różne rozszerzenia - SSE vs AVX i, jak sądzę, przy różnych kodach otrzymujemy inną precyzję obliczeń.
Więc teraz więcej o tym, dlaczego? .NET Core (nie jestem pewien co do wersji - przy założeniu 3.0 - ale został przetestowany w wersji 2.1) ma coś, co nazywa się „kompilacją warstwowego JIT”. Na początku tworzy kod, który jest generowany szybko, ale może nie być superoptymalny. Dopiero później, gdy środowisko wykonawcze wykryje, że kod jest wysoce wykorzystywany, poświęci trochę czasu na wygenerowanie nowego, bardziej zoptymalizowanego kodu. Jest to nowa rzecz w .NET Core, więc takie zachowanie może nie zostać zaobserwowane wcześniej.
Także dlaczego 34 połączenia? Jest to trochę dziwne, ponieważ spodziewałbym się, że tak się stanie około 30 wykonań, ponieważ jest to próg, przy którym rozpoczyna się kompilacja warstwowa. Stałą można zobaczyć w kodzie źródłowym coreclr . Może jest jakaś dodatkowa zmienność w stosunku do momentu uruchomienia.
Aby potwierdzić, że tak jest, możesz wyłączyć kompilację warstwową, ustawiając zmienną środowiskową, set COMPlus_TieredCompilation=0
ponownie wydając i sprawdzając wykonanie. Dziwny efekt zniknął.
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>
Czy jest to oczekiwane, czy jest to błąd w języku / środowisku wykonawczym?
Zgłoszono już błąd dotyczący tego problemu - numer 1119