【问题标题】:protobuf-net message serialized size propertyprotobuf-net 消息序列化大小属性
【发布时间】:2011-12-04 08:13:58
【问题描述】:

我们正在使用 protobuf-net 对公共协议基于 Google 协议缓冲区的应用程序中的消息进行序列化和反序列化。该库非常出色,涵盖了我们的所有需求,除了这个:我们需要在消息实际序列化之前找出序列化消息的长度(以字节为单位)。

The question 已经在一年半前被问过了,根据 Marc 的说法,唯一的方法是序列化到 MemoryStream 并在之后读取.Length 属性。这在我们的案例中是不可接受的,因为 MemoryStream 在后台分配了一个字节缓冲区,我们必须避免这种情况。

来自同一回复的这句话给了我们希望,毕竟它可能是可能的:

如果您阐明用例是什么,我相信我们可以轻松实现 可用(如果还没有)。

这是我们的用例。我们有大小在几字节到两兆字节之间变化的消息。应用程序预先分配用于套接字操作和序列化/反序列化的字节缓冲区,一旦预热阶段结束,就不能创建额外的缓冲区(提示:避免 GC 和堆碎片)。字节缓冲区本质上是池化的。我们还希望尽可能避免在缓冲区/流之间复制字节。

我们提出了两种可能的策略,它们都需要预先设置消息大小:

  1. 使用(大)固定大小的字节缓冲区并序列化所有可以放入一个缓冲区的消息;使用Socket.Send 发送缓冲区的内容。我们必须知道下一条消息何时无法放入缓冲区并停止序列化。如果没有消息大小,实现此目的的唯一方法是等待Serialize 期间发生异常。
  2. 使用(小)可变大小字节缓冲区并将每条消息序列化到一个缓冲区中;使用Socket.Send 发送缓冲区的内容。为了从池中检查出大小合适的字节缓冲区,我们需要知道序列化消息有多少字节。

由于协议已经定义(我们无法更改)并且要求消息长度前缀为 Varint32,因此我们不能使用SerializeWithLengthPrefix 方法。

那么是否可以添加一种方法来估计消息大小而无需序列化到流中?如果它不适合库的当前功能集和路线图,但可行,我们有兴趣自己扩展库。如果有的话,我们也在寻找替代方法。

【问题讨论】:

    标签: c# protobuf-net


    【解决方案1】:

    如上所述,这不是立即可用的,因为代码有意尝试对数据进行单次传递(尤其是IEnumerable<T> 等)。但是,根据您的数据,它可能已经进行了适度的复制,以考虑到子消息也是长度前缀的事实,因此可能需要戏法。通过在消息中在内部使用“分组”子格式可以大大减少这种杂耍,因为组允许仅向前构造而没有引用。

    那么是否有可能添加一种方法来估计消息大小而无需序列化到流中?

    估计几乎没有用;由于没有终结符,因此需要准确。最终,如果没有实际操作,尺寸有点难以预测。 v1 中有一些代码用于大小预测,但目前似乎首选单通道代码,并且在大多数情况下,缓冲区开销是名义上的(有代码可以重用内部缓冲区,因此它不会花费所有为小消息分配缓冲区的时间)。

    如果您的消息 internally 仅转发(分组),那么作弊可能是序列化为 测量 的假流,但会丢弃所有数据;但是,您最终会序列化两次。

    回复:

    并且要求消息长度前缀为 Varint32,我们不能使用SerializeWithLengthPrefix 方法

    我不太确定我看到了那里的关系 - 它允许在这里使用一系列格式等;或许你能更具体一点?

    重新复制数据——我在这里玩的一个想法是使用亚范式作为长度前缀。例如,可能在大多数情况下 5 个字节就足够了,所以 而不是玩弄,它可以留下 5 个字节,然后简单地覆盖 而不 压缩(因为八位字节10000000 仍然意味着“归零并继续”,即使它是多余的)。这仍然需要缓冲(以允许回填),但不需要移动数据。

    最后一个简单的想法是:序列化为FileStream;然后写入文件长度和文件数据。显然,它用内存来换取 IO。

    【讨论】:

    • 关于长度前缀和SerializeWithLengthPrefix:该方法可以编写编码为Base128、Fixed32和Fixed32BigEndian的前缀,但不支持Varint32类型。我们的协议定义了以下消息结构:[type:varint32][length:varint32][actual protobuf message]。
    • @Boris 这正是 base-128 和字段编号 的含义。字段编号将是 === 类型(可能带有 >>3 移位)。我需要查看确切字节,但这可能是可用的。如果没有,只需手动添加类型,并使用 base-128 和字段 0 进行编码(将跳过字段编号)
    • 感谢您的澄清,我想我错过了那个。并感谢您的详细回复。我们仍在评估可能的方法,并试图找到尽可能少的数据处理和缓冲区分配的最佳方法。
    猜你喜欢
    • 1970-01-01
    • 2013-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-10
    • 1970-01-01
    相关资源
    最近更新 更多