Benchmarks
@opentf/obj-diff is built for two things other diffing libraries trade away: compact diffs and correct handling of native JavaScript values. Every number on this page is generated by benchmarks/collect.mjs and rendered straight from its output — nothing here is typed by hand. Regenerate with bun benchmarks/collect.mjs.
Diff size (fewer operations)
Op counts are deterministic — they don't depend on the machine. This is where compact array diffs pay off: positional differs emit one operation per shifted element, while obj-diff emits one operation per actual edit.
| Edit on a 10,000-element array | obj-diff | microdiff |
|---|---|---|
| Insert 1 element at the front | 1 | 10,001 |
| Insert 1 element in the middle | 1 | 5,001 |
| Remove 1 element from the middle | 1 | 5,000 |
| Remove a run of 10 elements | 10 | 5,000 |
Smaller diffs mean smaller audit logs, smaller sync payloads, and smaller undo stacks — see Use Cases.
Diff speed
Average latency per call across modern diffing libraries; each column uses a single unit so the cells are directly comparable, and the fastest cell in each column is highlighted.
| Library | 1 prop | Mixed | Deep (9×) | Array 10k | TypedArray 10k |
|---|---|---|---|---|---|
| obj-diff | 1.46 µs | 1.96 µs | 2.39 µs | 2.53 ms | 0.019 ms |
| microdiff | 1.86 µs | 2.26 µs | 2.46 µs | 3.10 ms | 4.40 ms |
| deep-diff | 1.39 µs | 1.97 µs | 3.33 µs | 0.563 ms | 63.1 ms |
| deep-diff-ts | 1.17 µs | 1.17 µs | 1.81 µs | 0.816 ms | 5.66 ms |
| deep-object-diff | 4.12 µs | 3.63 µs | 3.96 µs | 8.46 ms | 8.29 ms |
| just-diff | 2.68 µs | 3.03 µs | 5.00 µs | 6.24 ms | 6.61 ms |
| @adobe/optimized-diff | 1.25 µs | 1.96 µs | 3.41 µs | 0.034 ms | 5.10 ms |
| recursive-diff | 2.21 µs | 2.29 µs | 4.25 µs | 3.16 ms | — |
| jsondiffpatch | 1.45 µs | 1.73 µs | 2.20 µs | 163 ms | 5.55 ms |
On the TypedArray column obj-diff is roughly 206× faster than microdiff, because it compares typed arrays natively instead of walking them element-by-element.
Speed alone can mislead: a library that emits a single "the whole value changed" replacement (rather than an element-level diff) looks instant but produces a useless result. We discard any cell whose diff serializes to roughly the size of the data itself, showing — instead — that is why recursive-diff has no TypedArray time (it dumps the entire array, since it doesn't understand typed arrays). obj-diff computes a minimal, element-level edit script, which is why it is not always the lowest number on the plain-array row but stays honest.
Patch speed
End-to-end diff + apply round-trip, for libraries that can reconstruct the target from their diff. Some need a separate apply package — just-diff pairs with just-diff-apply, fast-json-patch applies its own RFC 6902 ops — whereas @opentf/obj-diff ships diff and patch together.
| Library (diff + patch) | 1 prop | Mixed | Array 10k |
|---|---|---|---|
| obj-diff | 2.82 µs | 3.27 µs | 2.76 ms |
| jsondiffpatch | 3.94 µs | 7.75 µs | 164 ms |
| just-diff + just-diff-apply | 4.80 µs | 5.85 µs | 5.77 ms |
| deep-diff-ts | 4.01 µs | 5.22 µs | 1.34 ms |
| fast-json-patch | 2.30 µs | 3.83 µs | 2.79 ms |
Honest trade-offs
Benchmarks are only useful when they include the cases you don't win:
Small and deeply-nested objects:
obj-diffsits within a whisker of the fastest positional differ — nanoseconds apart, irrelevant at real-world scale.Large plain numeric arrays: computing a minimal edit script (Myers LCS) costs more than a raw scan, so a positional differ can diff a big plain array faster — the payoff is the far smaller diff
obj-diffproduces. When the edit distance exceeds an internal cap,obj-difffalls back to index-by-index comparison to keep worst-case time and memory bounded. See Caveats.
The takeaway: reach for obj-diff when you care about diff quality and correctness across real JS values, not just raw nanoseconds on flat JSON.