web-dev-qa-db-ja.com

高性能のシリアル化:Java vs Google Protocol Buffers vs ...?

今後のプロジェクトで行うことを考えている一部のキャッシングについては、Javaシリアル化について考えています。つまり、使用すべきですか?

今、私は以前、さまざまな理由でカスタムシリアル化と逆シリアル化(Externalizable)を書いてきました。最近では相互運用性がさらに問題になり、.Netアプリケーションと対話する必要性が予測できるため、プラットフォームに依存しないソリューションを使用することを考えました。

GPBの高性能使用の経験はありますか? Javaのネイティブシリアル化と速度と効率の点でどのように比較されますか?あるいは、検討する価値のある他のスキームはありますか?

52
cletus

プロトコルバッファとJavaのネイティブシリアル化を速度の面で比較したことはありませんが、相互運用性のためにJavaのネイティブシリアル化は非常に重要です。また、ほとんどの場合、プロトコルバッファほどスペースに関して効率的ではありません。もちろん、保存できるものや参照などの点で多少柔軟性があります。プロトコルバッファーは、目的に合わせて非常に優れており、必要に応じて最適です-しかし、相互運用性のために明らかな制限があります(と他のこと)。

最近、Java and .NETでプロトコルバッファベンチマークフレームワークを投稿しました。Javaバージョンは メインGoogleプロジェクトベンチマークディレクトリ )で、.NETバージョンは my C#ポートプロジェクト にあります。PB速度をJavaと比較する場合相互運用に興味がある場合は、ネイティブJavaシリアル化(または.NETネイティブバイナリシリアル化)を改めて考えないでください。

ただし、プロトコルバッファー以外にも相互運用可能なシリアル化には他のオプションがあります- Thrift[〜#〜] json [〜#〜] および [〜#〜] yaml [〜#〜] 心に湧き、そして間違いなく他のものがあります。

編集:さて、相互運用性はそれほど重要ではないので、シリアル化フレームワークから必要なさまざまな品質をリストアップする価値があります。あなたが考える必要があることの1つはバージョン管理です-これはPBが逆方向と順方向の両方をうまく処理するように設計されている別のことです(したがって、新しいソフトウェアは古いデータを読み取ることができ、逆も同様です)-もちろん、提案されたルールに従う場合:)

Javaパフォーマンスvsネイティブシリアライゼーションに注意を払おうとしたが、とにかくPBの方が高速であることを知って驚くことはないだろう。ベンチマークでは、サーバーをVMになるように2倍以上高速サンプルデータのシリアル化および非シリアル化で示しました。

サンプルのパフォーマンス数値、2つのメッセージ(1つは228バイト、1つは84750バイト)のシリアライズとデシリアライズのように、サーバーVMを使用してラップトップで次の結果を得ました。

ベンチマークgoogle.message1.datファイルを使用したベンチマーク.GoogleSize $ SizeMessage1 
バイト文字列へのシリアル化:30.16秒で2581851回の繰り返し。 18.613789MB/s 
バイト配列にシリアル化:29.842秒で2583547回の繰り返し。 18.824497MB/s 
メモリストリームへのシリアル化:30.125秒で2210320回の繰り返し。 15.953759MB/s 
バイト文字列からデシリアライズ:30.088秒で3356517回の反復。 24.256632MB/s 
バイト配列からデシリアライズ:29.958で3356517回の反復。 24.361889MB/s 
メモリストリームからデシリアライズ:29.821sで2618821回の反復。 19.094952MB/s 
 
ベンチマークgoogle_message1.datを使用したベンチマークGoogleSpeed $ SpeedMessage1 
バイト文字列にシリアル化:29.978秒で17068518回の繰り返し。 123.802124MB/s 
バイト配列にシリアル化:30.043sで17520066回の反復。 126.802376MB/s 
メモリストリームへのシリアル化:30.076sで7736665回の繰り返し。 55.93307MB/s 
バイト文字列からデシリアライズ:30.073秒で16123669回の反復。 116.57947MB/s 
バイト配列からデシリアライズ:30.109sで16082453回の反復。 116.14243MB/s 
メモリストリームからのシリアル化解除:30.03sで7496968回の繰り返し。 54.283176MB/s 
 
ベンチマークgoogle.message2.datでベンチマークGoogleSize $ SizeMessage2 
バイト文字列にシリアル化:30.034秒で6266回の繰り返し。 16.826494MB/s 
バイト配列にシリアル化:30.027sで6246回の反復。 16.776697MB/s 
メモリストリームへのシリアル化:29.916秒で6042回の反復。 16.288969MB/s 
バイト文字列からデシリアライズ:29.819秒で4675回の反復。 12.644595MB/s 
バイト配列からデシリアライズ:30.093秒で4694回の反復。 12.580387MB/s 
メモリストリームからデシリアライズ:29.579秒で4544回の反復。 12.389998MB/s 
 
ベンチマークベンチマーク。GoogleSpeed$ SpeedMessage2ファイルgoogle_message2.dat 
バイト文字列にシリアル化:30.055秒で39562回。 106.16416MB/s 
バイト配列にシリアル化:30.178sで39715回の繰り返し。 106.14035MB/s 
メモリストリームへのシリアル化:30.032秒で34161回の反復。 91.74085MB/s 
バイト文字列からデシリアライズ:29.794で36934回の反復。 99.98019MB/s 
バイト配列からデシリアライズ:29.915秒で37191回の反復。 100.26867MB/s 
メモリストリームからデシリアライズ:29.846秒で36237回の反復。 97.92251MB/s 

「速度」と「サイズ」は、生成されたコードが速度とコードサイズのどちらに最適化されているかです。 (シリアル化されたデータはどちらの場合も同じです。「サイズ」バージョンは、多くのメッセージが定義されていて、コードに大量のメモリを使用したくない場合に提供されます。)

ご覧のとおり、小さなメッセージの場合はvery fast-500を超える小さなメッセージがシリアル化または非シリアル化されますper millisecond。 87Kのメッセージでも、1メッセージあたり1ミリ秒未満しかかかりません。

60
Jon Skeet

もう1つのデータポイント:このプロジェクト:

http://code.google.com/p/thrift-protobuf-compare/

pBでのJavaシリアル化を含む、小さなオブジェクトの期待されるパフォーマンスのアイデアを提供します。

プラットフォームによって結果は大きく異なりますが、いくつかの一般的な傾向があります。

15
StaxMan

[〜#〜] fst [〜#〜] もご覧ください。組み込みJDKシリアル化のドロップイン代替品で、高速で出力が小さいはずです。

私が最近行った頻繁なベンチマークの生の推定:

100%=バイナリ/構造ベースのアプローチ(SBE、fst-structなど)

  • 不便
  • 後処理(受信側で「実際の」オブジェクトを構築する)はパフォーマンスの利点を損なう可能性があり、ベンチマークには含まれません

〜10%-35%プロトバフと誘導体

FSTやKRYOなどの〜10%-30%高速シリアライザー

  • 便利なデシリアライズされたオブジェクトは、追加の手動翻訳コードなしで最も頻繁に直接使用できます。
  • パフォーマンスを向上させることができます(注釈、クラスの登録)
  • オブジェクトグラフのリンクを保持します(2回シリアル化されたオブジェクトはありません)
  • 周期構造を処理できる
  • 汎用ソリューション、FSTはJDKシリアル化と完全に互換性があります

〜2%-15%JDKシリアル化

〜1%-15%高速JSon(例:ジャクソン)

  • オブジェクトグラフを処理できませんが、Javaデータ構造の小さなサブセットのみを処理できます
  • 参照の復元なし

0.001-1%フルグラフJSon/XML(JSON.ioなど)

これらの数値は、非常に大雑把な印象を与えるためのものです。パフォーマンスは、シリアル化/ベンチマークされるデータ構造に大きく依存することに注意してください。そのため、単一の単純なクラスベンチマークはほとんど役に立ちません(ただし、人気があります:たとえば、ユニコードを無視し、コレクションがないなど)。

また見なさい

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

8
R.Moeller

PBとネイティブJava速度と効率の直列化を混同している場合は、PBを選択してください。

  • PBは、このような要因を達成するために設計されました。 http://code.google.com/apis/protocolbuffers/docs/overview.html を参照してください
  • PBデータは非常に小さいのに対し、Javaシリアライゼーションは署名を含むオブジェクト全体を複製する傾向があります。受信機で?
  • 言語開発全体について考えてください。一方がJavaを使用し、一方がC++を使用する場合は困難になります...

一部の開発者はThriftを推奨していますが、私は「Googleを信じている」ためGoogle PBを​​使用します:-) ..とにかく、一見の価値があります。 http://stuartsierra.com/2008/07/10/thrift -vs-protocol-buffers

6
instcode

高性能とはどういう意味ですか?ミリ秒のシリアル化が必要な場合は、最も単純なシリアル化アプローチを使用することをお勧めします。サブミリ秒が必要な場合は、バイナリ形式が必要になる可能性があります。 10マイクロ秒よりずっと短い時間が必要な場合は、カスタムシリアル化が必要になる可能性があります。

シリアル化/逆シリアル化のベンチマークはあまり見ていませんが、シリアル化/逆シリアル化の200マイクロ秒未満をサポートするものはほとんどありません。

プラットフォームに依存しない形式では、コストがかかります(ユーザーの努力とレイテンシ)ので、パフォーマンスとプラットフォームのどちらを独立させるかを決定する必要があります。ただし、必要に応じて切り替える構成オプションとして両方を使用できない理由はありません。

5
Peter Lawrey

ここにその日の提案があります:-)(あなたは私の頭の中で私が今試したい何かを微調整しました)...

これを介してキャッシュソリューション全体を使用できる場合は、動作する可能性があります: Project Darkstar 。これは非常に高性能なゲームサーバーとして設計されており、特に読み取りが高速になります(キャッシュに適しています)。 JavaおよびC APIがあるため、Javaを使用してオブジェクトを保存し、読むことができると信じています(それを見てから長い時間が経っていると考えていました)。それらをCに戻し、逆も同様です。

他に何もなければ、今日読むべきものを提供します:-)

1
TofuBeer

ワイヤフレンドリーなシリアル化を行うには、Externalizableインターフェイスの使用を検討してください。巧妙に使用すると、特定のフィールドを最適にマーシャリングおよびアンマーシャリングする方法を決定するための詳細な知識が得られます。ただし、各オブジェクトのバージョン管理を正しく管理する必要があります-マーシャリング解除は簡単ですが、コードがV1をサポートするときにV2オブジェクトを再マーシャリングすると、アプリが壊れたり、情報が失われたり、データが破損したりします正しく処理できません。最適なパスを探している場合、妥協することなく問題を解決できるライブラリはないことに注意してください。一般に、ライブラリはほとんどのユースケースに適合し、アクティブなオープンソースプロジェクトを選択した場合、ユーザーの入力なしで時間の経過とともに適応および強化されるという追加の利点があります。また、パフォーマンスの問題を追加したり、バグを導入したり、まだ影響していないバグを修正したりすることもあります。

0
user4992332