Benchmarks · XSLT
XSLT transforms
The XSLT engine implements XSLT 1.0 completely and produces byte-identical output on 205 of 205 tests from the libxslt general suite, a gate that has been enforced on every release since it was closed. The engine also covers a growing working set from XSLT 2.0 and 3.0, including tunnel parameters, maps and arrays, accumulators, character maps, and higher-order functions. The measurements below compare transform performance against in-process libxslt on identical fixtures; process startup and stylesheet compilation are excluded on both sides.
| XSLT — competitor | C / C++ | Ruby | Python |
|---|---|---|---|
| libxslt | 0.72×–2.05× | — | — |
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++
libleptris versus libxslt, in-process
Each fixture is a committed benchmark program with a reference twin that drives in-process libxslt through lxml on the identical document and stylesheet. The reference twins are committed alongside the benchmarks, so every ratio on this page reproduces from the repository. Wall-clock comparisons againstxsltproc are deliberately not used, because process startup would dominate.
| operation | leptris side | libxslt 1.1.43 | standing |
|---|---|---|---|
| Template dispatch — diffuse 2000-book sheet | 3.42 ms | 2.65 ms | 0.77× the dispatch shapes sit behind a 20-year-tuned libxslt on its core fixture; the ceiling analysis (streaming + eval-side rewrites ≈ 25-30%) is published in the perf board — parity is the reachable bar here, and value-of/pred/copy shapes carry the wins |
| Template dispatch — heavy 120-template sheet | 3.82 ms | 3.42 ms | 0.90× streaming result emission (phases landed) closed most of the historic 3.3x gap on this fixture |
| Predicate dispatch — 120 pred templates / 2400 items | 19.43 ms | 32.95 ms | 1.70× the literal @attr='value' dispatch index |
| xsl:value-of hot loop — 50k top-level texts | 8.58 ms | 8.98 ms | 1.05× the fragment tail caches took leptris from 1424 ms to this; against libxslt the shape is now a wash |
| Transform — //book[@price>100] value-of fan (499 books) | 0.263 ms | 0.188 ms | 0.72× |
| Subtree copy — 100-book catalog | 0.044 ms/copy | 0.09 ms/copy | 2.05× pool-threaded copy_subtree_detached; through Ruby, Element#dup runs 2.27x ahead of Nokogiri 1.19.4 on the same fixture |
fresh runlibleptris 1.9.143-dev (main eea85cf)·best of 9 (transform: mean of 400), Release + LTO·Apple M1 Max, macOS 14.1.1, arm64··bench_xslt_* scorecard vs in-process libxslt twins (same fixtures, same protocol)
Interpretation of the dispatch results
The two dispatch fixtures remain behind libxslt, and a published ceiling analysis in the repository’s performance records quantifies why: the remaining coherent levers together account for roughly a quarter of the gap, which places parity, not a two-fold margin, as the reachable target on these shapes. Whether to pursue the streaming remainder or to accept the fixture verdict is recorded as an open decision in the performance board. The predicate-dispatch and subtree-copying fixtures are ahead, and the value-of fixture is at parity.
To reproduce: build the benchmark targets as described on theXML page, then run thebench_xslt_* binaries followed bypython3 benchmarks/xslt/scorecard_lxml.py for the reference side. The harness and twins are committed atbenchmarks/xslt.
XSLT
Measured deficits
Every XSLT fixture in the result data that remains behind libxslt, ordered from the weakest ratio, together with the recorded reason.
| operation | leptris side | versus | standing |
|---|---|---|---|
| Transform — //book[@price>100] value-of fan (499 books) | 0.263 ms | 0.188 ms | 0.72× |
| Template dispatch — diffuse 2000-book sheet | 3.42 ms | 2.65 ms | 0.77× the dispatch shapes sit behind a 20-year-tuned libxslt on its core fixture; the ceiling analysis (streaming + eval-side rewrites ≈ 25-30%) is published in the perf board — parity is the reachable bar here, and value-of/pred/copy shapes carry the wins |
| Template dispatch — heavy 120-template sheet | 3.82 ms | 3.42 ms | 0.90× streaming result emission (phases landed) closed most of the historic 3.3x gap on this fixture |