ホームページ >ウェブフロントエンド >jsチュートリアル >Websocket 用の JSON、バッファー/カスタム バイナリ プロトコル、Protobuf、および MessagePack のパフォーマンス分析
この記事では、データのシリアル化と逆シリアル化のメソッド/形式: JSON、バッファー (カスタム バイナリ プロトコル)、Protobuf、および MessagePack を調査および比較し、それらの実装方法に関するガイダンスを提供します。 。 (最後のパフォーマンスベンチマーク)
これは、メッセージを送信する最も一般的な方法である JSON です。データを文字列にエンコードして、Websocket メッセージを通過させて解析できるようにします。
ws.send(JSON.stringify({greeting: "Hello World"]})) ws.on("message", (message) => { const data = JSON.parse(message); console.log(data) })
カスタム バイナリ プロトコルは、データのシリアル化と逆シリアル化の軽量カスタム実装です。 速度、パフォーマンス、低遅延が重要な場合によく使用されます。 オンライン マルチプレイヤー ゲーム など (またはアプリを最適化したい場合)。 カスタム バイナリ プロトコルを構築するときは、バッファとバイナリを操作することになります。これは実装が難しい場合がありますが、バッファとバイナリの知識があれば問題ありません。
const encoder = new TextEncoder(); const decoder = new TextDecoder(); function binary(text, num) { const messageBytes = encoder.encode(text); // array buffers cannot store strings, so you must encode // the text first into an array of binary values // e.g. "Hello World!" -> [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33] const buffer = new ArrayBuffer(1 + messageBytes.length); // when creating array buffers, //you must predetermine the size of your array buffer const view = new DataView(buffer); // create a view to interact with the buffer view.setUint8(0, num); const byteArray = new Uint8Array(buffer); byteArray.set(messageBytes, 1); return buffer; } ws.on("open", () => { const msg = binary("Hello World!", 123); ws.send(msg); }) ws.on("message", (message) => { const buffer = message.buffer; const view = new DataView(buffer); const num = view.getUint8(0); const textLength = buffer.byteLength - 1 const textBytes = new Uint8Array(buffer, 1, textLength); const text = decoder.decode(textBytes); console.log(text, num); });
この関数は、2 つのプロパティを シリアル化し、1 つはテキストで、もう 1 つは数値で 配列バッファに入れます。
このコード例では、protobufs の JavaScript 実装である protobuf.js を使用します。リフレクションを使用して、実行時に protobuf コードを生成します。コードを静的に生成することもできますが、protobuf.js wiki によるとパフォーマンスには影響しません。ただし、protobuf コードの読み込みは速くなりますが、WebSocket メッセージの送信時のパフォーマンスにはまったく影響しません。
syntax = "proto3"; message TestMessage { string text = 1; uint32 num = 2; }
import protobuf from "protobufjs"; const ws = new Websocket("ws://localhost:3000"); ws.binaryType = "arraybuffer"; protobuf.load("testmessage.proto", function (err, root) { if (err) throw err; if (root === undefined) return; const TestMessage = root.lookupType("TestMessage") ws.on("open", () => { const message = TestMessage.create({text: "Hello World!", num: 12345}); const buffer = TestMessage.encode(message).finish(); ws.send(buffer); }); ws.on("message", (msg) => { const buffer = new Uint8Array(msg); const data = TestMessage.decode(buffer); console.log(data.text); console.log(data.num); }); })
import { encode, decode } from "@msgpack/msgpack"; ws.binaryType = "arraybuffer" ws.on("open", () => { const msg = encode({"Hello World!", 123}); ws.send(msg); }) ws.on("message", (msg) => { const data = decode(msg); console.log(data); })
各データシリアル化形式/メソッドのパフォーマンスを比較するために、Websocket経由でデータを送信するときのパフォーマンスを測定するベンチマークを作成しました。
ベンチマークをさまざまなグループに分割しました。
少量のデータ入力
中データ入力
ビッグデータ入力
これは、さまざまなデータ サイズにわたるこれらのデータのシリアル化のパフォーマンスを測定するためです。各グループのシリアル化、デシリアル化、合計時間のパフォーマンスも記録しました。これらのテストの信頼性を確保するために、各グループに対して正確なベンチマークを 5 回実行し、平均を計算しました。
ベンチマークは 100,000 回の反復で Websocket メッセージを送信します。コードはBun.js
で書かれていますこれらのベンチマークは終了までに時間内 (ミリ秒) で記録されたため、 小さいほど高速です.
ws.send(JSON.stringify({greeting: "Hello World"]})) ws.on("message", (message) => { const data = JSON.parse(message); console.log(data) })
Method | Byte size (bytes) |
---|---|
JSON | 33 |
Custom Binary Protocol | 13 |
Protobuf | 17 |
MessagePack | 24 |
const encoder = new TextEncoder(); const decoder = new TextDecoder(); function binary(text, num) { const messageBytes = encoder.encode(text); // array buffers cannot store strings, so you must encode // the text first into an array of binary values // e.g. "Hello World!" -> [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33] const buffer = new ArrayBuffer(1 + messageBytes.length); // when creating array buffers, //you must predetermine the size of your array buffer const view = new DataView(buffer); // create a view to interact with the buffer view.setUint8(0, num); const byteArray = new Uint8Array(buffer); byteArray.set(messageBytes, 1); return buffer; } ws.on("open", () => { const msg = binary("Hello World!", 123); ws.send(msg); }) ws.on("message", (message) => { const buffer = message.buffer; const view = new DataView(buffer); const num = view.getUint8(0); const textLength = buffer.byteLength - 1 const textBytes = new Uint8Array(buffer, 1, textLength); const text = decoder.decode(textBytes); console.log(text, num); });
Method | Byte size (bytes) |
---|---|
JSON | 117 |
Custom Binary Protocol | 70 |
Protobuf | 75 |
MessagePack | 102 |
syntax = "proto3"; message TestMessage { string text = 1; uint32 num = 2; }
Method | Byte size (bytes) |
---|---|
JSON | 329 |
Custom Binary Protocol | 220 |
Protobuf | 229 |
MessagePack | 277 |
MessagePack は、メッセージが約 6600 件送信された時点で突然動作を停止しました。
すべてのベンチマークで、カスタム バイナリ プロトコル が合計時間で最速です、シリアル化時のバイト サイズが最小/最も効率的ですメッセージ。ただし、パフォーマンスの違いは顕著です。
驚くべきことに、JSONのシリアル化時間は、 のシリアル化よりも大幅に高速です カスタム バイナリ プロトコル。これはおそらく、JSON.stringify() が Node ではネイティブ c で実装され、Bun ではネイティブ zig が実装されているためです。 Bun を使用した JSON.stringify() は Node.
より 3.5 倍高速であるため、Node を使用する場合も結果が異なる可能性があります。このベンチマークでは公式の JavaScript MessagePack 実装を使用したため、MessagePack の方が高速になる可能性があります。 MessagePackr など、他の潜在的に高速な MessagePack 実装もあります。
読んでいただきありがとうございます!
ベンチマーク (typescript で記述): https://github.com/nate10j/buffer-vs-json-websocket-benchmark.git
結果は Google スプレッドシートでご覧いただけます。
以上がWebsocket 用の JSON、バッファー/カスタム バイナリ プロトコル、Protobuf、および MessagePack のパフォーマンス分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。