IT

C #에서 부울의 크기는 얼마입니까?

lottoking 2020. 6. 24. 07:20
반응형

C #에서 부울의 크기는 얼마입니까? 실제로 4 바이트가 필요합니까?


바이트 배열과 부울 배열을 가진 두 개의 구조체가 있습니다.

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

그리고 다음 코드는

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

그 결과 다음과 같은 결과가 나옵니다.

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

boolean4 바이트의 저장 공간이 필요한 것 같습니다 . 이상적으로는 boolean1 비트 ( false또는 true, 0또는 1등) 만 사용합니다.

여기서 무슨 일이 일어나고 있습니까? 는 IS boolean유형은 매우 비효율적 정말?


부울 유형은 언어 런타임 사이에는 많은 호환되지 않는 선택과 체크 무늬 역사를 가지고 있습니다. 이것은 C 언어를 발명 한 데니스 리치 (Dennis Ritchie)가 디자인 한 역사적인 디자인 선택으로 시작되었습니다. 부울 유형 이 없었 습니다. 대안은 0 값이 false를 나타내고 다른 값이 true 로 간주 된 int 입니다 .

이 선택은 pinvoke를 사용하는 주된 이유 인 Winapi에서 수행되었으며 BOOLC 컴파일러의 int 키워드에 대한 별칭 인 typedef 있습니다. 명시적인 [MarshalAs] 속성을 적용하지 않으면 C # bool 이 BOOL로 변환되어 4 바이트 길이의 필드가 생성됩니다.

무엇을 하든지 struct 선언은 당신이 사용하는 언어로 만든 런타임 선택과 일치해야합니다. 언급했듯이 winapi의 경우 BOOL이지만 대부분의 C ++ 구현에서는 바이트를 선택했지만 대부분의 COM 자동화 interop은 짧은 VARIANT_BOOL을 사용합니다 .

C # 실제 크기 bool는 1 바이트입니다. CLR의 강력한 디자인 목표는 찾을 수 없다는 것입니다. 레이아웃은 프로세서에 너무 의존하는 구현 세부 사항입니다. 프로세서는 변수 유형 및 정렬에 매우 까다롭기 때문에 잘못된 선택은 성능에 크게 영향을 미치고 런타임 오류를 일으킬 수 있습니다. .NET은 레이아웃을 발견 할 수 없도록하여 실제 런타임 구현에 의존하지 않는 범용 유형 시스템을 제공 할 수 있습니다.

다시 말해, 레이아웃을 정리하려면 런타임에 항상 구조를 마샬링해야합니다. 이때 내부 레이아웃에서 interop 레이아웃으로 변환됩니다 . 레이아웃이 동일하면 매우 빠르며 필드를 다시 정렬해야 할 때 속도가 느릴 수 있습니다. 왜냐하면 항상 구조체의 복사본을 만들어야하기 때문입니다. 이 기술 용어는 blittable PInvoke를 마샬은 단순히 포인터를 전달 할 수 있기 때문에 네이티브 코드에 blittable 구조체를 통과하는 것은 빠르다.

부울 이 단일 비트가 아닌 핵심 이유도 성능입니다 . 직접 주소를 지정할 수있는 프로세서는 거의 없으며 가장 작은 단위는 바이트입니다. 추가 명령이 무료로 제공되지 않는 바이트의 비트를 물고기가 필요합니다. 그리고 그것은 결코 원자가 아닙니다.

C # 컴파일러는 그렇지 않으면 1 바이트가 필요하다는 것을 알려주지 않습니다 sizeof(bool). 이것은 필드가 런타임에 몇 바이트를 차지하는 지에 대한 환상적인 예측기는 아니지만 CLR은 .NET 메모리 모델을 구현해야하며 간단한 변수 업데이트가 원자 적이라고 약속합니다 . 따라서 메모리에서 변수를 올바르게 정렬해야 프로세서가 단일 메모리 버스 주기로 변수를 업데이트 할 수 있습니다. 종종 bool은 실제로이 때문에 메모리에 4 또는 8 바이트가 필요합니다. 다음 멤버가 올바르게 정렬 되도록 추가 패딩이 추가되었습니다 .

CLR은 실제로 레이아웃을 발견 할 수 없다는 장점을 가지고 있으며, 클래스의 레이아웃을 최적화하고 필드를 다시 정렬하여 패딩을 최소화 할 수 있습니다. 따라서 bool + int + bool 멤버가있는 클래스가 있으면 1 + (3) + 4 + 1 + (3) 바이트의 메모리가 필요하며 (3)은 패딩입니다. 바이트. 50 % 낭비. 자동 레이아웃은 1 + 1 + (2) + 4 = 8 바이트로 재 배열됩니다. 클래스에만 자동 레이아웃이 있고 구조체에는 기본적으로 순차적 레이아웃이 있습니다.

More bleakly, a bool can require as many as 32 bytes in a C++ program compiled with a modern C++ compiler that supports the AVX instruction set. Which imposes a 32-byte alignment requirement, the bool variable may end up with 31 bytes of padding. Also the core reason why a .NET jitter does not emit SIMD instructions, unless explicitly wrapped, it can't get the alignment guarantee.


Firstly, this is only the size for interop. It doesn't represent the size in managed code of the array. That's 1 byte per bool - at least on my machine. You can test it for yourself with this code:

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

Now, for marshalling arrays by value, as you are, the documentation says:

When the MarshalAsAttribute.Value property is set to ByValArray, the SizeConst field must be set to indicate the number of elements in the array. The ArraySubType field can optionally contain the UnmanagedType of the array elements when it is necessary to differentiate among string types. You can use this UnmanagedType only on an array that whose elements appear as fields in a structure.

So we look at ArraySubType, and that has documentation of:

You can set this parameter to a value from the UnmanagedType enumeration to specify the type of the array's elements. If a type is not specified, the default unmanaged type corresponding to the managed array's element type is used.

Now looking at UnmanagedType, there's:

Bool
A 4-byte Boolean value (true != 0, false = 0). This is the Win32 BOOL type.

So that's the default for bool, and it's 4 bytes because that corresponds to the Win32 BOOL type - so if you're interoperating with code expecting a BOOL array, it does exactly what you want.

Now you can specify the ArraySubType as I1 instead, which is documented as:

A 1-byte signed integer. You can use this member to transform a Boolean value into a 1-byte, C-style bool (true = 1, false = 0).

So if the code you're interoperating with expects 1 byte per value, just use:

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

Your code will then show that as taking up 1 byte per value, as expected.

참고URL : https://stackoverflow.com/questions/28514373/what-is-the-size-of-a-boolean-in-c-does-it-really-take-4-bytes

반응형