Benchmarks · committed v1.0.1 full run

How Surp compares,
measured, not claimed.

A deterministic regression suite (surp-bench) encodes and decodes six fixed datasets with Surp, Surp + Dedup, JSON, MessagePack, CBOR, and Protocol Buffers, then writes the raw numbers below to docs/assets/bench/v1.0.1/. No vendor cherry-picking — every dataset, every operation, every format is shown.

Datasets
6
Formats
6
Iterations
10
Regressions
0
Methodology

The harness is deterministic on purpose.

Same seeds, same datasets, same iteration count. If a number moves, something in the codec moved.

Mode
full
Iterations per measurement
10
OS / arch
macos / aarch64
CPU cores
10
Rust
rustc 1.94.1 (2026-03-25)
Dataset version
1.0.0 (sha256-pinned)
PRNG
xorshift64, fixed seeds
Allocator
system (jemalloc optional)
Output dir
docs/assets/bench/v1.0.1/
Regressions detected
none
The Protobuf comparison uses a generic Value schema so it can represent the same schema-less payloads as Surp and JSON. Numbers shown are median with p95 and coefficient of variation, taken from summary.csv for the committed v1.0.1 run.
Datasets

Six shapes that stress different parts of a codec.

small_objects

100,000 records

100k flat objects with 6 fields each (id, name, email, active, score, level). Exercises per-record overhead and small-string handling.

string_heavy

10,000 records

10k records where ~60% of values are drawn from a 50-string pool. Designed to measure the win from string deduplication.

nested_deep

10-deep tree + 50-deep linear

A branching tree (depth 10, fanout 3) plus a 50-level linear chain. Stresses recursion, depth limits, and small-object framing.

binary_blobs

100 × ~64 KiB

Records carrying ~64 KiB random byte payloads. Exposes how each format treats raw bytes (native vs base64).

mixed_api_events

5,000 events

Synthetic GitHub-event-shaped objects with mixed scalar and nested fields. Models a realistic API payload.

numeric_heavy

dense numerics

Arrays dominated by integers and floats. Exposes varint efficiency and float framing.

Committed charts · v1.0.1

The same SVGs the harness commits to the repo.

Rendered straight from docs/assets/bench/v1.0.1/charts/. No re-projection, no re-aggregation — exactly what surp-bench wrote.

Chart · size

Serialized size

source
Bytes on disk per dataset. Lower is better. Surp lands smaller than JSON on every dataset; Surp + Dedup pulls ahead on string-heavy.
Serialized size chart for the Surp v1.0.1 benchmark run
Chart · encode

Encode throughput

source
Bytes written per second, median over 10 iterations. Higher is better. MsgPack edges ahead on the smallest payloads; Surp stays competitive across shapes.
Encode throughput chart for the Surp v1.0.1 benchmark run
Chart · decode

Decode throughput

source
Bytes parsed per second, median over 10 iterations. Higher is better. Surp's decode path is the most consistent across shapes.
Decode throughput chart for the Surp v1.0.1 benchmark run
Serialized size

Bytes on disk, per dataset.

Surp lands smaller than JSON on every dataset; ratio shown in the right-most column. Surp + Dedup pulls ahead on string-heavy and ties Surp on raw binary.

small_objects

surp/json = 0.82×
Surp
8.6 MB
Surp+Dedup
12.0 MB
JSON
10.5 MB
MsgPack
7.8 MB
CBOR
7.9 MB
Protobuf
11.4 MB

string_heavy

surp/json = 0.96×
Surp
1.0 MB
Surp+Dedup
668.3 KB
JSON
1.1 MB
MsgPack
925.8 KB
CBOR
927.0 KB
Protobuf
1.2 MB

nested_deep

surp/json = 0.87×
Surp
1.0 MB
Surp+Dedup
1.5 MB
JSON
1.2 MB
MsgPack
835.1 KB
CBOR
835.3 KB
Protobuf
1.4 MB

binary_blobs

surp/json = 0.75×
Surp
6.4 MB
Surp+Dedup
6.4 MB
JSON
8.5 MB
MsgPack
8.5 MB
CBOR
8.5 MB
Protobuf
6.4 MB

mixed_api_events

surp/json = 0.92×
Surp
1.9 MB
Surp+Dedup
2.8 MB
JSON
2.0 MB
MsgPack
1.7 MB
CBOR
1.7 MB
Protobuf
2.2 MB

numeric_heavy

surp/json = 0.63×
Surp
3.7 MB
Surp+Dedup
3.7 MB
JSON
6.0 MB
MsgPack
3.5 MB
CBOR
3.5 MB
Protobuf
5.0 MB
Encode throughput

Bytes written per second.

MsgPack is fastest to encode on the smallest payloads; Surp is competitive everywhere and notably wins decode (next chart) by a healthy margin.

small_objects

higher is better · MB/s
Surp
505 MB/s · 17.90 ms
Surp + Dedup
174 MB/s · 72.27 ms
JSON
630 MB/s · 17.54 ms
MessagePack
566 MB/s · 14.51 ms
CBOR
524 MB/s · 15.83 ms
Protobuf
467 MB/s · 25.57 ms

string_heavy

higher is better · MB/s
Surp
999 MB/s · 1.07 ms
Surp + Dedup
143 MB/s · 4.79 ms
JSON
917 MB/s · 1.21 ms
MessagePack
940 MB/s · 1.01 ms
CBOR
892 MB/s · 1.06 ms
Protobuf
615 MB/s · 1.98 ms

nested_deep

higher is better · MB/s
Surp
451 MB/s · 2.42 ms
Surp + Dedup
154 MB/s · 10.52 ms
JSON
517 MB/s · 2.43 ms
MessagePack
380 MB/s · 2.25 ms
CBOR
302 MB/s · 2.84 ms
Protobuf
47 MB/s · 32.35 ms

binary_blobs

higher is better · MB/s
Surp
2386 MB/s · 2.80 ms
Surp + Dedup
1896 MB/s · 3.52 ms
JSON
1726 MB/s · 5.16 ms
MessagePack
6290 MB/s · 1.42 ms
CBOR
6550 MB/s · 1.36 ms
Protobuf
35246 MB/s · 189.8 µs

mixed_api_events

higher is better · MB/s
Surp
1120 MB/s · 1.74 ms
Surp + Dedup
352 MB/s · 8.26 ms
JSON
1016 MB/s · 2.09 ms
MessagePack
1225 MB/s · 1.50 ms
CBOR
1003 MB/s · 1.83 ms
Protobuf
544 MB/s · 4.22 ms

numeric_heavy

higher is better · MB/s
Surp
618 MB/s · 6.35 ms
Surp + Dedup
685 MB/s · 5.72 ms
JSON
744 MB/s · 8.41 ms
MessagePack
747 MB/s · 4.88 ms
CBOR
642 MB/s · 5.65 ms
Protobuf
457 MB/s · 11.41 ms
Decode throughput

Bytes parsed per second.

Surp's decode path is the most consistent across shapes — CBOR loses ground on small_objects, Protobuf rebounds on binary_blobs because it stores bytes natively.

small_objects

higher is better · MB/s
Surp
452 MB/s · 19.98 ms
Surp + Dedup
240 MB/s · 52.25 ms
JSON
171 MB/s · 64.72 ms
MessagePack
133 MB/s · 61.63 ms
CBOR
63 MB/s · 131.49 ms
Protobuf
156 MB/s · 76.72 ms

string_heavy

higher is better · MB/s
Surp
560 MB/s · 1.91 ms
Surp + Dedup
222 MB/s · 3.08 ms
JSON
215 MB/s · 5.16 ms
MessagePack
214 MB/s · 4.44 ms
CBOR
125 MB/s · 7.59 ms
Protobuf
201 MB/s · 6.05 ms

nested_deep

higher is better · MB/s
Surp
230 MB/s · 4.75 ms
Surp + Dedup
201 MB/s · 8.09 ms
JSON
127 MB/s · 9.94 ms
MessagePack
94 MB/s · 9.08 ms
CBOR
58 MB/s · 14.71 ms
Protobuf
137 MB/s · 10.98 ms

binary_blobs

higher is better · MB/s
Surp
15547 MB/s · 430.0 µs
Surp + Dedup
15403 MB/s · 433.9 µs
JSON
6466 MB/s · 1.38 ms
MessagePack
19295 MB/s · 461.8 µs
CBOR
9459 MB/s · 942.1 µs
Protobuf
15965 MB/s · 418.9 µs

mixed_api_events

higher is better · MB/s
Surp
745 MB/s · 2.62 ms
Surp + Dedup
537 MB/s · 5.41 ms
JSON
270 MB/s · 7.88 ms
MessagePack
272 MB/s · 6.75 ms
CBOR
145 MB/s · 12.64 ms
Protobuf
228 MB/s · 10.06 ms

numeric_heavy

higher is better · MB/s
Surp
520 MB/s · 7.55 ms
Surp + Dedup
480 MB/s · 8.16 ms
JSON
246 MB/s · 25.46 ms
MessagePack
192 MB/s · 18.95 ms
CBOR
85 MB/s · 42.49 ms
Protobuf
196 MB/s · 26.67 ms
Full numbers

Every measurement, every format.

Median, p95, coefficient of variation, throughput, and serialized size for all 6 datasets × 6 formats × 3 operations. Mirrored from summary.csv.

FormatDatasetOpMedianp95CV%MB/sSize
surpsmall_objectsencode17.90 ms20.82 ms6.9504.68.6 MB
surpsmall_objectsdecode19.98 ms22.46 ms6.2452.28.6 MB
surpsmall_objectsroundtrip37.46 ms40.96 ms5.7
surp_dedupsmall_objectsencode72.27 ms90.69 ms11.8173.612.0 MB
surp_dedupsmall_objectsdecode52.25 ms65.38 ms12.2240.112.0 MB
jsonsmall_objectsencode17.54 ms20.01 ms6.3629.510.5 MB
jsonsmall_objectsdecode64.72 ms71.76 ms4.4170.610.5 MB
jsonsmall_objectsroundtrip85.05 ms95.08 ms5.3
msgpacksmall_objectsencode14.51 ms14.91 ms2.8566.37.8 MB
msgpacksmall_objectsdecode61.63 ms85.14 ms11.4133.47.8 MB
msgpacksmall_objectsroundtrip67.95 ms77.35 ms5.8
cborsmall_objectsencode15.83 ms16.33 ms1.1523.87.9 MB
cborsmall_objectsdecode131.49 ms196.48 ms18.663.17.9 MB
cborsmall_objectsroundtrip132.00 ms140.96 ms3.3
protobufsmall_objectsencode25.57 ms29.56 ms6.7466.811.4 MB
protobufsmall_objectsdecode76.72 ms92.80 ms7.3155.511.4 MB
protobufsmall_objectsroundtrip111.78 ms140.83 ms11.3
surpstring_heavyencode1.07 ms1.10 ms1.0999.41.0 MB
surpstring_heavydecode1.91 ms1.94 ms0.9559.51.0 MB
surpstring_heavyroundtrip2.98 ms3.09 ms1.3
surp_dedupstring_heavyencode4.79 ms4.91 ms1.3142.9668.3 KB
surp_dedupstring_heavydecode3.08 ms3.19 ms1.5221.9668.3 KB
jsonstring_heavyencode1.21 ms1.29 ms3.3916.61.1 MB
jsonstring_heavydecode5.16 ms5.40 ms1.8214.51.1 MB
jsonstring_heavyroundtrip6.55 ms6.77 ms2.2
msgpackstring_heavyencode1.01 ms1.72 ms22.5939.5925.8 KB
msgpackstring_heavydecode4.44 ms4.53 ms0.8213.5925.8 KB
msgpackstring_heavyroundtrip5.42 ms5.50 ms0.7
cborstring_heavyencode1.06 ms1.08 ms0.5892.1927.0 KB
cborstring_heavydecode7.59 ms7.99 ms1.8125.1927.0 KB
cborstring_heavyroundtrip8.76 ms8.87 ms0.7
protobufstring_heavyencode1.98 ms1.99 ms0.2614.51.2 MB
protobufstring_heavydecode6.05 ms6.07 ms0.3201.41.2 MB
protobufstring_heavyroundtrip8.11 ms8.18 ms0.8
surpnested_deepencode2.42 ms5.11 ms31.4451.01.0 MB
surpnested_deepdecode4.75 ms4.80 ms0.4229.91.0 MB
surpnested_deeproundtrip6.47 ms7.16 ms4.5
surp_dedupnested_deepencode10.52 ms10.77 ms1.0154.11.5 MB
surp_dedupnested_deepdecode8.09 ms9.07 ms5.5200.51.5 MB
jsonnested_deepencode2.43 ms3.28 ms13.8516.71.2 MB
jsonnested_deepdecode9.94 ms10.04 ms0.7126.61.2 MB
jsonnested_deeproundtrip13.02 ms14.32 ms3.6
msgpacknested_deepencode2.25 ms2.46 ms3.5379.7835.1 KB
msgpacknested_deepdecode9.08 ms9.21 ms1.694.2835.1 KB
msgpacknested_deeproundtrip11.74 ms17.50 ms15.3
cbornested_deepencode2.84 ms3.08 ms5.3301.5835.3 KB
cbornested_deepdecode14.71 ms14.83 ms0.858.1835.3 KB
cbornested_deeproundtrip17.94 ms18.48 ms1.2
protobufnested_deepencode32.35 ms33.53 ms3.946.61.4 MB
protobufnested_deepdecode10.98 ms11.18 ms1.2137.21.4 MB
protobufnested_deeproundtrip44.35 ms45.29 ms0.7
surpbinary_blobsencode2.80 ms3.22 ms14.62385.76.4 MB
surpbinary_blobsdecode430.0 µs432.5 µs0.215547.06.4 MB
surpbinary_blobsroundtrip3.62 ms3.66 ms2.0
surp_dedupbinary_blobsencode3.52 ms3.64 ms7.71896.26.4 MB
surp_dedupbinary_blobsdecode433.9 µs436.5 µs0.515402.66.4 MB
jsonbinary_blobsencode5.16 ms5.39 ms2.81725.88.5 MB
jsonbinary_blobsdecode1.38 ms1.43 ms1.26466.28.5 MB
jsonbinary_blobsroundtrip7.06 ms9.75 ms12.0
msgpackbinary_blobsencode1.42 ms1.48 ms3.76289.98.5 MB
msgpackbinary_blobsdecode461.8 µs472.6 µs0.819295.38.5 MB
msgpackbinary_blobsroundtrip2.23 ms2.54 ms4.9
cborbinary_blobsencode1.36 ms1.49 ms5.86549.78.5 MB
cborbinary_blobsdecode942.1 µs982.6 µs1.89458.88.5 MB
cborbinary_blobsroundtrip2.76 ms3.23 ms6.4
protobufbinary_blobsencode189.8 µs194.8 µs3.435245.86.4 MB
protobufbinary_blobsdecode418.9 µs463.0 µs3.415964.76.4 MB
protobufbinary_blobsroundtrip649.1 µs661.9 µs1.7
surpmixed_api_eventsencode1.74 ms1.75 ms0.31120.21.9 MB
surpmixed_api_eventsdecode2.62 ms2.66 ms0.8744.51.9 MB
surpmixed_api_eventsroundtrip4.37 ms4.40 ms0.4
surp_dedupmixed_api_eventsencode8.26 ms8.35 ms0.8352.12.8 MB
surp_dedupmixed_api_eventsdecode5.41 ms5.65 ms1.5537.32.8 MB
jsonmixed_api_eventsencode2.09 ms2.11 ms0.61016.32.0 MB
jsonmixed_api_eventsdecode7.88 ms7.94 ms0.4269.52.0 MB
jsonmixed_api_eventsroundtrip9.97 ms10.09 ms0.6
msgpackmixed_api_eventsencode1.50 ms1.53 ms1.01225.41.7 MB
msgpackmixed_api_eventsdecode6.75 ms6.79 ms0.4271.51.7 MB
msgpackmixed_api_eventsroundtrip8.25 ms11.49 ms11.9
cbormixed_api_eventsencode1.83 ms1.83 ms0.21002.91.7 MB
cbormixed_api_eventsdecode12.64 ms12.75 ms0.4145.01.7 MB
cbormixed_api_eventsroundtrip14.65 ms19.25 ms11.3
protobufmixed_api_eventsencode4.22 ms4.51 ms3.3543.52.2 MB
protobufmixed_api_eventsdecode10.06 ms10.55 ms2.0228.22.2 MB
protobufmixed_api_eventsroundtrip14.47 ms14.83 ms2.3
surpnumeric_heavyencode6.35 ms6.40 ms2.2617.83.7 MB
surpnumeric_heavydecode7.55 ms11.36 ms15.8519.53.7 MB
surpnumeric_heavyroundtrip14.81 ms15.40 ms2.4
surp_dedupnumeric_heavyencode5.72 ms6.23 ms3.3685.23.7 MB
surp_dedupnumeric_heavydecode8.16 ms8.34 ms1.2480.23.7 MB
jsonnumeric_heavyencode8.41 ms9.49 ms5.0743.86.0 MB
jsonnumeric_heavydecode25.46 ms25.72 ms1.1245.86.0 MB
jsonnumeric_heavyroundtrip34.74 ms34.99 ms0.8
msgpacknumeric_heavyencode4.88 ms4.92 ms0.5746.53.5 MB
msgpacknumeric_heavydecode18.95 ms19.10 ms0.5192.33.5 MB
msgpacknumeric_heavyroundtrip24.55 ms24.70 ms0.8
cbornumeric_heavyencode5.65 ms6.29 ms4.9642.23.5 MB
cbornumeric_heavydecode42.49 ms42.87 ms4.685.43.5 MB
cbornumeric_heavyroundtrip49.61 ms51.33 ms4.5
protobufnumeric_heavyencode11.41 ms11.71 ms4.6457.35.0 MB
protobufnumeric_heavydecode26.67 ms27.21 ms2.7195.75.0 MB
protobufnumeric_heavyroundtrip38.18 ms39.01 ms3.5
Reproduce locally

Run it yourself.

All datasets are seeded; numbers will scale to your hardware but the rank order should hold.

# CI fast mode — minutes
cargo run -p surp-bench --release -- \
--mode ci --output bench/results
# Full mode — committed in repo
cargo run -p surp-bench --release -- \
--mode full --output bench/results/full
# Python harness
python bench/python/bench_surp.py