原始类型的最优序列化

时间:2009-02-10 21:24:46

标签: c# serialization

我们开始推出越来越多的我们产品的WAN部署(带有IIS托管的远程后端的.NET胖客户端)。因此,我们正在尝试减少线路上数据的大小。

我们通过实现ISerializable(类似于this)覆盖了默认序列化,我们看到了从12%到50%的增益。我们的大部分工作都集中在优化基元类型的数组上。 是否有一种奇特的方式来序列化原始类型,除了显而易见的?

例如,今天我们按如下方式序列化一组int:

  

[4字节(数组长度)] [4字节] [4字节]

任何人都可以做得更好吗?

对于布尔数组,最明显的改进示例是在每个字节中放置8个bool,我们已经这样做了。

注意:每个bool节省7位可能看起来浪费时间,但是当你处理大量数据(我们是这样)时,它的加速非常快。

注意:由于与之相关的延迟,我们希望避免使用通用压缩算法。远程处理仅支持缓冲的请求/响应(没有分块编码)。我意识到压缩和最佳序列化之间存在一条细微的界限,但是我们的测试表明我们可以以非常低的延迟成本提供非常具体的序列化优化。而将整个缓冲响应重新处理为新的压缩缓冲区的代价太高了。

9 个答案:

答案 0 :(得分:5)

(与消息/类有关,而不仅仅是原语)

谷歌为这种类型的场景设计了“协议缓冲区”(它们转移了大量数据) - 它们的格式紧凑(使用类似base-128编码的东西)但可扩展和版本容忍(因此客户端和服务器可以升级容易)。

在.NET世界中,我可以推荐2个协议缓冲区实现:

有关信息,protobuf-net直接支持ISerializable和远程处理(它是unit tests的一部分)。有效果/尺寸指标here

最重要的是,您所做的只是为您的课程添加一些属性。

警告:它并不是理论上的最佳 - 但是务实且易于理解 - 在性能,可移植性和简单性之间做出妥协。

答案 1 :(得分:2)

查看Google协议缓冲区中使用的base-128 varint类型;这可能就是你要找的东西。

(如果您在网上搜索,可以使用许多协议缓冲区的.NET实现,根据他们的许可证,您可以从中获取一些代码!)

答案 2 :(得分:2)

是的,有一种奇特的序列化原始类型的方法。作为奖励,它也快得多(通常为20-40次)。

Simon Hewitt的开源库,请参阅 Optimizing Serialization in .NET - part 2 ,使用各种技巧。例如,如果已知数组包含小整数,则序列化输出的数量较少。这将详细描述in part 1 of the article。例如:

  

...所以,一个更少的Int32   超过128可以存储在一个字节中(通过使用7位   编码)....

可以混合和匹配完整和大小优化的整数。这似乎很明显,但还有其他一些事情;例如,整数值0发生特殊情况 - 优化以存储数值类型和零值。

第1部分陈述:

  

...如果您曾经使用.NET远程处理大量数据,您会发现可伸缩性存在问题。对于少量数据,它运行良好,但较大的数量需要大量的CPU和内存,生成大量数据用于传输,并且可能因内存不足异常而失败。实际执行序列化所花费的时间也存在很大问题 - 大量数据可能会使其无法在应用程序中使用....

我在my application中使用了这个图书馆取得了巨大成功。

为了确保从未使用过.NET序列化 ASSERT 0Debug.WriteLine()或类似的地方 库代码,它依赖于.NET序列化。 那是在文件中的函数WriteObject()的末尾 FastSerializer.cscreateBinaryFormatter().Serialize(BaseStream, value);附近。

答案 3 :(得分:1)

如果可以对数组进行排序,则可以执行简单的RLE以节省空间。即使它们没有分类,RLE仍然是有益的。写作和阅读都很快实现。

答案 4 :(得分:1)

这是我用过一次编码整数数组的技巧:

  1. 将数组元素分组为4个。
  2. 在每个组前面加上一个字节(让我们称之为长度掩码),表示以下4个元素的长度。长度掩码是由dibits组成的位掩码,表示相应元素的长度(00 - 1字节,01 - 2字节,10 - 3字节,11 - 4字节)。
  3. 尽可能简短地写出元素。
  4. 例如,要表示无符号整数0x0000017B,0x000000A9,0xC247E8AD和0x00032A64,您可以写(假设小端):B1,7B,01,A9,AD,E8,47,C2,64,2A,03。

    在最佳情况下,它可以为您节省高达68.75%(11/16)的空间。在最坏的情况下,你实际上会浪费额外的6.25%(1/16)。

答案 5 :(得分:0)

如果您知道哪些int值更常见,则可以用较少的位对这些值进行编码(并使用相应的更多位对较不常见的值进行编码):这称为“霍夫曼”编码/编码/压缩。

总的来说,虽然我建议您可以做的最简单的事情之一是对数据运行标准的“zip”或“压缩”实用程序。

答案 6 :(得分:0)

对于整数,如果您通常使用小数字(在127或32768下),则可以使用MSB作为标志对数字进行编码,以确定它是否是最后一个字节。有点类似于UTF-8,但标志位实际上是浪费(UTF-8不是这种情况)

示例( big-endian ):

125 which is usually encoded as 00 00 00 7D
Could be encoded as 7D

270 which is usually encoded as 00 00 01 0E
Could be encoded as 82 0E

主要限制是32位值的有效范围减少到28位。但是对于小的价值,你通常会获得很多。

此方法实际上用于旧格式,如MIDI,因为旧电子设备需要非常有效和简单的编码技术。

答案 7 :(得分:0)

如果您想自己控制序列化格式,只需使用库帮助进行紧凑整数存储,就可以从使用BinaryWriterWrite7BitEncodedInt派生一个类。同样适用于BinaryReader.Read7BitEncodedInt

答案 8 :(得分:-1)

在自己实现ISerializable之前,您可能在Web服务中使用XmlSerializer或SOAP格式化程序。鉴于您所有胖客户端都运行.NET,您可以尝试使用BinaryFormatter。

相关问题