Saya refactoring perpustakaan saya untuk digunakan Span<T>
untuk menghindari alokasi tumpukan jika mungkin tetapi karena saya juga menargetkan kerangka kerja yang lebih tua saya menerapkan beberapa solusi cadangan umum juga. Tapi sekarang saya menemukan masalah aneh dan saya tidak yakin apakah saya menemukan bug di .NET Core 3 atau saya melakukan sesuatu yang ilegal.
Masalah:
// This returns 1 as expected but cannot be used in older frameworks:
private static uint ReinterpretNew()
{
Span<byte> bytes = stackalloc byte[4];
bytes[0] = 1; // FillBytes(bytes);
// returning bytes as uint:
return Unsafe.As<byte, uint>(ref bytes.GetPinnableReference());
}
// This returns garbage in .NET Core 3.0 with release build:
private static unsafe uint ReinterpretOld()
{
byte* bytes = stackalloc byte[4];
bytes[0] = 1; // FillBytes(bytes);
// returning bytes as uint:
return *(uint*)bytes;
}
Cukup menarik, ReinterpretOld
bekerja dengan baik di .NET Framework dan .NET Core 2.0 (jadi saya bisa senang dengan itu semua), masih, itu sedikit mengganggu saya.
Btw. ReinterpretOld
dapat diperbaiki juga di .NET Core 3.0 dengan modifikasi kecil:
//return *(uint*)bytes;
uint* asUint = (uint*)bytes;
return *asUint;
Pertanyaan saya:
Apakah ini bug atau ReinterpretOld
bekerja dalam kerangka kerja lama hanya secara tidak sengaja dan haruskah saya menerapkan perbaikannya juga untuk mereka?
Catatan:
- Membangun debug bekerja juga di .NET Core 3.0
- Saya mencoba menerapkan
[MethodImpl(MethodImplOptions.NoInlining)]
untukReinterpretOld
tetapi tidak berpengaruh.
sumber
return Unsafe.As<byte, uint>(ref bytes[0]);
ataureturn MemoryMarshal.Cast<byte, uint>(bytes)[0];
- tidak perlu digunakanGetPinnableReference()
; melihat ke dalam bit lainnya,Span<T>
melakukan kompilasi ke IL berbeda. Saya tidak berpikir Anda melakukan sesuatu yang tidak valid: Saya menduga ada bug JIT.stackalloc
(yaitu tidak menghapus ruang yang dialokasikan)Jawaban:
Ooh, ini adalah penemuan yang menyenangkan; apa yang terjadi di sini adalah bahwa lokal Anda semakin dioptimalkan - tidak ada penduduk setempat yang tersisa, yang berarti tidak ada
.locals init
, yang berartistackalloc
berperilaku berbeda , dan tidak menghapus ruang;menjadi:
Saya pikir saya akan senang mengatakan bahwa ini adalah bug penyusun, atau setidaknya: efek samping dan perilaku yang tidak diinginkan mengingat bahwa keputusan sebelumnya telah dibuat untuk mengatakan "emit the .locals init" , khusus untuk mencoba dan tetap
stackalloc
waras - tetapi apakah orang-orang kompilator setuju terserah mereka.Solusinya adalah: perlakukan
stackalloc
ruang sebagai tidak terdefinisi (yang, adil, adalah apa yang Anda maksudkan untuk dilakukan); jika Anda mengharapkannya nol: secara manual nol.sumber
locals init
. Bagus.maxstack
dan.locals
, membuatnya sangat mudah untuk tidak menyadari bahwa itu ada / tidak ada :)The content of the newly allocated memory is undefined.
menurut MSDN. Spesifikasi tidak mengatakan bahwa memori juga harus di-zeroed. Jadi sepertinya itu hanya bekerja pada kerangka kerja lama secara tidak sengaja, atau sebagai akibat dari perilaku non-kontrak.