对于一些缓存,我正在考虑为即将到来的项目做些什么,我一直在考虑Java序列化。即,它应该被使用吗?
现在我已经在过去几年中出于各种原因编写了自定义序列化和反序列化(Externalizable)。目前,互操作性已成为一个问题,我可以预见需要与.Net应用程序进行交互,因此我想到了使用独立于平台的解决方案。
有没有人有过使用GPB的高性能经验?它在速度和效率方面与Java的本机序列化相比如何?或者,还有其他值得考虑的方案吗?
答案 0 :(得分:58)
我没有在协议速度方面将Protocol Buffers与Java的本机序列化进行比较,但是为了实现互操作性,Java的本机序列化是一个严肃的禁忌。在大多数情况下,它在空间方面也不会像协议缓冲区那样高效。当然,就它可以存储的内容以及参考等方面来说,它更灵活一些。协议缓冲区非常擅长它的目的,当它满足你的需求它很棒 - 但是由于互操作性有明显的限制(和其他东西)。
我最近在Java和.NET中发布了一个Protocol Buffers基准测试框架。 Java版本位于main Google project(benchmarks directory中),.NET版本位于my C# port project。如果你想比较PB速度和Java序列化速度,你可以编写类似的类并对它们进行基准测试。如果您对interop感兴趣,我真的不会再考虑本机Java序列化(或.NET本机二进制序列化)。
除了协议缓冲区之外,还有其他可互操作的序列化选项 - Thrift,JSON和YAML让人想起,而且其他人无疑是其中之一。
编辑:好的,由于互操作不是那么重要,所以值得尝试从序列化框架中列出您想要的不同品质。您应该考虑的一件事是版本控制 - 这是PB旨在处理好的另一件事,无论是向后还是向前(因此新软件可以读取旧数据,反之亦然) - 当您坚持建议的规则时,当然:)在尝试对Java性能与本机序列化保持谨慎之后,我真的不会惊讶地发现PB无论如何都更快。如果有机会,请使用服务器虚拟机 - 我最近的基准测试表明服务器虚拟机在序列化和反序列化样本数据时的速度超过了两倍。我认为PB代码非常适合服务器VM的JIT:)
正如样本性能数据,序列化和反序列化两条消息(一个228字节,一个84750字节),我使用服务器VM在笔记本电脑上获得了这些结果:
Benchmarking benchmarks.GoogleSize$SizeMessage1 with file google_message1.dat Serialize to byte string: 2581851 iterations in 30.16s; 18.613789MB/s Serialize to byte array: 2583547 iterations in 29.842s; 18.824497MB/s Serialize to memory stream: 2210320 iterations in 30.125s; 15.953759MB/s Deserialize from byte string: 3356517 iterations in 30.088s; 24.256632MB/s Deserialize from byte array: 3356517 iterations in 29.958s; 24.361889MB/s Deserialize from memory stream: 2618821 iterations in 29.821s; 19.094952MB/s Benchmarking benchmarks.GoogleSpeed$SpeedMessage1 with file google_message1.dat Serialize to byte string: 17068518 iterations in 29.978s; 123.802124MB/s Serialize to byte array: 17520066 iterations in 30.043s; 126.802376MB/s Serialize to memory stream: 7736665 iterations in 30.076s; 55.93307MB/s Deserialize from byte string: 16123669 iterations in 30.073s; 116.57947MB/s Deserialize from byte array: 16082453 iterations in 30.109s; 116.14243MB/s Deserialize from memory stream: 7496968 iterations in 30.03s; 54.283176MB/s Benchmarking benchmarks.GoogleSize$SizeMessage2 with file google_message2.dat Serialize to byte string: 6266 iterations in 30.034s; 16.826494MB/s Serialize to byte array: 6246 iterations in 30.027s; 16.776697MB/s Serialize to memory stream: 6042 iterations in 29.916s; 16.288969MB/s Deserialize from byte string: 4675 iterations in 29.819s; 12.644595MB/s Deserialize from byte array: 4694 iterations in 30.093s; 12.580387MB/s Deserialize from memory stream: 4544 iterations in 29.579s; 12.389998MB/s Benchmarking benchmarks.GoogleSpeed$SpeedMessage2 with file google_message2.dat Serialize to byte string: 39562 iterations in 30.055s; 106.16416MB/s Serialize to byte array: 39715 iterations in 30.178s; 106.14035MB/s Serialize to memory stream: 34161 iterations in 30.032s; 91.74085MB/s Deserialize from byte string: 36934 iterations in 29.794s; 99.98019MB/s Deserialize from byte array: 37191 iterations in 29.915s; 100.26867MB/s Deserialize from memory stream: 36237 iterations in 29.846s; 97.92251MB/s
“速度”与“大小”是指生成的代码是针对速度还是代码大小进行了优化。 (在两种情况下,序列化数据都是相同的。“大小”版本是为您定义了大量消息并且不想为代码占用大量内存的情况提供的。)
正如您所看到的,对于较小的消息,它可以非常快速 - 超过500个小消息序列化或反序列化每毫秒。即使使用87K消息,每条消息的消息也不到一毫秒。
答案 1 :(得分:15)
还有一个数据点:这个项目:
http://code.google.com/p/thrift-protobuf-compare/
给出了小对象的预期性能的一些概念,包括PB上的Java序列化。
根据您的平台,结果会有很大差异,但有一些总体趋势。
答案 2 :(得分:7)
您可能还会看一下FST,它是内置JDK序列化的直接替代品,它应该更快,输出更小。
近年来我对频繁基准测试的原始估计:
100%=基于二进制/结构的方法(例如SBE,fst-structs)
~10%-35%protobuf&衍生物
~10%-30%快速序列化器,如FST和KRYO
~2%-15%JDK序列化
〜1%-15%快速JSon(例如杰克逊)
0.001-1%完整图表JSon / XML(例如JSON.io)
这些数字旨在给出非常粗略的数量级印象。 请注意,性能取决于要序列化/基准测试的数据结构。所以单个简单类基准测试大多没用(但很流行:例如忽略unicode,没有集合,......)。
另见
http://java-is-the-new-c.blogspot.de/2014/12/a-persistent-keyvalue-server-in-40.html
http://java-is-the-new-c.blogspot.de/2013/10/still-using-externalizable-to-get.html
答案 3 :(得分:6)
如果你在PB&之间混淆关于速度和效率的原生java序列化,只需去PB。
一些开发者建议Thrift,但我会使用Google PB,因为“我相信谷歌”:-) ..无论如何,它值得一看: http://stuartsierra.com/2008/07/10/thrift-vs-protocol-buffers
答案 4 :(得分:5)
高性能意味着什么?如果你想要毫秒级序列化,我建议你使用最简单的序列化方法。如果您想要亚毫秒,则可能需要二进制格式。如果您想要低于10微秒,则可能需要自定义序列化。
我没有看到许多序列化/反序列化的基准测试,但很少支持序列化/反序列化的200微秒。
独立于平台的格式需要付出代价(在您的努力和延迟方面),您可能需要决定是否需要性能或平台独立性。但是,没有理由不能将两者都作为配置选项,您可以根据需要进行切换。
答案 5 :(得分:1)
这是当天关于墙壁的建议:-)(你只是在我脑海里调整了一些东西,我现在想尝试一下)......
如果您可以通过此方式获取整个缓存解决方案,它可能会有效:Project Darkstar。它被设计为非常高性能的游戏服务器,特别是读取速度快(对缓存非常有用)。它有Java和C API,所以我相信(我认为它已经很长时间了,因此我没有想到这一点)你可以用Java保存对象并用C读取它们,反之亦然。
如果没有别的东西它会给你一些今天要读的东西: - )
答案 6 :(得分:0)
对于有线友好的序列化,请考虑使用Externalizable接口。巧妙地使用,您将拥有亲密的知识来决定如何最佳地编组和解组特定字段。也就是说,您需要正确管理每个对象的版本控制 - 易于卸载,但在代码支持V1时重新编组V2对象会破坏,丢失信息或更糟糕地破坏数据您的应用无法正确处理。如果您正在寻找最佳路径,请注意没有库会在没有妥协的情况下解决您的问题。一般而言,如果您选择了一个活跃的开源项目,那么图书馆将适合大多数用例,并且会带来额外的好处,即他们会在没有您输入的情况下随着时间的推移进行调整和增强。它们可能会增加性能问题,引入错误,甚至修复尚未对您造成影响的错误!