首頁 >web前端 >js教程 >Websockets 的 JSON、Buffer/自訂二進位協定、Protobuf 和 MessagePack 的效能分析

Websockets 的 JSON、Buffer/自訂二進位協定、Protobuf 和 MessagePack 的效能分析

DDD
DDD原創
2024-11-03 07:28:03290瀏覽

本文檢查並比較了資料序列化和反序列化方法/格式:JSON、Buffers(自訂二進位協定)、Protobuf 和MessagePack,並提供有關如何實現它們的指導。 (最後的效能基準)

JSON

這是發送訊息最常見的方法是 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);
});

此函數序列化兩個屬性,一個是文本,另一個是數字到數組緩衝區

原始緩衝區

在此程式碼範例中,我使用 protobuf.js,protobufs 的 JavaScript 實作。我使用反射在運行時產生 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);
})

性能基準

為了比較每種資料序列化格式/方法的效能,我編寫了一個基準測試來衡量透過Websockets傳送資料時的效能。

我已將基準測試分為不同的組別。

  • 小資料輸入

  • 中等資料輸入

  • 大數據輸入

這是為了衡量這些資料序列化在不同資料大小上的表現。我還記錄了每組序列化、反序列化和總時間的表現。我為每組運行了 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

總時間(毫秒)

Performance Analysis of JSON, Buffer / Custom Binary Protocol, Protobuf, and MessagePack for Websockets

序列化時間(毫秒)

Performance Analysis of JSON, Buffer / Custom Binary Protocol, Protobuf, and MessagePack for Websockets

反序列化時間(毫秒)

Performance Analysis of JSON, Buffer / Custom Binary Protocol, Protobuf, and MessagePack for Websockets

中等數據基準

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

總時間(毫秒)

Performance Analysis of JSON, Buffer / Custom Binary Protocol, Protobuf, and MessagePack for Websockets

序列化(毫秒)

Performance Analysis of JSON, Buffer / Custom Binary Protocol, Protobuf, and MessagePack for Websockets

反序列化(毫秒)

Performance Analysis of JSON, Buffer / Custom Binary Protocol, Protobuf, and MessagePack for Websockets

大數據基準

syntax = "proto3";

message TestMessage {
    string text = 1;
    uint32 num = 2;
}

每種序列化格式中訊息的位元組大小

Method Byte size (bytes)
JSON 329
Custom Binary Protocol 220
Protobuf 229
MessagePack 277

總時間(毫秒)

Performance Analysis of JSON, Buffer / Custom Binary Protocol, Protobuf, and MessagePack for Websockets

序列化(毫秒)

Performance Analysis of JSON, Buffer / Custom Binary Protocol, Protobuf, and MessagePack for Websockets

反序列化(毫秒)

Performance Analysis of JSON, Buffer / Custom Binary Protocol, Protobuf, and MessagePack for Websockets

MessagePack 在發送了大約 6600 則訊息時突然停止工作。

基準分析

在所有基準測試中,自訂二進位協定 總時間最快,並且在序列化時具有最小/最高效的位元組大小消息。然而,性能差異是顯著的。

令人驚訝的是,JSON 的的序列化時間 明顯快於 的序列化 自訂二進位協定。這可能是因為 JSON.stringify() 是使用 Node 實作的原生 c 以及使用 Bun 實作的原生 zig。使用 Node 時結果也可能會有所不同,因為使用 Bun 的 JSON.stringify() 比 Node 快 3.5 倍。

MessagePack 可能會更快,因為在這個基準測試中,我使用了官方的 javascript MessagePack 實作。還有其他可能更快的 MessagePack 實現,例如 MessagePackr。

感謝您的閱讀!


基準(用打字稿編寫):https://github.com/nate10j/buffer-vs-json-websocket-benchmark.git

在 Google 試算表中查看結果。

以上是Websockets 的 JSON、Buffer/自訂二進位協定、Protobuf 和 MessagePack 的效能分析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn