Microsoft MVP성태의 닷넷 이야기
.NET Framework: 774. C# - blittable 타입이란? [링크 복사], [링크+제목 복사]
조회: 2725
글쓴 사람
홈페이지
첨부 파일

C# - blittable 타입이란?

닷넷에서 blittable 타입에 대한 공식 문서는 다음에서 찾아볼 수 있습니다.

Blittable and Non-Blittable Types
; https://docs.microsoft.com/en-us/dotnet/framework/interop/blittable-and-non-blittable-types

위의 문서에 따르면 기존 타입들 중 다음의 것들이 blittable 타입입니다.

System.Byte (byte)
System.SByte (sbyte)
System.Int16 (short)
System.UInt16 (ushort)
System.Int32 (int)
System.UInt32 (uint)
System.Int64 (long)
System.UInt64 (ulong)
System.IntPtr
System.UIntPtr
System.Single (float)
System.Double (double)

이와 함께 다음의 것들도 blittable 타입입니다.

  • One-dimensional arrays of blittable types, such as an array of integers. However, a type that contains a variable array of blittable types is not itself blittable.
  • Formatted value types that contain only blittable types (and classes if they are marshaled as formatted types). For more information about formatted value types, see Default Marshaling for Value Types.

첫 번째 조건을 풀어보면, blittable types의 1차원 배열도 blittable 타입이지만, 반면 배열을 포함한 타입은 blittable 타입이 아니라는 것입니다. 예를 들어 다음의 arr은 blittable 타입의 인스턴스이지만,

void Main()
{
    int [] arr = new int [100];
}

다음의 TestStruct 타입은 배열을 포함하고 있으므로 blittable 타입이 아닙니다.

struct TestStruct
{
    public int Age;       // int만 담고 있다면 TestStruct도 blittable 타입이겠지만.
    public int [] Scores; // 배열을 담고 있으므로 TestStruct는 blittable 타입이 아님.
}

두 번째 조건은 복합 타입(complex type)인 경우에 대해 blittable 조건을 명시하고 있습니다. 멤버로 blittable 타입, 또는 formatted 타입으로 마샬링 가능한 클래스만을 필드로 포함하는 "Formatted value types"라면 그 타입은 blittable이라는 것입니다. "Formatted" + "value types"이기 때문에 기본적으로 class 타입은 무조건 blittable이 아닙니다. 그리고 나머지 "Formatted 타입"이라는 것은 다음의 문서에서 설명하고 있습니다.

Default Marshaling for Value Types
; https://msdn.microsoft.com/en-us/library/4d9a876c-e05a-40ba-bd85-bd22877f984a(v=vs.110)

A formatted type is a complex type that contains information that explicitly controls the layout of its members in memory.

결국 타입이 담고 있는 필드들에 대한 메모리 상의 정보를 가지고 있다면 "A formatted type"입니다. 그리고 그 정보라는 것은 StructLayout 특성과 연결됩니다.

눈으로 확인하는 LayoutKind 옵션 효과
; https://www.sysnet.pe.kr/2/0/1558

정리해 보면, "Formatted value types"는 값 형식 중에서 LayoutKind.Auto가 아닌 형식을 의미합니다. 모든 타입은 LayoutKind가 명시되어 있지 않으면 Auto가 되지만, 특별히 C# 컴파일러는 struct 타입에 한해 (사용자가 지정하지 않았다면) 자동으로 LayoutKind.Sequential을 지정해 줍니다. 따라서 사용자가 굳이 Auto로 지정하지 않은 struct 타입에 대해 오직 blittable 타입만을 필드로 포함하고 있다면 그 formatted value type도 blittable 타입이 됩니다.

따라서 struct인 경우 다음은 blittable이지만,

struct BlittableStructType
{
    public int Age;
}

다음의 struct들은 blittable이 아닙니다.

[StructLayout(LayoutKind.Auto)] // Auto Layout이므로.
struct AutoLayoutStructType
{
    public int Age;
}

struct NonBlittableStructType_HasString
{
    public int Age;
    public string Name; // 참조 타입인 string을 포함
}

struct NonBlittableStructType_HasArray
{
    public int Age;
    public int [] data; // 참조 타입인 배열을 포함
}




일단 문서는 그렇다 치고, 개인적으로 한 가지 혼란스러운 문구가 하나 있습니다.

Formatted value types that contain only blittable types (and classes if they are marshaled as formatted types). For more information about formatted value types, see Default Marshaling for Value Types.

위에서 말하는 "classes"에 속한 타입은 다음과 같은 것들을 의미합니다.

[StructLayout(LayoutKind.Explicit)]
class ExplicitClassType
{
    [FieldOffset(0)]
    public int Age;
}

그리고 문서에 의하면 저런 "classes"들을 포함하는 "Formatted value types"가 blittable하다고 합니다. 예를 들어 다음과 같은 struct가 blittable일 수 있습니다.

struct StructType
{
    public int Age;
    public ExplicitClassType data1;
}

blittable이 되기 위한 기본 조건은 문서의 처음에 언급한 대로,

Most data types have a common representation in both managed and unmanaged memory and do not require special handling by the interop marshaler. These types are called blittable types because they do not require conversion when they are passed between managed and unmanaged code.

해당 타입의 필드들이 managed/unmanaged 메모리에서 동일한 구성을 갖고 있어 "interop marshaler"에 의한 별도 처리가 필요하지 않은 것입니다. 좀 더 간단하게 표현하면, 그 인스턴스가 존재하는 메모리를 포인터로 가리킬 때 그 주소 영역에 모든 필드의 값들이 포함되어 있어야 합니다. 하지만 위의 StructType은 ExplicitClassType이 "marshaled as formatted types"에 속하고 따라서 그것의 메모리 크기까지 모두 계산이 되어 할당은 되지만 그래도 managed <-> unmanaged 간의 마샬링 절차가 필요합니다. 즉, 아래에서 설명할 interop marshaler를 고려해 보면 ExplicitClassType을 포함한 StructType 타입이 blittable 타입이라고 볼 수 없는 것입니다.




비록, 인스턴스의 필드들이 메모리의 연속된 공간에 펼쳐져 있지만 "do not require special handling by the interop marshaler" 조건을 만족시키지 못해 blittable이 아닌 타입도 있습니다. "Blittable and Non-Blittable Types" 문서에도 나오지만,

System.Array
System.Boolean
System.Char
System.Class
System.Object
System.Mdarray
System.String
System.Valuetype
System.Szarray

위의 것들은 Non-Blittable입니다. 이상한 것은 이 중에서 3개(System.Class, System.Mdarray, System.Szarray)는 원래 .NET BCL에 존재하지 않는 타입이고, 또 다른 3개(System.Array, System.Object, System.ValueType)는 딱히 자료형으로 쓰지 않는 타입입니다. 따라서 남은 자료형은 3개인데,

System.Boolean
System.Char
System.String

이게 왜 Non-Blittable이 아닌지 살짝 이해가 안 될 수도 있습니다. 이유는, 문서 상으로 보면 위의 것들은 interop marshaler가 그 중간에서 작업을 해줘야 하기 때문입니다.

우선 System.Boolean의 경우, 1바이트인데 unmanaged 공간에서는 BOOL 타입이 C/C++라면 1 바이트이거나 COM VARIANT에서는 short이기 때문에 중간에 interop marshaler가 변환 처리를 해야 합니다. (심지어 4바이트가 될 때도 있다고 합니다.)

System.Char는 managed 공간에서는 2바이트지만 unmanaged 공간으로 갈 때는 ANSI/UNICODE 2가지 버전으로 나뉠 수 있기 때문에 역시 중간에서 interop marshaler가 관여를 합니다.

마지막으로 System.String은 결국 System.Char의 연속 공간이기 때문에 마찬가지로 interop marshaler가 관여를 합니다. 재미있는 것은 System.String의 경우 설령 interop marshaler가 관여를 하지 않는다 해도 blittable 타입이 될 수 없습니다. 확인을 위해 .NET Reflector로 보면,

[Serializable, ComVisible(true), __DynamicallyInvokable]
public sealed class String : IComparable, ICloneable, IConvertible, IEnumerable, IComparable<string>, IEnumerable<char>, IEquatable<string>
{
    // Fields
    // ...[생략]...
    [NonSerialized]
    private char m_firstChar;
    [NonSerialized]
    private int m_stringLength;
    // ...[생략]...
}

System.String 타입이 포함하고 있는 필드는 char m_firstChar, int m_stringLength일 뿐 문자 배열 자체는 CLR 내부에 의해 숨겨져 있기 때문입니다.

그 이외의 타입들에 대해서는 interop marshaler는 대상 타입이 참조 객체를 포함하지 않고 LayoutKind.Auto만 아니라면 관여하지 않을 수 있습니다. 하지만, 그 이외의 경우라면 관여를 하게 됩니다.

우선 대상 타입이 Sequential이고, 그것이 ("classes if they are marshaled as formatted types" 조건에 해당하는) 참조 객체를 포함하고 있는 경우 managed와 unmanaged 간의 메모리 크기도 상이할뿐더러 필드 배치의 순서까지도 달라질 수 있으므로 당연히 interop marshaler가 중재할 수 밖에 없습니다.

또한 대상 타입이 Explicit 일지라도 managed와 unmanaged 간의 메모리 구조가 동일하다는 것만 빼고는 여전히 managed일 때 참조 타입인 필드에 대해서는 힙 주소만을 포함하고 있다가 unmanaged로 마샬링이 되어야 할 때 interop marshaler의 중재로 메모리가 동일하게 바뀌게 됩니다. 이 때문에 "Formatted value types"가 blittable이 되려면 어찌 되었건 필드로 참조 타입을 가져서는 안됩니다.




그건 그렇고, 혹시 해당 타입이 blittable 타입인지 코드로 알 수 있을까요? 아쉽게도 닷넷 차원에서는 이것을 알 수 있는 직접적인 정보를 Type 클래스에서 제공하지 않습니다. 예전에도 ^^ 값 형식인지 참조 형식인지에 대한 구분도 메타데이터에서 제공하지 않는다고 했었는데요.

닷넷 메타데이터에 struct/class(값/참조 형식)의 구분이 있을까요?
; https://www.sysnet.pe.kr/2/0/10993

검색해 보면, 적당하게 다음의 코드가 눈에 띕니다.

The fastest way to check if a type is blittable?
; https://stackoverflow.com/questions/10574645/the-fastest-way-to-check-if-a-type-is-blittable

BlittableStructs/BlittableStructs/BlittableHelper.cs
; https://github.com/AndreyAkinshin/BlittableStructs/blob/master/BlittableStructs/BlittableHelper.cs

그런데, 제가 정리한 이 글에서의 요구를 만족시키진 않습니다. 왜냐하면 참조 형식(class)도 blittable이라고 반환하는 경우가 있기 때문입니다. 따라서 이 글에서 정리한 조건을 만족하려면 다음과 같이 if 문 하나를 더 추가해야 합니다.

public static class IsBlittableCache<T>
{
    public static bool IsBlittable()
    {
        return IsBlittableCache<T>.Value;
    }

    public static bool IsBlittable(Type type)
    {
        if (type.IsArray)
        {
            var elem = type.GetElementType();
            return elem.IsValueType && IsBlittable(elem);
        }

        if (type.IsValueType == false)
        {
            return false;
        }

        try
        {
            object instance = FormatterServices.GetUninitializedObject(type);
            GCHandle.Alloc(instance, GCHandleType.Pinned).Free();
            return true;
        }
        catch
        {
            return false;
        }
    }

    public static readonly bool Value = IsBlittable(typeof(T));
}

위의 코드로 몇몇 타입을 테스트해 보면 다음과 같은 결과를 얻을 수 있습니다. (아래의 출력에 사용된 타입의 구체적인 정의는 첨부 파일에서 확인할 수 있습니다.)

ClassType: False
SequentialClassType: False
SequentialClassType_HasString: False
ExplicitClassType: False
ExplicitClassType_HasString: False
AutoLayoutStructType: False
BlittableStructType: True
NonBlittableStructType_HasClassWithLayoutInfo: False
NonBlittableStructType_HasClassWithLayoutInfo2: False
NonBlittableStructType_HasClassWithLayoutInfo3: False
NonBlittableStructType_HasString: False
NonBlittableStructType_HasArray: False
System.Boolean: False
System.Char: False
System.Int32[]: True
System.Boolean[]: False
BlittableStructType[]: True
System.String: False

(첨부 파일은 이 글의 예제 코드를 포함합니다.)




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]





[최초 등록일: ]
[최종 수정일: 6/22/2018 ]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer@outlook.com

비밀번호

댓글 쓴 사람
 




... 16  17  18  19  20  21  22  23  24  [25]  26  27  28  29  30  ...
NoWriterDateCnt.TitleFile(s)
11607정성태8/30/20184666Graphics: 2. Unity로 실습하는 Shader
11606정성태8/14/20184744사물인터넷: 19. PC에 연결해 동작하는 자신만의 USB 장치 만들어 보기파일 다운로드1
11605정성태8/9/20182633사물인터넷: 18. New NodeMcu v3 아두이노 호환 보드의 내장 LED 및 입력 핀 사용법파일 다운로드1
11604정성태7/12/20182114Math: 47. GeoGebra 기하 (24) - 정다각형파일 다운로드1
11603정성태7/12/20181955Math: 46. GeoGebra 기하 (23) - sqrt(n) 제곱근파일 다운로드1
11602정성태7/11/20182055Math: 45. GeoGebra 기하 (22) - 반전기하학의 원에 관한 반사변환파일 다운로드1
11601정성태7/11/20182255Math: 44. GeoGebra 기하 (21) - 반전기하학의 직선 및 원에 관한 반사변환파일 다운로드1
11600정성태7/10/20182403Math: 43. GeoGebra 기하 (20) - 세 점을 지나는 원파일 다운로드1
11599정성태7/10/20181918Math: 42. GeoGebra 기하 (19) - 두 원의 안과 밖으로 접하는 직선파일 다운로드1
11598정성태7/10/20181858Windows: 147. 시스템 복구 디스크를 USB 디스크에 만드는 방법
11597정성태8/9/20182224사물인터넷: 17. Thinary Electronic - ATmega328PB 아두이노 호환 보드의 개발 환경 구성
11596정성태7/10/20182009기타: 72. 과거의 용어 설명 - OWIN
11595정성태11/20/20184829사물인터넷: 16. New NodeMcu v3 아두이노 호환 보드의 기본 개발 환경 구성
11594정성태7/8/20182577Math: 41. GeoGebra 기하 (18) - 원의 중심 및 접선파일 다운로드1
11593정성태7/8/20182235Math: 40. GeoGebra 기하 (17) - 각의 복사파일 다운로드1
11591정성태7/7/20182143Math: 39. GeoGebra 기하 (16) - 삼각형의 방심과 방접원파일 다운로드1
11590정성태7/7/20181838Math: 38. GeoGebra 기하 (15) - 삼각형의 수심파일 다운로드1
11589정성태7/7/20182554.NET Framework: 787. object로 형변환된 인스턴스를 원래의 타입 인자로 제네릭 메서드를 호출하는 방법 [2]파일 다운로드1
11588정성태12/19/20192262디버깅 기술: 116. windbg 분석 사례 - ASP.NET 웹 응용 프로그램의 CPU 100% 현상
11587정성태7/5/20181995.NET Framework: 786. ASP.NET - HttpCookieCollection을 다중 스레드에서 사용할 경우 무한 루프 현상
11586정성태7/5/20182451Math: 37. GeoGebra 기하 (14) - 삼각형의 무게 중심파일 다운로드1
11585정성태7/5/20182485Math: 36. GeoGebra 기하 (13) - 삼각형의 외심과 외접하는 원파일 다운로드1
11584정성태7/5/20182834Math: 35. GeoGebra 기하 (12) - 삼각형의 내심과 내접하는 원파일 다운로드1
11583정성태7/5/20181837.NET Framework: 785. public으로 노출되지 않은 다른 어셈블리의 delegate 인스턴스를 Reflection으로 생성하는 방법파일 다운로드1
11582정성태5/8/20193063.NET Framework: 784. C# - 제네릭 인자를 가진 타입을 생성하는 방법 [1]파일 다운로드1
11581정성태7/4/20182798Math: 34. GeoGebra 기하 (11) - 3대 작도 불능 문제의 하나인 임의 각의 3등분파일 다운로드1
... 16  17  18  19  20  21  22  23  24  [25]  26  27  28  29  30  ...