Bir boole In C # boyutu nedir? Gerçekten 4 bayt alır mı?


137

Bayt ve boole dizilerinden oluşan iki yapım var:

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;
}

Ve aşağıdaki kod:

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();
    }
}

Bu bana şu çıktıyı verir:

sizeof array of bytes: 3
sizeof array of bools: 12

Görünüşe göre boolean4 bayt depolama gerekiyor. İdeal olarak boolean sadece bir bit ( falseveya true, 0veya 1, vb.)

Burada ne oluyor? Mı booleantip yüzden verimsiz gerçekten?


7
Bu, devam eden bekletme nedenleri savaşındaki en ironik çatışmalardan biridir: John ve Hans'ın iki mükemmel yanıtı, bu sorunun cevapları gerçekler, referanslar yerine neredeyse tamamen fikirlere dayanma eğiliminde olsa da, veya özel uzmanlık.
TaW

12
@TaW: Tahminimce yakın oylar cevaplardan değil, soruyu ilk ortaya koyduklarında OP'nin orijinal tonundan kaynaklandı - açıkça bir kavga başlatmayı amaçladılar ve açıkça silinen yorumlarda bunu gösterdi. Hamurun çoğu halının altına süpürüldü, ancak ne demek istediğime bir göz atmak için revizyon geçmişine bakın.
BoltClock

1
Neden BitArray kullanmıyorsunuz?
ded '

Yanıtlar:


242

Bool tipi dil çalışma zamanları arasında birçok uyumsuz seçenek ile damalı bir geçmişi vardır. Bu, C dilini icat eden Dennis Ritchie tarafından yapılan tarihi bir tasarım seçimi ile başladı. Bir bool tipi yoktu , alternatif, 0 değerinin yanlış olduğu ve başka herhangi bir değerin doğru olduğu düşünülen int idi .

Bu seçim, pinvoke kullanmanın birincil nedeni olan Winapi'de, BOOLC derleyicisinin int anahtar sözcüğü için bir diğer ad olan bir typedef'e sahipti . Açık bir [MarshalAs] özniteliği uygulamazsanız, bir C # bool bir BOOL'a dönüştürülür, böylece 4 bayt uzunluğunda bir alan oluşturulur.

Ne yaparsanız yapın, yapı bildiriminizin birlikte çalıştığınız dilde yapılan çalışma zamanı seçimiyle eşleşmesi gerekir. Belirtildiği gibi, winapi için BOOL ancak çoğu C ++ uygulaması bayt seçti , çoğu COM Otomasyonu birlikte çalışması kısa olan VARIANT_BOOL'u kullanıyor .

C # ' ın gerçek boyutu boolbir bayttır. CLR'nin güçlü bir tasarım hedefi, bulamamanızdır. Düzen, işlemciye çok fazla bağlı olan bir uygulama ayrıntısıdır. İşlemciler değişken türleri ve hizalama konusunda çok seçici, yanlış seçimler performansı önemli ölçüde etkileyebilir ve çalışma zamanı hatalarına neden olabilir. Düzeni keşfedilemez hale getirerek, .NET, gerçek çalışma zamanı uygulamasına bağlı olmayan evrensel bir tür sistemi sağlayabilir.

Başka bir deyişle, düzeni düzenlemek için her zaman çalışma zamanında bir yapıyı marshal etmeniz gerekir. Bu sırada dahili mizanpadan birlikte çalışma mizanpajına dönüşüm yapılır. Düzen aynısa çok hızlı olabilir, alanların yeniden düzenlenmesi gerektiğinde yavaş olabilir, çünkü her zaman yapının bir kopyasının oluşturulmasını gerektirir. Bunun için teknik terim blittable , blittable bir yapıyı yerel koda geçirmek hızlıdır çünkü pinvoke marshaller basitçe bir işaretçi geçebilir.

Performans aynı zamanda bir boolün tek bir bit olmamasının temel nedenidir . Biraz doğrudan adreslenebilen birkaç işlemci vardır, en küçük birim bir bayttır. Bir ekstra talimat ücretsiz gelmez byte, bit dışarı balık gereklidir. Ve asla atomik değildir.

C # derleyicisi, aksi takdirde 1 baytlık kullanım gerektiğini bildirmekten çekinmiyor sizeof(bool). Bu, bir alanın çalışma zamanında kaç bayt aldığı için fantastik bir öngörücü değil, CLR'nin de .NET bellek modelini uygulaması gerekiyor ve basit değişken güncellemelerin atomik olduğunu vaat ediyor . Bu, değişkenlerin bellekte düzgün bir şekilde hizalanmasını gerektirir, böylece işlemci tek bir bellek veri yolu döngüsüyle güncelleyebilir. Oldukça sık, bir bool aslında bu nedenle bellekte 4 veya 8 bayt gerektirir. Bir sonraki elemanın düzgün bir şekilde hizalandığından emin olmak için eklenen ekstra dolgu .

CLR aslında yerleşimin keşfedilemez olmasından yararlanır, bir sınıfın düzenini optimize edebilir ve alanları yeniden düzenleyerek dolguların en aza indirilmesini sağlayabilir. Yani, diyelim ki, bool + int + bool üyesi olan bir sınıfınız varsa, 1 + (3) + 4 + 1 + (3) bayt bellek alacaktır, (3) dolgu, toplam 12 bayt. % 50 atık. Otomatik düzen 1 + 1 + (2) + 4 = 8 bayta yeniden düzenlenir. Yalnızca bir sınıfın otomatik düzeni vardır, yapıların varsayılan olarak sıralı düzeni vardır.

Daha kasvetli bir şekilde, bir bool , AVX komut setini destekleyen modern bir C ++ derleyicisiyle derlenen bir C ++ programında 32 bayta kadar gerektirebilir. 32 baytlık bir hizalama gereksinimi getiren bool değişkeni 31 bayt dolgu ile sonuçlanabilir. Ayrıca, bir .NET titreşiminin SIMD talimatları yayınlamamasının temel nedeni, açıkça sarılmadıkça, hizalama garantisini alamaz.



2
İlgilenen ama bilgisiz bir okuyucu için, son paragrafın gerçekten 32 bit veya bit değil mi okuması gerektiğini açıklar mısınız?
Aptal Ucube

3
Tüm bunları neden okuduğumdan emin değilim (bu kadar fazla ayrıntıya ihtiyacım olmadığı için) ama bu büyüleyici ve iyi yazılmış.
Frank V

2
@Silly - bayt . AVX, tek bir komutla 8 kayan nokta değerinde matematik yapmak için 512 bit değişkenleri kullanır. Böyle bir 512 bit değişken 32'ye hizalamayı gerektirir.
Hans Passant

3
Vaov! bir gönderi anlaşılması gereken çok fazla konu verdi. Bu yüzden en iyi soruları okumayı seviyorum.
Chaitanya Gadkari

151

Birincisi, bu sadece birlikte çalışmanın boyutu. Dizinin yönetilen kodundaki boyutu temsil etmez. Bu 1 bayt başına bool- en azından makinemde. Bu kodla kendiniz test edebilirsiniz:

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); 
    } 
}

Şimdi, dizileri değere göre sıralamak için, olduğu gibi, belgeler şöyle diyor:

MarshalAsAttribute.Value özelliği olarak ayarlandığında ByValArray, SizeConst alanı dizideki öğelerin sayısını gösterecek şekilde ayarlanmalıdır. ArraySubTypeAlan, isteğe bağlı olarak içerebilir UnmanagedTypedizgesinin türleri arasında ayırt etmek için gerekli olduğunda, dizi elemanlarının. Bunu UnmanagedTypeyalnızca öğeleri bir yapıdaki alanlar olarak görünen bir dizide kullanabilirsiniz .

Bu yüzden bakıyoruz ArraySubTypeve bunun belgeleri var:

UnmanagedTypeDizinin öğelerinin türünü belirtmek için bu parametreyi numaralandırmadaki bir değere ayarlayabilirsiniz . Bir tür belirtilmezse, yönetilen dizinin öğe türüne karşılık gelen varsayılan yönetilmeyen tür kullanılır.

Şimdi baktığımızda UnmanagedType:

Bool
4 baytlık bir Boolean değeri (true! = 0, false = 0). Bu Win32 BOOL türüdür.

Bu varsayılan booldeğerdir ve 4 bayttır, çünkü bu Win32 BOOL türüne karşılık gelir - bu nedenle bir BOOLdizi bekleyen kodla birlikte çalışıyorsanız, tam olarak istediğinizi yapar.

Şimdi belirtebilirsiniz ArraySubTypeolarak I1yerine olarak belgelenmiştir ki:

1 bayt imzalı bir tam sayı. Bir Boole değerini 1 baytlık, C stili bir boole dönüştürmek için bu üyeyi kullanabilirsiniz (true = 1, false = 0).

Birlikte çalıştığınız kod, değer başına 1 bayt bekliyorsa, şunu kullanın:

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3, ArraySubType = UnmanagedType.I1)]
public bool[] values;

Kodunuz, beklendiği gibi, değer başına 1 bayt yer aldığını gösterir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.