Performance measurements

These development measurements describe one workload on one machine. They are not throughput guarantees or comparisons with another object store.

Conditions

  • AMD Ryzen 9 7950X3D, Linux x86-64, OpenJDK 27.

  • A 32 MiB byte array from java.util.Random, seed 42.

  • Five warmups and seven timed samples per operation. Reported times are medians.

  • SHA2-256, with FastCDC minimum 256 KiB, target 1 MiB and maximum 4 MiB.

  • Files under /tmp on ZFS, with warmed caches.

  • Native-storage measurements at commit b55b618. Catalog operations were not timed.

Input creation is outside timing. Publication timings exclude preparation. Fresh-file publication includes database creation and commit syncs. Preparation timings include temporary staging and cleanup but do not force durable writes.

Preparation

Operation MiB/s Milliseconds

Read cached file

9,514

3.363

Read cached file and hash twice

1,057

30.287

Prepare cached file

631

50.697

Prepare byte array with spill

536

59.703

Try zlib on random file content

58

556.209

The read controls use a reusable 64 KiB buffer. The two-hash control updates two SHA2-256 states but does not find chunk cuts. Random content compresses poorly, so the zlib case pays for compression attempts without a useful reduction in stored size.

Publication and reads

Operation MiB/s Milliseconds

Publish to a fresh memory database

791

40.480

Publish to a fresh file database

380

84.179

Publish an existing payload again

1,298

24.659

Read a cached payload with verification

1,589

20.140

Open and read a 32 KiB range

Not reported

2.197

Publish after changing one byte

845

37.887

Duplicate publication still checks the prepared source. Opening a stream checks the complete manifest. A range read verifies every covering chunk.

The one-byte edit reused 24 of 25 chunks, covering 32,898,057 bytes. It appended 672,605 bytes including metadata. Its throughput counts the full logical payload, not just the new bytes.

Small payloads

Each sample uses 2,000 distinct 256-byte payloads in a memory database. Publication uses one transaction for the whole batch.

Operation Median batch time Objects/s

Publish

43.549 ms

45,900

Open and read

19.107 ms

104,700

Median-sample thread allocation was 166,706,416 bytes for publication and 80,516,520 bytes for reads. These are cumulative allocations, not peak live heap. Read allocation includes the per-stream buffer used by InputStream.transferTo.

The results do not measure one durable transaction per small object.

Memory

Fresh JVM runs prepared, published and verified a 256 MiB file-backed payload with a 128 MiB heap limit. The source generator used a reusable 64 KiB buffer.

Payload Peak process RSS in three runs, KiB

256 bytes

227,744 / 218,632 / 200,828

256 MiB

220,676 / 226,344 / 244,792

RSS includes the JVM, compiled code and native allocations. It is neither peak live heap nor filesystem cache usage. Startup variation prevents a precise estimate of per-object memory from these runs.

The benchmark and large-payload check also completed on OpenJDK 17.0.20.1. The tables above report OpenJDK 27.

Limits of the evidence

The throughput runs use no independent JVM forks, CPU pinning or confidence intervals. They do not establish uncached device bandwidth. Concurrent-reader throughput and durable per-object latency have not been measured.

See Run measurements for reproduction commands.