Skip to content

tokenizer: index added tokens by first char and length - #1443

Merged
michalharakal merged 2 commits into
developfrom
perf/sentencepiece-encode
Oct 24, 2026
Merged

michalharakal merged 2 commits into
developfrom
perf/sentencepiece-encode

Conversation

@michalharakal

Copy link
Copy Markdown
Contributor

AddedTokenMatcher (behind SpecialTokenSplitter and the inline matchers) compared every added token against every input position. Gemma's GGUF declares ~6,400 added tokens, so one 70-character chat turn cost about 450k regionMatches per encode.

A token can only match at a position if it starts with that position's character, and at most one token of each length can match there. The matcher now keeps the distinct token lengths per first character and does one map lookup per length, longest first. The semantics are unchanged: the longest match wins, and a rejected single_word match falls back to the next shorter token.

Measured (JVM, Gemma-3 270M fine-tune vocab, 262k pieces, 6,442 added tokens, five chat-turn prompts): 2.0 ms → 36 µs per encode, ids byte-identical for all five prompts. On a 32-bit ARM Android device the old encode took 36–166 ms per prompt.

Tests: skainet-frontend-text jvmTest 67/67 and skainet-io-core jvmTest 143/143. A new SpecialTokenSplitterTest case covers the single_word fallback to a shorter token with the same first character, and a candidate longer than the remaining text. It passes both with and without the change.

🤖 Generated with Claude Code

michalharakal and others added 2 commits October 24, 2026 09:36
AddedTokenMatcher compared every added token against every input position. Gemma's GGUF declares
~6,400 added tokens, so one 70-character chat turn cost ~450k region compares. A token can only match
at a position if it starts with that character, and at most one token per length can match, so the
matcher now keeps the distinct lengths per first character and does one map lookup per length.
Same results (longest first, single_word falls back to shorter); 2.0 ms -> 36 us per encode on the
JVM with a Gemma-3 270M vocab, ids identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@michalharakal
michalharakal merged commit 5b6ac57 into develop Oct 24, 2026
15 checks passed
@michalharakal
michalharakal deleted the perf/sentencepiece-encode branch October 24, 2026 14:49
michalharakal added a commit that referenced this pull request Oct 24, 2026
…nd on AVX2

CHANGELOG [0.62.2] - 2026-10-24: the schedule-driven split of the dense FP32
x·Wᵀ product (#1441), the runtime-dispatched AVX2/FMA lane for the same kernel
on x86-64 (#1442), the CoroutineSchedule.forRange empty-range fix, the
added-token index in the text front-end (#1443), and the dependabot bumps. The
#1441 entry, dropped from Unreleased by a develop merge, is restored.

VERSION_NAME, CITATION, Antora version, README bom coordinate, What's New and
contributors, the Android tutorial coordinates; regenerated operator and
kernel-support matrices (version stamp).
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