我们正在使用protobuf-net对公共协议基于Google协议缓冲区的应用程序中的消息进行序列化和反序列化。该库非常出色,涵盖了除此之外的所有要求:我们需要在实际序列化消息之前找出序列化的消息长度(以字节为单位)。
根据Marc的说法, The question已经在一年半前被问过了,唯一的办法就是序列化到MemoryStream并随后读取.Length
属性。在我们的例子中这是不可接受的,因为MemoryStream在幕后分配一个字节缓冲区,我们必须避免这种情况。
来自同一回复的这一行给了我们希望它毕竟是可能的:
如果您澄清用例是什么,我相信我们可以轻松实现 可用(如果还没有)。
这是我们的用例。我们有大小在几个字节和两兆字节之间变化的消息。应用程序预先分配用于套接字操作和序列化/反序列化的字节缓冲区,一旦预热阶段结束,就不能创建额外的缓冲区(提示:avoding GC和堆碎片)。字节缓冲区基本上是合并的。我们还希望尽可能避免在缓冲区/流之间复制字节。
我们提出了两种可能的策略,它们都需要预先设置邮件大小:
Socket.Send
发送缓冲区的内容。我们必须知道下一条消息何时无法放入缓冲区并停止序列化。如果没有消息大小,实现此目的的唯一方法是等待Serialize
期间发生异常。Socket.Send
发送缓冲区的内容。为了从池中检出具有适当大小的字节缓冲区,我们需要知道序列化消息有多少字节。因为协议已经定义(我们不能改变它)并且要求消息长度前缀为Varint32,所以我们不能使用SerializeWithLengthPrefix
方法。
那么是否可以添加一种估算邮件大小而无需序列化到流中的方法?如果它不适合图书馆的当前功能集和路线图,但是可行,我们有兴趣自己扩展图书馆。我们也在寻找替代方法,如果有的话。
答案 0 :(得分:4)
如上所述,这不是立即可用的,因为代码故意尝试对数据进行单次传递(尤其是IEnumerable<T>
等)。但是,根据您的数据,它可能已经进行了适量的复制,以允许子消息也长度为前缀,因此可能需要戏法。通过在消息中使用“分组”子格式内部可以大大减少这种杂耍,因为组允许仅向前构造而不带回溯。
那么是否可以添加一种估算邮件大小而无需序列化的方法到流中?
估计是无用的;因为没有终结者,所以需要准确。最终,如果没有真正做到这一点,尺寸有点难以预测。在v1中有一些代码用于大小预测,但单通道代码目前似乎是首选,并且在大多数情况下缓冲器开销是标称的(有代码可以重新使用内部缓冲区,因此它不会花费全部为小消息分配缓冲区的时间。)
如果您的消息内部仅向前(已分组),则作弊可能是序列化为度量的虚假流,但会丢弃所有数据;但是,你最终会序列化两次。
回复:
并要求消息长度前缀为Varint32,我们不能使用
SerializeWithLengthPrefix
方法
我不太确定我看到那里的关系 - 它允许在这里使用一系列格式等;也许你可以更具体一点?
重新复制数据 - 我在这里玩的想法是使用长度前缀的子标准形式。例如,可能在大多数情况下5个字节是充足的,所以而不是juggle ,它可能会留下5个字节,然后只需覆盖而不用凝聚(因为八位字节) 10000000
仍然意味着“零并继续”,即使它是多余的)。这仍然需要缓冲(以允许回填),但不需要和移动数据。
最后一个简单的想法很简单:序列化为FileStream
;然后写入文件长度和文件数据。它显然可以交换IO的内存使用情况。