Saya memiliki dua struct dengan array byte dan boolean:
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential, Pack = 4)]
struct struct1
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public byte[] values;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
struct struct2
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public bool[] values;
}
Dan kode berikut:
class main
{
public static void Main()
{
Console.WriteLine("sizeof array of bytes: "+Marshal.SizeOf(typeof(struct1)));
Console.WriteLine("sizeof array of bools: " + Marshal.SizeOf(typeof(struct2)));
Console.ReadKey();
}
}
Itu memberi saya output berikut:
sizeof array of bytes: 3
sizeof array of bools: 12
Tampaknya boolean
dibutuhkan 4 byte penyimpanan. Idealnya sebuah boolean
hanya membutuhkan satu bit ( false
atau true
, 0
atau 1
, dll ..).
Apa yang terjadi disini? Apakah boolean
jenisnya benar-benar tidak efisien?
Jawaban:
The bool jenis memiliki sejarah kotak-kotak dengan banyak pilihan sesuai antara runtimes bahasa. Ini dimulai dengan pilihan desain historis yang dibuat oleh Dennis Ritchie, orang yang menemukan bahasa C. Itu tidak memiliki tipe bool , alternatifnya adalah int di mana nilai 0 mewakili salah dan nilai lainnya dianggap benar .
Pilihan ini dibawa ke depan di Winapi, alasan utama untuk menggunakan pinvoke, ia memiliki typedef
BOOL
yang merupakan alias untuk kata kunci int kompiler C. Jika Anda tidak menerapkan atribut [MarshalAs] eksplisit maka C # bool akan diubah menjadi BOOL, sehingga menghasilkan bidang yang panjangnya 4 byte.Apa pun yang Anda lakukan, deklarasi struct Anda harus sesuai dengan pilihan waktu proses yang dibuat dalam bahasa yang Anda gunakan. Seperti disebutkan, BOOL untuk winapi tetapi sebagian besar implementasi C ++ memilih byte , sebagian besar interop COM Automation menggunakan VARIANT_BOOL yang merupakan singkatan .
The aktual ukuran C #
bool
adalah salah satu byte. Tujuan desain CLR yang kuat adalah bahwa Anda tidak dapat menemukannya. Tata letak adalah detail implementasi yang terlalu bergantung pada prosesor. Prosesor sangat pilih-pilih tentang jenis dan penyelarasan variabel, pilihan yang salah dapat memengaruhi kinerja secara signifikan dan menyebabkan kesalahan waktu proses. Dengan membuat tata letak tidak dapat ditemukan, .NET dapat menyediakan sistem tipe universal yang tidak bergantung pada implementasi runtime yang sebenarnya.Dengan kata lain, Anda harus selalu menyusun struktur pada waktu proses untuk memastikan tata letaknya. Pada saat konversi dari tata letak internal ke tata letak interop dilakukan. Itu bisa sangat cepat jika tata letaknya identik, lambat ketika bidang perlu diatur ulang karena itu selalu membutuhkan pembuatan salinan struct. Istilah teknis untuk ini adalah blittable , meneruskan struct blittable ke kode native dengan cepat karena marshaller pinvoke dapat dengan mudah meneruskan sebuah pointer.
Kinerja juga merupakan alasan utama mengapa bool tidak sedikit pun. Ada beberapa prosesor yang membuat sedikit dapat langsung dialamatkan, unit terkecil adalah byte. Sebuah tambahan instruksi diperlukan untuk ikan sedikit keluar dari byte, yang tidak datang secara gratis. Dan itu tidak pernah atom.
Kompiler C # tidak malu untuk memberitahu Anda bahwa ini membutuhkan 1 byte, gunakan
sizeof(bool)
. Ini masih bukan prediktor yang bagus untuk berapa banyak byte yang dibutuhkan sebuah field saat runtime, CLR juga perlu mengimplementasikan model memori .NET dan menjanjikan bahwa pembaruan variabel sederhana bersifat atomic . Itu membutuhkan variabel untuk diselaraskan dengan benar dalam memori sehingga prosesor dapat memperbaruinya dengan satu siklus bus memori. Cukup sering, bool sebenarnya membutuhkan 4 atau 8 byte dalam memori karena ini. Bantalan ekstra yang ditambahkan untuk memastikan bahwa anggota berikutnya disejajarkan dengan benar.CLR sebenarnya memanfaatkan tata letak yang tidak dapat ditemukan, ini dapat mengoptimalkan tata letak kelas dan mengatur ulang bidang sehingga padding diminimalkan. Jadi, katakanlah, jika Anda memiliki kelas dengan anggota bool + int + bool maka akan membutuhkan 1 + (3) + 4 + 1 + (3) byte memori, (3) adalah padding, dengan total 12 byte. 50% limbah. Tata letak otomatis diatur ulang menjadi 1 + 1 + (2) + 4 = 8 byte. Hanya kelas yang memiliki tata letak otomatis, struct memiliki tata letak berurutan secara default.
Lebih suram lagi, bool dapat memerlukan sebanyak 32 byte dalam program C ++ yang dikompilasi dengan kompiler C ++ modern yang mendukung set instruksi AVX. Yang memberlakukan persyaratan penyelarasan 32-byte, variabel bool mungkin berakhir dengan padding 31 byte. Juga alasan inti mengapa jitter .NET tidak memancarkan instruksi SIMD, kecuali dibungkus secara eksplisit, itu tidak bisa mendapatkan jaminan penyelarasan.
sumber
Pertama, ini hanya ukuran untuk interop. Ini tidak mewakili ukuran dalam kode array yang dikelola. Itu 1 byte per
bool
- setidaknya di komputer saya. Anda dapat mengujinya sendiri dengan kode ini:using System; class Program { static void Main(string[] args) { int size = 10000000; object array = null; long before = GC.GetTotalMemory(true); array = new bool[size]; long after = GC.GetTotalMemory(true); double diff = after - before; Console.WriteLine("Per value: " + diff / size); // Stop the GC from messing up our measurements GC.KeepAlive(array); } }
Sekarang, untuk menyusun array berdasarkan nilai, seperti Anda, dokumentasinya mengatakan:
Jadi kami melihat
ArraySubType
, dan itu memiliki dokumentasi tentang:Sekarang lihatlah
UnmanagedType
, ada:Jadi itu default untuk
bool
, dan itu 4 byte karena itu sesuai dengan jenis Win32 BOOL - jadi jika Anda beroperasi dengan kode yang mengharapkanBOOL
larik, itu melakukan persis apa yang Anda inginkan.Sekarang Anda dapat menentukan
ArraySubType
asI1
, yang didokumentasikan sebagai:Jadi jika kode yang Anda gunakan untuk interoperasional mengharapkan 1 byte per nilai, cukup gunakan:
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3, ArraySubType = UnmanagedType.I1)] public bool[] values;
Kode Anda kemudian akan menunjukkan bahwa mengambil 1 byte per nilai, seperti yang diharapkan.
sumber