perf(pipeline): synthesise the metaspace splitter, with its exceptions baked in - #2303
Merged
ArthurZucker merged 1 commit intoAug 6, 2026
Conversation
…s baked in
Two changes, both removing an all-or-nothing check that one vocabulary entry
could defeat.
1. A declared pre-tokenizer that cannot cut is the `None` case.
gemma-3 declares `Split` on the literal " " while its normalizer has already
replaced every space with `▁`, so the splitter is installed, runs, and matches
nothing: the model receives the whole document as one span. tokenizers 0.23.1
does the same, so the ids are right -- but every word merges in one span and
the word cache can only hit on a byte-identical repeat of the document. It
cannot be decided from the pre-tokenizer alone, because whether it can match
depends on what the normalizer did first, so probe the pair: a declared
splitter that yields one span for a multi-word sample cannot cut anything.
Errs toward "it cuts", so a failed probe leaves the config untouched.
2. The cut guard is a set of bytes, not a yes/no.
`metaspace_cuts_are_safe` asked whether cutting was safe anywhere, and one
piece could take the answer from the whole vocabulary. gemma-3 holds exactly
one piece with an interior `▁` -- `>▁</` -- out of 262,144, so the splitter was
never installed. llama-2 has none, which is the only reason it worked there.
`metaspace_cut_guard` returns the 256-bit set of bytes that may precede an
interior `▁`. The synthesised splitter carries it and skips a cut after one of
those bytes; for gemma-3 that is the single byte `>`. Skipping a cut is always
sound: not cutting is what the reference does, so any subset of the safe cuts
gives the reference's ids, and only cutting where a piece spans the boundary
can change them.
gpt2, ns/byte, 10 kB documents each encoded once with the cache carried:
gemma-3/chat-mistral 17 -> 76 MB/s 4.48x
gemma-3/english 17 -> 58 MB/s 3.39x
gemma-3/xnli 29 -> 73 MB/s 2.48x
gemma-3/code 19 -> 41 MB/s 2.17x
gemma-3/chinese 70 -> 80 MB/s 1.14x
llama-2 and gpt2 are unchanged: llama-2 medians over three runs are 60/52/164/88
MB/s on english/code/chinese/xnli against 58/54/163/89 before, a spread of <= 3.
Their guard sets are empty, so the added test is one bitmap probe per `▁`.
Exactness: full benchmark matrix, 8 models x 30 corpora, 203 cells byte-exact
against tokenizers 0.23.1 and 29 mismatched -- the same albert (Unigram) cells
that already failed. The new test pins ids for text containing `>` before a
space, which a splitter that ignored the guard would cut through.
|
The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update. |
ArthurZucker
marked this pull request as ready for review
August 6, 2026 06:46
ArthurZucker
merged commit Aug 6, 2026
629d2a6
into
perf/metaspace-runs-pretok
29 of 41 checks passed
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.
Stacked on #2296.
Two changes, both removing an all-or-nothing check that a single vocabulary entry could defeat.
1. A declared pre-tokenizer that cannot cut is the
Nonecasegemma-3 declares
Spliton the literal" ", and its normalizer replaces every space with▁before that splitter runs. So it is installed, executes, and matches nothing — the model gets the whole document as one span:tokenizers0.23.1 does the same, so the ids were already right — but every word merges inside one span, and theWordCachecan only hit on a byte-identical repeat of the whole document. #2296 fixed this for llama-2 by matching onpre_tokenizer: null; gemma-3 declares a no-op splitter instead and slipped through.It cannot be decided from the pre-tokenizer alone, because whether it can match depends on what the normalizer did first. So probe the pair: a declared splitter that yields one span for a multi-word normalized sample cannot cut anything. Errs toward "it cuts", so a failed probe leaves the declared config exactly as written.
2. The cut guard is a set of bytes, not a yes/no
metaspace_cuts_are_safeasked whether cutting was safe anywhere, so one piece took the answer away from the whole vocabulary. gemma-3 holds exactly one piece with an interior▁out of 262,144:llama-2 has zero, which is the only reason it worked there.
metaspace_cut_guardnow returns the 256-bit set of bytes that may precede an interior▁, and the synthesised splitter carries it, skipping a cut after one of those bytes. For gemma-3 that is the single byte>— so it cuts everywhere except immediately after a>.Skipping a cut is always sound. Not cutting is what the reference does — it merges the whole span — so any subset of the safe cuts reproduces the reference's ids. Only cutting where a piece spans the boundary can change them, and that is exactly what the set forbids.
Numbers
10 kB documents each encoded once with the cache carried across them:
llama-2 and gpt2 are unchanged. llama-2 medians over three runs: 60/52/164/88 MB/s on english/code/chinese/xnli against 58/54/163/89 before — spread ≤ 3, so the one 0.89× I first measured was a single-sample artifact, not a regression. Their guard sets are empty, so the cost is one bitmap probe per
▁.Exactness
Full benchmark matrix, 8 models × 30 corpora: 203 cells byte-exact against
tokenizers0.23.1, 29 mismatched — the same albert (Unigram) cells that already fail. No new mismatch. 352 tests pass.the_synthesised_splitter_never_changes_the_idspins ids for text containing>before a space, which a splitter that ignored the guard would cut straight through.Prior art
gigatokenreaches the same conclusion by a different route: it pattern-matches gemma's config specifically (its comment names "gemma-3/4"), resolves it to "no metaspace config", then independently picksWordSplit::SpaceRunsfrom the vocab — and keeps across_prevbitset of predecessor bytes plus up to 32 crossing pieces, guarding those boundaries with a memcmp. This PR is the same idea with the exception baked into the splitter at load rather than checked as a special case, and byte-level rather than piece-level, which is more conservative and needs no memcmp.