Итак, мой вопрос: почему результат вызова Vector2.Normalize (v) изменяется с <0.9750545, -0.22196561> на <0.97505456, -0.22196563> после его вызова 34 раза?
Итак, во-первых, почему происходит изменение. Изменения наблюдаются, потому что код, который вычисляет эти значения, тоже изменяется.
Если мы начнем с WinDbg в начале выполнения первого кода и немного углубимся в код, который вычисляет Normalize
вектор ed, мы можем увидеть следующую сборку (более или менее - я сократил некоторые части):
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