fix(newmm): resolve exponential BFS path explosion in ambiguous tokenization - #1319
Merged
Conversation
- Add visited set to _bfs_paths_graph to prevent re-exploring already-visited nodes, reducing worst-case BFS from O(2^n) to O(V+E) - Clear ambiguity graph dict after each commit point in _onecut to prevent unbounded accumulation - Remove unnecessary graph edge insertion in the no-candidate (elif) branch - Use Trie.prefixes(text, begin_pos) with start offset to avoid creating large string copies on every dictionary lookup call - Use re.Pattern.match(text, pos) to avoid string slices in regex matches - Add optional start parameter to Trie.prefixes() (backward-compatible) - Add test_newmm_ambiguous_performance regression test for issue #893 - Update segment() docstring and CHANGELOG.md Co-authored-by: bact <128572+bact@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Analyze and address newmm tokenization engine issue
fix(newmm): resolve exponential BFS path explosion in ambiguous tokenization
Mar 9, 2026
bact
marked this pull request as ready for review
March 9, 2026 13:58
|
2 tasks
Member
|
From a simple test, run locally, this PR speed up the tokenization. But newmm in normal mode (safe_mode = False) consumes way too much memory. Trying to fix this with PR #1323 |
Member
|
Got it. The real "issue" is that the dictionary loading was made lazy by #1186 so at the first run of the tokenizer, it needs to load the dictionary. Try switching the order of two tokenizers (safe_mode=False, then safe_mode=True) yields similar result. The first run will got the higher peak memory, because of the dictionary loading. So this is not a performance bug, but by design. |
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.



newmmwould hang or run extremely slowly on text with many ambiguous breaking points — e.g., repeated "ด้านหน้า" (splittable as "ด้าน"+"หน้า" or "ด้านหน้า", with "หน้าด้าน" also valid). Tokenizing 1,000 repetitions took ~1.4 s before; after this fix it takes ~9 ms.What do these changes do
visitedset to_bfs_paths_graph(O(2^n) → O(V+E))graph.clear()after each commit point in_onecut(both the ambiguous and no-candidate branches), replacing unbounded accumulationgraph[begin_pos].append(end_pos)in the no-candidate branchTrie.prefixes(text, start=0)gains an optionalstartoffset — avoids creating an O(n) string copy (text[begin_pos:]) on every dictionary lookup_PAT_NONTHAI.match(text[pos:])→_PAT_NONTHAI.match(text, pos)for the same reasontest_newmm_ambiguous_performanceregression test:"ด้านหน้า" * 1000must complete in < 5 sWhat was wrong
Three compounding bugs in
newmm._onecut/_bfs_paths_graph:graphnever cleared — edges from all prior commit steps accumulated indefinitely, growing the BFS search space over the lifetime of a_onecutcall.text[begin_pos:]slice per Trie lookup — created an O(n) string copy on every call, making the per-character work O(n) and the total O(n²) for large inputs.How this fixes it
The visited set makes BFS O(V+E); with
_MAX_GRAPH_SIZE = 50, that is effectively O(1) per commit. Clearing the graph after each commit keeps V and E small. Passing a start offset toTrie.prefixes()eliminates the string-copy overhead entirely.Trie.prefixes()change is backward-compatible (defaultstart=0preserves the existing API):To fix #893
Your checklist for this pull request
Original prompt
🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.