【问题标题】:Parallel Binary DeSerialization?并行二进制反序列化?
【发布时间】:2009-12-20 07:20:18
【问题描述】:

我有一个解决方案,我需要非常快速地将对象读入内存,但是二进制流可能会在内存中缓存压缩以节省磁盘 io 时间。

我已经尝试过不同的解决方案,显然 XmlTextWriter 和 XmlTextReader 并不是那么好,内置的二进制序列化也不是那么好。 Protobuf-net 非常好,但还是有点太慢了。以下是一些统计数据:

文件大小 XML:217 kb

二进制文件大小:87 kb

压缩二进制:26 KB

压缩的 XML:26 KB

使用 XML (XmlTextReader) 反序列化:8.4 sek

使用二进制反序列化(Protobuf-net):6.2 sek

使用 Binary wo string.interning (Protobuf-net) 反序列化:5.2 sek

使用内存中的二进制反序列化:5.9 Sek

二进制文件解压到内存的时间:1.8 sek

使用 Xml (XmlTextWriter) 序列化:11 sek

使用二进制序列化(Protobuf):4 sek

使用二进制长度前缀序列化(Protobuf-net):3.8 sek

这让我开始思考,似乎(如果我错了,请纠正我)反序列化的主要罪魁祸首是实际的字节转换而不是 IO。如果是这样,那么它应该是使用新的并行扩展的候选者。

由于我在二进制 IO 方面有点新手,但在我投入时间解决之前,我会很感激一些输入 :)

为了简单起见,假设我们想要反序列化一个没有可选字段的对象列表。我的第一个想法是简单地存储每个长度前缀。将每个的 byte[] 读入一个 byte[] 列表并使用 PLINQ 进行 byte[] -> 对象反序列化。

但是,使用该方法我仍然需要单线程读取 byte[],因此也许可以将整个二进制流读入内存(顺便说一句,多大的二进制文件是可行的?)并在二进制文件的开头而是存储有多少对象以及它们的每个长度和偏移量。然后我应该能够创建 ArraySegments 或其他东西并并行进行分块。

那你们怎么看,可行吗?

【问题讨论】:

  • 对不起,有些问题我并没有真正得到明确的答案,但一开始我根本没有意识到要设置它。虽然吸取了教训,但感谢您指出这一点。不过,您似乎无法为旧问题设置答案,还是我遗漏了什么?
  • 为了支持 Alon,您发布的那个 java 字符串比较问题有一些经过深思熟虑的答案...
  • 同意,如何设置正确答案?
  • 没关系,我很笨,没想到你可以点击 v 符号 :)
  • 是“sek”秒吗?如果是这样,似乎其他地方出现了可怕的错误。我们在几分之一秒内反序列化数十 MB 大小的文档...

标签: c# serialization binary-data


【解决方案1】:

我经常做这样的事情,没有什么比使用 BinaryReader 读取内容更好的了。据我所知,没有比使用 BinaryReader.ReadInt32 读取 32 位整数更快的方法了。

您可能还会发现将其并行化并重新连接在一起的开销太大。如果你真的想走并行路线,我建议使用多线程读取多个文件,而不是多线程读取多个块中的一个文件。

您也可以调整块大小以使其与磁盘块大小相匹配,但是在您的应用程序和磁盘之间存在如此多的抽象级别,这可能会浪费时间。

【讨论】:

    【解决方案2】:

    二进制文件可以被多个线程同时读取。为此,必须使用适当的访问/共享修饰符打开它。然后每个线程都可以在该文件中获得自己的偏移量和长度。因此并行读取不是问题。

    让我们假设您将坚持简单的二进制格式:每个对象都以它的长度为前缀。知道您可以“滚动”文件并知道放置反序列化线程的偏移量。

    反序列化算法可能如下所示: 1)分析文件(分成几个比较大的chunk,chunk的边界要和object的边界重合) 2)产生必要数量的反序列化器线程并“指示”它们以适当的偏移量和长度读取 3) 将所有反序列化器线程的结果合并到一个列表中

    【讨论】:

    • 嗨,我正在研究这样的解决方案,而不是从磁盘并行读取,我决定先将整个文件检索到字节缓冲区,然后对其进行反序列化/序列化。似乎要快得多,如果您从磁盘并行读取,您将受到磁盘速度的限制。完成后我会在此处发布有关我的解决方案的一些信息,谢谢
    • 确实,对于较小的数据量,将其缓存在内存中更为可行。我建议文件读取,因为它是更通用的解决方案。内存缓存可以作为优化添加(永远不知道有人想要反序列化多少数据:))
    【解决方案3】:

    这让我开始思考,似乎 (如果我错了,请纠正我) 反序列化的罪魁祸首是 实际的字节转换而不是 IO。

    不要假设时间都花在了哪里,给自己找一个分析器并找出答案。

    【讨论】:

      【解决方案4】:

      当我反序列化大于 1 MB xml 的对象列表时,我使用此代码反序列化文件然后 2 秒:

      public static List<T> FromXML<T>(this string s) where T : class
              {
                  var ls = new List<T>();
                  var xml = new XmlSerializer(typeof(List<T>));
                  var sr = new StringReader(s);
                  var xmltxt = new XmlTextReader(sr);
                  if (xml.CanDeserialize(xmltxt))
                  {
                      ls = (List<T>)xml.Deserialize(xmltxt);
                  }
                  return ls;
              }
      

      如果更适合 XML 案例,试试这个?

      【讨论】:

      • xml 序列化是这样工作的,但部分开销可能是大量相对较小的对象,因此对象创建成为一个问题。尽管如此,XML 序列化几乎从不比二进制快,而且由于文件 io 而导致更多时间
      猜你喜欢
      • 2014-01-04
      • 2013-07-10
      • 2021-12-03
      • 2016-09-15
      • 1970-01-01
      • 2016-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多