paint-brush
Ruby の世界における Protobuf と JSON の比較@alexstaro
861 測定値
861 測定値

Ruby の世界における Protobuf と JSON の比較

Oleksandr Starodubtsev10m2023/04/26
Read on Terminal Reader

長すぎる; 読むには

Protobuf は、高速でコンパクトなクロスプラットフォーム メッセージ システムです。定義言語と言語固有のコンパイラで構成されます。たとえば、後方互換性と前方互換性が高く、高速で (まだわかりません)、JSON よりもコンパクトです。圧縮されていないため、一部の特定の形式がデータに対してより適切に機能する場合があります。
featured image - Ruby の世界における Protobuf と JSON の比較
Oleksandr Starodubtsev HackerNoon profile picture
0-item
1-item

私の現在のプロジェクトでは、GRPC だけでなく、 RabbitMQメッセージ形式としても protobuf を使用しています。 protobuf の利点は速度だけにとどまりませんが、特に ruby の世界では、 JSONライブラリと比較して本当に高速なのか疑問に思っていました。それを確認するためにいくつかのベンチマークを作成することにしましたが、最初に各フォーマットの簡単な紹介を追加したいと思います。

プロトブフとは?

これは、前方互換性と後方互換性を考慮して設計された、高速でコンパクトなクロスプラットフォーム メッセージ システムです。定義言語と言語固有のコンパイラで構成されます。

小さなオブジェクトのようなデータに対しては完璧に機能し、優れた後方互換性と前方互換性があり、高速で (まだわかりません)、たとえば JSON よりもコンパクトですが、直接比較をサポートしていないなどの制限があります (比較するオブジェクトを逆シリアル化します)。


圧縮されておらず、一部の特定の形式の方がデータに適しています (JPEG など)。それは自己記述的ではありません。


詳細については、公式ドキュメントを参照してください。

JSONとは

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 の配列

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によって生成されました。