Baseten、Kimik K3 のトークン化を 18 倍高速化し百万トークン規模の作業負荷に対応
本文の状態
日本語全文を表示中
詳細モードで約11分の本文を読めます。
同じ出来事の情報源
この情報源を基点に整理
Baseten Engineering
Baseten Engineering は、エージェントワークロードにおける超長文入力への対応を強化するため、Kimi K3 のトークン化処理を Rust ベースの独自実装へ移行し、最大 18 倍の高速化を実現したと発表した。
AI深層分析を開く2026年8月4日 10:43
AI深層分析
キーポイント
エージェントワークロードにおけるボトルネックの変化
従来の推論エンジニアはトークン化時間を無視していたが、Kimi K3 のようなオープンフロンティアモデルが百万トークンの入力をサポートするようになり、エージェントループ内でのトークン化時間が主要なボトルネックとなっている。
Baseten Tokenizer(Basetenkenizer)の導入
Baseten Engineering は、Kimi K3 の最適化を目的として、カスタム Python tiktoken 実装から Rust ベースの独自トークナイザー「Baseten Tokenizer」へ移行し、サービス開始日(day zero)から採用した。
18 倍の高速化と完全な互換性
長文入力において、この新トークナイザーは既存の tiktoken と比較して最大 18 倍高速でありながら、正確なトークン ID を完全に保持している。
Baseten Tokenizer の性能向上
短シーケンスでは tiktoken より 6 倍以上、百万トークンの長文シーケンスでは 18 倍高速化を実現している。
Kimik K3 トークナイザーの複雑性
Kimi K3 は標準的な BPE ボキャブラリに加え、構造的トークンに対する正規表現事前処理と Python チャットレンダリングを必要とする。
重要な引用
Open frontier models like Kimi K3 support input sequences of up to one million tokens.
Within the Baseten Inference Stack, we migrated from a custom Python tiktoken implementation to the Rust-based Basetenkenizer, for the day zero release.
the complete serving path is up to 18x faster than tiktoken while preserving exact token IDs.
Baseten Tokenizer is more than 6x faster than tiktoken for short sequences and 18x faster for million-token sequences, with exact token ID parity.
編集コメントを表示
編集コメント
エージェントアプリケーションが複雑化する中で、トークン化処理のパフォーマンス最適化は推論速度のボトルネック解消において決定的な役割を果たす。Baseten Engineering が Rust による実装で 18 倍の高速化を達成した事実は、長文コンテキストを扱うシステム設計における重要な指針となる。
Source Article
元記事を日本語で読む
本文に関係しない購読案内、埋め込み通知、サイト内プロモーションは除いています。
長年、推論エンジニアたちはトークン化にかかる時間を無視できるほど微小なコストだと考えてきました。短い入力シーケンスのトークン化には数ミリ秒しかかからず、プリフィルやデコードに必要な時間のほんの一部に過ぎないからです。
しかし状況は変わりました。Kimi K3 などのオープンフロンティアモデルでは、最大 100 万トークンもの長い入力シーケンスをサポートしています。こうした長い入力は、エージェントのループがツールからの結果、観測データ、取得したドキュメント、中間状態、推論の痕跡、更新された指示などを繰り返し次のリクエストに追加する「アジェンティック・ワークロード」において、ますます一般的になっています。

アジェンティック・ループ内では、入力シーケンスの長さとプレフィックスキャッシュのヒット率が急速に増加します。
私たちは、Kimi K3 を対象に長い入力シーケンス向けのトークン化を最適化する「Baseten Tokenizer(Basetenkenizer)」を開発しました。Baseten Inference Stack 内では、ゼロ日リリース時点でカスタム Python の tiktoken 実装から Rust ベースの Basetenkenizer へ移行しました。トークン化時間が最も重要となる長い入力シーケンスにおいては、完全なサービングパスが tiktoken と比べて最大で 18 倍高速化されつつも、正確なトークン ID は維持されています。

Baseten Tokenizer は、短いシーケンスでは tiktoken よりも 6 倍以上、百万トークン規模のシーケンスでは 18 倍高速に動作し、トークン ID の完全な整合性も維持しています。
この記事では、Baseten Tokenizer の実装内容とベンチマーク手法について解説します。
なぜ Kimi K3 のトークナイゼーションは非自明なのか
多くの大規模言語モデル(LLM)は、標準的なチャットテンプレートに包まれたバイトペアエンコーディング(BPE)トークナイザーを採用しています。この設計思想は、GPT-2 や RoBERTa といった初期の Transformer モデルまで遡るものです。
Kimi K3 では、tiktoken.model 形式の BPE 語彙に加え、<|open|> や <|sep|>、<|close|>、<|end_of_msg|> といった構文トークンに対する正規表現による前処理も用意されています。
Python のチャットレンダラーは型付きのエンコードセグメントを生成し、長文チャンキングの動作はカスタム tiktoken パスから継承されます。
レンダリングされたプロンプトの構造的な部分では、<|open|> のような文字列は制御トークンとしてエンコードされる必要があります。一方、ユーザー入力やツールのテキスト内では、全く同じ文字列は通常のテキストとして扱われなければなりません。
例えば、ユーザーが以下のように記述した場合:
please explain the literal token <|open|><|open|> という文字列は構造的なマーカーに変換されてはいけません。これは通常のユーザー入力としてエンコードされる必要があります。この区別を失うと、プロンプトの意図が損なわれます。
従来のトークナイザーは、チャットをセグメントに分割して処理していました。
EncodeSegment(text="<|open|>", allow_special=True)
EncodeSegment(text="message role=\"user\"", allow_special=False)
EncodeSegment(text="<|sep|>", allow_special=True)
EncodeSegment(text="literal user text <|open|>", allow_special=False)正確なトークンIDを維持するためには、Baseten Tokenizer においても同様の挙動を実装する必要がありました。また、他のライブラリとのトークンIDを容易に比較できるようにするために、本日 Kimi K3 の tokenizer.json を Hugging Face で公開します。
推論パス全体をカバーするネイティブ呼び出し
従来の Kimi パスは、Python 上でこれらのセグメントをループ処理していました。また、tiktoken の長文入力に対する安全性ルール(テキストを最大 40 万文字に分割し、その後空白または非空白の連続部分を 2.5 万文字ごとにさらに分割する)も再現していましたが、最終的にはトークン ID を格納した Python リストを組み立てていました。その結果、各セグメントと各チャンクが Python とネイティブコードの間を行き来するたびにオーバーヘッドが発生していました。
Baseten Tokenizer は、順序付きの (text, allow_special) セグメントを 1 つのネイティブ呼び出しで受け取り、トークンを配列インターフェースとして返します。
encoding = tokenizer.encode_segments(
[(segment.text, segment.allow_special) for segment in segments],
add_special_tokens=False,
tiktoken_safe=True,
)
input_ids = encoding.into_numpy(ids=True)["ids"]チャットレンダラーは測定対象となるエンコード領域の外側、つまり Python 側に残ります。特殊トークンの選択、安全なチャンク分割、正規表現による事前トークン化、BPE(Byte Pair Encoding)、順序付きの組み立て、そして NumPy への引き渡しといった処理はすべて、1 つのネイティブ境界線の内側で完結します。推論コードが百万個の Python 整数を生成することはありません。
高速化の源泉
私たちの実装では、以下の複数の最適化を組み合わせています:
「Specialized pre-tokenization scanners」: Kimi の分割用正規表現パターンを事前に認識し、汎用的な正規表現エンジンではなく手書きの関数に処理を委譲します。この実装に興味がない場合は、Kimi モデルファミリーの正規表現を PCRE2 JIT ライブラリ向けに変換・再構築してマッチングさせることも可能です。
「スタック常駐 BPE マージ階層」: 最大 32 バイトの短いプレトークンは、ヒープや優先度キューのオーバーヘッドを回避するため、スタック割り当てされた連結リスト上で動作します。ここでは線形な最小ランクスキャンと密集した第 1 ラウンドのバイトペアテーブルが使用されます。
「マルチコアセマンティクス」: コールドかつ長い入力において、同一のプレトークンは同じ CPU コアにルーティングされ、繰り返し出現する単語は一度だけマージされた後、出力順序を復元する前にキャッシュから提供されます。encode_segments には 400,000 文字という制限がありますが、これは tiktoken の名残によるものです。Basetenkenizer はこれを逆手に取り、各 400,000 文字のチャンクを別々のコアにスケジューリングすることで、長いコンテキストにおける ISL(Initial Sequence Length)処理を Gigatoken よりも高速化しています。
「ネイティブ型セグメントと安全なチャンキング」: スパンごとの特殊トークンポリシーや互換性ルールは Rust で実行され、Python での作業リスト作成を防ぎます。
「ゼロコピー NumPy 所有権移転」: トークン配列は中間の整数リストを介さずに Python に渡され、エンコーディングはアクセスされた時点で初めて実体化されます。
「スマートポインタによる PyO3 バインディング」:abi3-py310 バインディングを使用し、Rust 側では文字列を Cow 値として借用することで効率を向上させています。
これらの改善点は、長いコンテキストや複数回の対話において相乗効果を生みます。
Kimi K3 サービングにおけるベンチマーク結果
本稿では、このパフォーマンス向上が最も効果を発揮するワークロードプロファイル、すなわち「長い入力シーケンスかつ高いプリフィックスキャッシュヒット率」という条件に対して評価を行いました。

プリフィックスの高速化によりキャッシュヒットが発生すると、長い入力シーケンスにおける TTFT(Time to First Token)が顕著に低下します。この時間短縮は、複数回の対話ラウンドを通じて累積していきます。
主要なワークロードは、レンダリング済みの Kimi K3 チャットプロンプトで構成されており、54 の入力セグメントが含まれています。測定される各ラウンドでは、同じ文字長を持つ新しい決定論的なコーパスを使用し、文章、コード、JSON、Unicode スクリプト、句読点、数字、一意のリクエスト ID、ハッシュ値、そしてユーザーテキスト内に埋め込まれたリテラルな K3 コントロールストリングを混合しています。測定対象のプロンプトは二度とエンコードされないため、BPE(Byte Pair Encoding)キャッシュは常に初期状態から開始し、実トラフィックと同様に自然に出現する語彙のみを利用します。
Basetenkenizer は内部で固定された CPU セットに対して並列処理を行います。これは、各ライブラリがこのワークロードに対して提供できる最速の完全パスを比較したものです。推論サーバーでは通常、利用可能な CPU コアが多数用意されています。
Kimi K2.7 の平文処理におけるパフォーマンス改善のベンチマーク結果
提供経路での速度向上と、BPE(バイトペア符号化)そのものの速度を区別するために、Kimi K2.7 ベーストークナイザーに対して、すでに Python 文字列として読み込まれたデータを対象に測定を行いました。各ライブラリは、それぞれが利用可能な最速の API を使用しています。
完全なベンチマークでは、tiktoken、fastokens、gigatoken はいずれも、チャットテンプレートのレンダリングが必要となる点を省略しています。この処理経路は文字列割り当ての負荷が高くなりますが、Basetenkenizer は、Kimi K3 に対して初めてこれを解決するライブラリです。これは融合されたエンコード呼び出し(fused encode call)によるものであり、jinja テンプレートを使用する GLM5.2 や Kimi K2.7 など他のモデルでも同様に機能します。
付録:ベンチマーク手法について
ベンチマークは常に難しい課題です。比較をできるだけ公平にするために実施した手順についての注記です:
競合製品はいずれも型付きセグメントエンコーディングを公開しておらず、その測定値には K3 の意味構造を維持するための Python オーケストレーションが含まれています。一方、Gigatoken はネイティブで NumPy を返すため、ベンチマークではそのパスが使用されています。また、単一の Python 呼び出しでディスパッチする Kimi K2.7 ベンチマークも用意しました。
10k トークンの領域では Gigatoken が最速ですが、交差点は 200k トークンの手前で現れ、1M トークン入力では Baseten Tokenizer が 1.41 倍の速度差でリードします。Gigatoken の本来のターゲットである「Rust で直接読み込むオフラインコーパスファイル」という負荷においては、Gigatoken は全く異なるスループットクラスに属しています(64 MiB の直接ファイルベンチマークで 2.68 GiB/s を記録する一方、当社の Python ストリング経路では 118 MiB/s です)。結論として、型付きかつ既にレンダリングされたセグメントからの K3 オンラインサービングにおいては Baseten Tokenizer が最速のパスですが、オフラインデータセットのトークン化には Gigatoken の encode_files を使用すべきです。Baseten はサービング効率のためにキャッシュを特化させ、Gigatoken はグローバルなスループット最適化に注力しています。
全測定値は、Intel Xeon Platinum 8480+ プロセッサの 52 個のピン留め vCPU 上でのエンドツーエンドのエンコード中央値であり、各ライブラリがサポートする NumPy uint32 トークン配列への変換時間を含みます。tiktoken の公開された encode コールはシングルスレッドです。
Fastokens は Kimi レギュラー表現に対して pcrc2 JIT バックエンドを活用できないため性能が出ませんでした。手書きのスキャナを除けば、このレギュラー表現を再構築することで、フォールバックのレギュラー表現スキャナよりも約 1.5 倍高速化が可能です。
原文を表示
For years, inference engineers have been able to disregard tokenization time as negligible. Short input sequences take a couple of milliseconds to tokenize, a tiny fraction of the time required for prefill and decode.
This has changed. Open frontier models like Kimi K3 support input sequences of up to one million tokens. These long input sequences are increasingly common in agentic workloads, where an agent loop repeatedly appends tool results, observations, retrieved documents, intermediate state, reasoning traces, and updated instructions back into the next request.
✕

Input sequence lengths and prefix cache hit rates increase quickly in agentic loops
We built the Baseten Tokenizer (Basetenkenizer) to optimize tokenization for long input sequences, starting with Kimi K3. Within the Baseten Inference Stack, we migrated from a custom Python tiktoken implementation to the Rust-based Basetenkenizer, for the day zero release. On the long input sequences where tokenization time matters most, the complete serving path is up to 18x faster than tiktoken while preserving exact token IDs.
✕

Baseten Tokenizer is more than 6x faster than tiktoken for short sequences and 18x faster for million-token sequences, with exact token ID parity.
This post describes the implementation and the benchmarking process for Baseten Tokenizer.
Why Kimi K3 tokenization is non-trivial
Most LLMs use a byte-pair encoding (BPE) tokenizer wrapped in a standard chat template. This tokenizer design dates back to the earliest Transformer models like GPT-2 and RoBERTa.
Kimi K3 ships with a tiktoken.model BPE vocabulary along with a regex pre-tokenization for structural tokens like <|open|>, <|sep|>, <|close|>, and <|end_of_msg|>. A Python chat renderer emits typed encode_segments, and long-text chunking behavior is inherited from the custom tiktoken path.
In structural parts of the rendered prompt, a string like <|open|> should encode as a control token. In user or tool text, the exact same string must be treated as ordinary text.
For example, if a user writes:
please explain the literal token <|open|>The characters <|open|> must not become a structural marker. It has to be encoded as normal user text. Losing this distinction changes the prompt.
The old tokenizer handled this by rendering chat into segments:
EncodeSegment(text="<|open|>", allow_special=True)
EncodeSegment(text="message role=\"user\"", allow_special=False)
EncodeSegment(text="<|sep|>", allow_special=True)
EncodeSegment(text="literal user text <|open|>", allow_special=False)To maintain exact token IDs, we needed to implement the same behavior in the Baseten Tokenizer. To make it easy to compare token IDs with other libraries, we are also releasing a Kimi K3 tokenizer.json on Hugging Face today.
One native call for the whole serving path
The old Kimi path looped over those segments in Python. It also reproduced tiktoken's long-input safety rules — splitting text into at most 400k characters, then splitting runs of whitespace or non-whitespace at 25k characters — and finally assembled Python lists of token IDs. Every segment and every chunk paid for a trip across the Python/native boundary.
Baseten Tokenizer accepts the ordered (text, allow_special) segments in one native call, returning the tokens in an array interface:
encoding = tokenizer.encode_segments(
[(segment.text, segment.allow_special) for segment in segments],
add_special_tokens=False,
tiktoken_safe=True,
)
input_ids = encoding.into_numpy(ids=True)["ids"]The chat renderer stays in Python, outside the measured encode region. Special-token selection, safe chunking, regex pre-tokenization, BPE, ordered assembly, and the NumPy handoff all happen behind one native boundary. Serving code never materializes a million Python integers.
Where the speed comes from
Our implementation combines several optimizations:
- Specialized pre-tokenization scanners: Recognizes Kimi's split regex pattern ahead of time and dispatches to a handwritten function instead of a general regex engine. For those not interested in writing this, the Kimi model family's regex can be matched and reformulated for the PCRE2 JIT library.
- A stack-resident BPE merge tier: Short pre-tokens of up to 32 bytes run in a stack-allocated linked list with a linear minimum-rank scan and dense first-round byte-pair table, avoiding heap and priority queue overhead.
- Multi-core semantics: Identical pre-tokens are routed to the same CPU core on cold, long inputs, ensuring repeated words are merged once and served from cache before output order is restored. encode_segments has a limit of 400,000 characters, a legacy limitation of tiktoken. Basetenkenizer uses this as an opportunity to schedule each 400,000-character chunk on a separate core, making long-context ISL faster than Gigatoken.
- Native typed segments and safe chunking: Per-span special-token policies and compatibility rules run in Rust, preventing the creation of Python work lists.
- Zero-copy NumPy ownership transfer: The token array is handed to Python without intermediate lists of integers, and encodings are only materialized when accessed.
- Smart-pointer PyO3 bindings: Uses abi3-py310 bindings, with strings borrowed as Cow values in Rust to improve efficiency.
These improvements compound on long context and multiple turns.
Benchmark results for Kimi K3 serving
We evaluated this performance gain against the workload profile it is designed to help the most: a long input sequence with a high prefix cache hit rate.
✕

When prefill is fast due to cache hit, optimizing tokenization noticeably lowers TTFT for long input sequences. This time savings compounds over multiple turns.
The primary workload is a rendered Kimi K3 chat prompt containing 54 typed segments. Every measured round uses a new deterministic corpus of the same character length, mixing prose, code, JSON, Unicode scripts, punctuation, numbers, unique request IDs and hashes, and literal K3 control strings inside user text. No measured prompt is encoded twice, so BPE caches start cold and only benefit from naturally recurring vocabulary — as they would in real traffic. Basetenkenizer parallelizes internally across the pinned CPU set — this is a comparison of the fastest complete path each library offers for this workload. Inference Servers typically provide a large amount of available CPU cores.
Benchmark results for performance improvements on plain text for Kimi K2.7
To separate the serving-path win from raw BPE speed, we also measured plain already-loaded Python strings on the Kimi K2.7 base tokenizer, each library using its fastest applicable API:
For a full benchmark, tiktoken, fastokens and gigatoken omit the fact that a chat-template needs to be rendered. This codepath is more String allocation heavy, Basetenkenizer is the first library solving this for Kimi K3 though a fused encode call, as well as all other models like GLM5.2 or Kimi K2.7 that use jinja templates.
Appendix: Benchmarking methodology
Benchmarking is always a tricky subject. Notes on what we did to make the comparison as fair as possible:
- Neither published competitor exposes typed segment encoding, so their measurements include the Python orchestration required to preserve K3 semantics; gigatoken returns NumPy natively and the benchmark uses that path. We have included Kimi K2.7 benchmarks that dispatch in a single Python call.
- Gigatoken is fastest at 10k tokens; the crossover appears before 200k, and at 1M Baseten Tokenizer leads it by 1.41x on this input. And on gigatoken's own target workload — offline corpus files read directly in Rust — gigatoken is in a different throughput class entirely (2.68 GiB/s on a 64 MiB direct-file benchmark, versus 118 MiB/s for our Python-string path). The conclusion is specific: for K3 online serving from typed, already-rendered segments, Baseten Tokenizer is the fastest path we measured; for offline dataset tokenization, use gigatoken's
encode_files. Baseten specialized the caches for serving efficiency, while gigatokens optimizes global throughput. - All times are end-to-end encode medians on 52 pinned vCPUs (Intel Xeon Platinum 8480+), including each library's supported conversion to a NumPy uint32 token array. tiktoken's public encode call is single-threaded;
- Fastokens underperforms since it cannot exercise its pcrc2 jit backend for the Kimi regex. Aside from a handrolled scanner, the regex can be rewritten, making it around 1.5x faster than the fallback regex scanner.
今日のまとめ
AIデイリーブリーフで今日の重要ニュースをまとめ読み