Release v0.4.0: clustering 3.7× less CPU on the many-small-groups shape, bit-identical - #3
Merged
Merged
Conversation
…al (0.4.0) cluster_canonicals in the shape find-dup-defs issues it — thousands of calls per repository, most of two or three long canonical bodies, a few of hundreds — replayed call for call through the library: 9.78 s -> 2.66 s single-threaded, 1.30 s -> 0.38 s on 12 threads, every cluster and every min_sim bit identical to 0.3.5; the tool's ten output modes diffed identical on two corpora. - Automata are built lazily, only for the strings that are actually scanned (a pair scans one side against the other's automaton). - Only spanning edges are tested: candidates go most-similar-first in batches over a union-find, and a pair already connected is not tested. Edge tests 104k -> 32k. - The cluster minimum runs every uncached intra pair under the cluster's shared running minimum; a common prefix/suffix or a single long block decides a pair before (or during) the scan; the minimum pair is always computed exactly. - Narrow recursion windows (b side <= 8) use a direct row comparison instead of the chain walk; the early-exit recursions take the largest window first. - Builder: per-thread, four inline transitions per state, endpos ranges by two length-order passes instead of a tree walk, sorted copies only for the few large endpos sets (radix) instead of a merge-sort tree, a 32-byte scan slot whose inline transition continues along b. Breaking: gestalt_qualifies_ms takes (a, b, ..) instead of lengths; Sam::nodes() returns a Vec.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TL;DR
cluster_canonicalsв форме, которую реально выдаёт find-dup-defs (тысячи вызовов по 2–3 длинных канонических тела, несколько по сотням), стал в 3.7× дешевле по CPU (9.78 s → 2.66 s на одном потоке) и в 3.4× по wall (1.30 s → 0.38 s на 12 потоках) на реплее всех 2 892 вызовов одного прогона. Вывод — побайтово тот же: реплей сверен по кластерам и битамmin_simс бинарём 0.3.5 на трёх дампах, find-dup-defs прогнан в десяти режимах на двух корпусах против своего baseline.Что было
loпо автоматуhi: в группе из двух строк половина билдов не нужна, а билд — самая дорогая операция на строку.min_simсчитал внутрикластерные пары под cap'ом, но cap был локальным на задачу и стартовал с 1.0.Что стало
LazySams): строятся только дляhi-сторон пар, дошедших до скана.spanning_edges): кандидаты идут батчами от самых похожих (поquick_ratio), между батчами union-find, уже связанная пара не тестируется. Тестов рёбер 104k → 32k.assemble: все внутрикластерные пары без кэшированного ratio считаются под атомарным минимумом кластера; сначала пара с наименьшей верхней оценкой сидирует cap. Точность: пара-минимум всегда ≤ любого cap'а, который ей достаётся, и считается точно.M ≥ need/ratio > cap.longest_directдля окон сwb ≤ 8(16 и 64 — медленнее), max-heap по размеру окна в рекурсиях с ранним выходом.b.Что измерено и не дало: DP-оценка сверху по
fmatchкак reject внутри скана (на коде не срабатывает), уточнение bound'ов по позициям вниз по рекурсии, чередование 4 сканов (скан instruction-bound: +30 % хуже), 128-битное множество символов в слоте.Что изменилось
src/gestalt.rsSamбезnode/merge-sort-tree:edges,root_next,fast: Vec<ScanSlot>(32 B, align 32),pos_state,epos,chain_slot(32 B),big_sorted.Builderсinl: Vec<[u32; 8]>+ overflow-арена.matching_stats_bounded(in-scan accept),common_ends,prefix_fill,longest_direct,Window+HEAP_BUF,wlen-параметрlongest_in,gestalt_edge_bounded,capped_recursion.Breaking:
gestalt_qualifies_ms(a, b, sam_b, …)вместо(na, nb, …);Sam::nodes()возвращаетVec<[u32; 4]>.src/lib.rscluster_canonicals_chars=char_counts→candidate_pairs→spanning_edges→assemble;assemble(n, edges: Vec<(usize, usize, Option<f64>)>, chars, &LazySams);SERIAL_BELOW,EDGE_BATCH_MAX; ASCII-гистограмма вchar_counts.src/rationer.rs,src/gpu.rsGPU-пути отдают
assembleте жеOption<f64>(Some) иLazySams::built;CorpusGpu::buildчитаетnodes()какVec.Проверка
mono, lenses), 1 потокmonodefault (3 440 вызовов)mixedlenses (305 вызовов)min_sim) на трёх дампахmono --kinds lenses, фазаpass1-name/ весь прогон (hyperfine ×5)monodefault, фазаpass1-name/ весь прогонcargo test --release(+--features gpu)cargo clippy --release --all-targets(+--features gpu,instrument)