Отже, моє запитання полягає в тому, чому результат виклику Vector2.Normalize (v) змінюється від <0.9750545, -0.22196561> до <0.97505456, -0.22196563> після виклику його 34 рази?
Отже, спочатку - чому відбувається зміна. Змінене спостерігається, оскільки змінюється і код, який обчислює ці значення.
Якщо ми ввірвемося в WinDbg на початку перших виконань коду і трохи підемо вниз до коду, який обчислює Normalize
вектор вектора, ми можемо побачити наступну збірку (більш-менш - я скоротив деякі частини):
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]
і після ~ 30 виконання (докладніше про це число згодом) це буде код:
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
Різні опкоди, різні розширення - SSE vs AVX і, я думаю, з різними кодами ми отримуємо різну точність обчислень.
Тож тепер докладніше про те, чому? .NET Core (не впевнений у версії - припускаючи 3.0 - але вона була протестована в 2.1) має щось, що називається "багатоярусна компіляція JIT". Що робиться, це на початку, він створює код, який генерується швидко, але може бути не надто оптимальним. Лише пізніше, коли під час виконання роботи виявиться, що код широко використовується, він витратить додатковий час для створення нового, більш оптимізованого коду. Це нова річ у .NET Core, тому така поведінка може не спостерігатися раніше.
Також чому 34 дзвінки? Це дещо дивно, тому що я б очікував, що це станеться близько 30 виконання, оскільки це поріг, при якому починається багаторівнева компіляція. Константа можна побачити у вихідному коді coreclr . Можливо, є якась додаткова мінливість, коли вона починається.
Тільки щоб підтвердити, що це так, ви можете відключити багаторівневу компіляцію, встановивши змінну навколишнього середовища, видавши set COMPlus_TieredCompilation=0
та перевіряючи виконання ще раз. Дивний ефект пропав.
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>
Це очікується, чи це помилка в мові / час виконання?
Про це вже повідомляється про помилку - випуск 1119