tokie

Library Listed

Blazingly fast tokenizer - 50x faster tokenization, 10x smaller model files, 100% accurate drop-in replacement for HuggingFace

Checked · Sources

Downloads
711
Dependents
3
Stars
56
Forks
6
Contributors
3
Commits
243
Latest release
0.1.4 ·

About

From the project README ·

tokie

25 to 150 times faster than Hugging Face tokenizers, with the same token ids

Install • Quick start • Examples • Benchmarks • Why tokie?

[!CAUTION] tokie is in its alpha stage and might produce mis-aligned output. Please report any issues you encounter.

tokie is a Rust tokenizer library with Python bindings. It loads a Hugging Face tokenizer.json and encodes 25 to 150 times faster than Hugging Face tokenizers. It implements BPE, WordPiece, SentencePiece, and Unigram. The token ids match Hugging Face tokenizers.

benchmark

Install

Python
pip install tokie
Rust
[dependencies]
tokie = "0.2"

This tree is 0.2. crates.io and PyPI still serve 0.1.4, which does not have these calls.

Coming from Hugging Face tokenizers? See the migration guide.

Read the full README

Quick start

Python
import tokie

tokenizer = tokie.Tokenizer.from_pretrained("google-bert/bert-base-uncased")

ids = tokenizer.encode("Hello, world!")
print(ids.tolist())

plus = tokenizer.encode_plus("Hello, world!")
print(plus.tolist())
print(tokenizer.encode_plus("Hello", pair="world", truncate=True, pad=True, max_len=8).tolist())

print(tokenizer.decode(plus.tolist(), skip=True))

flat, ends = tokenizer.batch(["Hello!", "World"])
print(tokenizer.count_tokens("Hello, world!"))
print(tokenizer.count_tokens_batch(["Hello!", "World"]))

encode returns a uint32 array and leaves the template off. encode_plus writes the single-sequence template, so BERT uncased is [CLS], the ids, [SEP]. pair uses the pair template. truncate and pad share max_len, and that length counts the template ids. batch returns one id array and ends[i], the id count after text i. count_tokens returns the length encode would append. count_tokens_batch returns one length per text. Both leave the template off.

Rust
use tokie::{Plus, Specials, Tokenizer};

let mut tokenizer = Tokenizer::pretrained("google-bert/bert-base-uncased")?;
let mut ids = Vec::new();
tokenizer.encode("Hello, world!", &mut ids)?;

ids.clear();
tokenizer.encode_plus("Hello, world!", &mut ids)?;
tokenizer.encode_plus(Plus::new("Hello").pair("world").truncate(8).pad(8), &mut ids)?;

let mut text = String::new();
tokenizer.decode(&ids, &mut text, Specials::Skip)?;

let n = tokenizer.count_tokens("Hello, world!")?;
let lengths = tokenizer.count_tokens_batch(&["Hello!", "World"])?;

encode and encode_plus append into the Vec the caller owns. Clear it to start a new sequence. count_tokens and count_tokens_batch write ids into a buffer on the tokenizer and return the lengths. They leave the template off.

Examples

Batch
tokenizer.threads(4)
flat, ends = tokenizer.batch(["Short text", "A much longer piece of text"])
tokenizer.threads(4);
let lines = ["Short text", "A much longer piece of text"];
let mut ids = Vec::new();
let mut ends = Vec::new();
tokenizer.batch(&lines, &mut ids, &mut ends)?;

Text i is ids[prev..ends[i]]. ends[i] is ids.len() after that text.

Vocabulary
tokenizer.id("[SEP]")
tokenizer.token(101)
tokenizer.id("[SEP]");
tokenizer.token(101);
Save and load a .tokie file
tokenizer.save("model.tokie")
again = tokie.Tokenizer.open("model.tokie")
tokenizer.save("model.tokie")?;
let mut again = Tokenizer::open("model.tokie")?;

from_pretrained in Python, and pretrained in Rust, reads the repo's .tokie file and then tokenizer.json.

Benchmarks

All benchmarks run on an Apple M3 with tokie 0.1.3. The ids match Hugging Face tokenizers.

BPE encoding (GPT-2, Llama, Qwen, ModernBERT)

For tiktoken-style BPE, tokie uses a backtracking encoder on an Aho-Corasick automaton. It takes a greedy longest match in O(n), and it backtracks only when two adjacent tokens are not a valid pair. It does not merge one byte pair at a time. Pretokenization is the SIMD mask scanner in pretokie. That scanner classifies 64-byte blocks into piece-boundary bitmasks and runs above 1 GB/s per core. A repeated piece hits a per-worker cache instead of running BPE again. Work stealing splits the text so every core has a chunk. On one string this is 39 to 154 times faster than Hugging Face tokenizers.

BPE encoding speed

WordPiece (BERT, MiniLM, BGE, GTE)

WordPiece searches a vocabulary trie for the longest prefix. tokie stores that trie as a Double-Array trie and looks it up in O(n), with a BERT pretokenizer. On BERT this is 25 to 71 times faster than Hugging Face tokenizers, and the ids match.

WordPiece encoding speed

SentencePiece BPE and Unigram (Gemma, XLM-R, T5)

SentencePiece BPE merges in an order that is not topological. tokie uses a radix heap. Insert and pop are O(1) amortized because a later BPE rank is never smaller. On Gemma 3 this is 2 to 3 times faster than Hugging Face tokenizers.

Unigram models such as T5, XLM-RoBERTa, and ALBERT run Viterbi on each metaspace-delimited word and store the frequent units. Encoding one string is about 4 times faster than whole-string Viterbi. Batch encoding is about 3 times faster. The ids match. A vocabulary with a metaspace inside a token uses whole-string Viterbi.

SentencePiece BPE speed

Unigram encoding speed

Python benchmarks

All results on Apple M3, single-string encode, median of 10 runs.

tokie vs Hugging Face tokenizers
ModelText sizetokieHugging FaceSpeedup
BERT45 KB0.38 ms9.6 ms25x
BERT900 KB3.60 ms257 ms71x
GPT-245 KB0.13 ms8.6 ms65x
GPT-2900 KB1.44 ms191 ms133x
Llama 345 KB0.22 ms8.6 ms39x
Llama 3900 KB2.49 ms195 ms78x
Qwen 345 KB0.13 ms9.1 ms68x
Qwen 3900 KB1.36 ms210 ms154x
ModernBERT45 KB0.15 ms9.3 ms62x
ModernBERT900 KB1.42 ms211 ms149x
Gemma 345 KB4.54 ms9.7 ms2x
Gemma 3900 KB114 ms289 ms3x
tokie vs tiktoken (OpenAI models)
ModelText sizetokietiktokenSpeedup
cl100k (GPT-4)45 KB0.13 ms2.12 ms17x
cl100k (GPT-4)900 KB1.28 ms42.3 ms33x
o200k (GPT-4o)45 KB0.16 ms3.57 ms22x
o200k (GPT-4o)900 KB1.66 ms70.7 ms42x

The ids match on every model in these tables. Batch encoding is 17 to 36 times faster than Hugging Face tokenizers. encode_batch_flat returns one contiguous NumPy array.

Tokenizer loading

Loading tokenizer.json parses the JSON, builds the vocabulary, and, for BPE, builds the Aho-Corasick automaton. tiktoken parses its BPE data and compiles its regexes on every load. A .tkz file stores the Double-Array Aho-Corasick state, the normalized vocabulary, the encoder configuration, and, since v13, the added and special tokens. Loading that file is deserialization. from_pretrained reads the hub disk cache before the network and writes the compiled .tkz next to the snapshot, so a warm load does not use the network. A warm load is 13 to 44 times faster than Hugging Face tokenizers. BERT takes 2.8 ms. GPT-2 takes 6.2 ms.

ModeltokieHugging FaceSpeedup
BERT2.8 ms123 ms44x
GPT-26.2 ms151 ms24x
Llama 3.210.6 ms261 ms25x
cl100k11.1 ms244 ms22x
o200k23.2 ms310 ms13x

Tokenizer loading time

Verified tokenizers

A passing tokenizer is tested against the Hugging Face tokenizer on 1 MB of enwik8, about 300,000 tokens, in CI. Pass means every token matches. Not supported means tokie 0.2 refuses the file.

The full matrix is in docs/TOKENIZERS.md.

Full accuracy table, 75 models

ModelTypeStatus
GPT-2BPEPass
cl100kBPEPass, compared with tiktoken-rs
o200kBPEPass, compared with tiktoken-rs
RoBERTaBPEPass
Phi-2BPEPass
Phi-3 MiniBPEPass
ModernBERTBPEPass
CodeLlama 7BBPEPass
DeepSeek-V3BPEPass
DeepSeek-R1BPEPass
Gemma 2 2BSentencePiece BPENot supported, splits on a space
Gemma 3 4BSentencePiece BPENot supported, splits on a space
Llama 3.2 1BBPEPass
Llama 4 ScoutBPEPass
Mistral 7BBPENot supported, metaspace prepend is first
Mistral NemoBPEPass
Mixtral 8x7BBPEPass
NV-Embed-v2SentencePiece BPEPass
Qwen2 7BBPEPass
Qwen3 Embed 0.6BBPEPass
Qwen3 Embed 4BBPEPass
Qwen3 Embed 8BBPEPass
Qwen3 0.6BBPEPass
Qwen3 8BBPEPass
Qwen3 Coder 30BBPEPass
Qwen3.5 0.8BBPEPass
Qwen3.5 4BBPEPass
SmolLM2 135MBPEPass
StableLM 2 1.6BBPEPass
Nomic Embed v1WordPiecePass
BERT baseWordPiecePass
all-MiniLM-L6-v2WordPiecePass
all-MiniLM-L12-v2WordPiecePass
all-mpnet-base-v2WordPiecePass
BGE base en v1.5WordPiecePass
BGE large en v1.5WordPiecePass
BGE small en v1.5WordPiecePass
BGE en ICLBPEPass
BGE M3SentencePiece BPENot supported, regex replace
E5 base v2WordPiecePass
E5 large v2WordPiecePass
E5 small v2WordPiecePass
GTE baseWordPiecePass
GTE largeWordPiecePass
GTE smallWordPiecePass
GTE Qwen2 7BBPEPass
MS MARCO MiniLM L-4WordPiecePass
MS MARCO MiniLM L-6WordPiecePass
mxbai embed large v1WordPiecePass
mxbai embed 2d large v1WordPiecePass
mxbai embed xsmall v1WordPiecePass
deepset mxbai embed de largeUnigramPass
Jina v2 base enBPEPass
Jina v2 base codeBPEPass
Jina v3UnigramPass
Jina v4BPEPass
Cohere embed english v3BPEPass
Cohere embed english light v3BPEPass
Cohere embed multilingual v3UnigramPass
Cohere embed multilingual light v3UnigramPass
Voyage 3BPEPass
Voyage 3 largeBPEPass
Voyage 3 liteBPEPass
Voyage 3.5BPEPass
Voyage 3.5 liteBPEPass
Voyage Code 2BPEPass
Voyage Code 3BPEPass
Voyage Finance 2BPEPass
Voyage Law 2BPEPass
Voyage Multilingual 2BPEPass
Voyage Multimodal 3BPEPass
Snowflake Arctic Embed v2SentencePiece BPENot supported, regex replace
potion multilingual 128MUnigramNot supported, regex replace
T5 baseUnigramPass
XLM-RoBERTaSentencePiece BPEPass

69 pass. 6 are not supported, out of 75 listed. The passing tokenizers' ids match Hugging Face tokenizers.

Why tokie?

When I started building Chonkie, tokenization took longer than chunking.

Each pretokenization pattern has its own parser: GPT-2, cl100k, o200k, and BERT. The parser uses that pattern's character classes and does not run a general regex engine. Since 0.1.0 those parsers are a SIMD mask scanner. They classify 64-byte blocks into character-class bitmasks and take piece boundaries from the masks. Pretokenization passes 1 GB/s per core, more than 20 times the reference regexes.

No single library loaded every Hugging Face tokenizer. I tried AutoTikTokenizer and expected tiktoken's BPE to cover them. It does not. Tiktoken-style models need backtracking BPE. A merge order that is not topological needs a heap. SentencePiece needs a radix heap. WordPiece and Unigram each need their own encoder.

Tokenization runs in parallel when the split keeps token boundaries. chunk splits the text on those boundaries. Each core encodes one chunk, and the ids are concatenated. On 8 cores that is about 5 times faster.

.tkz skips rebuilding those structures at load time. tokenizer.json is parsed and validated, and the Aho-Corasick automaton is built. That build is slow for a large vocabulary. .tkz stores the automaton, the vocabulary, the configuration, and the added tokens as a flat binary. Loading deserializes that file. With the hub cache and a compiled file on disk, a warm BERT load is 2.8 ms and a warm GPT-2 load is 6.2 ms. Hugging Face tokenizers takes 120 to 310 ms on those two models.

Acknowledgements

tokie uses ideas from Hugging Face tokenizers, tiktoken, and GitHub's rust-gems for backtracking BPE via Aho-Corasick. It also uses chunk for SIMD text splitting, and gigatoken. Gigatoken's piece cache, mask-scanner pretokenization, and benchmarks shaped the 0.1.0 hot path.

Citation

If you use tokie in your research, please cite it as follows:

@software{tokie2025,
  author = {Minhas, Bhavnick},
  title = {tokie: Fast, correct tokenizer library for every Hugging Face model},
  year = {2025},
  publisher = {GitHub},
  howpublished = {\url{https://github.com/chonkie-inc/tokie}},
}
Runs on
GPU support unknown
What shows it
  • no dependency or feature matches a backend row
Native code
None found
  • No native code found
Python bindings
No Python binding crate found
License
Apache-2.0 OR MIT

Compare with similar projects

Order: strongest tier first, then most downloads, then name, with unmeasured values last. A library counts downloads over 30 days. An app counts binary downloads per release. An app with none counts its crate downloads over 30 days. ML Rust runs this catalogue, so its closest project always gets a row.

tokie beside the projects that share a topic with it
ProjectDownloadsDependentsStarsLatest releaseRuns on (GPU)Native codeLicense
tokie This page Listed

Blazingly fast tokenizer - 50x faster tokenization, 10x smaller model files, 100% accurate drop-in replacement for HuggingFace

711 downloads over 30 days 3 56 0.1.4 GPU support unknown No native code found Apache-2.0 OR MIT
whichlang Listed

A blazingly fast and lightweight language detection library for Rust.

52,880 downloads over 30 days 11 464 0.1.1 GPU support unknown No native code found MIT
splintr Listed

Tokenizer for Rust and Python: pure Rust by default, with 18 bundled vocabularies and four backends covering BPE, SentencePiece and WordPiece. Its output matches Hugging Face tokenizers id for id on 14 model families, including GPT-2, Qwen, Whisper, T5 and BERT.

Python bindings

8,204 downloads over 30 days 7 63 0.22.2 GPU support unknown Binds native code: pcre2-sys (feature pcre2) MIT
udpipe-rs Listed

Rust bindings for UDPipe - a trainable pipeline for tokenization, tagging, lemmatization and dependency parsing of CoNLL-U files

3,069 downloads over 30 days 2 5 0.2.0 GPU support unknown Build uses a native toolchain: cc Apache-2.0 OR MIT
kitoken Listed

Fast tokenizer for language models, supporting BPE, Unigram and WordPiece tokenization

2,949 downloads over 30 days 3 62 0.11.1 GPU support unknown No native code found BSD-2-Clause
tokenx-rs Listed

Fast token count estimation for LLMs at 96% accuracy without a full tokenizer

1,235 downloads over 30 days 3 3 0.1.0 GPU support unknown No native code found MIT
Why ListedNo archive, New or Featured rule places it, so it is Listed.

What it lacks for Featured

  • It has 2 active dependents and 2 new dependents in 365 days. Featured needs 11 active dependents or 9 new dependents in 365 days. The repository route reads 56 stars, 6 forks and 3 contributors. It needs 4,539 stars, 434 forks and 79 contributors together.

What else the check found

  • The last activity is on 3 Nov 2026, 6 days ago. The rules expect an update within 450 days. Dormancy rule

Exact rules: Listed, Featured library. The check on applied thresholds version 9.

Every rule outcome, as the check records it
  • expected-interval: version 0.1.4 base 450: E = 450 days
  • dormancy: last activity 2026-11-03, 6 days ago, E = 450: active
  • featured: not featured: active_users 2 < 11 and new_adopters 2 < 9 and repository route stars 56 (needs 4539), forks 6 (needs 434), contributors 3 (needs 79)
  • listed: listed: no archive, new or featured rule applies
Crates2 crates

Measurements and sources

Each signal row shows the raw value, its source and the fetch date. The Sources list gathers every distinct source on this page. Movement needs more history.

Sources6 sources
Adoption2 active dependents, 2 new dependents in 365 days1 not measured
Adoption measurements
SignalValueSourceFetchedCounts
Active dependents2crates.io database dumpYes
New dependents (365 days)2crates.io database dumpYes
Peak active dependents3crates.io database dumpYes
Active dependents as a share of the peak1crates.io database dumpYes
Binary downloads per releaseNot measuredYes
MaintenanceLatest human commit: 6 days ago3 not measured
Maintenance measurements
SignalValueSourceFetchedCounts
Latest human commit2026-11-03GitHub repositoryYes
Latest release2026-07-24crates.io database dumpYes
First release2026-03-23crates.io database dumpYes
Latest version0.1.4crates.io database dumpYes
Days the oldest outside issue waits for a reply184GitHub repositoryYes
Maintainer replies over 365 days1GitHub repositoryYes
Repository archivedfalseGitHub repositoryYes
Repository gonefalseGitHub repositoryYes
Release commit or manifest in the repotruecrates.io packageYes
Published docstruecrates.io database dumpYes
Latest release fails cargo checkNot measuredYes
Build check dateNot measuredYes
Build check toolchainNot measuredYes
SecurityUnpatched vulnerabilities: none
Security measurements
SignalValueSourceFetchedCounts
Unpatched vulnerabilities past the grace windownoneRustSec advisory databaseYes
RustSec unmaintained advisorynoneRustSec advisory databaseYes
Deprecation noticenonecrates.io database dumpYes
Deprecated requirementnonecrates.io database dumpYes
Distribution17 releases, 711 downloads over 30 days5 not measured
Distribution measurements
SignalValueSourceFetchedCounts
Releases17crates.io database dumpYes
Downloads over 30 days711crates.io database dumpYes
Binary downloads over the last 3 releasesNot measuredYes
Homebrew installs on request over 365 daysNot measuredYes
Repology repository familiesNot measuredYes
Docker pull growth over 90 daysNot measuredYes
Docker pulls at the growth baselineNot measuredYes
Every version a prereleasefalsecrates.io database dumpYes
Every version 0.0.xfalsecrates.io database dumpYes
Every version yankedfalsecrates.io database dumpYes
Crate deleted from crates.iofalsecrates.io database dumpYes
Repository56 stars, 6 forks, 3 contributors, 243 commits. Stars alone never qualify a project.
Repository measurements
SignalValueSourceFetchedCounts
GitHub stars56GitHub repositoryYes — one part of the repository route
GitHub forks6GitHub repositoryYes — one part of the repository route
Contributors with a GitHub account3GitHub contributorsYes — one part of the repository route
Commits on the default branch243GitHub repositoryNo
Repository's main crate left out of the entrynonecrates.io database dumpYes
Facts that never count
Facts that never count measurements
SignalValueSourceFetchedCounts
LicenseApache-2.0 OR MITcrates.io database dumpNo
Runs on, from listed crates and their direct dependenciesGPU support unknowncrates.io database dumpNo
What shows each backendno dependency or feature matches a backend rowcrates.io database dumpNo
Native code, from listed crates and their dependenciesNo native code foundcrates.io database dumpNo
Python bindings, from direct dependencies of listed cratesNo Python binding crate foundcrates.io database dumpNo
Crates that depend on a listed crate directly3crates.io database dumpNo

Corrections and review history

Report a correction

No correction applies to this entry.

Judgments

A judgment enters only through an approved issue and a merged pull request.

No judgment applies to this entry.