Benchmarks · TOML
teptris, the TOML engine
teptris is the TOML counterpart in the leptris ecosystem: a TOML 1.0 parser and writer in pure C11 with zero required runtime dependencies and a stable C ABI. The benchmark below races it against five widely used C and C++ TOML parsers on a deterministic, seeded corpus of nine document shapes, with parsing measured from memory and destruction timed separately. Every library that provides a key-counting API is cross-checked for agreement on the corpus, so a fast failure cannot masquerade as a fast parse.
| TOML — competitor | C / C++ | Ruby | Python |
|---|---|---|---|
| cpptoml | 3.09×+ | — | — |
| toml11 | 114.64×+ | — | — |
| tomlc17 | 2.85×+ | — | — |
| tomlc99 | 3.43×+ | — | — |
| tomli | — | — | 13.44×+ |
| tomlib | — | 1.84×+ | — |
| tomlplusplus | 5.28×+ | — | — |
| tomlrb | — | 12.43×+ | — |
A green cell with a single value means every measured operation in that race is ahead, and the value shown is the smallest margin. A neutral cell shows the range from the weakest to the strongest operation; hovering lists the operations that remain behind. All rows, including deficits, appear in the tables below.
C and C++
teptris versus the reference parsers
The competitors span the C and C++ ecosystems: tomlc99 and tomlc17 in C, and cpptoml, toml11 and tomlplusplus in C++. Each is pinned to a recorded commit or release in the benchmark ledger. Two of them do not parse the full corpus: tomlc17 rejects the largest scalar shapes with its own keys-per-table cap, and cpptoml applies TOML v0.5 semantics and rejects mixed-type arrays that are valid in TOML 1.0. Those shapes are excluded from the affected competitor’s rows rather than counted against it.
| operation | leptris side | the reference parsers | standing |
|---|---|---|---|
| Parse — array heavy (738 KB) | 330.3 MB/s | 45.9 MB/s | 7.20× |
| Parse — array heavy (738 KB) | 330.3 MB/s | toml11 4.2.0 | 3303.00× the competitor requires seconds rather than milliseconds on this shape (quadratic behavior); the ratio is a lower bound |
| Parse — array heavy (738 KB) | 330.3 MB/s | 62.5 MB/s | 5.28× |
| Parse — array heavy (738 KB) | 330.3 MB/s | 105.9 MB/s | 3.12× |
| Parse — cargo like (362 KB) | 265.2 MB/s | 15.9 MB/s | 16.68× |
| Parse — cargo like (362 KB) | 265.2 MB/s | toml11 4.2.0 | 442.00× the competitor requires seconds rather than milliseconds on this shape (quadratic behavior); the ratio is a lower bound |
| Parse — cargo like (362 KB) | 265.2 MB/s | 48.8 MB/s | 5.43× |
| Parse — cargo like (362 KB) | 265.2 MB/s | 16.1 MB/s | 16.47× |
| Parse — cargo like (362 KB) | 265.2 MB/s | 85.7 MB/s | 3.09× |
| Parse — datetime heavy (1108 KB) | 321.6 MB/s | tomlc99 29076df (2026-01-30) | 643.20× the competitor requires seconds rather than milliseconds on this shape (quadratic behavior); the ratio is a lower bound |
| Parse — datetime heavy (1108 KB) | 321.6 MB/s | 1.8 MB/s | 178.67× |
| Parse — datetime heavy (1108 KB) | 321.6 MB/s | 39.9 MB/s | 8.06× |
| Parse — datetime heavy (1108 KB) | 321.6 MB/s | 95.5 MB/s | 3.37× |
| Parse — deep tables (248 KB) | 294.6 MB/s | 85.9 MB/s | 3.43× |
| Parse — deep tables (248 KB) | 294.6 MB/s | toml11 4.2.0 | 736.50× the competitor requires seconds rather than milliseconds on this shape (quadratic behavior); the ratio is a lower bound |
| Parse — deep tables (248 KB) | 294.6 MB/s | 43.7 MB/s | 6.74× |
| Parse — deep tables (248 KB) | 294.6 MB/s | 81.7 MB/s | 3.61× |
| Parse — deep tables (248 KB) | 294.6 MB/s | 82 MB/s | 3.59× |
| Parse — mixed (446 KB) | 289.8 MB/s | 32.9 MB/s | 8.81× |
| Parse — mixed (446 KB) | 289.8 MB/s | toml11 4.2.0 | 362.25× the competitor requires seconds rather than milliseconds on this shape (quadratic behavior); the ratio is a lower bound |
| Parse — mixed (446 KB) | 289.8 MB/s | 42.4 MB/s | 6.83× |
| Parse — mixed (446 KB) | 289.8 MB/s | 101.8 MB/s | 2.85× |
| Parse — mixed (446 KB) | 289.8 MB/s | 82.9 MB/s | 3.50× |
| Parse — scalar float (1502 KB) | 190.5 MB/s | tomlc99 29076df (2026-01-30) | 635.00× the competitor requires seconds rather than milliseconds on this shape (quadratic behavior); the ratio is a lower bound |
| Parse — scalar float (1502 KB) | 190.5 MB/s | 1.3 MB/s | 146.54× |
| Parse — scalar float (1502 KB) | 190.5 MB/s | 21.8 MB/s | 8.74× |
| Parse — scalar float (1502 KB) | 190.5 MB/s | 46.1 MB/s | 4.13× |
| Parse — scalar int (1306 KB) | 226.5 MB/s | tomlc99 29076df (2026-01-30) | 755.00× the competitor requires seconds rather than milliseconds on this shape (quadratic behavior); the ratio is a lower bound |
| Parse — scalar int (1306 KB) | 226.5 MB/s | 1.1 MB/s | 205.91× |
| Parse — scalar int (1306 KB) | 226.5 MB/s | 33.4 MB/s | 6.78× |
| Parse — scalar int (1306 KB) | 226.5 MB/s | 57.4 MB/s | 3.95× |
| Parse — scalar string (1329 KB) | 321 MB/s | 1.2 MB/s | 267.50× |
| Parse — scalar string (1329 KB) | 321 MB/s | 2.8 MB/s | 114.64× |
| Parse — scalar string (1329 KB) | 321 MB/s | 51 MB/s | 6.29× |
| Parse — scalar string (1329 KB) | 321 MB/s | 86.6 MB/s | 3.71× |
| Parse — table heavy (1150 KB) | 254.6 MB/s | 3.8 MB/s | 67.00× |
| Parse — table heavy (1150 KB) | 254.6 MB/s | toml11 4.2.0 | 509.20× the competitor requires seconds rather than milliseconds on this shape (quadratic behavior); the ratio is a lower bound |
| Parse — table heavy (1150 KB) | 254.6 MB/s | 38.4 MB/s | 6.63× |
| Parse — table heavy (1150 KB) | 254.6 MB/s | 4.3 MB/s | 59.21× |
| Parse — table heavy (1150 KB) | 254.6 MB/s | 81.4 MB/s | 3.13× |
fresh runteptris 0.1.0 (working tree, post run-3 profile round)·min of 10 (Release + LTO), parse from memory·Apple M1 Max, macOS 14.1.1, arm64··bench_parse — parse matrix vs five reference TOML parsers, deterministic seeded corpus
teptris is the fastest parser on every measured shape, with the smallest margin at 2.9 times tomlc17 and the widest margins against toml11, whose generic error-recovery machinery costs two orders of magnitude on large files. The measurements follow three profile-driven optimization rounds recorded, with before-and-after numbers, in theteptris benchmark ledger; the same file records the corpus seed and the exact command that regenerates every number on this page.
To reproduce: run scripts/run_benchmarks.sh in the teptris repository, which builds the Release configuration, regenerates the seeded corpus, and writesbenchmarks/results/bench_parse.json. The harness is committed atbenchmarks.
Ruby
The teptris gem versus tomlib and tomlrb
The Ruby binding provides a tomlib-shaped API over the native engine. The harness parses every corpus file through each library in the same process, reporting the best of twelve iterations after warm-up. tomlib is a pure-Ruby parser and tomlrb is a Ragel-based Ruby extension; both are measured at their pinned release versions.
| operation | leptris side | tomlib / tomlrb | standing |
|---|---|---|---|
| Parse — array heavy | 5.05 ms | 54.12 ms | 10.72× |
| Parse — array heavy | 5.05 ms | 715.04 ms | 141.59× |
| Parse — cargo like | 5.06 ms | 63.67 ms | 12.58× |
| Parse — cargo like | 5.06 ms | 378.82 ms | 74.87× |
| Parse — datetime heavy | 142.16 ms | 11553.19 ms | 81.27× the competitor requires seconds on this shape; the ratio is a lower bound recorded at the harness iteration count |
| Parse — datetime heavy | 142.16 ms | 1766.68 ms | 12.43× the competitor requires seconds on this shape; the ratio is a lower bound recorded at the harness iteration count |
| Parse — deep tables | 3.38 ms | 6.23 ms | 1.84× |
| Parse — deep tables | 3.38 ms | 344.78 ms | 102.01× |
| Parse — mixed | 6.56 ms | 34.13 ms | 5.20× |
| Parse — mixed | 6.56 ms | 578.33 ms | 88.16× |
| Parse — scalar float | 30.07 ms | 16832.57 ms | 559.78× the competitor requires seconds on this shape; the ratio is a lower bound recorded at the harness iteration count |
| Parse — scalar float | 30.07 ms | 761.9 ms | 25.34× |
| Parse — scalar int | 17.32 ms | 12674.42 ms | 731.78× the competitor requires seconds on this shape; the ratio is a lower bound recorded at the harness iteration count |
| Parse — scalar int | 17.32 ms | 770.67 ms | 44.50× |
| Parse — scalar string | 11.77 ms | 2171.18 ms | 184.47× the competitor requires seconds on this shape; the ratio is a lower bound recorded at the harness iteration count |
| Parse — scalar string | 11.77 ms | 352.7 ms | 29.97× |
| Parse — table heavy | 17.41 ms | 581.13 ms | 33.38× |
| Parse — table heavy | 17.41 ms | 925.2 ms | 53.14× |
fresh runteptris-ruby 0.2.0 (fix/ext-platform-glob tree)·best of 12 after 2 warm-up iterations·Apple M1 Max, macOS 14.1.1, arm64; Ruby 3.4.8··lang_tier.rb — Teptris::TOML.load vs tomlib and tomlrb over the bench corpus
One observation belongs here rather than in a table: the datetime-heavy corpus costs the engine 3.7 milliseconds through the C API but 142 milliseconds through the Ruby binding, because constructing one Ruby Time object per datetime dominates the binding’s time on that shape. Binding overhead of this kind is inherent to rich object models and is recorded so it stays visible.
To reproduce: runTEPTRIS_LIB_PATH=… bundle exec ruby benchmark/lang_tier.rbin theteptris-rubyrepository.
Python
The teptris package versus tomli
The Python binding mirrors the standard library’s tomllib interface and is measured against tomli, the parser that tomllib itself wraps, under the same protocol as the Ruby tier.
| operation | leptris side | tomli 2.4.1 | standing |
|---|---|---|---|
| Parse — array heavy | 3.94 ms | 149.96 ms | 38.06× |
| Parse — cargo like | 3.08 ms | 66.16 ms | 21.48× |
| Parse — datetime heavy | 9.75 ms | 131.04 ms | 13.44× |
| Parse — deep tables | 1.79 ms | 54.17 ms | 30.26× |
| Parse — mixed | 3.91 ms | 88.9 ms | 22.74× |
| Parse — scalar float | 13.36 ms | 188.6 ms | 14.12× |
| Parse — scalar int | 11.68 ms | 181.12 ms | 15.51× |
| Parse — scalar string | 7.34 ms | 136.2 ms | 18.56× |
| Parse — table heavy | 10.67 ms | 224.44 ms | 21.03× |
fresh runteptris 0.1.1 (binding) on libteptris 0.1.0·best of 12 after 2 warm-up iterations·Apple M1 Max, macOS 14.1.1, arm64; Python 3.10.5··lang_tier.py — teptris.loads vs tomli.loads over the bench corpus
To reproduce: runTEPTRIS_LIB_PATH=… python3 benchmark/lang_tier.py in theteptris-pyrepository.
TOML
Measured deficits
Every TOML operation in the result data that remains behind its competitor. There are none at present; this section is generated from the same result files as the tables above and will list any deficit that a future measurement records.