Byte-level BPE tokenization with a Pure Swift portable data path for Native, WebAssembly, and Embedded Swift, plus compile-time-gated native kernels where the target contract guarantees the required instructions.
On an Apple M4 Max, the single-threaded warm r50k_base path reaches
988.76 MB/s while producing exactly the same token IDs as the reference
implementation.
Acknowledgements
This project is an independent Swift implementation inspired by
gigatoken, created by
Marcel Roed. The original project established
the gigatoken name and demonstrated high-throughput language-model
tokenization. swift-gigatoken exists because of that work, and references to
this implementation's lineage should credit the original project and author.
Performance
swift-gigatoken is designed for tokenizer hot paths where copying, allocation,
branching, and cache misses are measurable costs.
16 MiB enwik8, warm median |
Throughput | Token IDs | Checksum |
|---|---|---|---|
swift-gigatoken |
988.76 MB/s | 4,929,342 | 9bb0d84c9a7a327d |
Original Rust gigatoken 0.9.0 by Marcel Roed |
975.45 MB/s | 4,929,342 | 9bb0d84c9a7a327d |
That result is 1.014x the throughput of the original Rust implementation on the same Apple M4 Max, model, input bytes, and single-threaded benchmark path. The strict interleaved gate rejects concurrent compiler activity and more than 5% paired common-mode drift instead of treating a busy host as an implementation regression. Every process in the accepted run preserved the full token identity.
These are measured results on one machine rather than a universal throughput guarantee. See the complete benchmark methodology for toolchain versions, pinned revisions, input hashes, and reproduction steps.
General tokenizer API comparison
The same 16 MiB input was also encoded through the public APIs of two widely
used general-purpose tokenizer packages. Every implementation produced the
same 4,929,342-token count and the same 9bb0d84c9a7a327d checksum over all
token IDs.
| Public encode API, seven-run warm median | Throughput | Swift relative throughput |
|---|---|---|
swift-gigatoken |
667.27 MB/s | 1.00x |
Original Rust gigatoken 0.9.0 |
603.56 MB/s | 1.11x |
OpenAI tiktoken 0.12.0 |
9.29 MB/s | 71.82x |
Hugging Face tokenizers 0.22.1 |
1.30 MB/s | 514.88x |
This is an end-to-end comparison of public encode APIs, including each API's
required output materialization. It is not a claim that the underlying
tiktoken or Hugging Face native kernels alone are slower by those factors.
Python input decoding, model loading, garbage collection, and checksum
calculation are outside the timed warm encode region. Hugging Face parallelism
is disabled so all four paths remain single-threaded. The absolute numbers
differ from the stricter interleaved Swift/Rust gate above because this
four-implementation comparison was recorded in a separate low-load run.
Why it is fast
| Hot-path feature | Implementation |
|---|---|
| Zero-copy input and output | Borrows caller-owned UTF-8 bytes and writes directly into a reusable, move-only TokenBuffer. |
| 64-byte classification | Uses NEON on Apple ARM64 and SWAR on portable targets. |
| Two-instruction cache hashing | Emits two ARM crc32x instructions on supported ARM64 targets. |
| Cache-aware lookup | Uses aligned two-slot buckets, low native load factor, branchless home-pair probes, and explicit L2/L1 prefetch. |
| Reused working storage | Reuses token arenas, merge scratch, pretoken batches, caches, and output capacity across calls. |
| Batched output | Uses one segment-wide, closure-scoped MutableSpan cursor and a bounds-proven four-entry store. |
| Verified machine code | Release checks reject missing NEON, CRC32, or prefetch instructions and unexpected hot-loop calls. |
Features
- Foundation-free
GigaTokenCorewith typed failures and no silent fallback. - Native Apple ARM64 acceleration with a Pure Swift portable implementation.
- WebAssembly and Embedded Swift build and smoke-test coverage.
- Caller-owned, reusable storage for allocation-sensitive applications.
- Contiguous
UInt32token access for MLX Swift, Core AI, and other runtimes. - Exact
r50k_baseparity against pinned reference fixtures. - Exact cold/warm parity on a pinned 110,126-byte corpus that crosses multiple 256-pretoken batches and grows both cache paths.
The current compatibility baseline is r50k_base. The package separates the Foundation-free tokenizer core from host model loading and benchmarking.
import Foundation import GigaToken var tokenizer = try GigaTokenizer(r50kModelAt: modelURL) let tokens = try tokenizer.encodeOrdinary("Hello, world!") let text = try tokenizer.decode(tokens)
Build
The package baseline pins one Swift 6.4 development snapshot and matching standard and Embedded Wasm SDKs:
| Component | Identifier |
|---|---|
| Toolchain | swift-6.4.x-DEVELOPMENT-SNAPSHOT-2026-07-23-a |
| Compiler commit | ef761e567dc94ee |
| Standard Wasm SDK | swift-6.4.x-DEVELOPMENT-SNAPSHOT-2026-07-23-a_wasm |
| Embedded Wasm SDK | swift-6.4.x-DEVELOPMENT-SNAPSHOT-2026-07-23-a_wasm-embedded |
source Scripts/swift-toolchain.sh verify_swift64_environment swift64 build swift64 build --swift-sdk "$SWIFT_WASM_SDK" --target GigaTokenCore swift64 build --swift-sdk "$SWIFT_EMBEDDED_WASM_SDK" --target GigaTokenCore Scripts/verify-portability.sh
GigaTokenCore accepts rank-ordered token bytes directly, so Wasm and
Embedded Swift applications can embed model tables without Foundation, JSON,
Base64, filesystem APIs, or Rust. The GigaToken host target parses the
standard .tiktoken text format when Foundation is available.
The tokenizer's public API, storage ownership, BPE logic, and portable hash are
Pure Swift. On ARM64 targets whose platform contract guarantees CRC32,
VectorKernelsNative contributes only the two-instruction cache-hash primitive.
On macOS, release codegen validation requires those intrinsics to inline into
the Swift hot loop. Other ARM64 targets use the same two-instruction kernel
behind one C ABI call. Targets without CRC32 use the Pure Swift pair hash.
Low-level data path
The performance API borrows the input storage and writes directly into a move-only, caller-owned token allocation:
import GigaTokenCore var encoder = BPEEncoder(model: model) var output = TokenBuffer(minimumCapacity: input.count / 3) try input.withUnsafeBufferPointer { bytes throws(TokenizerError) in try encoder.encodeOrdinary(bytes, appendingTo: &output) } output.withUnsafeBufferPointer { tokens in consume(tokens) }
This path does not materialize input slices or an intermediate token array.
String, [TokenID], bytes(for:), and decode(_:) are convenience/output
boundaries and intentionally materialize owned results. Use
withTokenBytes(for:_:), borrowed buffer overloads, and TokenBuffer when the
caller needs scoped views and reusable storage.
For MLX Swift, Core AI, or another tensor runtime, depend on TokenEncoding
and consume the contiguous UInt32 IDs without an intermediate array:
output.withUnsafeRawTokenIDs { tokenIDs in constructRuntimeInput(tokenIDs) }
The pointer is valid only for the closure. Runtime-specific adapters and device transfer are intentionally outside this package; the borrowed buffer and ownership rules are the stable connection contract.
The r50k test fixture is OpenAI's public r50k_base.tiktoken model with
SHA-256 306cd27f03c1a714eca7108e03d66b7dc042abe8c258b44c199a7ed9838dd930.
See Documentation/Architecture.md for target boundaries and correctness
requirements, and Documentation/Benchmark.md for the reproducible Rust and
general-tokenizer comparisons.