Skip to content

perf: pick best merge over counts.items() - #34

Merged
AmitMY merged 2 commits into
mainfrom
perf/argmax-items
Jun 26, 2026
Merged

AmitMY merged 2 commits into
mainfrom
perf/argmax-items

Conversation

@AmitMY

@AmitMY AmitMY commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Choosing the best merge did a dict lookup per candidate:

nodes = max(counts, key=lambda k: (len(k) - 1) * counts[k])   # counts[k] = re-lookup

Iterating counts.items() hands the count straight to the key function:

nodes = max(counts.items(), key=lambda kv: (len(kv[0]) - 1) * kv[1])[0]

Same iteration order (insertion order either way), so the chosen merge and its tie-breaking are identical — the exact-merge parity tests cover this.

Output identical, 137 tests pass. Measured back-to-back (50 texts / 200 merges; time = best of 3):

main this PR
BPE 0.836s 0.813s (−2.8%)
BNE n=4 1.415s 1.280s (−9.5%)
Boundless 0.950s 0.920s (−3.2%)

BNE gains most — it has the most candidates (up to 4-grams), so the most saved lookups. Memory unchanged.

🤖 Generated with Claude Code

max(counts, key=lambda k: (len(k)-1)*counts[k]) did a dict lookup
counts[k] for every candidate. Iterating counts.items() hands the count
straight to the key function, skipping the re-lookup. Same iteration
order, so the merge chosen (and its tie-break) is identical.

~3% faster for BPE/Boundless, ~9% for BNE (more candidates -> more saved
lookups). Output identical; 137 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread complex_tokenization/trainer.py Outdated
break

nodes = max(counts, key=lambda k: (len(k) - 1) * counts[k])
nodes = max(counts.items(), key=lambda kv: (len(kv[0]) - 1) * kv[1])[0]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it possible to have them named in the lambda, like "lambda (key, value): ...."?

Addresses review: lambda tuple-unpacking (lambda (k, v): ...) was removed
in Python 3 (PEP 3113), so a small named function gives the readable
(nodes, count) unpacking instead.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@AmitMY

AmitMY commented Jun 26, 2026

Copy link
Copy Markdown
Contributor Author

Good idea, but Python 3 removed tuple-parameter unpacking (PEP 3113), so lambda (key, value): ... is a SyntaxError. Pulled it out into a small named function _merge_score(item) that unpacks nodes, count = item — same readability. Pushed in 48df39c.

@AmitMY

AmitMY commented Jun 26, 2026

Copy link
Copy Markdown
Contributor Author

Verified after the refactor: 137 tests pass and the merge-sequence digests are identical to baseline, so behavior is unchanged. Speed is the same as the inline-lambda version (a named function call has identical overhead) — vs main: BPE 0.846→0.818s (−3.3%), BNE 1.452→1.312s (−9.6%), Boundless 0.982→0.933s (−5.0%); memory unchanged.

@AmitMY
AmitMY merged commit ef57ef4 into main Jun 26, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant