Skip to content

Benchmark

This page publishes pinned size results from the Twilic benchmark harness (twilic/benchmark) and explains how to regenerate them. Measurements use @twilic/core 3.1.0 on Node.js 24 with the N-API backend. v3 schema-aware comparisons follow the v3 benchmark contract.

Machine-local timing varies; byte counts are deterministic for these fixtures and are the numbers used in Why Twilic and Comparison.

For interactive size comparisons in the browser, see the Playground. For fixture definitions, see Benchmark Fixtures. Raw JSON: /benchmark-snapshot.json.

Setup

bash
git clone https://github.com/twilic/benchmark
cd benchmark
pnpm install
pnpm --dir ../twilic-js build # if local @twilic/core artifacts are stale
pnpm bench -- --backend napi --time-ms 1500 --warmup-ms 500 --json-out results/pinned-snapshot.json

Options:

bash
pnpm bench -- --backend napi         # Node.js N-API (default)
pnpm bench -- --backend wasm # WebAssembly
pnpm bench -- --mode max # maximum benchmark suite
pnpm bench -- --twilic-vs-msgpack-only # hide JSON rows
pnpm bench -- --time-ms 3000 --warmup-ms 1000 # longer windows

What Is Measured

BenchmarkDescription
Dynamic sizesencode / encodeBatch vs MessagePack, CBOR, BSON, JSON
Bound streamencodeBoundStream (BOUND_STREAM, 0x0F) vs Protobuf/Avro raw streams
Schema batchencodeBatchWithSchema (SCHEMA_BATCH, 0x0E) vs the same Protobuf/Avro streams
Throughputencode/decode ops/s via tinybench (hardware-dependent)

Dynamic vs MessagePack (pinned)

Fixture set: single-small, batch-homogeneous-256, batch-mixed-256. No generic compression.

PayloadTwilicMessagePackCBORJSONvs MessagePackvs JSON
single-small1401401441800.0%22.2%
batch-homogeneous-2565,31619,50520,63328,20272.7%81.2%
batch-mixed-2569,79920,89322,20429,65953.1%67.0%

On one-shot values Twilic Dynamic matches MessagePack. On a homogeneous 256-record batch, key/shape interning cuts MessagePack size by about 73%.

Bound / Batch vs Protobuf / Avro (pinned)

Fixture: UserRecordV1 ×256 (schema-example.json style). Schema shared out of band.

FormatBytesNotes
Twilic BOUND_STREAM2,395compact schema-bound record stream
Twilic SCHEMA_BATCH798schema-order columnar batch
Protobuf stream3,458concatenated messages, no length prefixes
Avro raw stream2,852no OCF header
Twilic modevs Protobufvs Avro
BOUND_STREAM30.7% smaller16.0% smaller
SCHEMA_BATCH76.9% smaller72.0% smaller

Assumptions match §18.13: schema id / field layout are not re-sent inside the Protobuf/Avro streams; Twilic still carries an in-band schema id and framing for the chosen message kind.

Throughput (same pinned run)

Hardware-dependent; regenerate locally for CI claims. From the pinned N-API run:

OperationTwilicMessagePack
Single encode~440k ops/s~519k ops/s
Single decode~249k ops/s~687k ops/s
256-record batch encode~9.6k batches/s~6.1k batches/s

Batch encode favors Twilic once interning amortizes; single-record decode still trails MessagePack on this path.

Max Speed Tips

  • Use --backend napi on Node.js (highest throughput)
  • Use Node.js 24+ (project baseline)
  • Increase run windows for stability: --time-ms 3000 --warmup-ms 1000
  • Prefer SCHEMA_BATCH for shared-schema tabular datasets; use dynamic encodeBatch when no schema is available

State Patch Performance

On a hot object stream where a few fields change per tick, encodePatch sends only the delta after the first full frame. See the playground Encoded sizes view and harness session-patch-hot fixture.

Regenerating the pinned snapshot

bash
pnpm bench -- --backend napi --time-ms 1500 --warmup-ms 500 \
--json-out results/pinned-snapshot.json

Copy size tables into this page and into Why Twilic when fixtures or codecs change. Keep docs/public/benchmark-snapshot.json on the website in sync.

Released under the CC-BY-4.0 License.