-
Notifications
You must be signed in to change notification settings - Fork 3
Diff Engines
SimpleRCS ์์๋ diff ์๊ณ ๋ฆฌ์ฆ์ด ์ฌ๋ฌ ๋ฒ ์๋ค. ์ ์ด๋ ๊ฒ ๋ง๊ณ , ์ค์ ๋ก ๋ญ๊ฐ ๋์๊ฐ๊ณ ์๊ณ , ๋๋จธ์ง๋ ์ ์์ง๋ ๋ถ์ด์๋์ง๋ฅผ ์ ๋ฆฌํ๋ค.
matchers.py๊ฐ import ์์ ์ ๋ฐฑ์๋ ํ๋๋ฅผ ์ ํ๊ณ , commit()/checkout()์
๊ทธ๊ฑธ ์ด๋ค. ์์๋ ์ด๋ ๋ค.
SIMPLE_RCS_MATCHER (์ค์ ๋ผ ์์ผ๋ฉด) -> Cython Myers (dmp -> ses) -> StreamSequenceMatcher
์ฆ C ํด์ฒด์ธ์ด ์์ด Cython ํ์ฅ์ด ๋น๋๋ผ ์์ผ๋ฉด ๊ธฐ๋ณธ ๋ฐฑ์๋๋ Cython
Myers(dmp_cython)์ด๊ณ , StreamSequenceMatcher๋ ๊ทธ๊ฒ ์์ ๋์ ํด๋ฐฑ์ด๋ค.
ํ์ธ์ ์ด๋ ๊ฒ ํ๋ค.
>>> from simple_rcs.matchers import ACTIVE_BACKEND
>>> ACTIVE_BACKEND
'dmp_cython'ํด๋ฐฑ์ด ์์ ํ์ด์ฌ Myers ์๋ฅ์ด(myersdiff_*.py)๋ก ๊ฐ์ง ์๋ ๊ฒ์ด ์ค์ํ๋ค โ
๊ทธ์ชฝ์ ์ด๊ธฐ ๊ตฌํ ๋ ํผ๋ฐ์ค์ด๊ณ ์๋ก ๋ค๋ฅธ ์
๋ ฅ์์ O(ND) ์ต์
์ ๋ฐ๋๋ค. ์ด๋ฆ์ผ๋ก๋
์ ํํ ์ ์์ง๋ง ํด๋ฐฑ ๋์์ ์๋๋ค.
SIMPLE_RCS_MATCHER์ ๋ฃ๋ ์ด๋ฆ์ tools/bench_diff.py๊ฐ ์ถ๋ ฅํ๋ id์ ๊ฐ์์,
๋ฒค์น๋งํฌ ๊ฒฐ๊ณผ๋ฅผ ๊ทธ๋๋ก ์ค์ ์ผ๋ก ์ฎ๊ธธ ์ ์๋ค.
SIMPLE_RCS_MATCHER=ses_cython uv run tools/srcs_commit.py FILE -m msg๋ฐฑ์๋๋ฅผ ๋ฐ๊พธ๋ฉด ์ ์ฅ๋๋ ๋ธํ ๋ฐ์ดํธ๊ฐ ๋ฌ๋ผ์ง๋ค. ๋๊ฐ์ด ์ ํจํ ์ฌ๋ฌ opcode ์ํ์ค ์ค ์ด๋ ๊ฒ์ ๊ณ ๋ฅด๋๋๊ฐ ๋ฌ๋ผ์ง๊ธฐ ๋๋ฌธ์ด๋ค. ๋ธ๋ก ํด์๋ ์ ์ฅ ํํ์ด ์๋๋ผ ๋ ผ๋ฆฌ์ ์ ์ฒด ๋ด์ฉ์ ๋ฎ์ผ๋ฏ๋ก, ๋ฐฑ์๋๋ฅผ ๊ฐ์๋ ๊ธฐ์กด ์ด๋ ฅ์ ๊ฒ์ฆ์ ๊ณ์ ํต๊ณผํ๋ค.
ํด์ ๊ธฐ๋ฐ ๊ทธ๋ฆฌ๋ ๋งค์นญ์ด๋ค. ๋๋ต ์ด๋ฐ ์์๋ก ๋์ํ๋ค.
- ์์ชฝ ์ํ์ค๋ฅผ ํด์๋ก ์ธ๋ฑ์ฑํด์ ๋์ผํ ๋ผ์ธ๋ค์ ๋น ๋ฅด๊ฒ ์ฐพ๋๋ค
- ๋งค์นญ๋๋ ๋ธ๋ก๋ค์ ๊ทธ๋ฆฌ๋ํ๊ฒ ๊ณจ๋ผ ๋ถ์ธ๋ค
- ๋งค์นญ ์ ๋๊ณ ๋จ์ "๊ต์ฒด(replace)" ๊ตฌ๊ฐ๋ง ํ์ค
difflib์ผ๋ก ๋ค๋ฌ๋๋ค
์ต๋จ ํธ์ง ๊ฑฐ๋ฆฌ๋ฅผ ์ํ์ ์ผ๋ก ๋ณด์ฅํ์ง๋ ์๋๋ค. ํ์ง๋ง O(N)์ ๊ฐ๊น๊ฒ ๋น ๋ฅด๊ณ , ์ค๋ฌด์์ ๋์ค๋ diff ๊ฒฐ๊ณผ๊ฐ ๋๋ถ๋ถ ์ถฉ๋ถํ ์์ฐ์ค๋ฝ๋ค. ์ปค๋ฐ ๊ฒฝ๋ก์ฒ๋ผ "์ ํํ ์ต์ ํธ์ง ์คํฌ๋ฆฝํธ"๋ณด๋ค "๋น ๋ฅด๊ณ ํฉ๋ฆฌ์ ์ธ ๋ธํ"๊ฐ ๋ ์ค์ํ ์๋ฆฌ์ ๋ง๋ ์ ํ์ด๋ค.
myersdiff.py, myersdiff_ses.py, myersdiff_dmp.py๋ ์ ํต์ ์ธ Myers
O(ND) ์๊ณ ๋ฆฌ์ฆ์ ์์ ํ์ด์ฌ ๊ตฌํ์ด๋ค. ์ด์ชฝ์ ์ต๋จ ํธ์ง ์คํฌ๋ฆฝํธ๋ฅผ
์ํ์ ์ผ๋ก ๋ณด์ฅํ๋ค โ Git์ xdiff๊ฐ ์ฐ๋ ๊ฒ๊ณผ ๊ฐ์ ๊ณ์ด์ ์๊ณ ๋ฆฌ์ฆ.
- SES ๋ณํ: ํ์ค์ ์ธ ์ต๋จ ํธ์ง ์คํฌ๋ฆฝํธ ๊ณ์ฐ
- DMP ๋ณํ: Neil Fraser์ diff-match-patch ๋ฐฉ์๋๋ก "middle snake" ์ ๋ต์ ์ฐ๋ ๋ฆฌ์คํธ ์ฌ๋ผ์ด์ฑ ๋์ ์คํ์ ์ ๊ณ์ ๋๊ฒจ์ ๊ณ์ฐํ๋ค. ๊ฒฐ๊ณผ์ ์ผ๋ก SES์ ๊ฐ์ ํธ์ง ์คํฌ๋ฆฝํธ๋ฅผ ๋ด์ง๋ง ๊ตฌํ ๋ฐฉ์์ด ๋ค๋ฅด๋ค.
O(ND) ์๊ณ ๋ฆฌ์ฆ์ ์ฝ์ ์ ๋ ์
๋ ฅ์ด ๋ง์ด ๋ค๋ฅผ ๋ ์ฑ๋ฅ์ด ๋๋น ์ง๋ค๋ ๊ฒ์ด๋ค
โ ์ต์
์ ๊ฒฝ์ฐ 500KB ์ ๋ ํฌ๊ธฐ์ 10% ์ ๋๋ง ๋ฌ๋ผ๋ ์ด ๋จ์๋ก ๋๋ ค์ง ์
์๋ค. StreamSequenceMatcher๊ฐ ์ด ์ผ์ด์ค์์ ํจ์ฌ ์์ ํ๋ค.
์ ๋ Myers ๊ตฌํ์ Cython์ผ๋ก ํฌํ
ํ ๊ฒ์ด๊ณ , ๊ธฐ๋ณธ ๋ฐฑ์๋๊ฐ ๋ฐ๋ก ์ด๊ฒ์ด๋ค.
pyproject.toml์ ext-modules์ ๋ฑ๋ก๋ผ ์์ด uv sync ์ ํจ๊ป ๋น๋๋๋ฉฐ,
setup.py๊ฐ ์ปดํ์ผ ์คํจ๋ฅผ ๊ฒฝ๊ณ ๋ก ๋ฎ์ถ๋ฏ๋ก ํด์ฒด์ธ์ด ์๋ ํ๊ฒฝ์์๋ ์ค์น๋
๋๋ค โ ๊ทธ ๊ฒฝ์ฐ StreamSequenceMatcher๋ก ํด๋ฐฑํ๋ค.
uv run tools/bench_diff.py --size 200 --diff-ratio 0.1 --runs 3
(2,409์ค, 10% ๋ณ๊ฒฝ):
| ์์ง | ์๊ฐ | Peak | ๋น๊ณ |
|---|---|---|---|
| Myers DMP (Cython) | 3.3 ms | 657 KB | ๊ธฐ๋ณธ ๋ฐฑ์๋ |
| Myers SES (Cython) | 3.9 ms | 787 KB | |
difflib (stdlib) |
26.8 ms | 338 KB | ๋ฒค์น๋งํฌ ๊ธฐ์ค์ |
StreamSequenceMatcher |
47.4 ms | 1,146 KB | ํด๋ฐฑ |
| Myers DMP (์์ ํ์ด์ฌ) | 983.6 ms | 660 KB | |
| Myers SES (์์ ํ์ด์ฌ) | 990.8 ms | 700 KB | |
| Myers (linked-list) | 1,295.2 ms | 7,101 KB |
์ฝ์ ์ ์ ์ด๋ค.
- Cython Myers๊ฐ ํด๋ฐฑ๋ณด๋ค ~14๋ฐฐ ๋น ๋ฅด๋ค. ๊ธฐ๋ณธ ๋ฐฑ์๋๊ฐ ๊ทธ์ชฝ์ธ ์ด์ ๋ค.
- ๊ฐ์ ์๊ณ ๋ฆฌ์ฆ์ธ๋ฐ ์์ ํ์ด์ฌ์ ~300๋ฐฐ ๋๋ฆฌ๋ค (983.6 โ 3.3 ms). ์ด๊ฒ
myersdiff_*.py๊ฐ ํด๋ฐฑ ๋์์ด ์๋ ์ด์ ๊ณ , ์ด ์ ๋ ฅ์ด O(ND)์ ๋ถ๋ฆฌํ ์กฐ๊ฑด(์๋ก ๋ค๋ฅธ ์ค์ด ๋ง์)์ด๋ผ ๊ฒฉ์ฐจ๊ฐ ๋ ๋ฒ์ด์ง๋ค. - ๋ค ์์ง ๋ชจ๋ opcode 3๊ฐ๋ก ๊ฐ์ ๊ฒฐ๊ณผ๋ฅผ ๋ธ๋ค โ ์๋ ์ฐจ์ด์ง ํ์ง ์ฐจ์ด๊ฐ ์๋๋ค.
์ด ์์น๋ ์ด ํ๋ผ๋ฏธํฐยท์ด ๋จธ์ ๊ธฐ์ค์ด๋ค. ๋ค๋ฅธ ํฌ๊ธฐยท๋ณ๊ฒฝ๋น์จ์์๋
tools/bench_diff.py๋ฅผ ์ง์ ๋๋ ค ํ์ธํ ๊ฒ.
docs/parser_benchmark.md๋ ๋ธ๋ก ํ์(regex vs no_regex) ์ด์ผ๊ธฐ์ด๊ณ ์ฌ๊ธฐ diff ์์ง ๋น๊ต์๋ ๋ค๋ฅธ ์ธก์ ์ด๋ค. ๊ทธ ๋ฌธ์์ "Cython 5~10๋ฐฐ"๋ ํ์๋ฅผ ํฌํ ํ์ ๋์ ์ถ์ ์น์ด๋ฉฐ ํ์ ํฌํ ์ ์ฐฉ์๋์ง ์์๋ค.
- ๊ธฐ๋ณธ๊ฐ ๊ทธ๋๋ก ๋๋ฉด ๋๋ค. ํด์ฒด์ธ์ด ์์ผ๋ฉด Cython Myers๊ฐ, ์์ผ๋ฉด
StreamSequenceMatcher๊ฐ ์๋์ผ๋ก ์กํ๋ค. - ํด๋ฐฑ์ผ๋ก ๋๊ณ ์๋๋ฐ ์ปค๋ฐ์ด ๋๋ฆฌ๋ค๋ฉด ํ์ฅ์ด ๋น๋๋๋์ง๋ถํฐ ๋ณธ๋ค
(
ACTIVE_BACKENDํ์ธ).SIMPLE_RCS_MATCHER๋ก ๊ฐ์ ํ ์๋ ์๋ค. - ์์ ํ์ด์ฌ Myers๋ฅผ ํ๋ก๋์ ์ ์ฐ์ง ๋ง ๊ฒ โ ์ด๋ฆ์ผ๋ก ์ ํ์ ๋์ง๋ง ๋ ํผ๋ฐ์ค ๊ตฌํ์ด๊ณ O(ND) ์ต์ ์ ๋ ธ์ถ๋๋ค.
- ์ ์๊ณ ๋ฆฌ์ฆ์ ๋ถ์ด๋ ค๋ฉด
tools/bench_diff.py์ ์ผ์ด์ค๋ฅผ ์ถ๊ฐํด ์ค์ธก๋ถํฐ ํ๋ ๊ฑธ ๊ถํ๋ค.
uv run tools/bench_diff.py์๊ฐ๊ณผ ๋ฉ๋ชจ๋ฆฌ(peak, tracemalloc ๊ธฐ์ค) ๋ ๋ค ๋น๊ตํด์ค๋ค. ํน์ ์์ง
ํ๋๋ก ๋ ๋ฒ์ ์ฌ์ด diff๋ฅผ ์ง์ ๋ณด๊ณ ์ถ์ผ๋ฉด:
uv run tools/srcs_diff.py <ํ์ผ> --engine ses # ๋๋ dmp, pydifflib, myers, difflibsrcs_diff --engine์ ์ด๋ฆ(ses/dmp/...)์ ์ด ๋๊ตฌ ์ ์ฉ์ด๊ณ ,
SIMPLE_RCS_MATCHER๊ฐ ๋ฐ๋ ์ด๋ฆ(ses_cython/dmp_cython/stream/...)๊ณผ๋
๋ค๋ฅด๋ค. ํท๊ฐ๋ฆฌ๋ฉด bench_diff.py๊ฐ ์ถ๋ ฅํ๋ id ์ชฝ์ด ํ๊ฒฝ๋ณ์์ฉ์ด๋ค.