如何有效地写入带长度前缀的二进制数据

本文关键字:前缀 二进制 数据 有效地 | 更新日期: 2023-09-27 18:20:01

我正在为包含序列化对象图的文件编写二进制数据格式。为了对错误更有弹性(并能够调试问题),我正在考虑在流中为每个对象添加长度前缀。我目前正在使用C#和BinaryWriter,但这是一个相当普遍的问题。

在完全序列化之前,每个对象的大小是未知的,因此能够写长度前缀有很多策略:

  1. 使用具有足够空间的写缓冲区进行随机访问,并在序列化对象后将长度插入正确的位置。

  2. 将每个对象写入其自己的MemoryStream,然后将缓冲区的长度和缓冲区内容写入主流。

  3. 在第一个过程中为所有对象写一个零长度,记住文件中所有对象大小的位置(对象大小表),然后进行第二个过程填充所有大小。

  4. ??

总尺寸(以及第一个/最外面物体的尺寸)通常在1mb左右,但也可以大到50-100mb。我关心的是进程的性能和内存使用情况。

哪种策略最有效?

如何有效地写入带长度前缀的二进制数据

哪种策略最有效?

确定这一点的唯一方法是测量。

我的第一直觉是使用#2,但知道这可能会给GC增加压力(如果工作流超过80Kb,则会给大对象堆增加碎片)。然而,#3听起来很有趣,假设跟踪这些位置的复杂性不会影响可维护性。

最后,您需要使用数据进行测量,并考虑到除非您遇到异常情况,否则性能将由网络或存储性能决定,而不是由内存中的处理决定。

100MB仅为"小型"服务器(或标准台式计算机)内存的2.5%。我会序列化到内存(例如,使用BinaryWriter的byte[]数组/MemoryStream),然后在完成后将其刷新到磁盘。

这也将使您的代码保持干净、紧凑和易于管理,使您免于数小时的撕扯头发和在一个大斑点中来回寻找:)

希望这能有所帮助!

如果控制格式,可以累积一个对象大小列表,并在文件末尾附加一个目录。但是,不要忘记,在.NET世界中,在实际传输到磁盘之前,您的写缓冲区会被复制多次。因此,你通过避免(比如)额外的MemoryStream而获得的任何收益都不会大大提高整体效率。