Graven

Benchmarks

The reference implementation compared with FlatBuffers and Cap'n Proto on the same data. These numbers come from one machine and one run. Treat them as a measurement of this implementation, not of the format in general, and run them on your own hardware before relying on them. How we got to these numbers is in History.

Setup

Machine Intel Core i9-14900HX (32 threads), Linux 7.3 (CachyOS)
Compiler rustc 1.94.0, --release
Libraries flatbuffers 25.12.19 (flatc 25.12.19), capnp 0.27.3 (capnp 1.5.0), criterion 0.8.2
Run --warm-up-time 1 --measurement-time 3, median of the samples; run-to-run noise is about ±5%
Date 2026-11-13

To reproduce:

cargo bench -p graven-bench --bench codec
cargo run --release -p graven-bench --bin sizes
cargo run --release -p graven-bench --bin allocs

Scenarios

The same four messages are implemented in all three formats, and a test (crates/graven-bench/tests/equivalence.rs) checks that every implementation reads the same values for 20 random seeds per scenario.

scenario message
bank/N a transaction with N entries, each with an optional memo string
frame/N a game frame with N entities (position, velocity, health, flag)
telemetry one IoT reading: five scalars, one optional
chunk/N a stream chunk carrying N bytes of payload

Keeping the comparison fair

Size

Bytes per message. Smaller is better.

scenario Graven FlatBuffers Cap'n Proto Cap'n Proto packed
bank/1 118 168 144 106
bank/10 418 576 480 352
bank/100 3607 4536 4080 2977
frame/10 331 488 360 322
frame/500 15521 20104 16040 15827
telemetry, with humidity 24 48 40 28
telemetry, without humidity 24 48 40 23
chunk/64 87 112 104 90
chunk/65536 65559 65584 65576 65626

Graven is smaller than FlatBuffers and unpacked Cap'n Proto everywhere. Packed Cap'n Proto is smaller for messages with many small integers, and pays CPU time to pack and unpack.

Allocations

Allocations per written message, after warm-up.

scenario Graven (stream) Graven (known) FlatBuffers Cap'n Proto
bank/100 0 0 0 1
frame/500 0 0 0 1
telemetry 0 0 0 1
chunk/65536 0 0 0 1

Time

Nanoseconds per message, median. Lower is better; bold is the fastest.

Writing

scenario Graven (stream) Graven (known) FlatBuffers Cap'n Proto
bank/1 31.4 22.9 56.3 57.0
bank/10 85.6 56.6 343.1 82.1
bank/100 632.4 378.2 3130.9 414.7
frame/10 26.3 333.8 61.9
frame/500 777.4 17751.0 876.4
telemetry 6.0 21.5 33.8
chunk/64 9.7 20.0 44.0
chunk/65536 966.4 1731.6 2819.6

Only bank has a list whose length can be unknown; in the other rows the first column is the only Graven measurement.

Owned and batched messages:

scenario borrowed batched owned
telemetry 6.0 7.1 22.6
chunk/64 9.7 11.4 27.5
frame/500 777.4 951.9 926.0

An owned message pays an atomic reference count; batching amortizes it over many messages. A large owned message is also slower than a borrowed one because it needs fresh memory instead of rewriting the same cache-hot buffer.

Reading every field

scenario Graven Graven (trusted) FlatBuffers (unchecked) Cap'n Proto
bank/1 18.8 7.5 22.6 67.7
bank/10 57.9 23.6 79.3 111.9
bank/100 476.0 194.6 685.4 633.4
frame/10 58.9 122.6 96.1
frame/500 2414.5 6433.0 2296.1
telemetry 1.4 10.6 34.5
chunk/64 2.4 7.1 45.9
chunk/65536 2.4 7.0 45.9

Reading one field

scenario Graven FlatBuffers Cap'n Proto
bank/1, /10, /100 1.3–1.4 2.3 32.3
frame/10, /500 2.5–2.6 4.1–4.2 41.7–42.4
telemetry 1.3 2.2 32.7
chunk/64, /65536 1.2 2.3 32.6

Verifying and reading every field

scenario Graven FlatBuffers Cap'n Proto (lazy)
bank/1 28.7 114.6 66.5
bank/10 86.5 466.4 111.8
bank/100 712.4 4084.4 620.3
frame/10 67.5 557.6 94.9
frame/500 2705.1 27858.0 2250.2
telemetry 3.2 51.6 33.9
chunk/64 4.7 39.0 45.4
chunk/65536 4.6 38.7 45.3

The envelope

The optional envelope adds 24 bytes and a CRC32C over the whole message.

scenario write, owned write, enveloped verify + read one field from_bytes + read one field
telemetry 21.8 32.0 2.4 12.0
chunk/64 26.4 39.2 2.6 12.6
bank/10 70.3 108.8 61.1 87.9
chunk/65536 1567.2 2549.4 2.6 1622.1

Verification does not touch payload bytes, so a 64 KiB chunk verifies in under 3 ns; the CRC reads all of them. For small messages the envelope costs about 10 ns on each side.

CRC32C throughput, hardware instruction against the portable software version (used without std):

size hardware software
64 B 6.0 ns 11.7 ns
1 KiB 29.7 ns 347.7 ns
64 KiB 1614.1 ns (≈ 40 GB/s) 23463.0 ns (≈ 2.8 GB/s)

Where Graven loses