Skip to content

Payload Size

Size is the axis shorn leads. Every number is from pnpm bench:all on Node v22.23.1, Apple M4 Pro, macOS arm64.

CodecPersonUnicode personNested event100 events
shorn831434,135
Avro / avsc831444,249
SchemaPack932444,235
msgpackr shared records1033524,993
Protobuf.js reflection1134575,684
cbor-x shared records1438625,972
@msgpack/msgpack234611511,281
msgpackr plain254812111,881
cbor-x plain265012211,972
JSON355816316,148

shorn is smallest or tied on every fixture. Two caveats apply: bare shorn, Avro, and Protobuf payloads require the correct schema outside the payload, and shared-record sizes exclude their record table.

The Unicode row shows where the savings come from. shorn removes field names, tags, and syntax, but it does not shrink string content. Payloads dominated by structure and numbers can be about 75% smaller than JSON. Payloads dominated by free text see smaller gains.

CodecRawGzipBrotli q6
shorn4,231,777924,494603,189
SchemaPack4,331,777937,975597,776
Avro4,344,802935,333632,034
msgpackr records4,995,3391,114,738513,630
Protobuf.js5,781,774980,559618,608
JSON16,340,6861,474,952985,919

shorn is smallest raw and under gzip. It is not smallest under Brotli: msgpackr records are 17% smaller, and SchemaPack is slightly smaller. Brotli compresses their repeated metadata particularly well.

Compression CPU for the shorn payload: gzip 78.99 ms, gunzip 4.12 ms, Brotli q6 42.19 ms, unbrotli 5.25 ms. Brotli was smaller and faster here, which reverses below.

CodecRawGzipBrotli q6
shorn6,987,3332,337,0802,084,918
SchemaPack7,087,3332,540,0702,089,259
Avro7,100,3582,446,3692,187,026
msgpackr records7,750,8952,598,2882,235,235
Protobuf.js8,487,3302,508,5281,943,144
JSON18,996,2423,001,7862,580,139

shorn is smallest raw and under gzip, and second under Brotli. Compared with JSON, it is 63% smaller raw, 22% smaller under gzip, and 19% smaller under Brotli. Protobuf compresses best under Brotli in this fixture despite being 21% larger raw.

Compression CPU: gzip 111.90 ms, gunzip 8.29 ms, Brotli q6 163.15 ms, unbrotli 12.47 ms.

Do not claim that shorn is smallest under every compressor. Brotli can make msgpackr records or Protobuf smaller than shorn. The measured claim is narrower: shorn is smallest raw and under gzip in both 100,000-event profiles.

  • Declare non-negative integers. ZigZag doubles the magnitude, so an int crosses every varint boundary at half the value.
  • Use enums, not free strings, for closed sets — one varint index against length plus content.
  • Prefer literals where a field is constant: zero bytes.
  • Choose a compact timestamp form. ISO-8601 is ~25 bytes, epoch millis 6–7. shorn will not choose for you — see rich types.
  • Ask whether every payload needs the fingerprint. It is 3 bytes: keep it for stored data; for pinned RPC, or when fingerprintHex rides in a header, the payload can stay bare.
Terminal window
pnpm bench # small fixtures
pnpm bench:large # 100,000 repetitive events
pnpm bench:entropy # 100,000 high-entropy events
pnpm bench:all # everything, plus correctness checks

Before quoting these results externally, regenerate every table with one bench:all run. Do not combine rows from different runs or machines.