2017-05-18 2 views
0

각 구조체에는 3 바이트의 데이터로 구성된 14,400,360 개의 구조체 배열이 있습니다. 고정 크기 버퍼의 직렬화

[ProtoContract] 
struct A 
{ 
    [ProtoMember(1)] 
    private unsafe fixed byte[3] data; 
} 

불행하게도 protobuf - 그물 2.0.0.668은 고정 된 크기의 버퍼를 직렬화 및 직렬화에 예외를 throw 할 수 없다. ("FixedArray에 대한 serializer 없음"과 같은) 대답은 here으로 설명됩니다.

현재 해결 방법은 고정 배열을 세 개의 개별 바이트로 분할하고 레이아웃을 고정하는 것입니다.

[ProtoContract] 
[StructLayout(LayoutKind.Explicit, Size = 3, CharSet = CharSet.Ansi)] 
struct A 
{ 
    [ProtoMember(1)] 
    [FieldOffset(0)] 
    private byte data; 

    [ProtoMember(2)] 
    [FieldOffset(1)] 
    private byte data1; 

    [ProtoMember(3)] 
    [FieldOffset(2)] 
    private byte data2; 
} 

Q : 어떤 마술 해킹 또는 그냥 일반 트릭 내가 밖으로 누락 있는가, 수동으로 배열을 분할 나는 가지고 있지 않은거야?

이 배열을 포함하는 개체의 힙 사용량을 측정 할 때 43201,160 바이트 또는 구조체 당 3 바이트를 차지합니다. 디스크의 직렬화 된 파일은 struct 당 72,814,584 바이트 또는 약 5.05 바이트를 차지합니다.

Q : 구조체 당 여분의 2 바이트를 차지하는 것은 무엇입니까? 시도한 적은 없지만 대신 3 * 14,400,360 바이트의 배열을 만들어 직렬화 된 크기를 줄일 수 있습니까?

편집 (최후의 수단을 넘어) : 수정 직렬화 된 파일의 크기는 126,246,995 바이트 구조체 당 8.8 바이트 대신 원래보고 구조체 당 5.5 바이트이었다.

편집 : 후속 this answer에서 단일 회원 트릭을 사용하여 90,952,228 바이트 구조체 당 6.3 바이트까지 파일 크기를 가져왔다.

답변

0

흥미로운 질문입니다. 나는 흥미로운 질문을 좋아한다!

프로토 타입에서이를 나타낼 수있는 방법이 없습니다. 미리 정의 된 형식에는 고정 된 크기의 데이터 형식 개념이 없기 때문입니다. 우리는 다양한 옵션이 있습니다

  • 사용 요소마다 필드를하지만 (큰 필드 번호를, 또는 그 이상)의 크기를 두 배로, 요소마다 헤더 + 값을 비용 -없는 훌륭한 옵션을 (참조, 내 생각 당신이보고있는 이유 5.05는 임의의 0 값을 건너 뛰기 때문입니다.)
  • 길이가 접두어가 붙어있는 청크 (bytes 또는 repeated packed)를 사용합니다. 그러나 헤더와 페이로드에 페이로드를 더한 비용이 소요됩니다. 우리가 데이터를 비 직렬화 할 경우 수행해야 할 작업에 대한 어색한 질문도 있습니다. 이 더 많고은 3 바이트가 넘지 않아야합니다. 나는 라이브러리 작성자이며 일어날 일이 많지 않습니다.
  • 헤더와 4 바이트 = 5 바이트
  • 다음과 같이 단일 가변 정수 형식을 사용하십시오. 헤더와 1-4 바이트 (24 비트는 1-4 바이트를 사용할 수 있습니다. varint 더하기 연속 7 비트입니다)

이들 모두에서 2 바이트를 차지할 모든 개체 래퍼가 있습니다. 이러한 옵션을 감안할 때

, 나는 후자는 당신의 최선의 방법이 될 것입니다 생각 - 단일 부재를 통하여 할 수있는 :

[ProtoMember(1)] // varint by default 
private uint SerializedValue { 
    get { /* pack bits from the field and return */ } } 
    set { /* unpack "value" into the field */ } 
} 

오늘을 할 수있는 최고의 관하여; 그러나, 제가 흥미로운 시나리오는 vFuture에 대한 개선을 고려하는 것입니다 - 어쩌면 우리가 외부 객체 오버 헤드를 피할 수있게 해줄 수 있습니다; 본질적으로 전체를을 하나의 이진 문자열로 나타내며 내부 필드 마커가 필요하지 않습니다.


그러나, 나는 그것이 이상적인 솔루션은 여기 A의 배열/목록 자체가 thunkable와 "포장 된"으로 처리 할 수 ​​있도록, 레벨을 이동하는 것입니다 궁금합니다.

WriteFieldHeaderWithLengthPrefix(1, WireType.String, arr.Length * 3); 
foreach(var item in arr) Append(item); // writes 3 bytes 

과 : 이것은 우리가 본질적으로 같은 것을 가지고 것을 의미

var arr = new A[ReadLengthPrefix()/3]; 
for(int i = 0 ; i < arr.Length; i++) 
    arr[i] = Parse(item); // reads 3 bytes 

는 현재 코드에서 사용할 수 없습니다를하지만 이 가능 수 있습니다 뭔가 새로운 코드 - 우리는 커스텀 시리얼 라이저 개념을 가지고 있습니다. 이것은 확실히 나를 탐험 할 수있는 영역입니다.