Benchmarks

Documents decomposes a value into one {doc}::{field} key per field and runs a CBOR encode/decode per field. These microbenchmarks measure that overhead against using raw MMKV directly — encoding the same 5-field value once with CBOR and calling MMKV's byte API.

Sample type:

@Serializable
data class Profile(
    val id: Long,
    val name: String,
    val email: String,
    val age: Int,
    val active: Boolean,
)

Methodology

Each raw-MMKV baseline does the same underlying work as the Documents call it's paired with — the same field keys, the same per-field CBOR encode/decode calls, the same existence prefix-scan before a read, and the same prefix-scan-then-remove on clear — by hand, with no library machinery (no lock, no change bus, no composite encoder). This isolates what the abstraction costs on top of doing the identical raw operations manually, rather than comparing Documents' per-field decomposition against a single whole-object blob, which would be a different, unfair comparison.

The numbers are captured on-device — this is not a JVM-host benchmark, since MMKV is mmap-backed and needs a real device or simulator. They are device- and OS-specific, illustrative rather than authoritative, and are not run in CI.

iOS

OperationDocumentsRaw MMKV (same work, by hand)
set(value) (5 field)22.3 µs18.8 µs
get (5 field)20.2 µs9.1 µs
update { } (whole-object, full get+set)44.2 µs28.9 µs
update(prop, value) (1 field)3.9 µs2.3 µs
delete (incl. set, 5 field)23.5 µs18.8 µs
field delegate write (1 field)5.2 µs2.3 µs

Device: iPhone 17 Pro simulator · iOS 26.1 · median of 20k iterations, CBOR encoding. The whole-object update { } builder always does a full 5-field get followed by a full 5-field set, even when only one field changed — it's the read-modify-write path, and the raw baseline replicates that same round trip rather than a hand-optimized single-key patch, so the pairing stays honest. update(prop, value) is the true single-key write with no read — it comes in even below field delegate write since it skips the ReadWriteProperty object the delegate allocates. delete measures set + delete. Absolute timings are simulator (host CPU), not real hardware — compare Documents vs raw MMKV within this table, not against other platforms.

Android

Pending re-run. An instrumented harness already exists (connectedAndroidDeviceTest, same timing-harness shape as iOS); the numbers predate the CBOR switch and need a fresh on-device run before they can be published here.

See Field Decomposition for why update(prop, value) and field delegates measure so much cheaper than a whole-object update { }, or Read & Write for when to reach for each call shape.