marcelroed/gigatoken

GitHub: marcelroed/gigatoken

Gigatoken 是一款用 Rust 实现的超高速语言模型分词工具,在多核 CPU 上可达到 HuggingFace tokenizers 数百至上千倍的吞吐量。

Stars: 836 | Forks: 36

# Gigatoken
比 HuggingFace 的 tokenizers 快约 1000 倍,可直接替换。 *以 GB/s 的速度为你的文本数据进行 token化!* ![GPT-2 提速](https://static.pigsec.cn/wp-content/uploads/repos/cas/a5/a5cc06172a2a43875f72981b93d5fdb8fd86f06010649b5527650a18e80be421.svg) 请注意,HF tokenizers 和 tiktoken 都已经是在运行多线程的 Rust 代码了!
## 什么是 Gigatoken? Gigatoken 是用于语言建模的最快 tokenizer。 它支持广泛的 CPU 硬件,以及几乎所有常用的 tokenizer。 请参阅[基准测试](#benchmarks)部分,了解不同 tokenizer 和 CPU 的详细吞吐量数据。 ## 安装 ``` pip install gigatoken ``` ## 用法 Gigatoken 可以使用其自身的 API,或者以兼容模式与 HuggingFace Tokenizers 或 Tiktoken 一起使用。 ### 兼容模式(最简单) ``` import gigatoken as gt # 与现有 HuggingFace tokenizers 用法的最小差异(compatibility mode) hf_tokenizer = ... tokenizer = gt.Tokenizer(hf_tokenizer).as_hf() # tokenizer 可以在与 hf_tokenizer 相同的 context 中使用 tokens = tokenizer.encode_batch(["This is a test string", "And here is another"]) # 或者与 tiktoken 一起使用 tiktokenizer = ... tokenizer = gt.Tokenizer(tiktokenizer).as_tiktoken() # 现在的工作方式类似于现有的 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 | vs HF | vs 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 核) | Tokenizer | gigatoken | HF tokenizers | tiktoken | vs HF | vs 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 核) | Tokenizer | gigatoken | HF tokenizers | tiktoken | vs HF | vs 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 对整个未拆分的文件进行编码,因此在寻找拆分边界和自动并行化方面,它比其他 tokenizer 做了更多的工作。 HuggingFace tokenizers (`encode_batch_fast`) 获取前 100 MB 数据,而 tiktoken (`encode_ordinary_batch`) 获取前 1 GB 数据,两者都在 `<|endoftext|>` 处进行了预拆分。 这是公平的,因为对比的两个 tokenizer 都没有进行缓存,这意味着处理过程中的速度大致是均匀的。 Tiktoken 行目前仅填写了官方支持的 tokenizer。 最慢的行是基于Piece 的 tokenizer,它们在 Gigatoken 中没有得到很好的优化。 每一行都是一个独立的 tokenizer(具有相同的词表/合并/预分词器),在具有代表性的仓库上进行测量。 如果你在这里没有看到你的 tokenizer,它很可能是基于某个现有的 tokenizer。 例如: - **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 和 tokenizer 进行了过度优化吗?怎么会有这么快? 不,我对这些所有的组合都进行了极度的优化! 结果在各种 CPU(现代 x86 和 ARM)以及各种特定的 tokenizer 上都非常一致。 主要的改进在于,使用 SIMD、最小化分支和其他技巧,对通常外包给 Regex 引擎(预分词)的实现进行了深度优化,同时对预分词映射的缓存进行了深度优化(如果一个词以前出现过,就能高效地查找其编码后的 token)。 在这个领域,缓存是一个非常困难的问题,因为缓存增长得非常快,而且预分词的分布是长尾的。 一些性能提升还来自于最小化与 Python 的交互,以及避免线程之间的通信。 ### 问:我该如何快速检查我的 tokenizer 是否受支持? 你可以无需安装任何东西直接尝试!以下命令将针对给定的 HuggingFace 模型仓库验证并对 token 化过程进行计时: ``` # 下载您的数据 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](https://arxiv.org/pdf/2211.04325)(通常被认为是整个互联网,包含 130 万亿个 token)进行 token 化! 此示例使用了来自[此数据集](https://huggingface.co/datasets/stanford-cs336/owt-sample)的训练样本,并且 CLI 默认只截取文件的前 100MB 进行验证,以便与 HF 进行比较。 你可以通过 `uvx gigatoken bench --help` 查看这些标志的帮助信息。 在 macOS 上,你可能需要运行两次命令才能获得准确的读数,因为第一次运行总是会执行安全扫描,这会减慢 Rust 代码的执行速度。 ### 问:我发现了一个结果不一致/运行缓慢的用例,这正常吗? 很可能不正常!尽管经过了相当广泛的测试,但我手头并没有所有的用例,所以请在 [GitHub Issue](https://github.com/marcelroed/gigatoken/issues) 中报告你发现的任何问题,以便我尽快解决。