Skip to main content

Extremely fast bert tokenizer

Project description

FlashTokenizer

Tokenizer Library for LLM Serving

EFFICIENT AND OPTIMIZED TOKENIZER ENGINE FOR LLM INFERENCE SERVING

FlashTokenizer is a high-performance tokenizer implementation in C++ of the BertTokenizer used for LLM inference. It has the highest speed and accuracy of any tokenizer, such as FlashAttention and FlashInfer, and is 4-5 times faster than BertTokenizerFast in transformers.

[!NOTE]
FlashBertTokenizer is 4x faster than transformers.BertTokenizerFast and 15.5x faster than transformers.BertTokenizer.

Banner



FlashTokenizer includes the following core features

[!TIP]

  • Implemented in C++17 and is fastest when built with GNUC.

    • MacOS: g++(14.2.0) is faster than clang++(16.0.0).
    • Windows: g++(8.1.0)-MinGW64 is faster than Visual Studio 2019.
    • Ubuntu: g++(11.4.0) is faster than clang++(14.0.0).
  • Equally fast in Python via pybind11.

  • Blingfire was difficult to use in practice due to its low accuracy, but FlashBertTokenizer has both high accuracy and high speed.

  • Although it's only implemented as a single thread, it's capable of 40K RPS in C++ and 25K RPS in Python, and it's thread-safe, so you can go even faster with multi-threading if you need to.

News

[!IMPORTANT]
[Mar 14 2025] The performance of the WordPieceTokenizer and WordPieceBackwordTokenizer has been improved using Trie, which was introduced in Fast WordPiece Tokenization. Using FastPoolAllocator in std::list improves performance in SingleEncoding, but it is not thread-safe, so std::list<std::string> is used as is in BatchEncoding. In BatchEncoding, OPENMP is completely removed and only std::thread is used.

[Mar 10 2025] Performance improvements through faster token mapping with robin_hood and memory copy minimization with std::list.

Container Elapsed Time Max RPS Description
std::list 10.3458 39660.5 When combining containers, std::list is the fastest because it doesn't allocate extra memory and just appends to the end.
std::deque 15.3494 26473.1 Because it is organized in chunks, it requires memory allocation even when combining containers and has the slowest performance due to its low cache hit rather than contiguous memory.
std::vector 11.9718 33913.3 It allocates new memory each time when combining containers, but it has a high cache hit for fast performance.

Token Ids Map Table Performance Test.

Token and Ids Map used the fastest unordered_flat_map as shown in the test results below.

Map Elapsed Time(Access)
✅ robin_hood::unordered_flat_map<std::string, int> 0.914775
robin_hood::unordered_node_map<std::string, int> 0.961003
robin_hood::unordered_map<std::string, int> 0.917136
std::unordered_map<std::string, int, XXHash> 1.1506
std::unordered_map<std::string, int> 1.20015

XXHash is implemented as follows.

#define XXH_STATIC_LINKING_ONLY
#define XXH_INLINE_ALL
#include "xxhash.h"
struct XXHash {
size_t operator()(const std::string &s) const {
     return XXH3_64bits(s.data(), s.size());
 }
};

[Mar 09 2025] Completed development of flash-tokenizer for BertTokenizer.

1. Installation

Requirements

  • g++ / clang++ / MSVC
  • python3.9 ~ 3.12

Install from PIP

pip install -U flash-tokenizer

Install from Source

git clone https://github.com/NLPOptimize/flash-tokenizer
cd flash-tokenizer
pip install -r requirements.txt
python -m build # `*.whl` file will be created in the `dist` folder.

2. Usage

from flash_tokenizer import FlashBertTokenizer
tokenizer = FlashBertTokenizer("path/to/vocab.txt", do_lower_case=True)
# Tokenize text
ids = tokenizer("Hello, world!")
print(ids)

3. Other Implementations

Most BERT-based models use the WordPiece Tokenizer, whose code can be found here. (A simple implementation of Huggingface can be found here).

Since the BertTokenizer is a CPU intensive algorithm, inference can be a bottleneck, and unoptimized tokenizers can be severely slow. A good example is the BidirectionalWordpieceTokenizer introduced in KR-BERT. Most of the code is the same, but the algorithm traverses the sub token backwards and writes a larger value compared to the forward traversal. The paper claims accuracy improvements, but it's hard to find other quantitative metrics, and the accuracy improvements aren't significant, and the tokenizer is seriously slowed down.

  • transformers (Rust Impl, PyO3)
  • paddlenlp (C++ Impl, pybind)
  • tensorflow-text (C++ Impl, pybind)
  • blingfire (C++ Impl, Native binary call)

Most developers will either use transformers.BertTokenizer or transformers.AutoTokenizer, but using AutoTokenizer will return transformers.BertTokenizerFast.

Naturally, it's faster than BertTokenizer, but the results aren't exactly the same, which means you're already giving up 100% accuracy starting with the tokenizer.

BertTokenizer is not only provided by transformers. PaddleNLP and tensorflow-text also provide BertTokenizer.

Then there's Blingfire, which is developed by Microsoft and is being abandoned.

PaddleNLP requires PaddlePaddle and provides tokenizer functionality starting with version 3.0rc. You can install it as follows

##### Install PaddlePaddle, PaddleNLP
python -m pip install paddlepaddle==3.0.0b1 -i https://www.paddlepaddle.org.cn/packages/stable/cpu/
pip install --upgrade paddlenlp==3.0.0b3
##### Install transformers
pip install transformers==4.47.1
##### Install tf-text
pip install tensorflow-text==2.18.1
##### Install blingfire
pip install blingfire

With the exception of blingfire, vocab.txt is all you need to run the tokenizer right away. (blingfire also requires only vocab.txt and can be used after 8 hours of learning).

The implementations we'll look at in detail are PaddleNLP's BertTokenizerFast and blingfire.

  • blingfire: Uses a Deterministic Finite State Machine (DFSM) to eliminate one linear scan and unnecessary comparisons, resulting in a time of O(n), which is impressive.
    • Advantages: 5-10x faster than other implementations.
    • Disadvantages: Long training time (8 hours) and lower accuracy than other implementations. (+Difficult to get help due to de facto development hiatus).
  • PaddleNLP: As shown in the experiments below, PaddleNLP is always faster than BertTokenizerFast (HF) to the same number of decimal places, and is always faster on any OS, whether X86 or Arm.
    • Advantages: Internal implementation is in C++ Compared to transformers.BertTokenizerFast implemented in Rust, it is 1.2x faster while outputting exactly the same values.
      • You can't specify pt(pytorch tensor) in return_tensors, but this is not a problem.[^1]
    • Disadvantages: none, other than the need to install PaddlePaddle and PaddleNLP.

4. Performance test

4.1 Performance test (Batch text encoding)

The graph below compares transformers.BertTokenizerFast and paddlenlp.transformers.bert.tokenizer_fast.BertTokenizerFast for batch size.

Both libraries are faster to return as np.ndarray. Perhaps the implementations have logic to convert to pt or pd at the end, which takes longer.

batchtest

BatchSize transformers(pt) paddlenlp(pd) transformers(np) paddlenlp(np)
1 2.32744 1.74695 1.87685 1.56597
2 1.87427 1.53865 1.50911 1.45918
4 1.54254 1.13622 1.12902 1.07593
8 1.25432 0.821463 0.850269 0.798163
16 1.09129 0.640243 0.67293 0.617309
32 0.994335 0.528553 0.587379 0.519887
64 0.971175 0.476652 0.537753 0.471145
128 0.952003 0.478113 0.531592 0.451384

[^1]: As you can see in the graph above, returning to pt(pytorch tensor)' becomes very slow.

4.2 Performance test (Single text encoding)

Accuracy is the result of measuring transformers.BertTokenizer as a baseline. If even one of the input_ids is incorrect, the answer is considered incorrect. Surprisingly, the performance of tensorflow-text is much faster than before. However, there is still no advantage for `tensorflow-text' when comparing the four libraries.

DeepCT (BertTokenizer)

Tokenizer Elapsed Time (s) titles Accuracy (%)
BertTokenizer(Huggingface) 255.651 404,464 100 (Baseline)
BertTokenizerFlash 19.1325 ➡️ 16.526 ➡️ 12.5391🔺 404,464 99.3248 ➡️ 99.8442 🔺
BertTokenizerFast(PP) 64.6828 404,464 99.8615
BertTokenizerFast(HF) 69.6647 404,464 99.8615
FastBertTokenizer(TF) 85.5056 404,464 99.8507
Blingfire 12.1941 404,464 96.8979

DeepCT (BidirectionalBertTokenizer)

Tokenizer Elapsed Time (s) titles Accuracy (%)
BidirectionalBertTokenizer 193.1238 404,464 100(baseline)
FlashBertTokenizerBidirectional 17.8542 ➡️ 9.41044 🔺 404,464 99.9913

KcBert_base

Tokenizer Elapsed Time titles Accuracy
BertTokenizerFlash 7.9542 1,000,000 99.5792
BertTokenizerFast(PP) 38.3839 1,000,000 99.9995
BertTokenizerFast(HF) 49.0197 1,000,000 99.9995
FastBertTokenizer(TF) 188.633 1,000,000 99.9826
Blingfire 13.454 1,000,000 99.9244

For both single text and batch text, PaddleNLP's implementation is always faster than HuggingFace's implementation, and the results are exactly the same, so there is no unique advantage of HuggingFace's transformers.BertTokenizerFast.

Now you may have to make a decision between speed (blingfire) vs balance (PaddleNLP).

BertTokenizer requires a fast single-core CPU to get fast results.

The flash-tokenizer, which I implemented because I didn't like the other tokenizers, has a clear advantage in both speed and accuracy.

FlashTokenizer

FlashTokenizer

%%{ init: { "er" : { "layoutDirection" : "LR" } } }%%
erDiagram
    Text ||--o{ Preprocess : tokenize
    Preprocess o{--|| Inference : memcpy_h2d
    Inference o{--|| Postprocess : memcpy_d2h

5. Case where the result is different from BertTokenizer

WA

As can be seen from the above relationship, if transformers.BertTokenizerFast is wrong, then tensorflow-text's FastBertTokenizer and FlashBertTokenizer are also wrong, and the difference set between FlashBertTokenizer and FastBertTokenizer(TF) is different.

6. Compatibility

FlashBertTokenizer can be used with any framework. CUDA version compatibility for each framework is also important for fast inference of LLMs.

  • PyTorch no longer supports installation using conda.
  • ONNXRUNTIME is separated by CUDA version.
  • PyTorch is also looking to ditch CUDA 12.x in favor of the newer CUDA 12.8. However, the trend is to keep CUDA 11.8 in all frameworks.
    • CUDA 12.x was made for the newest GPUs, Hopper and Blackwell, and on GPUs like Volta, CUDA 11.8 is faster than CUDA 12.x.
DL Framework Version OS CPU CUDA 11.8 CUDA 12.3 CUDA 12.4 CUDA 12.6 CUDA 12.8
PyTorch 2.6 Linux, Windows
PyTorch 2.7 Linux, Windows
ONNXRUNTIME(11) 1.20.x Linux, Windows
ONNXRUNTIME(12) 1.20.x Linux, Windows
PaddlePaddle 3.0-beta Linux, Windows

7. GPU Tokenizer

You can run WordPiece Tokenizer on GPUs on rapids(cudf).

As you can see in how to install rapids, it only supports Linux and the CUDA version is not the same as other frameworks, so docker is the best choice, which is faster than CPU for batch processing but slower than CPU for streaming processing.

TODO

Implemention Problem

[!WARNING]
The following data structures are not applicable or are slower.

  • std::list<std::reference_wrapper<std::string>>
  • std::string_view
  • std::pmr::list<std::pmr::string>

Using robbin_hood's fastest unordered_flat_map as a cache for BasicTokenizer and WordpieceTokenizer actually makes them slower, despite 95% cache hits, due to access time.

Acknowledgement

FlashTokenizer is inspired by FlashAttention, FlashInfer, FastBertTokenizer and tokenizers-cpp projects.

References

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

flash_tokenizer-1.0.3.tar.gz (5.4 MB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

flash_tokenizer-1.0.3-cp312-cp312-win_amd64.whl (120.7 kB view details)

Uploaded CPython 3.12Windows x86-64

flash_tokenizer-1.0.3-cp312-cp312-macosx_15_0_arm64.whl (169.5 kB view details)

Uploaded CPython 3.12macOS 15.0+ ARM64

flash_tokenizer-1.0.3-cp311-cp311-win_amd64.whl (120.2 kB view details)

Uploaded CPython 3.11Windows x86-64

flash_tokenizer-1.0.3-cp311-cp311-macosx_15_0_arm64.whl (169.3 kB view details)

Uploaded CPython 3.11macOS 15.0+ ARM64

flash_tokenizer-1.0.3-cp310-cp310-win_amd64.whl (118.8 kB view details)

Uploaded CPython 3.10Windows x86-64

flash_tokenizer-1.0.3-cp310-cp310-macosx_15_0_arm64.whl (167.1 kB view details)

Uploaded CPython 3.10macOS 15.0+ ARM64

flash_tokenizer-1.0.3-cp39-cp39-win_amd64.whl (118.3 kB view details)

Uploaded CPython 3.9Windows x86-64

flash_tokenizer-1.0.3-cp39-cp39-macosx_15_0_arm64.whl (166.9 kB view details)

Uploaded CPython 3.9macOS 15.0+ ARM64

File details

Details for the file flash_tokenizer-1.0.3.tar.gz.

File metadata

  • Download URL: flash_tokenizer-1.0.3.tar.gz
  • Upload date:
  • Size: 5.4 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.2

File hashes

Hashes for flash_tokenizer-1.0.3.tar.gz
Algorithm Hash digest
SHA256 c2588f5aeb4b2e729fc85a1c5662c7e8389547666f279f2204ed99ab76062839
MD5 6ef21247ebac65aba805903042df0802
BLAKE2b-256 5bf69a9a71f6eb9b85198646513fab218935810560c9d4ea9e52187c764bea8e

See more details on using hashes here.

File details

Details for the file flash_tokenizer-1.0.3-cp312-cp312-win_amd64.whl.

File metadata

File hashes

Hashes for flash_tokenizer-1.0.3-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 d25a53a72e92431a089c1180dd7d54b634feba939b2afe84677fab3eb928044b
MD5 9aa1c4abd30b81e5079d85d93588de18
BLAKE2b-256 4bfeaba09e614573776036315166fc71eca6a6a323be91619c66c9fd00fad626

See more details on using hashes here.

File details

Details for the file flash_tokenizer-1.0.3-cp312-cp312-macosx_15_0_arm64.whl.

File metadata

File hashes

Hashes for flash_tokenizer-1.0.3-cp312-cp312-macosx_15_0_arm64.whl
Algorithm Hash digest
SHA256 13216648ee9033af63d21fea9ef51fa2f5942c4e2c6a9e1ae869f800859dc5a4
MD5 e960c3407bf9a48a9d02d75d2e3272b5
BLAKE2b-256 5ba57ea70a3a03900dc84b7f66da027ce1896a890ae0f540026d377a8d3dafa6

See more details on using hashes here.

File details

Details for the file flash_tokenizer-1.0.3-cp311-cp311-win_amd64.whl.

File metadata

File hashes

Hashes for flash_tokenizer-1.0.3-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 b0707c2213e7a47264f7c79ca976f9aef59c44a07dcd01cb151beb548335ac54
MD5 67c1e600016854e99880c41056cf7a2a
BLAKE2b-256 638c3a91f25b29e2b6394867516ef1782c4634136525d8b80fb1ee27a0d161ce

See more details on using hashes here.

File details

Details for the file flash_tokenizer-1.0.3-cp311-cp311-macosx_15_0_arm64.whl.

File metadata

File hashes

Hashes for flash_tokenizer-1.0.3-cp311-cp311-macosx_15_0_arm64.whl
Algorithm Hash digest
SHA256 f541e68446d5fc48a46a6755181e33dd9b86214591c00da85e3b0f915c863785
MD5 9941e899e4b88ed969dd7fc5ca85ed2e
BLAKE2b-256 475627282d197bb3a844a2709fc752206b1daf21b743dcc8d70985b2d193a9d3

See more details on using hashes here.

File details

Details for the file flash_tokenizer-1.0.3-cp310-cp310-win_amd64.whl.

File metadata

File hashes

Hashes for flash_tokenizer-1.0.3-cp310-cp310-win_amd64.whl
Algorithm Hash digest
SHA256 a2ad2e283aa7a603fcdab2a339dc89dea0b928184e4672ecdc9cf32432aa0d4e
MD5 a60d3b7e33d1928d1376d92a37386375
BLAKE2b-256 1c9e5df6a6c0baf1fc3d22eaf776117a2d630b82c64e01a46300b378dba7063c

See more details on using hashes here.

File details

Details for the file flash_tokenizer-1.0.3-cp310-cp310-macosx_15_0_arm64.whl.

File metadata

File hashes

Hashes for flash_tokenizer-1.0.3-cp310-cp310-macosx_15_0_arm64.whl
Algorithm Hash digest
SHA256 42d69719d80da379fa56cd9eb2dc4733e6715572e77af96c0c243ee97a66a04f
MD5 0c5d64752708abe82dc5c7f623d0c0ab
BLAKE2b-256 e7aa95b8dde59c3e49781a632abe974cf7df42b521121136114870ac5970b63c

See more details on using hashes here.

File details

Details for the file flash_tokenizer-1.0.3-cp39-cp39-win_amd64.whl.

File metadata

File hashes

Hashes for flash_tokenizer-1.0.3-cp39-cp39-win_amd64.whl
Algorithm Hash digest
SHA256 1697cbfc3b30053cd4b945ca452380cf0fc5c1cd274085a78f4c9671d67192cf
MD5 733d1eb10e61725f01663c505c279be3
BLAKE2b-256 44db77fa74c42a523caa392b7a05ad7164f2031bff6dfd080464c4c7552cd9f5

See more details on using hashes here.

File details

Details for the file flash_tokenizer-1.0.3-cp39-cp39-macosx_15_0_arm64.whl.

File metadata

File hashes

Hashes for flash_tokenizer-1.0.3-cp39-cp39-macosx_15_0_arm64.whl
Algorithm Hash digest
SHA256 6be0da9d345a8f09c15837a8668cf0cfe104eaa9c7f0b6e2017772de809be389
MD5 58f5dc13155d1cf641a1f9326de68523
BLAKE2b-256 00437a39a34fd22a7d685592b4882f3fa9604018b848717a572571df9efa441d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page