【问题标题】:Improving performance of protocol buffers提高协议缓冲区的性能
【发布时间】:2014-03-18 06:15:12
【问题描述】:

我正在编写一个应用程序,它需要从一个文件中快速反序列化数百万条消息。

应用程序所做的基本上是从文件中获取一条消息,做一些工作然后丢弃该消息。每条消息由大约 100 个字段组成(并非所有字段都总是被解析,但我需要它们,因为应用程序的用户可以决定他想要处理哪些字段)。

此时,应用程序包含在一个循环中,该循环在每次迭代中只使用readDelimitedFrom() 调用来执行。

有没有办法优化问题以更好地适应这种情况(拆分为多个文件等...)。此外,此时由于消息的数量和每条消息的维度,我需要对文件进行 gzip 压缩(并且由于字段的值非常重复,因此在减小大小方面相当有效) - 这虽然减少了性能。

【问题讨论】:

  • 不能用多线程处理模型替换循环吗?
  • 我避免说这个,但它已经是多线程的。本质上,文件被分成一定数量的切片,每个线程都在一个切片上工作。我在问题中描述的循环实际上是单个线程的工作
  • 您能否提供一些关于应用程序架构的额外信息?乍一看,我认为文件 I/O 将是您的主要瓶颈,我很好奇文件是如何分割成片的,以及每个线程如何访问它们的文件片。
  • 与应用程序相比,拆分是外部的。每个线程启动,选择 1 个切片并在其上工作,产生一个独立的输出——就结构而言,线程之间没有任何共享。线程被分配了它们在启动时必须处理的切片。

标签: java optimization protocol-buffers


【解决方案1】:

如果 CPU 时间是您的瓶颈(如果您使用冷缓存直接从 HDD 加载,这不太可能,但在其他情况下可能会出现这种情况),那么您可以通过以下方式提高吞吐量:

  • 如果可能,请使用 C++ 而不是 Java,并为循环的每次迭代重复使用相同的消息对象。这减少了花费在内存管理上的时间,因为每次都将重复使用相同的内存。

  • 不使用readDelimitedFrom(),而是构造一个CodedInputStream 并使用它来读取多条消息,如下所示:

    // Do this once:
    CodedInputStream cis = CodedInputStream.newInstance(input);
    
    // Then read each message like so:
    int limit = cis.pushLimit(cis.readRawVarint32());
    builder.mergeFrom(cis);
    cis.popLimit(limit);
    cis.resetSizeCounter();
    

    (类似的方法也适用于 C++。)

  • 使用 Snappy 或 LZ4 压缩而不是 gzip。这些算法仍然可以获得合理的压缩比,但针对速度进行了优化。 (LZ4 可能更好,尽管 Snappy 是由 Google 开发的,考虑到 Protobufs,所以您可能需要在您的数据集上测试两者。)

  • 考虑使用Cap'n Proto 而不是协议缓冲区。 不幸的是,还没有 Java 版本,但是 编辑:有 capnproto-java 以及许多其他语言的实现。在它支持的语言中,它已被证明要快得多。 (披露:我是 Cap'n Proto 的作者。我也是 Protocol Buffers v2 的作者,这是 Google 发布的开源版本。)

【讨论】:

    【解决方案2】:

    我预计您的 CPU 花费的大部分时间都用于垃圾收集。我希望将默认垃圾收集器替换为更适合您的短期对象用例的垃圾收集器。

    如果您决定用 C++ 编写此代码 - 在解析之前使用 Arena 创建第一条消息:https://developers.google.com/protocol-buffers/docs/reference/arenas

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-18
      • 2021-12-19
      • 2011-11-15
      • 1970-01-01
      • 1970-01-01
      • 2016-12-19
      相关资源
      最近更新 更多