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.46 µs1.96 µs2.39 µs2.53 ms0.019 ms
microdiff1.86 µs2.26 µs2.46 µs3.10 ms4.40 ms
deep-diff1.39 µs1.97 µs3.33 µs0.563 ms63.1 ms
deep-diff-ts1.17 µs1.17 µs1.81 µs0.816 ms5.66 ms
deep-object-diff4.12 µs3.63 µs3.96 µs8.46 ms8.29 ms
just-diff2.68 µs3.03 µs5.00 µs6.24 ms6.61 ms
@adobe/optimized-diff1.25 µs1.96 µs3.41 µs0.034 ms5.10 ms
recursive-diff2.21 µs2.29 µs4.25 µs3.16 ms
jsondiffpatch1.45 µs1.73 µs2.20 µs163 ms5.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.

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.82 µs3.27 µs2.76 ms
jsondiffpatch3.94 µs7.75 µs164 ms
just-diff + just-diff-apply4.80 µs5.85 µs5.77 ms
deep-diff-ts4.01 µs5.22 µs1.34 ms
fast-json-patch2.30 µs3.83 µs2.79 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