2017-05-18 45 views
0

我有14400360個結構陣列,每個結構由3個字節的數據。 序列化固定大小的緩衝器

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

不幸的是protobuf網2.0.0.668不能序列固定大小的緩衝區,並在系列化拋出異常。 (如「不爲串行FixedArray」的東西)我想答案是解釋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; 
} 

問:有一些黑魔法黑客或只是簡單的技巧,我錯過了,所以我沒有手動拆分數組?

當測量含有此陣列的對象的堆的使用情況,它佔用43201160個字節或每結構僅有3個字節。 磁盤上的序列化文件佔用72,814,584字節或每個結構約5.05字節。

問:什麼佔用了每個結構這些額外的2個字節? 我還沒有嘗試過,但也許序列化的大小可以通過製作一個3 * 14,400,360字節的數組來減少呢? (超出不得已)

編輯:更正 序列化文件的大小爲126246995個字節或每結構8.8字節而不是原來報每結構5.5個字節。

編輯:後續this answer使用單個成員惡作劇使文件大小降至90952228個字節或每結構6.3個字節。

回答

0

有趣的問題。我喜歡有趣的問題!

有沒有完美方式來表示這protobuf - 因爲預定義的格式不具有固定大小的數據類型的概念。我們有多種選擇:

  • 使用每個元件的區域,但收費元素的頭+值,規模擴大一倍(或更長時間,大視場數) - 不是一個很好的選擇(僅供參考,我想你看到的原因5。05是因爲它跳過任何零值)
  • 使用長度前綴塊(bytesrepeated packed) - 但仍然需要一個標頭加一個長度加上有效負載 - 因此:在您的情況下爲5個字節;如果我們正在反序列化數據,還有一些尷尬的問題,它是更多比3字節,這應該不會發生,但是:我是一個庫作者和很多不應該發生的事情:發生
  • 使用單一固定大小整數格式化,所以:首部加上4個字節= 5個字節
  • 使用單個varint整數格式,所以:首部加上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 

不在當前代碼可用,但它的東西,可能會在有可能新的代碼 - 我們有自定義序列化器的概念。這當然是我探索的一個領域。