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 arrayobj-diffmicrodiff
Insert 1 element at the front110,001
Insert 1 element in the middle15,001
Remove 1 element from the middle15,000
Remove a run of 10 elements105,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.

Library1 propMixedDeep (9×)Array 10kTypedArray 10k
obj-diff1.48 µs1.84 µs2.34 µs2.47 ms0.019 ms
microdiff1.89 µs1.59 µs2.05 µs3.03 ms4.34 ms
deep-diff1.41 µs1.96 µs3.20 µs0.553 ms62.7 ms
deep-diff-ts1.11 µs1.25 µs2.01 µs0.742 ms5.67 ms
deep-object-diff4.08 µs3.42 µs3.67 µs8.44 ms8.31 ms
just-diff2.80 µs2.83 µs4.63 µs6.13 ms6.45 ms
@adobe/optimized-diff1.29 µs1.73 µs3.34 µs0.034 ms4.90 ms
recursive-diff2.20 µs2.48 µs4.33 µs3.29 ms
jsondiffpatch1.31 µs1.47 µs1.80 µs160 ms5.64 ms

On the TypedArray column obj-diff is roughly 173× faster than microdiff, because it compares typed arrays natively instead of walking them element-by-element.

A "diff" the size of the data isn't a diff. 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 propMixedArray 10k
obj-diff2.67 µs3.27 µs2.69 ms
jsondiffpatch3.81 µs7.20 µs161 ms
just-diff + just-diff-apply4.70 µs5.96 µs5.87 ms
deep-diff-ts4.01 µs5.45 µs1.58 ms
fast-json-patch2.71 µs3.89 µs2.60 ms

Honest trade-offs

Benchmarks are only useful when they include the cases you don't win:

  • Small and deeply-nested objects: obj-diff sits 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-diff produces. When the edit distance exceeds an internal cap, obj-diff falls 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.

Last updated on
Edit this page