Berikut ini adalah program C # .NET Core 3.1 sederhana yang memanggil System.Numerics.Vector2.Normalize()
dalam satu lingkaran (dengan input identik setiap panggilan) dan mencetak vektor yang dinormalisasi yang dihasilkan:
using System;
using System.Numerics;
using System.Threading;
namespace NormalizeTest
{
class Program
{
static void Main()
{
Vector2 v = new Vector2(9.856331f, -2.2437377f);
for(int i = 0; ; i++)
{
Test(v, i);
Thread.Sleep(100);
}
}
static void Test(Vector2 v, int i)
{
v = Vector2.Normalize(v);
Console.WriteLine($"{i:0000}: {v}");
}
}
}
Dan ini adalah output dari menjalankan program itu di komputer saya (terpotong karena singkatnya):
0000: <0.9750545, -0.22196561>
0001: <0.9750545, -0.22196561>
0002: <0.9750545, -0.22196561>
...
0031: <0.9750545, -0.22196561>
0032: <0.9750545, -0.22196561>
0033: <0.9750545, -0.22196561>
0034: <0.97505456, -0.22196563>
0035: <0.97505456, -0.22196563>
0036: <0.97505456, -0.22196563>
...
Jadi pertanyaan saya adalah, mengapa hasil dari panggilan Vector2.Normalize(v)
perubahan dari <0.9750545, -0.22196561>
ke <0.97505456, -0.22196563>
setelah menyebutnya 34 kali? Apakah ini yang diharapkan, atau ini bug dalam bahasa / runtime?
Jawaban:
Jadi pertama - mengapa perubahan terjadi. Perubahan diamati karena kode yang menghitung perubahan nilai-nilai itu juga.
Jika kita membobol WinDbg sejak awal dalam eksekusi kode pertama dan masuk sedikit ke dalam kode yang menghitung
Normalize
vektor ed, kita bisa melihat perakitan berikut (lebih atau kurang - saya telah mengurangi beberapa bagian):dan setelah ~ 30 eksekusi (lebih lanjut tentang nomor ini nanti) ini akan menjadi kode:
Opcode yang berbeda, ekstensi yang berbeda - SSE vs AVX dan, saya kira, dengan opcode yang berbeda kita mendapatkan ketepatan perhitungan yang berbeda.
Jadi sekarang lebih banyak tentang mengapa? .NET Core (tidak yakin tentang versi - dengan asumsi 3.0 - tetapi telah diuji pada 2.1) memiliki sesuatu yang disebut "Kompilasi JIT Tiered". Apa yang dilakukannya adalah pada awalnya menghasilkan kode yang dihasilkan cepat, tetapi mungkin tidak super optimal. Hanya nanti ketika runtime mendeteksi bahwa kode tersebut sangat dimanfaatkan maka akan menghabiskan waktu tambahan untuk menghasilkan kode baru yang lebih dioptimalkan. Ini adalah hal baru di .NET Core sehingga perilaku seperti itu mungkin tidak diamati sebelumnya.
Juga mengapa 34 panggilan? Ini agak aneh karena saya berharap ini terjadi sekitar 30 eksekusi karena ini adalah ambang di mana kompilasi berjenjang dimulai. Konstanta dapat dilihat pada kode sumber coreclr . Mungkin ada beberapa variabilitas tambahan saat tendangan masuk.
Hanya untuk mengonfirmasi bahwa ini masalahnya, Anda dapat menonaktifkan kompilasi berjenjang dengan mengatur variabel lingkungan dengan mengeluarkan
set COMPlus_TieredCompilation=0
dan memeriksa lagi eksekusi. Efek anehnya hilang.Sudah ada bug yang dilaporkan untuk ini - Masalah 1119
sumber