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
| Operation | Documents | Raw MMKV (same work, by hand) |
|---|---|---|
set(value) (5 field) | 22.3 µs | 18.8 µs |
get (5 field) | 20.2 µs | 9.1 µs |
update { } (whole-object, full get+set) | 44.2 µs | 28.9 µs |
update(prop, value) (1 field) | 3.9 µs | 2.3 µs |
delete (incl. set, 5 field) | 23.5 µs | 18.8 µs |
| field delegate write (1 field) | 5.2 µs | 2.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.