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 tsr benchmarks:collect.
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.08 µs | 1.53 µs | 1.71 µs | 2.73 ms | 0.02 ms |
| microdiff | 1.85 µs | 1.49 µs | 1.37 µs | 2.88 ms | 4.05 ms |
| deep-diff-ts | 1.19 µs | 1.21 µs | 2.04 µs | 0.834 ms | 6.37 ms |
| deep-object-diff | 4.10 µs | 3.39 µs | 3.64 µs | 8.29 ms | 8.01 ms |
| just-diff | 2.74 µs | 2.71 µs | 4.10 µs | 6.53 ms | 6.73 ms |
| @adobe/optimized-diff | 1.15 µs | 2.00 µs | 3.71 µs | 0.036 ms | 5.14 ms |
| recursive-diff | 2.17 µs | 2.18 µs | 4.03 µs | 2.89 ms | — |
| jsondiffpatch | 1.38 µs | 1.52 µs | 1.85 µs | 164 ms | 5.54 ms |
On the TypedArray column obj-diff is roughly 315× 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 | 1.98 µs | 2.85 µs | 2.88 ms |
| jsondiffpatch | 3.95 µs | 7.68 µs | 160 ms |
| just-diff + just-diff-apply | 4.32 µs | 5.45 µs | 5.81 ms |
| deep-diff-ts | 3.82 µs | 4.93 µs | 1.17 ms |
| fast-json-patch | 2.10 µs | 4.27 µs | 2.26 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.