Skip to content

tryAGI/Tiktoken

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

252 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Tiktoken

Nuget package dotnet License: MIT Discord Throughput

One of the fastest BPE tokenizers in any language — the fastest in .NET, competitive with pure Rust implementations. Zero-allocation token counting, built-in multilingual cache, and up to 41x faster than other .NET tokenizers. We will be happy to accept any PR.

Implemented encodings

  • o200k_base
  • cl100k_base
  • r50k_base
  • p50k_base
  • p50k_edit

Usage

using Tiktoken;

var encoder = TikTokenEncoder.CreateForModel(Models.Gpt4o);
var tokens = encoder.Encode("hello world"); // [15339, 1917]
var text = encoder.Decode(tokens); // hello world
var numberOfTokens = encoder.CountTokens(text); // 2
var stringTokens = encoder.Explore(text); // ["hello", " world"]

// Alternative APIs:
var encoder = ModelToEncoder.For("gpt-4o");
var encoder = new Encoder(new O200KBase());

Load from HuggingFace tokenizer.json

The Tiktoken.Encodings.Tokenizer package enables loading any HuggingFace-format tokenizer.json file — supporting GPT-2, Llama 3, Qwen2, DeepSeek, and other BPE-based models.

using Tiktoken;
using Tiktoken.Encodings;

// From a local file
var encoding = TokenizerJsonLoader.FromFile("path/to/tokenizer.json");
var encoder = new Encoder(encoding);

// From a stream (HTTP responses, embedded resources)
using var stream = File.OpenRead("tokenizer.json");
var encoding = TokenizerJsonLoader.FromStream(stream);

// From a URL (e.g., HuggingFace Hub)
using var httpClient = new HttpClient();
var encoding = await TokenizerJsonLoader.FromUrlAsync(
    new Uri("https://huggingface.co/openai-community/gpt2/raw/main/tokenizer.json"),
    httpClient,
    name: "gpt2");

// Custom regex patterns (optional — auto-detected by default)
var encoding = TokenizerJsonLoader.FromFile("tokenizer.json", patterns: myPatterns);

Supported pre-tokenizer types:

  • ByteLevel — GPT-2 and similar models
  • Split with regex pattern — direct regex-based splitting
  • Sequence[Split, ByteLevel] — Llama 3, Qwen2, DeepSeek, and other modern models

Count message tokens (OpenAI chat format)

Count tokens for chat messages using OpenAI's official formula, including support for function/tool definitions:

using Tiktoken;

// Simple message counting
var messages = new List<ChatMessage>
{
    new("system", "You are a helpful assistant."),
    new("user", "What is the weather in Paris?"),
};
int count = TikTokenEncoder.CountMessageTokens("gpt-4o", messages);

// With tool/function definitions
var tools = new List<ChatFunction>
{
    new("get_weather", "Get the current weather", new List<FunctionParameter>
    {
        new("location", "string", "The city name", isRequired: true),
        new("unit", "string", "Temperature unit",
            enumValues: new[] { "celsius", "fahrenheit" }),
    }),
};
int countWithTools = TikTokenEncoder.CountMessageTokens("gpt-4o", messages, tools);

// Or use the Encoder instance directly
var encoder = ModelToEncoder.For("gpt-4o");
int toolTokens = encoder.CountToolTokens(tools);

Nested object parameters and array types are also supported:

new FunctionParameter("address", "object", "Mailing address", properties: new List<FunctionParameter>
{
    new("street", "string", "Street address", isRequired: true),
    new("city", "string", "City name", isRequired: true),
});

Custom encodings

Load encoding data from .tiktoken text files or .ttkb binary files:

using Tiktoken.Encodings;

// Load from file (auto-detects format by extension)
var ranks = EncodingLoader.LoadEncodingFromFile("my_encoding.ttkb");
var ranks = EncodingLoader.LoadEncodingFromFile("my_encoding.tiktoken");

// Load from binary byte array (e.g., from embedded resource or network)
byte[] binaryData = File.ReadAllBytes("my_encoding.ttkb");
var ranks = EncodingLoader.LoadEncodingFromBinaryData(binaryData);

// Convert text format to binary for faster loading
var textRanks = EncodingLoader.LoadEncodingFromFile("my_encoding.tiktoken");
using var output = File.Create("my_encoding.ttkb");
EncodingLoader.WriteEncodingToBinaryStream(output, textRanks);

The .ttkb binary format loads ~30% faster than .tiktoken text (no base64 decoding) and is 34% smaller. See data/README.md for the format specification and conversion tools.

Benchmarks

Benchmarked on Apple M4 Max, .NET 10.0, o200k_base encoding. Tested with diverse inputs: short ASCII, multilingual (12 scripts + emoji), CJK-heavy, Python code, and long documents.

CountTokens — zero allocation, fastest in class

Input SharpToken TiktokenSharp Microsoft.ML Tiktoken Throughput Speedup
Hello, World! (13 B) 226 ns 168 ns 317 ns 104 ns 119 MiB/s 1.6-3.0x
Multilingual (382 B, 12 scripts) 15.3 us 9.8 us 5.2 us 1.2 us 304 MiB/s 4.4-12.9x
CJK-heavy (1,676 B, 6 scripts) 113.0 us 68.9 us 40.1 us 2.8 us 571 MiB/s 14.5-40.8x
Python code (879 B) 13.5 us 10.2 us 22.5 us 6.6 us 127 MiB/s 1.5-3.4x
Multilingual long (4,312 B) 306.9 us 170.3 us 77.8 us 9.7 us 424 MiB/s 8.0-31.6x
Bitcoin whitepaper (19,884 B) 425.9 us 269.8 us 343.8 us 132.4 us 143 MiB/s 2.0-3.2x

Zero allocation across all inputs (0 B). Tiktoken's advantage is most pronounced on multilingual/CJK text — up to 41x faster than competitors. Throughput on cached multilingual text reaches 571 MiB/s, competitive with the fastest Rust tokenizers.

Cache effect on CountTokens

Built-in token cache dramatically accelerates repeated non-ASCII patterns:

Input No cache Cached Cache speedup
Hello, World! (13 B) 108 ns 109 ns
Multilingual (382 B) 7.7 us 1.2 us 6.6x
CJK-heavy (1,676 B) 58.1 us 2.7 us 21.3x
Python code (879 B) 6.8 us 7.2 us
Multilingual long (4,312 B) 165.8 us 10.7 us 15.5x
Bitcoin whitepaper (19,884 B) 188.5 us 169.6 us 1.1x

Cache has no effect on ASCII-dominant inputs (already on fast path). On multilingual/CJK text, cache provides 6-21x speedup by skipping UTF-8 conversion and BPE on subsequent calls.

Encode — returns token IDs

Input SharpToken TiktokenSharp Microsoft.ML Tiktoken Throughput Speedup
Hello, World! (13 B) 218 ns 168 ns 322 ns 128 ns 97 MiB/s 1.3-2.5x
Multilingual (382 B, 12 scripts) 14.7 us 9.6 us 5.4 us 1.4 us 260 MiB/s 3.9-10.5x
CJK-heavy (1,676 B, 6 scripts) 111.6 us 65.7 us 37.9 us 3.4 us 470 MiB/s 11.1-32.6x
Python code (879 B) 13.5 us 9.8 us 22.3 us 7.0 us 120 MiB/s 1.4-3.2x
Multilingual long (4,312 B) 294.2 us 156.4 us 72.5 us 12.5 us 329 MiB/s 5.8-23.5x
Bitcoin whitepaper (19,884 B) 399.8 us 257.0 us 335.0 us 142.4 us 133 MiB/s 1.8-2.8x

Same performance characteristics as CountTokens, with additional allocation for the output int[] array.

Construction — encoder initialization

Encoding Time Description
o200k_base 0.78 ms GPT-4o (200K vocab, pre-computed hash table, lazy FastEncoder)
cl100k_base 0.46 ms GPT-3.5/4 (100K vocab)

Encoder construction includes loading embedded binary data, building hash tables, and compiling regex. FastEncoder and Decoder dictionaries are lazy-initialized on first use only. Reuse Encoder instances across calls for best performance.

Cross-language context

All numbers below measured on Apple M4 Max with identical inputs and o200k_base encoding. See benchmarks/cross-language/results/ for full reports.

Implementation Language Encode Throughput CountTokens Throughput Notes
Tiktoken (cached) .NET/C# 97-467 MiB/s 119-577 MiB/s Zero-alloc counting; cache gives 6-21x on multilingual
Tiktoken (no cache) .NET/C# 97-133 MiB/s 28-123 MiB/s Cold/first-call — fairest comparison to Rust/Python
tiktoken v3 Rust 34-88 MiB/s Pure Rust, arena-based
GitHub bpe v0.3 Rust 33-64 MiB/s 29-66 MiB/s Aho-Corasick, O(n) worst case
OpenAI tiktoken 0.12 Python 7-20 MiB/s Rust core, but Python FFI overhead

.NET Tiktoken's token cache makes it dramatically faster than native Rust on repeated/multilingual text. Even without the cache, .NET is competitive with or faster than both Rust crates on most inputs. Contributions welcome to further close the gap on cold-path multilingual text!

You can view the full raw BenchmarkDotNet reports for each version here.

Support

Priority place for bugs: https://github.com/tryAGI/LangChain/issues
Priority place for ideas and general questions: https://github.com/tryAGI/LangChain/discussions
Discord: https://discord.gg/Ca2xhfBf3v

About

High-performance .NET BPE tokenizer — up to 571 MiB/s, competitive with Rust. Zero-allocation counting, multilingual cache, o200k/cl100k/r50k/p50k encodings + HuggingFace tokenizer.json support.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Contributors

Languages