私の現在のプロジェクトでは、GRPC だけでなく、 RabbitMQメッセージ形式としても protobuf を使用しています。 protobuf の利点は速度だけにとどまりませんが、特に ruby の世界では、 JSONライブラリと比較して本当に高速なのか疑問に思っていました。それを確認するためにいくつかのベンチマークを作成することにしましたが、最初に各フォーマットの簡単な紹介を追加したいと思います。
これは、前方互換性と後方互換性を考慮して設計された、高速でコンパクトなクロスプラットフォーム メッセージ システムです。定義言語と言語固有のコンパイラで構成されます。
小さなオブジェクトのようなデータに対しては完璧に機能し、優れた後方互換性と前方互換性があり、高速で (まだわかりません)、たとえば JSON よりもコンパクトですが、直接比較をサポートしていないなどの制限があります (比較するオブジェクトを逆シリアル化します)。
圧縮されておらず、一部の特定の形式の方がデータに適しています (JPEG など)。それは自己記述的ではありません。
詳細については、公式ドキュメントを参照してください。
JSON はJavaScript object notationの略です。もともと JavaScript で使用されていたテキストベースのデータ形式ですが、後に JS アプリとバックエンドの間だけでなく、マイクロサービス間の通信形式としても広く普及し、他にも複数の用途があります。
文字列をキーとして使用し、値として使用可能な型として文字列、数値、ブール値、オブジェクト、配列、および nul を持ちます。その主な利点は、人間が判読でき、プログラミング言語のシリアル化と解析が非常に簡単であることです。
詳しくはサイトをご覧ください。
人気の ruby JSON ライブラリを 3 つピックアップしました。それらは、Oj、Yajl、および標準の JSON ライブラリです。 protobuf については、標準の google protoc と google ruby gem を使用します。
フィールドタイプが混在する複雑なペイロードである限り、さまざまな特定のタイプのペイロードを測定して、どのデータタイプが最も違いを示すかを確認します。
すべてのコードはhttps://github.com/alexstaro/proto-vs-jsonで確認できます。
ハードウェアとして、AMD Ryzen 3 PRO 5450U と 16 GB の ddr4 RAM を搭載したラップトップを使用しています。
オペレーティング システムとして、Ubuntu 22.10 kinetic を使用します。
Ruby バージョン 3.2.1 は asdf 経由でインストールされました。
ベンチマークには、benchmark/ips gem ( https://github.com/evanphx/benchmark-ips ) を使用します。
セットアップは次のようになります。
Benchmark.ips do |x| x.config(time: 20, warmup: 5) x.report('Yajl encoding') do Yajl::Encoder.encode(data) end ... x.compare! end
整数のみから始めます。 JSON の数値は非常に難しいため、protobuf は他の競合他社から遠く離れていると予想されます。
テストデータ:
data = { field1: 2312345434234, field2: 31415926, field3: 43161592, field4: 23141596, field5: 61415923, field6: 323423434343443, field7: 53141926, field8: 13145926, field9: 323423434343443, field10: 43161592 }
ベンチマーク結果:
protobuf encoding: 4146929.7 i/s Oj encoding: 1885092.0 i/s - 2.20x slower standard JSON encoding: 505697.5 i/s - 8.20x slower Yajl encoding: 496121.7 i/s - 8.36x slower
protobuf が絶対的な勝者であることに疑いの余地はありませんが、テストをより現実のシナリオに近づけるとどうなるでしょうか。ほとんどの場合、シリアル化のためだけに proto メッセージを作成します。
結果は次のとおりです。
protobuf encoding: 4146929.7 i/s Oj encoding: 1885092.0 i/s - 2.20x slower standard JSON encoding: 505697.5 i/s - 8.20x slower Yajl encoding: 496121.7 i/s - 8.36x slower protobuf with model init: 489658.0 i/s - 8.47x slower
結果はそれほど明白ではありません。メッセージの初期化を伴うエンコードは遅くなると予想していましたが、最も遅くはありませんでした。
逆シリアル化を確認しましょう。
protobuf parsing: 737979.5 i/s Oj parsing: 448833.9 i/s - 1.64x slower standard JSON parsing: 297127.2 i/s - 2.48x slower Yajl parsing: 184361.1 i/s - 4.00x slower
ここに驚きはありません。
ペイロード サイズに関しては、protobuf は json と比較してほぼ 4 倍コンパクトです。
JSON payload bytesize 201 Protobuf payload bytesize 58
double は、JSON の最も難しいペイロードであると予想されます。これを確認してみましょう。
私たちのペイロード:
data = { field1: 2312.345434234, field2: 31415.926, field3: 4316.1592, field4: 23141.596, field5: 614159.23, field6: 3234234.34343443, field7: 53141.926, field8: 13145.926, field9: 323423.434343443, field10: 43161.592 }
結果:
protobuf encoding: 4814662.9 i/s protobuf with model init: 444424.1 i/s - 10.83x slower Oj encoding: 297152.0 i/s - 16.20x slower Yajl encoding: 160251.9 i/s - 30.04x slower standard JSON encoding: 158724.3 i/s - 30.33x slower
モデルの初期化を行っても、Protobuf の方がはるかに高速です。逆シリアル化を確認しましょう。
Comparison: protobuf parsing: 822226.6 i/s Oj parsing: 395411.3 i/s - 2.08x slower standard JSON parsing: 241438.7 i/s - 3.41x slower Yajl parsing: 157235.7 i/s - 5.23x slower
ここでも驚きはありません。
およびペイロード サイズ:
JSON payload bytesize 211 Protobuf payload bytesize 90
4回ではありませんが、それでも目立ちます。
文字列は JSON の方が簡単であると予想されます。これを確認してみましょう。
ペイロード:
data = { field1: "2312.345434234", field2: "31415.926", field3: "4316.1592", field4: "23141.596", field5: "614159.23", field6: "3234234.34343443", field7: "53141.926", field8: "13145.926", field9: "323423.434343443", field10: "43161.592" }
ベンチ結果:
Comparison: protobuf encoding: 3990298.3 i/s oj encoder: 1848941.3 i/s - 2.16x slower yajl encoder: 455222.0 i/s - 8.77x slower standard JSON encoding: 444245.6 i/s - 8.98x slower protobuf with model init: 368818.3 i/s - 10.82x slower
逆シリアル化:
Comparison: protobuf parser: 631262.5 i/s oj parser: 378697.6 i/s - 1.67x slower standard JSON parser: 322923.5 i/s - 1.95x slower yajl parser: 187593.4 i/s - 3.37x slower
ペイロードのサイズ:
JSON payload bytesize 231 Protobuf payload bytesize 129
整数ベンチを分離しましたが、protobuf がコレクションを処理する方法は興味深いものです。
データは次のとおりです。
data = { field1: [ 2312345434234, 31415926, 43161592, 23141596, 61415923, 323423434343443, 53141926, 13145926, 323423434343443, 43161592 ] }
シリアル化ベンチ:
Comparison: protobuf encoding: 4639726.6 i/s oj encoder: 2929662.1 i/s - 1.58x slower standard JSON encoding: 699299.2 i/s - 6.63x slower yajl encoder: 610215.5 i/s - 7.60x slower protobuf with model init: 463057.9 i/s - 10.02x slower
逆シリアル化ベンチ:
Comparison: oj parser: 1190763.1 i/s protobuf parser: 760307.3 i/s - 1.57x slower standard JSON parser: 619360.4 i/s - 1.92x slower yajl parser: 414352.4 i/s - 2.87x slower
正直なところ、デシリアライズの結果はかなり予想外です。
ペイロードのサイズを確認しましょう:
JSON payload bytesize 121 Protobuf payload bytesize 50
double の配列が同じ動作を共有するかどうかを確認することにしました。
データ:
data = { field1: [ 2312.345434234, 31415.926, 4316.1592, 23141.596, 614159.23, 3234234.34343443, 53141.926, 13145.926, 323423.434343443, 43161.592 ] }
シリアライゼーション:
Comparison: protobuf encoding: 7667558.9 i/s protobuf with model init: 572563.4 i/s - 13.39x slower Oj encoding: 323818.1 i/s - 23.68x slower Yajl encoding: 183763.3 i/s - 41.73x slower standard JSON encoding: 182332.3 i/s - 42.05x slower
逆シリアル化:
Comparison: Oj parsing: 953384.6 i/s protobuf parsing: 883899.0 i/s - 1.08x slower standard JSON parsing: 452799.0 i/s - 2.11x slower Yajl parsing: 356091.2 i/s - 2.68x slower
ここでも同様の結果が得られました。 protobuf には配列に関する問題があるようです。
ペイロードのサイズ:
JSON payload bytesize 131 Protobuf payload bytesize 82
「複雑な」ペイロードとして、いくつかのユーザー データを投稿とそれらの投稿に対するコメントでモックして、実際のアプリケーションのようにしました。
data = { user_id: 12345, username: 'johndoe', email: '[email protected]', date_joined: '2023-04-01T12:30:00Z', is_active: true, profile: { full_name: 'John Doe', age: 30, address: '123 Main St, Anytown, USA', phone_number: '+1-555-123-4567' }, posts: [ { post_id: 1, title: 'My first blog post', content: 'This is the content of my first blog post.', date_created: '2023-04-01T14:00:00Z', likes: 10, tags: ['blog', 'first_post', 'welcome'], comments: [ { comment_id: 101, author: 'Jane', content: 'Great first post!', date_created: '2023-04-01T15:00:00Z', likes: 3 }, ... ] }, ... ] }
結果:
Comparison: protobuf encoding: 1038246.0 i/s Oj encoding: 296018.6 i/s - 3.51x slower Yajl encoding: 125909.6 i/s - 8.25x slower protobuf with model init: 119673.2 i/s - 8.68x slower standard JSON encoding: 115773.4 i/s - 8.97x slower Comparison: protobuf parsing: 291605.9 i/s Oj parsing: 76994.7 i/s - 3.79x slower standard JSON parsing: 64823.6 i/s - 4.50x slower Yajl parsing: 34936.4 i/s - 8.35x slower
そしてペイロードサイズ:
JSON payload bytesize 1700 Protobuf payload bytesize 876
ここでは、最初に純粋な protobuf エンコーディングで予想される動作を確認できますが、「実際の」例を見ると、標準の JSON エンコーディングよりも高速ではないことがわかります。
速度のためだけに JSON から Protobuf に切り替える場合は、その価値がないかもしれません。
Protobuf を使用する理由は、パフォーマンスの向上ではなく、データ交換のための優れた言語間スキーマ定義であるべきです。
この記事のリード画像は、プロンプト「プログラミング言語」を介して HackerNoon のAI Image Generatorによって生成されました。