0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

「同じJSONを256件送ると約73%小さくなる」— MessagePackの次を狙う Twilic を公開しました!

0
Last updated at Posted at 2026-05-23

はじめに

API や WebSocket、バッチ連携で「形は同じなのに、キー名を毎回送り続けている」ことに違和感を覚えたことはありませんか?

私は JSON や MessagePack をベンチマークで比較しながら調べていましたが、同じスキーマのレコードを大量に送るときは、どうしてもバイト数が伸びると感じていました
Protobuf ほどスキーマを固定したくない場面でも、もっと小さく・速く送りたい!

そこで Twilic というバイナリフォーマットと、そのマルチ言語実装を GitHub の twilic で公開しました!

この記事では、Twilic が何を解決するのか、どう使うのか、そしてベンチマークでどれくらい効くのかをまとめます

Twilic とは

Twilic は、構造化データ向けのコンパクトなバイナリフォーマットです

  • MessagePack のように スキーマなしでも 任意の JSON 相当の値をそのまま encode / decode できる
  • 同じ形のオブジェクト・同じキー・同じ文字列が繰り返されるほど、自動的に小さくなる!
  • スキーマが分かる場合は Bound プロファイルでフィールド名を送らない
  • 256 件まとめて送る Batch、前回メッセージとの差分 Patch も同じフォーマット族で扱える

名前は古英語 twilic(現代英語の twill =綾織)に由来します
繰り返す糸を織り込むように、繰り返すデータ構造を 1 本のバイナリに「織り込む」イメージです(プロトコルの README 参照)

仕様と各言語実装は twilic/twilic のプロトコル monorepo にまとまっています(仕様は CC-BY-4.0、runtimes/ 配下の実装は MIT)
Rust をコアに TypeScript / Go / Zig / Python など多数のランタイムがあります

なぜ MessagePack だけでは足りないことがあるか

MessagePack は「JSON をバイナリにした」感覚で扱いやすく、単発のオブジェクトでは Twilic と同等サイズになることも多いです

一方、次のようなデータでは キー名・型情報・文字列の繰り返しがそのままコストになります

  • テレメトリやログの 同型レコードのバッチ
  • WebSocket で流す 同じ shape のイベント
  • userId / active / country のように 列が揃った表形式データ

Twilic は v2 ワイヤで shape_def / key_ref / str_ref や row_batch / col_batch / state_patch などを使い、1 ショットでも学習しつつ、セッションがあればさらに圧縮する設計です(詳細は SPEC.md)

5 分で試す(Node.js)

npm パッケージは @twilic/core です(v3 系、Twilic v2 ワイヤ)

pnpm add @twilic/core
import {
  encode,
  decode,
  createSessionEncoder,
  type TwilicValue,
} from "@twilic/core";

const value: TwilicValue = {
  id: 1001n,
  name: "alice",
  active: true,
};

const bytes = encode(value);
const roundtrip = decode(bytes);

const session = createSessionEncoder();
session.encode(value);
const patch = session.encodePatch({ ...value, name: "alicia" });

Node.js では N-API(Rust コア)が自動で使われ、bigint もそのまま扱えます
ブラウザでは init({ prefer: "wasm" }) で WASM を読み込みます

Hono で Content-Type ごとバイナリ body を扱うなら @twilic/hono も用意しています

import { Hono } from "hono";
import { twilicParser, twilicResponse } from "@twilic/hono";

const app = new Hono();

app.post("/users", twilicParser(), async (c) => {
  const input = c.var.twilicBody;
  return twilicResponse(c, { ok: true, received: input });
});

Go では go get github.com/twilic/twilic-go、ソースは twilic/twilic の runtimes/(rust / javascript / go / zig など)を参照してください

ベンチマーク:どれくらい小さくなるか

benchmark リポジトリの fixture と同じデータで、ローカル(Node 24 + N-API)計測した エンコード後サイズが次のとおりでした

payload Twilic MessagePack CBOR BSON vs MessagePack
single-small(1 件) 140 B 140 B 144 B 198 B 0%
batch-homogeneous-256 5,316 B 19,505 B 20,633 B 28,241 B 72.75% 削減
batch-mixed-256 9,799 B 20,893 B 22,204 B 32,956 B 53.10% 削減
session-patch-hot(初回) 89 B 89 B 93 B 120 B 0%

ポイントは次の 2 つです

  1. 単発 1 件では MessagePack と互角(悪化しない設計)
  2. 同型 256 件では MessagePack の約 1/4 サイズまで縮む!

スループットも、同じベンチで encode / decode は MessagePack と同オーダーで、セッション patch 系はホットパス最適化の余地が大きい領域です
数値はマシン依存なので、興味があれば手元で再現してください

git clone https://github.com/twilic/benchmark.git
cd benchmark
pnpm install
pnpm --dir ../twilic-js build   # 初回のみ
pnpm bench -- --twilic-vs-msgpack-only

ブラウザでフォーマット比較だけ試したい場合は playground があり、Twilic / MessagePack / CBOR / BSON / JSON に加え、スキーマあり比較(Protobuf・Avro・FlatBuffers・Arrow IPC)も載せています!

設計で大事にしていること

仕様で特に意識しているのは次の点です

1. スキーマなしで始められる

最初の 1 通は self-describing
繰り返しが見えたら shape / key / string の interning に移行します
Protobuf を導入するほどではないサービスでも、そのまま試せます

2. スキーマがあればさらに詰められる

encodeWithSchema(Bound プロファイル)でフィールド名を送らず、型に沿ったビットパックができます
playground の「Schema-first」タブで他フォーマットとのサイズ比較ができます

3. バッチと patch が同じ家族

  • Batch: 256 件を 1 パケットに(row / column 両方)
  • Session patch: 前回から変わったフィールドだけ送る

リアルタイム同期やゲーム状態、在庫差分など、「毎回フル JSON」がもったいない場面向きです

4. 決定的なワイヤ

同じプロファイル・同じ学習状態なら同じバイト列になるよう、再現性を仕様に含めています

向いている場面・向いていない場面

向いている

  • 同じ JSON shape の API / WebSocket / ジョブキュー
  • エッジ〜クラウド間の帯域が気になるバッチ
  • MessagePack は使っているが、配列バッチだけまだ大きいと感じているチーム

向いていない(Non-Goals より)

  • すべての JSON / 既存プロトコルを置き換えること
  • アプリケーション意味論の定義
  • 単発の小さなペイロードだけを延々送る(そこでは MessagePack で十分なことが多い)

リポジトリ一覧

仕様・共有 conformance・多言語実装は twilic/twilic の monorepo(runtimes/<language>/)に集約しています

リポジトリ 内容
twilic/twilic 仕様・conformance・多言語 runtimes
twilic/hono Hono ミドルウェア
twilic/express Express ミドルウェア
twilic/fastify Fastify プラグイン
twilic/fetch Fetch ヘルパー
twilic/axios Axios インターセプター
twilic/benchmark ベンチハーネス
twilic/playground ブラウザ比較 UI
twilic/explorer エンコード可視化
twilic/website 公式サイト・ドキュメント
twilic/cli CLI
twilic/ai AI ストリーム / エージェント記録
twilic/examples 利用例
twilic/media-kit ロゴ・ブランドガイド

※ プロジェクトはもともと Recurram という名前で開発を始め、v3 で Twilic にリネームしています

今後

  • 各言語実装の API 安定化と相互運用テストの拡充
  • フレームワーク連携の拡充とドキュメント整備
  • コミュニティからのフィードバックを仕様プロファイルに反映

バイナリフォーマットは「仕様が読める」「数字で比較できる」「すぐ試せる」の 3 点が大事だと思っています
Twilic はその 3 つを揃えるために、仕様とランタイムは monorepo に、ベンチや playground・フレームワーク連携は製品リポジトリとして分けています

まとめ

Twilic は MessagePack 的な使いやすさと、繰り返しデータ向けの圧縮を同じフォーマットに載せた OSS です
同型 256 件のバッチでは MessagePack 比 約 73% サイズ削減(上記ベンチ)まで見えます!

スター・Issue・PR・実プロダクトでの試用、どれも歓迎です!
同じ「繰り返し JSON」に悩んでいる方のフィードバックが、次のワイヤ改善に直結します!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?