我正在编写一个应用程序,需要从单个文件中快速反序列化数百万条消息.
应用程序的作用主要是从文件中获取一条消息,做一些工作然后丢弃消息.每条消息由~100个字段组成(并非所有消息都被解析,但我需要它们,因为应用程序的用户可以决定他想要处理哪些字段).
在这一刻,应用程序包含一个循环,在每次迭代中使用readDelimitedFrom()调用执行.
有没有办法优化问题,以更好地适应这种情况(拆分多个文件等…).另外,在这一刻由于消息的数量和每条消息的维度,我需要gzip文件(并且它在减小大小方面相当有效,因为字段的值非常重复) – 这虽然减少了性能.
解决方法
如果cpu时间是你的瓶颈(如果你直接从具有冷缓存的硬盘加载,这是不太可能的,但在其他情况下可能就是这种情况),那么这里有一些方法可以提高吞吐量:
>如果可能,使用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而不是Protocol Buffers.不幸的是,还没有Java版本,但编辑:有capnproto-java,还有许多其他语言的实现.在它支持的语言中已经证明它要快得多. (披露:我是Cap’n Proto的作者.我也是Protocol Buffers v2的作者,这是谷歌发布的开源版本.)