2017-05-18 16 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 

不在当前代码可用,但它的东西,可能会在有可能新的代码 - 我们有自定义序列化器的概念。这当然是我探索的一个领域。