-
Notifications
You must be signed in to change notification settings - Fork 3
Diff Engines en
There are several diff algorithms living inside SimpleRCS. Here's why there are so many, which one actually runs, and why the rest are still sitting there.
commit()/checkout() compute deltas through exactly one engine:
StreamSequenceMatcher in pydifflib.py. Everything else — the Myers
family, the Cython versions — exists purely for comparison through
tools/bench_diff.py.
Greedy hash-based matching. Roughly:
- Hash-index both sequences to find identical lines quickly
- Greedily stitch together the matching blocks
- Run the leftover "replace" regions (the parts that didn't match)
through standard
difflibfor refinement
It doesn't mathematically guarantee the shortest edit distance. But it's close to O(N) and fast, and in practice most real-world diffs come out looking perfectly reasonable anyway. For a commit path, where "fast, sensible delta" matters more than "provably minimal edit script," it's the right tradeoff.
myersdiff.py, myersdiff_ses.py, and myersdiff_dmp.py are pure-Python
implementations of the classic Myers O(ND) algorithm. This family
mathematically guarantees the shortest edit script — the same family
of algorithm Git's xdiff uses.
- SES variant: standard shortest-edit-script computation
- DMP variant: follows Neil Fraser's diff-match-patch approach, using the "middle snake" strategy but passing offsets around instead of slicing lists. Produces the same edit script as SES, just implemented differently.
The catch with O(ND) algorithms is that they degrade badly when the two
inputs are very different — worst case, something around 500KB with 10%
changed can take seconds. StreamSequenceMatcher is much safer in that
scenario.
Direct Cython ports of the two Myers implementations above. Benchmarks
show they're 15–26x faster than their pure-Python equivalents, but
they're not connected to the commit path today. They're registered in
pyproject.toml's ext-modules, so they get built as part of uv sync.
Summarizing the benchmark results in docs/parser_benchmark.md:
- For small delta blocks (a few hundred bytes), the difference between parsers/diff engines isn't really noticeable — well within noise.
- The gap widens as blocks get bigger (1.6KB → 92KB). That's mostly about block parsing rather than the diff engine itself, but the same underlying pattern — "the gap widens on large input" — applies to diff engine choice too.
- Cython porting realistically buys 5–10x over pure Python, mostly by eliminating Python bytecode dispatch overhead in the inner loop.
- Keep using what's there.
StreamSequenceMatcheris already proven on the production path, and for ordinary line-level text edits, the practical difference from a minimal edit script is negligible. - The Myers family (especially the Cython versions) starts to matter when you have a specific need for a provably minimal edit script, or you're recomputing a large batch of deltas offline.
- If you want to wire in a new diff algorithm,
tools/bench_diff.pyalready has the comparison harness — add your case there and measure before deciding anything.
uv run tools/bench_diff.pyCompares both time and peak memory (via tracemalloc). To see a diff
between two versions through one specific engine:
uv run tools/srcs_diff.py <file> --engine ses # or dmp, pydifflib, myers, difflib