Gigatoken
比 HuggingFace 的 tokenizers 快约 1000 倍,可直接替换使用。
以 GB/s 的速度对文本数据进行 token 化!
请注意,HF tokenizers 和 tiktoken 本身已经是在多线程 Rust 环境下运行的!
什么是 Gigatoken?
Gigatoken 是用于语言建模的最快 tokenizer。它支持广泛的 CPU 硬件,以及几乎所有常用的 tokenizer。详细吞吐量数据请参见基准测试部分,涵盖不同 tokenizer 和 CPU。
安装
pip install gigatoken
使用
Gigatoken 可以使用其自有 API,也可以在与 HuggingFace Tokenizers 或 Tiktoken 的兼容模式下使用。
兼容模式(最简单)
import gigatoken as gt
# Minimum change from existing HuggingFace tokenizers usage (compatibility mode)
hf_tokenizer = ...
tokenizer = gt.Tokenizer(hf_tokenizer).as_hf()
# tokenizer can be used in the same contexts as hf_tokenizer
tokens = tokenizer.encode_batch(["This is a test string", "And here is another"])
# OR with tiktoken
tiktokenizer = ...
tokenizer = gt.Tokenizer(tiktokenizer).as_tiktoken()
# Now works like existing tiktoken tokenizers
tokens = tokenizer.encode_batch(["This is a test string", "And here is another"])
我们投入了大量精力,确保在此模式下输出结果与使用 HuggingFace Tokenizers 得到的结果完全一致,但这会对性能造成不可忽视的损耗。即便如此,您仍能预期获得全面更快的性能,但无法达到使用 Gigatoken API 时 1000 倍的提升。
Gigatoken API(最快)
import gigatoken as gt
tokenizer = gt.Tokenizer("Qwen/Qwen3-8B") # Accepts HF model names
file_source = gt.TextFileSource(["owt_train.txt"], separator=b"<|endoftext|>")
tokens = tokenizer.encode_files(file_source)
使用 Gigatoken API 可以让 Rust 实现直接读取数据,尽可能跳过开销,同时实现最大程度的并行化。请记住,通过此 API 传递 Python 数据结构仍会产生从 Python 读取数据的开销。
基准测试
对 owt_train.txt(11.9 GB)的编码吞吐量 — AMD EPYC 9565 72 核处理器 x 2 路(144 核)
| Tokenizer | gigatoken | HF tokenizers | tiktoken | 对比 HF | 对比 tiktoken |
|---|---|---|---|---|---|
| GPT-2 | 24.53 GB/s | 24.8 MB/s | 36.0 MB/s | 989× | 681× |
| Phi-4 | 24.00 GB/s | 29.9 MB/s | — | 801× | — |
| GPT-OSS | 23.96 GB/s | 49.7 MB/s | 42.8 MB/s | 482× | 560× |
| OLMo 2 / 3 | 23.06 GB/s | 27.7 MB/s | — | 833× | — |
| Nemotron 3 | 22.79 GB/s | 49.4 MB/s | — | 462× | — |
| Qwen 3 | 22.16 GB/s | 34.2 MB/s | — | 648× | — |
| Llama 3 / 3.1 / 3.2 | 22.15 GB/s | 48.5 MB/s | — | 457× | — |
| GLM 5 | 20.97 GB/s | 74.8 MB/s | — | 280× | — |
| Llama 3.3 | 20.82 GB/s | 48.3 MB/s | — | 431× | — |
| Llama 4 | 20.77 GB/s | 72.7 MB/s | — | 286× | — |
| GLM 4 | 20.61 GB/s | 72.3 MB/s | — | 285× | — |
| Phi-4-mini | 20.05 GB/s | 27.6 MB/s | — | 726× | — |
| DeepSeek V3 / R1 / V4 | 19.69 GB/s | 26.2 MB/s | — | 750× | — |
| Qwen 2 / 2.5 | 19.12 GB/s | 27.7 MB/s | — | 691× | — |
| Kimi K2 | 18.85 GB/s | — | — | — | — |
| Qwen 3.5 / 3.6 | 15.49 GB/s | 27.7 MB/s | — | 558× | — |
| Gemma 4 | 4.82 GB/s | 334.1 MB/s | — | 14× | — |
| ModernBERT | 4.18 GB/s | 26.9 MB/s | — | 155× | — |
| Mistral 7B v0.3 | 3.57 GB/s | 354.7 MB/s | — | 10× | — |
| TinyLlama / Phi-3 (Llama 2) | 3.48 GB/s | 323.6 MB/s | — | 11× | — |
| CodeLlama | 3.47 GB/s | 347.4 MB/s | — | 10.0× | — |
| Gemma 3 | 3.43 GB/s | 357.2 MB/s | — | 9.6× | — |
| Gemma 1 | 2.51 GB/s | 342.2 MB/s | — | 7.3× | — |
在 owt_train.txt(11.9 GB)上的编码吞吐量 — Apple M4 Max(16 核)
| 分词器 | gigatoken | HF tokenizers | tiktoken | 对比 HF | 对比 tiktoken |
|---|---|---|---|---|---|
| GPT-2 | 8.79 GB/s | 6.9 MB/s | 62.8 MB/s | 1,268× | 140× |
| Nemotron 3 | 7.82 GB/s | 10.9 MB/s | — | 715× | — |
| Phi-4 | 7.76 GB/s | 7.7 MB/s | — | 1,012× | — |
| Llama 3 / 3.1 / 3.2 | 7.60 GB/s | 11.2 MB/s | — | 676× | — |
| OLMo 2 / 3 | 7.56 GB/s | 5.8 MB/s | — | 1,299× | — |
| Llama 3.3 | 7.50 GB/s | 15.7 MB/s | — | 479× | — |
| Phi-4-mini | 6.97 GB/s | 7.2 MB/s | — | 964× | — |
| Kimi K2 | 6.88 GB/s | — | — | — | — |
| Llama 4 | 6.81 GB/s | 11.6 MB/s | — | 590× | — |
| Qwen 2 / 2.5 | 6.37 GB/s | 5.8 MB/s | — | 1,105× | — |
| Qwen 3 | 6.36 GB/s | 6.9 MB/s | — | 918× | — |
| Qwen 3.5 / 3.6 | 6.31 GB/s | 6.3 MB/s | — | 994× | — |
| GPT-OSS | 6.20 GB/s | 20.2 MB/s | 87.2 MB/s | 306× | 71× |
| GLM 4 | 6.17 GB/s | 15.8 MB/s | — | 392× | — |
| DeepSeek V3 / R1 / V4 | 5.68 GB/s | 7.2 MB/s | — | 788× | — |
| GLM 5 | 5.55 GB/s | 12.2 MB/s | — | 456× | — |
| ModernBERT | 2.64 GB/s | 5.8 MB/s | — | 452× | — |
| Mistral 7B v0.3 | 1.99 GB/s | 95.1 MB/s | — | 21× | — |
| Gemma 4 | 1.82 GB/s | 85.2 MB/s | — | 21× | — |
| CodeLlama | 1.73 GB/s | 80.2 MB/s | — | 22× | — |
| TinyLlama / Phi-3 (Llama 2) | 1.69 GB/s | 80.1 MB/s | — | 21× | — |
| Gemma 1 | 1.42 GB/s | 85.7 MB/s | — | 17× | — |
| Gemma 3 | 1.38 GB/s | 82.2 MB/s | — | 17× | — |
在 owt_train.txt(11.9 GB)上的编码吞吐量 — AMD Ryzen 7 9800X3D 8 核处理器(16 核)
| 分词器 | gigatoken | HF tokenizers | tiktoken | 对比 HF | 对比 tiktoken |
|---|---|---|---|---|---|
| GPT-2 | 6.27 GB/s | 59.0 MB/s | 92.1 MB/s | 106× | 68× |
| Phi-4 | 6.09 GB/s | 55.4 MB/s | — | 110× | — |
| OLMo 2 / 3 | 6.06 GB/s | 55.4 MB/s | — | 109× | — |
| Phi-4-mini | 5.80 GB/s | 54.6 MB/s | — | 106× | — |
| GPT-OSS | 5.68 GB/s | 79.6 MB/s | 112.7 MB/s | 71× | 50× |
| Qwen 3 | 5.34 GB/s | 54.4 MB/s | — | 98× | — |
| Qwen 2 / 2.5 | 5.30 GB/s | 51.7 MB/s | — | 103× | — |
| Llama 3.3 | 5.26 GB/s | 79.9 MB/s | — | 66× | — |
| Llama 3 / 3.1 / 3.2 | 5.24 GB/s | 79.5 MB/s | — | 66× | — |
| Kimi K2 | 5.23 GB/s | — | — | — | — |
| Qwen 3.5 / 3.6 | 5.22 GB/s | 51.6 MB/s | — | 101× | — |
| Nemotron 3 | 5.20 GB/s | 79.0 MB/s | — | 66× | — |
| GLM 5 | 5.05 GB/s | 79.5 MB/s | — | 63× | — |
| GLM 4 | 5.04 GB/s | 79.5 MB/s | — | 63× | — |
| Llama 4 | 5.03 GB/s | 78.2 MB/s | — | 64× | — |
| DeepSeek V3 / R1 / V4 | 4.21 GB/s | 51.6 MB/s | — | 82× | — |
| ModernBERT | 2.84 GB/s | 52.1 MB/s | — | 54× | — |
| Mistral 7B v0.3 | 1.47 GB/s | 91.6 MB/s | — | 16× | — |
| Gemma 4 | 1.45 GB/s | 78.8 MB/s | — | 18× | — |
| CodeLlama | 1.38 GB/s | 85.2 MB/s | — | 16× | — |
| TinyLlama / Phi-3 (Llama 2) | 1.37 GB/s | 84.9 MB/s | — | 16× | — |
| Gemma 1 | 1.14 GB/s | 84.9 MB/s | — | 13× | — |
| Gemma 3 | 1.12 GB/s | 83.0 MB/s | — | 13× | — |
基准测试详情
OWT(OpenWebText)被选中是因为它大致能代表从 CommonCrawl 文档中提取后得到的文本。Gigatoken 对整个文件进行不分割的编码,因此相比其他分词器,它需要做更多工作来寻找分割边界并自动并行化。HuggingFace 分词器(encode_batch_fast)处理前 100 MB,tiktoken(encode_ordinary_batch)处理前 1 GB,两者都在 `<|endoftext|>` 处预先分割。这样做是公平的,因为被比较的分词器都不做缓存,这意味着处理过程中的速度大致均匀。Tiktoken 行目前仅针对官方支持的分词器填写。
最慢的行是基于 SentencePiece 的分词器,它们在 Gigatoken 中优化得不够好。
每一行代表一个不同的分词器(词汇表/合并规则/预分词器相同),在一个有代表性的代码仓库上测量。如果你在这里没有看到自己的分词器,它很可能基于某个现有的分词器。例如:
- Llama 3 / 3.1 / 3.2 — Llama 3 / 3.1 / 3.2、DeepSeek-R1-Distill-Llama、Hermes 3、Saiga 以及其他 Llama-3 微调模型
- Llama 3.3 — Llama 3.3、Llama-3.1-Nemotron-Nano-VL、SmolLM3、Kanana 1.5、jina-embeddings-v5、Ultravox
- Qwen 2 / 2.5 — Qwen 2 和 2.5(包括 Coder 和 VL)、Qwen3-Coder、Qwen3-VL、DeepSeek-R1 Qwen 蒸馏版、MiMo V2.5、MiniCPM-o 2.6、InternVL3
- Qwen 3 — Qwen 3(包括 Embedding 和 Reranker)、Qwen2.5-Omni、Qwen3-VL-Embedding、MiMo V2.5 Pro、jina-reranker-m0、pplx-embed、MOSS-TTS、Zeta
- DeepSeek V3 / R1 / V4 — DeepSeek V3 / V3.1 / V3.2、R1、V4 Flash 和 Pro、DeepSeek-VL2
- GLM 4 — GLM 4.1V、4.5 和 4.7
- GLM 5 — GLM 5 / 5.2 和 GLM-4.7-Flash
- Nemotron 3 — Nemotron 3 Nano、Super 和 Ultra
- Kimi K2 — Kimi K2 / K2.5 / K2.6 / K2.7、Kimi-Linear、Kimi-VL、Moonlight
- Phi-4-mini — Phi-4-mini 和 Phi-4-multimodal
- TinyLlama / Phi-3(Llama 2) — TinyLlama、Phi-3-mini、Phi-3.5-mini 和 Phi-3.5-vision(使用 Llama 2 词汇表)
- Gemma 3 — Gemma 3(270M–27B)和 EmbeddingGemma
- Gemma 4 — Gemma 4(dense、MoE 和 E 系列)和 DiffusionGemma
常见问题解答
问:你是不是针对某个特定的 CPU 和分词器过度优化了?为什么它这么快?
不,我对这些组合的每一种都过度优化了!结果在各类 CPU(现代 x86 和 ARM)以及不同分词器上都非常一致。
主要的改进在于对通常外包给正则表达式引擎(预分词)的实现,使用 SIMD 进行了重度优化,减少了分支判断并采用了其他技巧,同时还重度优化了预分词映射的缓存(如果某个词之前出现过,就高效地查找其编码后的 token)。缓存在这个领域是一个非常棘手的问题,因为缓存增长非常快,而且预分词的分布呈极长的尾部分布。
部分性能提升还来自于减少与 Python 的交互,以及避免线程间的通信。
问:如何快速检查我的分词器是否受支持?
你无需安装任何东西即可尝试!以下命令将针对给定的 HuggingFace 模型仓库验证分词功能并计时:
# Download your data
wget https://huggingface.co/datasets/stanford-cs336/owt-sample/resolve/main/owt_train.txt.gz # Just an example!
gunzip owt_train.txt.gz
uvx --with tokenizers gigatoken bench 'openai-community/gpt2' owt_train.txt \
--validate --doc-separator "<|endoftext|>"
cpu: Apple M4 Max, 16 cores
gigatoken: 1.432 s | 11920.51 MB at 8327.05 MB/s | 2701.65 Mtok at 1887.23 Mtok/s
hf: 16.250 s | 100.00 MB at 6.15 MB/s | 22.76 Mtok at 1.40 Mtok/s
gigatoken is 1353.13x faster than hf
validation OK: 20401 documents match
cpu: AMD EPYC 9565 72-Core Processor, 144 cores, 2 sockets
gigatoken: 0.486 s | 11920.51 MB at 24532.45 MB/s | 2701.65 Mtok at 5564.94 Mtok/s
hf: 4.033 s | 100.00 MB at 24.80 MB/s | 22.76 Mtok at 5.63 Mtok/s
gigatoken is 989.21x faster than hf
validation OK: 20401 documents match
按照我们在 EPYC CPU 上看到的速度,你可以在不到 6.5 小时内完成对整个 Common Crawl(通常被认为是整个互联网,包含 130 万亿个 token)的分词!
此示例使用了该数据集中的训练样本,CLI 默认会截取文件的前 100MB 用于验证并与 HF 进行比较。你可以通过 `uvx gigatoken bench --help` 查看这些标志的帮助信息。在 macOS 上你可能需要运行两次命令才能获得准确的读数,因为第一次运行总会执行安全扫描,这会拖慢 Rust 代码的速度。
问:我发现了不匹配或速度慢的用例,这是预期行为吗?
很可能不是!尽管经过了相当广泛的测试,但我手头并没有覆盖所有用例,所以请将你发现的任何问题通过 GitHub Issue 报告给我,以便我尽快处理。
引用
如果你在研究中使用 Gigatoken,请按如下方式引用:
@software{roed2026gigatoken,
author = {Marcel R{\o}d},
title = {{G}igatoken: SIMD and Cache Hierarchies for 1000x Faster Byte-Pair Encoding Tokenization on Modern CPUs},
url = {https://github.com/marcelroed/gigatoken},
year = {2026},
}
已知问题
- Python 迭代在 Rust 中处理,但使用了 ABI3,这比使用内部特定版本的 CPython API 要慢。未来我打算针对每个 Python 版本进行专门优化以减少此开销。早期实验显示,在受开销限制的用例中,速度可提升 2 倍。
- 文件输出功能尚未在 Gigatoken API 中实现。
- WordPiece 分词器尚未得到支持。
- 基于 SentencePiece 的分词方式远不如更常见的 BPE 分词器优化。目前这属于低优先级事项,因为主要是 Google 模型 / BERT 风格模型在使用 SentencePiece。
- Windows 系统尚未经过充分测试,因此目前建议优先使用 WSL。
AI 使用声明 本代码库的绝大部分代码均由人工编写,未使用任何 AI(可从项目的 Git 历史记录中看出)。在项目的最后阶段,AI 被用于辅助以下工作:
- 实现面向用户的 API
- 扩展兼容性,例如泛化并移植预分词器实现以支持更多分词器,以及填充/截断/Unicode 归一化等不太引人注目的功能
- 在 AVX512/AVX2/NEON 之间移植 SIMD 策略
- 最后的性能分析阶段,以及通过消除分支和优化预分词缓存层级结构带来的约 4 倍性能提升
- 重构与代码复用