LEPTRIS

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 — competitorC / C++RubyPython
cpptoml3.09×+
toml11114.64×+
tomlc172.85×+
tomlc993.43×+
tomli13.44×+
tomlib1.84×+
tomlplusplus5.28×+
tomlrb12.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.

operationleptris sidethe reference parsersstanding
Parse — array heavy (738 KB)330.3 MB/s45.9 MB/s7.20×
Parse — array heavy (738 KB)330.3 MB/stoml11 4.2.03303.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/s62.5 MB/s5.28×
Parse — array heavy (738 KB)330.3 MB/s105.9 MB/s3.12×
Parse — cargo like (362 KB)265.2 MB/s15.9 MB/s16.68×
Parse — cargo like (362 KB)265.2 MB/stoml11 4.2.0442.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/s48.8 MB/s5.43×
Parse — cargo like (362 KB)265.2 MB/s16.1 MB/s16.47×
Parse — cargo like (362 KB)265.2 MB/s85.7 MB/s3.09×
Parse — datetime heavy (1108 KB)321.6 MB/stomlc99 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/s1.8 MB/s178.67×
Parse — datetime heavy (1108 KB)321.6 MB/s39.9 MB/s8.06×
Parse — datetime heavy (1108 KB)321.6 MB/s95.5 MB/s3.37×
Parse — deep tables (248 KB)294.6 MB/s85.9 MB/s3.43×
Parse — deep tables (248 KB)294.6 MB/stoml11 4.2.0736.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/s43.7 MB/s6.74×
Parse — deep tables (248 KB)294.6 MB/s81.7 MB/s3.61×
Parse — deep tables (248 KB)294.6 MB/s82 MB/s3.59×
Parse — mixed (446 KB)289.8 MB/s32.9 MB/s8.81×
Parse — mixed (446 KB)289.8 MB/stoml11 4.2.0362.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/s42.4 MB/s6.83×
Parse — mixed (446 KB)289.8 MB/s101.8 MB/s2.85×
Parse — mixed (446 KB)289.8 MB/s82.9 MB/s3.50×
Parse — scalar float (1502 KB)190.5 MB/stomlc99 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/s1.3 MB/s146.54×
Parse — scalar float (1502 KB)190.5 MB/s21.8 MB/s8.74×
Parse — scalar float (1502 KB)190.5 MB/s46.1 MB/s4.13×
Parse — scalar int (1306 KB)226.5 MB/stomlc99 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/s1.1 MB/s205.91×
Parse — scalar int (1306 KB)226.5 MB/s33.4 MB/s6.78×
Parse — scalar int (1306 KB)226.5 MB/s57.4 MB/s3.95×
Parse — scalar string (1329 KB)321 MB/s1.2 MB/s267.50×
Parse — scalar string (1329 KB)321 MB/s2.8 MB/s114.64×
Parse — scalar string (1329 KB)321 MB/s51 MB/s6.29×
Parse — scalar string (1329 KB)321 MB/s86.6 MB/s3.71×
Parse — table heavy (1150 KB)254.6 MB/s3.8 MB/s67.00×
Parse — table heavy (1150 KB)254.6 MB/stoml11 4.2.0509.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/s38.4 MB/s6.63×
Parse — table heavy (1150 KB)254.6 MB/s4.3 MB/s59.21×
Parse — table heavy (1150 KB)254.6 MB/s81.4 MB/s3.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.

operationleptris sidetomlib / tomlrbstanding
Parse — array heavy5.05 ms54.12 ms10.72×
Parse — array heavy5.05 ms715.04 ms141.59×
Parse — cargo like5.06 ms63.67 ms12.58×
Parse — cargo like5.06 ms378.82 ms74.87×
Parse — datetime heavy142.16 ms11553.19 ms81.27×

the competitor requires seconds on this shape; the ratio is a lower bound recorded at the harness iteration count

Parse — datetime heavy142.16 ms1766.68 ms12.43×

the competitor requires seconds on this shape; the ratio is a lower bound recorded at the harness iteration count

Parse — deep tables3.38 ms6.23 ms1.84×
Parse — deep tables3.38 ms344.78 ms102.01×
Parse — mixed6.56 ms34.13 ms5.20×
Parse — mixed6.56 ms578.33 ms88.16×
Parse — scalar float30.07 ms16832.57 ms559.78×

the competitor requires seconds on this shape; the ratio is a lower bound recorded at the harness iteration count

Parse — scalar float30.07 ms761.9 ms25.34×
Parse — scalar int17.32 ms12674.42 ms731.78×

the competitor requires seconds on this shape; the ratio is a lower bound recorded at the harness iteration count

Parse — scalar int17.32 ms770.67 ms44.50×
Parse — scalar string11.77 ms2171.18 ms184.47×

the competitor requires seconds on this shape; the ratio is a lower bound recorded at the harness iteration count

Parse — scalar string11.77 ms352.7 ms29.97×
Parse — table heavy17.41 ms581.13 ms33.38×
Parse — table heavy17.41 ms925.2 ms53.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.

operationleptris sidetomli 2.4.1standing
Parse — array heavy3.94 ms149.96 ms38.06×
Parse — cargo like3.08 ms66.16 ms21.48×
Parse — datetime heavy9.75 ms131.04 ms13.44×
Parse — deep tables1.79 ms54.17 ms30.26×
Parse — mixed3.91 ms88.9 ms22.74×
Parse — scalar float13.36 ms188.6 ms14.12×
Parse — scalar int11.68 ms181.12 ms15.51×
Parse — scalar string7.34 ms136.2 ms18.56×
Parse — table heavy10.67 ms224.44 ms21.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.