What every layer of oneyaml costs against the other Rust YAML libraries, on a fixed, generated corpus of configuration-file shapes. Coming from the chart in the README? Results at a glance has the same comparison bar by bar and as tables (five workloads against the fastest feature-complete serde library or yaml-edit, in instructions and in wall-clock time) and says where oneyaml wins and where it loses; Regenerate has the commands that produce every table here.
This page covers:
- the parser:
oneyaml-syntax's lossless event stream against saphyr-parser, yaml-rust2, libyaml and yaml-edit, and its syntax tree (Parser); - serde: reading (Serde read) and writing (Serde write) against serde_yaml_ng, serde_norway, serde-saphyr and serde_yaml2, with serde_json as a reference;
Document: parse, emit and edits against yaml-edit (Document).
How to read these numbers
- The primary metric is CPU instructions, counted by valgrind (callgrind), per input byte (Ir/B) or per operation. Instruction counts are deterministic for a given build: repeated runs of the same binary give the same count (to within 0.1% where a hash map with per-process random keys is involved), so a difference between two cells of one run is real, not noise. Rebuilding (a different toolchain, or a dependency rebuilt with other code generation) can shift a library's counts by a few percent, so compare numbers from one run, as every table here does. Counts cover user-space work only, not cache misses, page faults or kernel time.
- Allocations (blocks, bytes, peak heap) are counted by valgrind DHAT and are deterministic too.
- Wall-clock times are informational. They were taken on a shared, loaded 16-core desktop (each wall-clock table prints the load average), so read them as ratios and orders of magnitude. Where wall-clock and instructions point in different directions, both are shown.
- Ratios are oneyaml ÷ competitor in these tables, so below 1.0× oneyaml is cheaper. The summary tables in Results at a glance and the README chart invert them into "× faster" (competitor ÷ oneyaml, higher is better).
- Serde ratios are against the fastest feature-complete library on each row: serde_yaml_ng, serde_norway and serde-saphyr all support aliases, multi-document streams and merge keys. serde_yaml2 does not (no multi-document API, no merge keys); its column and ratio are shown, marked partial, no target, but it never decides a verdict.
- Every competitor's output is checked before it is measured (the
correctness-guard tables): a library that rejects an input, gives a
different result, or can't do the equivalent work gets a
rejects,differsorn/acell instead of a number.
The run's provenance (the summary's method notes are read from it):
| Measured | |
|---|---|
| Revision | oneyaml 1.2.0 release tree |
| Date | 2026-10-21 |
| CPU | AMD Ryzen 9 7950X3D 16-Core Processor |
| OS | Ubuntu 26.04 LTS (x86_64) |
| rustc | rustc 1.97.1 (8bab26f4f 2026-07-14) |
| Build | release profile |
| valgrind | valgrind-3.27.1 |
| Comparators | serde_yaml_ng 0.10.0, serde_norway 0.9.42, serde-saphyr 1.3.0, serde_yaml2 0.1.3, serde_json 1.0.151, yaml-edit 0.3.2, saphyr-parser 0.1.0, yaml-rust2 0.13.0, unsafe-libyaml 0.2.11 |
Results at a glance
The numbers behind the README chart: five workloads for each operation,
each serde row against the fastest feature-complete serde library on
that row and each Document row against yaml-edit, the other lossless
editor.
The chart has one bar per row; the tables give the absolute cost of both
libraries, the instruction ratio and the wall-clock ratio, and label
every row where oneyaml is slower slower. Both are generated from the
per-operation tables further down this page, so they always match them.
| serde read (typed) | vs | oneyaml Ir/B | competitor Ir/B | × faster | × faster (wall-clock) |
|---|---|---|---|---|---|
| gha-workflow (3.1 kB) | serde_yaml_ng | 231.1 | 292.2 | 1.3× | 1.0× |
| compose-merge (2.4 kB) | serde_yaml_ng | 529.6 | 623.3 | 1.2× | 1.0× |
| helm-values (100 kB) | serde_norway | 102.1 | 174.7 | 1.7× | 1.1× |
| openapi-100k (100 kB) | serde_yaml_ng | 333.1 | 348.4 | 1.0× | 0.80× — slower |
| k8s-multi (1.1 MB) | serde_norway | 340.9 | 422.1 | 1.2× | 1.1× |
| serde write (typed) | vs | oneyaml Ir/B | competitor Ir/B | × faster | × faster (wall-clock) |
|---|---|---|---|---|---|
| gha-workflow (3.1 kB) | serde-saphyr | 128.8 | 186.6 | 1.4× | 1.5× |
| compose-merge (2.4 kB) | serde-saphyr | 236.7 | 330.2 | 1.4× | 1.3× |
| helm-values (100 kB) | serde-saphyr | 32.2 | 42.4 | 1.3× | 1.3× |
| openapi-100k (100 kB) | serde-saphyr | 218.6 | 305.6 | 1.4× | 1.3× |
| k8s-multi (1.1 MB) | serde-saphyr | 142.5 | 206.9 | 1.5× | 1.4× |
Document parse | vs | oneyaml Ir/B | competitor Ir/B | × faster | × faster (wall-clock) |
|---|---|---|---|---|---|
| gha-workflow (3.1 kB) | yaml-edit | 286.0 | 417.5 | 1.5× | 1.3× |
| compose-merge (2.4 kB) | yaml-edit | 444.0 | 512.9 | 1.2× | 1.0× |
| helm-values (100 kB) | yaml-edit | 135.8 | 196.0 | 1.4× | 1.5× |
| openapi-100k (100 kB) | yaml-edit | 399.2 | 449.0 | 1.1× | 1.1× |
| k8s-multi (1.1 MB) | yaml-edit | 385.4 | 435.2 | 1.1× | 1.1× |
Document edit script (insert + set + remove) | vs | oneyaml Ir per script | competitor Ir per script | × faster | × faster (wall-clock) |
|---|---|---|---|---|---|
| gha-workflow (3.1 kB) | yaml-edit | 826,673 | 1,870,115 | 2.3× | 2.5× |
| compose-merge (2.4 kB) | yaml-edit | 503,520 | 302,397 | 0.60× — slower | 0.46× — slower |
| helm-values (100 kB) | yaml-edit | 1,192,304 | 11,824,702 | 9.9× | 17× |
| openapi-100k (100 kB) | yaml-edit | 584,641 | 37,349,309 | 64× | 53× |
| k8s-multi (1.1 MB) | yaml-edit | 559,559 | 1,786,870 | 3.2× | 4.2× |
- × faster = the competitor's CPU instructions ÷ oneyaml's on the same input (higher is better; below 1.0× oneyaml is slower), counted by valgrind (callgrind): deterministic from run to run, not wall-clock time. Ir/B = instructions per input byte; the edit script counts one script of three edits (insert + set + remove) on the parsed document; lower is better, the better value in bold.
- × faster (wall-clock) = the competitor's median time ÷ oneyaml's on the same host (for serde, from the median throughput); it varies more from run to run than the instruction count. oneyaml is slower in wall-clock time on 2 of 20 rows: serde read openapi-100k; Document edit script (insert + set + remove) compose-merge. The medians are equal on 2 rows: serde read gha-workflow and compose-merge.
- Measured oneyaml 1.2.0 release tree on 2026-10-21 · AMD Ryzen 9 7950X3D 16-Core Processor · Ubuntu 26.04 LTS (x86_64) · rustc 1.97.1 (8bab26f4f 2026-07-14) · valgrind-3.27.1, against serde_yaml_ng 0.10.0, serde_norway 0.9.42, serde-saphyr 1.3.0, yaml-edit 0.3.2. Serde rows compare with the fastest feature-complete YAML serde library on that row (aliases, multi-document, merge keys),
Documentrows with yaml-edit, the other lossless editor; every output is checked for equivalent work. Inputs are generated in the shape of GitHub Actions, docker-compose, Helm, OpenAPI and multi-document Kubernetes files.
Where oneyaml wins
- Parsing. The lossless event parser uses fewer instructions per byte than saphyr-parser, yaml-rust2 and libyaml on every input, comment- and whitespace-heavy ones included, although it does more work: it also produces events for every comment, space and indicator. Against yaml-edit, the other lossless parser, it is cheaper on every input yaml-edit accepts (Instructions).
- Serde writes.
to_stringuses fewer instructions than every feature-complete writer on every input, typed and untyped (Serde write). - Serde reads use fewer instructions than the fastest
feature-complete reader on every
typedrow, everyvaluerow but one (compose-merge, at parity) and mostsparserows, and stay within the 1.25× target on every row (Serde read; the targets are in Targets and results). Document. Parsing aDocumentcosts fewer instructions than yaml-edit's parse on every input, although oneyaml's parse also checks the document's meaning (duplicate keys, merge keys, aliases) and yaml-edit's checks syntax only. Writing an unchanged document back is several times to an order of magnitude cheaper, byte-identical on every input, and a parsed document keeps less memory than yaml-edit's tree (Document).- Edits stay local. The edit script on a root mapping costs about the same number of instructions on every input from 2.4 KB to 10 MB but helm-values, where yaml-edit's script grows with the document: openapi-10m, the openapi-100k shape at 100× the size, costs oneyaml about the same as openapi-100k (Edits). helm-values costs more per script because each edit there decides the owners of a long run of comment lines. The tables measure the size of the document, not the width of the edited mapping: a patch reads the keys of each mapping it looks at once per patch, so its cost does grow with the width of those mappings.
- Allocations. The parser borrows from the input and decodes scalars on request, so it makes a handful of allocations per input where event parsers make one or more per event; serde reads into borrowing types stream with O(depth) state (Allocations, serde allocations).
- No pathological input. oneyaml's instruction count grows linearly with input size on every stress shape, including 512k levels of flow nesting, which saphyr, yaml-rust2 and yaml-edit reject (Linear scaling).
Where oneyaml loses or is at parity
- Edits on documents below a few kilobytes (compose-merge, 2.4 KB) cost more than yaml-edit's, in instructions and in wall-clock time: each edit has a fixed cost for deciding which entry owns each nearby comment and checking that the edited text still reads as the tree says, which exceeds yaml-edit's whole-document cost on a small file. On every larger input in the Edits tables oneyaml's script is the cheaper one, so among the measured inputs the loss is on the smallest file.
- In wall-clock time, typed serde reads of openapi-100k are slower
than serde_yaml_ng, although they use fewer instructions. Across the
full table, serde reads are slower in wall-clock time on most
sparserows, onvaluereads of compose-merge, k8s-multi and openapi-100k and on thattypedrow, and at parity ontypedreads of gha-workflow and compose-merge (serde wall-clock). If wall-clock read speed is what matters most to you, measure on your own data. sparsereads (a struct that reads a few fields) of the OpenAPI inputs take a few percent more instructions than serde_norway, and k8s-manifest's are at parity with serde_yaml_ng: oneyaml still resolves and checks the content a struct ignores (duplicate keys, merge keys), so a typed read accepts exactly the documents aValueread accepts (Instructions).valuereads of the file with merge keys (compose-merge) are at parity in instructions with serde_yaml_ng.- In wall-clock time, parsing
anchorsis slower than libyaml andflow-heavyis at parity, although both use fewer instructions (Wall-clock). - In wall-clock time,
valuewrites of compose-merge are slightly slower than serde-saphyr's, although they use fewer instructions (serde write wall-clock). - serde_yaml2 is cheaper than oneyaml on the rows it can run, and serde_json reading the equivalent JSON is several times cheaper than any YAML library; both do less work (see their columns).
Targets and results
The performance targets oneyaml holds itself to, and how the tables below measure them. "met" means every row of the measure is within the target.
| Target | Measured by | Result |
|---|---|---|
Typed serde reads (from_str::<T>) ≤ 1.25× the instructions of the fastest feature-complete YAML serde library | serde read instructions, typed and sparse rows | met on every row |
from_str::<Value> ≤ 1.25× | serde read instructions, value rows | met on every row |
to_string ≤ 1.25× | serde write instructions | met on every row; cheaper than every feature-complete writer |
Document parse at parity with yaml-edit | Document, parse | met on every row |
Unchanged Document emit at parity with yaml-edit | Document, emit | met on every row, byte-identical output |
| Edit operations at parity with yaml-edit | Edits, script | met, except on documents below a few kilobytes (compose-merge); see above |
| Local edits never cost O(document) | Edits, script instructions and wall-clock | met for the measured root-mapping script: about the same from 2.4 KB to 10 MB, and openapi-10m about the same as openapi-100k |
A parsed Document retains ≤ 8× its input (real-world inputs of 100 KB or more) | Document, memory | met; smaller files and dense synthetic shapes are reported, without a target |
| Serde reads into borrowing types use O(depth + anchored content) memory | serde allocations, sparse rows | met |
| Parsing is O(n) for every input | Linear scaling | met |
Regenerate
You need valgrind 3.27 or later on PATH (or pass --valgrind PATH)
and, for the wall-clock tables, a machine as quiet as you can get.
cargo build -p bench --release target/release/bench update docs/performance.md --timeout 300 # every table (~24 min with --jobs 4) target/release/bench update docs/performance.md --section wall # one section target/release/bench table scaling # print a table, don't write target/release/bench probe yaml-edit deep-flow 10 # one parser, one input target/release/bench serde-check serde_yaml2 value gha-workflow # one read-guard verdict target/release/bench ser-check serde-saphyr typed k8s-multi # one write-guard verdict (+ output bytes) target/release/bench probe serde:oneyaml:typed openapi-10m 1 # one serde read probe target/release/bench probe ser:oneyaml:typed openapi-10m 1 # one serde write probe target/release/bench probe doc:yaml-edit:script openapi-100k 1 # one Document probe target/release/bench readme # summary and README charts only, from the tables below
- Tables come from the default release build, the one users compile
oneyaml and the comparators with, so every library's numbers are the
ones its users get.
updateandtablerefuse any other build, and a build with an override they can see: an optimization level other than 3, anyCARGO_PROFILE_*variable in the build's environment (thebenchprofile's included:cargo build --profile benchwrites totarget/releasetoo), orRUSTFLAGS. They can't see overrides passed with--configor set in a Cargo config file, so don't measure with those. Theenvironmenttable'sBuildrow records the build. - Comparing oneyaml with itself (probe counts before and after a
change) can use the
measureprofile instead:cargo build -p bench --profile measure, thentarget/measure/bench probe …. In a release build, how the compiler splits code into codegen units moves some counts by up to about 1% when unrelated code changes; themeasureprofile builds in a single codegen unit, which removes that. It also changes how every library is optimized: on every serde read row, serde_yaml_ng's counts rise by about 6% to 14% and serde-saphyr's by about 2% to 6%, while oneyaml's move by about 1% or less. That would flatter oneyaml's ratios, soprobeandtimerun from it, andupdateandtabledon't. - Every table sits between
<!-- bench:NAME:begin/end -->markers.updaterewrites only what's between them, one section at a time (every section, in document order, without--section), and writes the file after each section, so don't edit it whileupdateruns. --jobs Nlimits how many valgrind processes run at once (default: every core).--timeout SECS(default 100) kills a probe that runs too long; wall-clock children get a quarter of it. These tables used 300: at 100, libyaml'sdeep-flowat 100× and yaml-edit'sdeep-flowat 10× time out instead of finishing.- The summary and the README chart are generated from this file.
After every
update, and on its own withbench readme(which measures nothing),benchrewrites thebench:summarytables and chart in Results at a glance, the chart and its link in thebench:readmeregion ofcrates/oneyaml/README.mdand thebench:chartregion of the repositoryREADME.md, and the chartsdocs/img/bench-{summary,detail}-{light,dark}.svg, from theserde-instructions,ser-instructions,documentandeditstables, their wall-clock tables and theenvironmenttable. Every number in them is a cell of those tables or the ratio of two, so the READMEs can't disagree with this file. - The
environmenttable records the run's revision, date, CPU, OS, rustc, build, valgrind and the comparator versions (fromCargo.lock).updatere-records it whenever it re-measures a table the summary reads.--revision TEXT,--revision-note TEXTand--date YYYY-MM-DDoverride what it records. - Releases pin the links. The READMEs' chart URLs and links to this
file name a git ref,
mainby default. A published crate's README is frozen at release, so a release runscargo run -p bench --release -- pin-links <tag>on the release commit to point every repository link of the published docs at the tag (--dry-runshows what would change), andpin-links mainafterwards to point them back.
How long each section took in this run (--timeout 300 --jobs 4):
| Section | Time |
|---|---|
environment | < 0.1 s |
corpus | 0.4 s |
instructions | 207 s (one yaml-edit deep-flow probe dominates) |
allocations | 112 s |
collect | 6 s |
tree | 14 s |
scaling | 305 s (the 300 s timeout of libyaml deep-flow at 100× dominates) |
wall | 111 s |
wall-scaling | 41 s |
serde-check | 5 s |
serde-instructions | 89 s |
serde-allocations | 99 s |
serde-wall | 63 s |
ser-check | 5 s |
ser-instructions | 71 s |
ser-allocations | 138 s |
ser-wall | 28 s |
document | 65 s |
edits | 63 s |
| total | 23 min 42 s |
valgrind 3.27.1 was built from source
(https://sourceware.org/pub/valgrind/valgrind-3.27.1.tar.bz2, sha256
5d589152eb8071c02feab8ce6ab719e431a1fbc3e2b1700f5432632a8b9264dc)
with ./configure --prefix=$HOME/.local && make && make install.
Method
Instructions. The bench binary runs itself as
bench probe <parser> <case> <scale> under
valgrind --tool=callgrind --toggle-collect=*measured_drain*. Only
instructions executed while the measured function is on the stack are
counted: the parse (or read, write, edit), consuming the whole output,
and dropping it. Input generation and start-up are excluded. This is the
iai-callgrind/gungraun technique without their runner.
Allocations. valgrind DHAT (--tool=dhat) reports total blocks,
total bytes and the heap peak (t-gmax) of the whole probe. A
baseline probe generates the same input and does nothing else; its
blocks and bytes are subtracted from each probe's, which leaves exactly
the measured work's allocations (a realloc counts as one). The peak
can't be subtracted, because the baseline's peak happens during input
generation, so peak tables show the whole-process peak ÷ input with the
baseline beside it.
Wall-clock. bench time runs one child process per library and
input, one after another: one validating run, one warm-up, then at least
5 and at most 1,000 timed runs, stopping once 300 ms have been spent.
Tables show the median.
Linear scaling. Every parser runs each stress shape at 1×, 10× and
100× of 10 KB (about 1 MB at 100×), measured in instructions. A cell is
(Ir_k / bytes_k) / (Ir_1 / bytes_1); 1.00 is perfectly linear, and a
ratio above 1.25 would be flagged SUPERLINEAR.
Failures are cells. Every measurement runs in a child process, so a
library that errors, crashes or hangs becomes a rejects, crash or
timeout cell, not a failed run. Every corpus input is valid YAML 1.2,
and a test checks that oneyaml accepts it.
Isolation. The competitors are reached only through the unpublished
oracles crate; no published oneyaml crate depends on any of them
(cargo tree shows none of them in any published crate's graph).
Corpus
crates/bench/src/corpus.rs generates every input from templates written
for this project. There is no third-party text, so the corpus is
license-clean and versioned with the code; the FNV-1a column shows when
a generator changes. The real-world shapes follow the public structure of
each format; they aren't copies of real files:
- Kubernetes (k8s-manifest, and k8s-multi with over a thousand documents): Deployment, Service, ConfigMap and Ingress, as Helm renders them.
- GitHub Actions (gha-workflow):
on,env, matrix jobs,uses/with, andrun: |/>-. - docker-compose (compose-merge):
x-extension fields with&anchors, and<<: *mergekeys. - OpenAPI 3.1 (openapi-100k, openapi-10m):
pathswith parameters, responses and$refs, pluscomponents/schemas. - Helm values (helm-values): an umbrella chart's
values.yamldocumented the helm-docs way (# --lines), with commented-out example blocks; about 70% of its bytes are comment lines.
The synthetic stress shapes (deep-block, deep-flow, wide-map,
long-scalars, block-scalars, flow-heavy, comment-heavy,
anchors, whitespace) appear at 10× (about 100 KB) in the
deterministic tables.
size_of::<oneyaml_syntax::Event>() = 48 bytes.
| Case | Kind | Scale | Bytes | FNV-1a 64 | oneyaml events (with trivia) | saphyr events (YAML only) | Contents |
|---|---|---|---|---|---|---|---|
| k8s-manifest | real-world | 1× | 5,633 | 055defb9d646c354 | 1,721 | 752 | Deployment + Service + ConfigMap + Ingress, one set |
| gha-workflow | real-world | 1× | 3,078 | 02c8bd00cd7377f3 | 666 | 267 | GitHub Actions workflow: matrix, steps, literal-block run scripts |
| compose-merge | real-world | 1× | 2,442 | 3099f74496483a97 | 812 | 333 | docker-compose with x- anchors and <<: * merge keys |
| k8s-multi | real-world | 1× | 1,050,323 | d4e1c07e13a3d3b2 | 316,871 | 138,252 | ~300 ----separated Kubernetes documents |
| openapi-100k | real-world | 1× | 102,778 | 1d0382cd74933d58 | 30,974 | 13,112 | OpenAPI-style spec: paths, schemas, $refs |
| openapi-10m | real-world | 1× | 10,486,088 | f1ada321f5a8840e | 3,114,534 | 1,318,016 | the same OpenAPI shape at ~10 MB |
| helm-values | real-world | 1× | 102,418 | dc0a6f4a2039c07b | 14,268 | 3,400 | Helm umbrella-chart values: long # -- comment runs, commented-out blocks, comments under {}/[] |
| deep-block | stress | 10× | 103,739 | 7a1e7da75647cb05 | 2,718 | 1,361 | one chain of nested block mappings (depth = sqrt(2 × size)) |
| deep-flow | stress | 10× | 102,402 | 161a42f706068b17 | 102,406 | rejects | [[[...]]] nested flow sequences (depth = size/2) |
| wide-map | stress | 10× | 102,414 | 0320a218d7370faa | 26,781 | 10,716 | one flat block mapping with many keys |
| long-scalars | stress | 10× | 102,444 | 550a8ced7e575398 | 26 | 14 | four huge scalars: plain, single, double (escapes), multi-line |
| block-scalars | stress | 10× | 102,508 | 5ccc9aacc9ce1375 | 1,620 | 544 | many literal and folded scalars, every chomping indicator |
| flow-heavy | stress | 10× | 102,495 | 182d89629cba5cca | 72,681 | 31,371 | block sequence of nested flow mappings/sequences |
| comment-heavy | stress | 10× | 102,400 | 89d4337fd94d69d9 | 19,044 | 4,014 | comment blocks and trailing comments on every line |
| anchors | stress | 10× | 102,474 | fdc35eda9c97bb49 | 54,556 | 21,826 | many anchors, aliases and << merges |
| whitespace | stress | 10× | 102,456 | 6bbeaf871cb95ae5 | 41,242 | 7,936 | blank lines, trailing spaces, wide indentation |
Parser
What each parser does
Every column consumes its parser's complete output for the whole input; no parser has an event cap, and none renders anything.
| Column | Call | What it produces |
|---|---|---|
| oneyaml | oneyaml_syntax::events_with(input, &settings) iterated to StreamEnd, each event counted and dropped | Lossless events with trivia. Every YAML event, plus trivia events that tile every input byte (whitespace, line breaks, comments, and indicators such as :, -, [, ,, ---), with spans and in-band diagnostics. Scalar values are decoded from the source on demand, not during the parse. Streamed. |
| saphyr | saphyr_parser::Parser::new_from_str iterated to StreamEnd | YAML events only, no trivia and no comments. Streamed. |
| yaml-rust2 | yaml_rust2::parser::Parser::next_token to StreamEnd | YAML events only, no trivia and no comments. Owned scalar strings, streamed. |
| libyaml | unsafe_libyaml::yaml_parser_parse to STREAM-END, with yaml_event_delete after each event | YAML events only, no trivia and no comments. C-heap scalar copies, streamed. |
| yaml-edit | YamlFile::parse, then a full preorder walk of the rowan CST (every node and token) | Lossless CST: the like-for-like comparator. |
They do different work. oneyaml and yaml-edit are lossless: they keep every byte, including comments and whitespace. saphyr, yaml-rust2 and libyaml throw that away, so on the same input oneyaml's stream has 2–5× more events (see the corpus table's event counts).
Settings. oneyaml runs under
Settings::new().limits(Limits::unlimited()): the default max_depth of
1,024 would reject deep-flow at every scale and deep-block at 100×.
The limit checks still run; they just never trip. The competitors run
with their defaults. saphyr and yaml-rust2 hold the flow level in a u8,
so they reject flow nesting deeper than 255; yaml-edit rejects
deep-block from 10× and deep-flow at every scale; libyaml rejects
whitespace, which has a tab after - (valid in YAML 1.2).
Instructions
Lower is better. The ratio columns compare oneyaml with the fastest of the three event parsers, and with yaml-edit.
| Case | Bytes | oneyaml Ir | oneyaml Ir/B | saphyr Ir/B | yaml-rust2 Ir/B | libyaml Ir/B | yaml-edit Ir/B | oneyaml ÷ fastest event parser | oneyaml ÷ yaml-edit |
|---|---|---|---|---|---|---|---|---|---|
| k8s-manifest | 5,633 | 756,527 | 134.3 | 265.1 | 215.0 | 243.9 | 842.5 | 0.62× | 0.16× |
| gha-workflow | 3,078 | 304,121 | 98.8 | 201.3 | 163.6 | 215.6 | 664.6 | 0.60× | 0.15× |
| compose-merge | 2,442 | 372,294 | 152.5 | 265.8 | 225.3 | 244.3 | 856.5 | 0.68× | 0.18× |
| k8s-multi | 1,050,323 | 138,676,026 | 132.0 | 256.2 | 212.8 | 243.2 | 777.5 | 0.62× | 0.17× |
| openapi-100k | 102,778 | 14,226,043 | 138.4 | 259.1 | 216.7 | 244.3 | 827.1 | 0.64× | 0.17× |
| openapi-10m | 10,486,088 | 1,431,450,575 | 136.5 | 243.0 | 214.2 | 241.2 | 811.3 | 0.64× | 0.17× |
| helm-values | 102,418 | 4,894,822 | 47.8 | 78.5 | 77.7 | 137.3 | 304.2 | 0.62× | 0.16× |
| deep-block | 103,739 | 2,017,955 | 19.5 | 47.5 | 48.4 | 101.1 | rejects | 0.41× | – |
| deep-flow | 102,402 | 67,603,105 | 660.2 | rejects | rejects | 26952.1 | rejects | 0.024× | – |
| wide-map | 102,414 | 14,197,231 | 138.6 | 286.3 | 226.6 | 251.7 | 821.3 | 0.61× | 0.17× |
| long-scalars | 102,444 | 2,265,015 | 22.1 | 63.2 | 71.6 | 144.5 | 209.6 | 0.35× | 0.11× |
| block-scalars | 102,508 | 1,407,541 | 13.7 | 51.9 | 56.6 | 133.7 | 340.0 | 0.26× | 0.040× |
| flow-heavy | 102,495 | 37,277,421 | 363.7 | 586.0 | 479.5 | 449.0 | 1669.5 | 0.81× | 0.22× |
| comment-heavy | 102,400 | 6,003,000 | 58.6 | 84.4 | 87.2 | 138.3 | 361.6 | 0.69× | 0.16× |
| anchors | 102,474 | 26,074,600 | 254.5 | 454.4 | 365.1 | 312.9 | 1338.9 | 0.81× | 0.19× |
| whitespace | 102,456 | 12,859,828 | 125.5 | 158.0 | 150.5 | rejects | 752.1 | 0.83× | 0.17× |
Allocations
Allocation count (blocks, realloc included; baseline subtracted)
| Case | Bytes | oneyaml | saphyr | yaml-rust2 | libyaml | yaml-edit | oneyaml ÷ fewest event parser |
|---|---|---|---|---|---|---|---|
| k8s-manifest | 5,633 | 15 | 885 | 1,710 | 1,869 | 5,358 | 0.017× |
| gha-workflow | 3,078 | 14 | 396 | 666 | 726 | 2,202 | 0.035× |
| compose-merge | 2,442 | 15 | 392 | 668 | 817 | 2,594 | 0.038× |
| k8s-multi | 1,050,323 | 15 | 161,753 | 314,378 | 343,014 | 921,272 | 0.000093× |
| openapi-100k | 102,778 | 16 | 16,405 | 30,947 | 33,356 | 94,374 | 0.00098× |
| openapi-10m | 10,486,088 | 16 | 1,652,331 | 3,114,921 | 3,363,735 | 9,452,449 | 0.0000097× |
| helm-values | 102,418 | 11 | 3,728 | 7,406 | 8,249 | 36,470 | 0.0030× |
| deep-block | 103,739 | 14 | 932 | 1,836 | 1,846 | rejects | 0.015× |
| deep-flow | 102,402 | 53 | rejects | rejects | 61 | rejects | 0.87× |
| wide-map | 102,414 | 8 | 21,425 | 42,844 | 42,854 | 101,861 | 0.00037× |
| long-scalars | 102,444 | 6 | 62 | 76 | 90 | 19,056 | 0.097× |
| block-scalars | 102,508 | 8 | 3,501 | 3,836 | 3,242 | 30,864 | 0.0025× |
| flow-heavy | 102,495 | 16 | 34,438 | 66,565 | 70,397 | 225,099 | 0.00046× |
| comment-heavy | 102,400 | 8 | 5,350 | 10,692 | 10,702 | 47,901 | 0.0015× |
| anchors | 102,474 | 13 | 27,618 | 46,928 | 46,927 | 187,786 | 0.00047× |
| whitespace | 102,456 | 8 | 9,523 | 19,036 | rejects | 106,334 | 0.00084× |
Bytes allocated ÷ input bytes (baseline subtracted)
| Case | Bytes | oneyaml | saphyr | yaml-rust2 | libyaml | yaml-edit | oneyaml ÷ fewest event parser |
|---|---|---|---|---|---|---|---|
| k8s-manifest | 5,633 | 2.56 | 12.73 | 10.11 | 20.84 | 93.88 | 0.25× |
| gha-workflow | 3,078 | 4.59 | 10.87 | 7.39 | 28.57 | 67.95 | 0.62× |
| compose-merge | 2,442 | 5.84 | 10.41 | 8.22 | 36.32 | 102.15 | 0.71× |
| k8s-multi | 1,050,323 | 0.01 | 11.84 | 9.53 | 8.61 | 96.43 | 0.0016× |
| openapi-100k | 102,778 | 0.16 | 11.70 | 9.34 | 8.68 | 86.99 | 0.018× |
| openapi-10m | 10,486,088 | 0.00 | 11.51 | 9.21 | 7.95 | 91.49 | 0.00019× |
| helm-values | 102,418 | 0.13 | 2.92 | 2.32 | 2.68 | 38.16 | 0.054× |
| deep-block | 103,739 | 0.22 | 2.20 | 1.51 | 2.16 | rejects | 0.14× |
| deep-flow | 102,402 | 107.51 | rejects | rejects | 90.87 | rejects | 1.18× |
| wide-map | 102,414 | 0.12 | 16.84 | 13.39 | 10.71 | 97.67 | 0.011× |
| long-scalars | 102,444 | 0.03 | 2.58 | 2.57 | 3.24 | 31.10 | 0.012× |
| block-scalars | 102,508 | 0.12 | 3.42 | 2.84 | 3.82 | 29.08 | 0.042× |
| flow-heavy | 102,495 | 0.24 | 25.68 | 20.44 | 17.33 | 230.15 | 0.014× |
| comment-heavy | 102,400 | 0.12 | 4.21 | 3.35 | 3.18 | 54.05 | 0.038× |
| anchors | 102,474 | 0.14 | 17.29 | 15.51 | 11.66 | 181.56 | 0.012× |
| whitespace | 102,456 | 0.12 | 7.48 | 5.95 | rejects | 111.40 | 0.020× |
Whole-process peak heap ÷ input bytes (the baseline column is input generation alone)
| Case | Bytes | baseline | oneyaml | saphyr | yaml-rust2 | libyaml | yaml-edit | oneyaml ÷ yaml-edit |
|---|---|---|---|---|---|---|---|---|
| k8s-manifest | 5,633 | 1.94 | 3.12 | 2.25 | 2.09 | 14.02 | 27.09 | 0.12× |
| gha-workflow | 3,078 | 3.49 | 3.67 | 3.49 | 3.49 | 23.67 | 23.03 | 0.16× |
| compose-merge | 2,442 | 4.59 | 5.30 | 4.58 | 4.59 | 30.46 | 30.65 | 0.17× |
| k8s-multi | 1,050,323 | 1.00 | 1.01 | 1.01 | 1.00 | 1.07 | 29.98 | 0.034× |
| openapi-100k | 102,778 | 1.71 | 1.71 | 1.71 | 1.71 | 2.01 | 24.54 | 0.070× |
| openapi-10m | 10,486,088 | 1.67 | 1.67 | 1.67 | 1.67 | 1.67 | 27.13 | 0.061× |
| helm-values | 102,418 | 3.03 | 3.03 | 3.03 | 3.03 | 3.03 | 14.12 | 0.21× |
| deep-block | 103,739 | 1.04 | 1.14 | 1.78 | 1.51 | 2.23 | rejects | – |
| deep-flow | 102,402 | 1.02 | 54.77 | rejects | rejects | 46.78 | rejects | – |
| wide-map | 102,414 | 1.06 | 1.11 | 1.06 | 1.06 | 1.72 | 28.94 | 0.038× |
| long-scalars | 102,444 | 1.30 | 1.30 | 1.34 | 1.33 | 2.00 | 13.22 | 0.098× |
| block-scalars | 102,508 | 1.06 | 1.11 | 1.06 | 1.06 | 1.72 | 11.92 | 0.093× |
| flow-heavy | 102,495 | 1.06 | 1.17 | 1.21 | 1.16 | 1.81 | 66.97 | 0.017× |
| comment-heavy | 102,400 | 1.06 | 1.11 | 1.06 | 1.06 | 1.72 | 18.29 | 0.061× |
| anchors | 102,474 | 1.06 | 1.12 | 2.48 | 3.17 | 1.72 | 50.22 | 0.022× |
| whitespace | 102,456 | 1.06 | 1.11 | 1.06 | 1.06 | rejects | 35.72 | 0.031× |
Collecting (parse_with)
What keeping the whole stream costs: oneyaml-collect runs parse_with
(an EventList, 48 bytes per event) and walks it, against the pull
parser dropping each event. DHAT peak heap and bytes allocated, net of
the input-generation baseline, divided by the input size.
| Case | Bytes | pulled: peak ÷ input | collected: peak ÷ input | collected: bytes allocated ÷ input |
|---|---|---|---|---|
| k8s-manifest | 5,633 | 1.18 | 18.63 | 37.43 |
| gha-workflow | 3,078 | 0.18 | 16.15 | 36.47 |
| compose-merge | 2,442 | 0.72 | 20.85 | 46.02 |
| k8s-multi | 1,050,323 | 0.01 | 23.97 | 47.93 |
| openapi-100k | 102,778 | 0.00 | 15.02 | 30.76 |
| openapi-10m | 10,486,088 | 0.00 | 18.87 | 38.40 |
| helm-values | 102,418 | 0.00 | 6.72 | 15.48 |
| deep-block | 103,739 | 0.10 | 2.00 | 4.01 |
| deep-flow | 102,402 | 53.75 | 115.19 | 230.39 |
| wide-map | 102,414 | 0.05 | 15.41 | 30.83 |
| long-scalars | 102,444 | 0.00 | 0.00 | 0.06 |
| block-scalars | 102,508 | 0.05 | 1.01 | 2.04 |
| flow-heavy | 102,495 | 0.12 | 61.50 | 123.01 |
| comment-heavy | 102,400 | 0.05 | 15.41 | 30.84 |
| anchors | 102,474 | 0.06 | 30.76 | 61.53 |
| whitespace | 102,456 | 0.05 | 30.76 | 61.53 |
Syntax tree (SyntaxTree::parse_with)
What the lossless syntax tree costs. oneyaml-tree builds the tree from
the pull parser (the tree owns a copy of the input, as a Document
does), walks every element, and drops it; oneyaml-tree-retained keeps
the built tree, so DHAT's end-of-run figure is exactly what a tree
retains. Both are divided by the input size and include the tree's own
copy of the input (1.00×). "peak" is the whole-process peak minus the
heap the baseline still holds at its end (the generated input). The
arena stores 9 bytes per element, so retained ≈ 1 + 9 × elements ÷
byte; the densest synthetic shapes, where nearly every byte is its own
element, retain the most. "stream Ir/B" is the pull parser drained;
"tree Ir/B" is parse + build + walk + drop.
| Case | Bytes | tree elements | elements ÷ byte | peak ÷ input | retained ÷ input | stream Ir/B | tree Ir/B | tree ÷ stream |
|---|---|---|---|---|---|---|---|---|
| k8s-manifest | 5,633 | 2,053 | 0.36 | 10.64 | 4.31 | 134.3 | 200.2 | 1.49× |
| gha-workflow | 3,078 | 793 | 0.26 | 7.84 | 3.48 | 98.8 | 147.0 | 1.49× |
| compose-merge | 2,442 | 997 | 0.41 | 9.92 | 4.67 | 152.5 | 223.6 | 1.47× |
| k8s-multi | 1,050,323 | 378,253 | 0.36 | 6.56 | 4.27 | 132.0 | 190.6 | 1.44× |
| openapi-100k | 102,778 | 38,392 | 0.37 | 8.16 | 4.36 | 138.4 | 201.1 | 1.45× |
| openapi-10m | 10,486,088 | 3,860,896 | 0.37 | 5.93 | 4.31 | 136.5 | 194.9 | 1.43× |
| helm-values | 102,418 | 16,215 | 0.16 | 4.50 | 2.42 | 47.8 | 75.6 | 1.58× |
| deep-block | 103,739 | 2,717 | 0.03 | 2.51 | 1.24 | 19.5 | 25.4 | 1.31× |
| deep-flow | 102,402 | 153,605 | 1.50 | 81.36 | 14.50 | 660.2 | 890.3 | 1.35× |
| wide-map | 102,414 | 37,488 | 0.37 | 7.86 | 4.29 | 138.6 | 197.8 | 1.43× |
| long-scalars | 102,444 | 31 | 0.00 | 2.02 | 1.00 | 22.1 | 23.2 | 1.05× |
| block-scalars | 102,508 | 2,155 | 0.02 | 2.74 | 1.34 | 13.7 | 19.2 | 1.40× |
| flow-heavy | 102,495 | 97,158 | 0.95 | 13.68 | 9.53 | 363.7 | 512.3 | 1.41× |
| comment-heavy | 102,400 | 21,045 | 0.21 | 4.98 | 2.85 | 58.6 | 95.0 | 1.62× |
| anchors | 102,474 | 70,918 | 0.69 | 13.63 | 7.23 | 254.5 | 368.2 | 1.45× |
| whitespace | 102,456 | 44,411 | 0.43 | 7.86 | 4.90 | 125.5 | 201.7 | 1.61× |
Linear scaling
| Shape | Bytes 1× / 10× / 100× | oneyaml Ir at 100× | oneyaml (10× / 100×) | saphyr (10× / 100×) | yaml-rust2 (10× / 100×) | libyaml (10× / 100×) | yaml-edit (10× / 100×) |
|---|---|---|---|---|---|---|---|
| deep-block | 10,730 / 103,739 / 1,028,894 | 14,075,486 | 0.52 / 0.37 | 0.55 / 0.41 | 0.63 / 0.51 | 0.84 / 0.78 | rejects at 10× |
| deep-flow | 10,242 / 102,402 / 1,024,002 | 671,315,759 | 0.87 / 0.86 | rejects at 1× | rejects at 1× | 1.09 / 1.10 | rejects at 1× |
| wide-map | 10,249 / 102,414 / 1,024,009 | 141,913,640 | 1.00 / 1.00 | 1.00 / 1.00 | 1.00 / 1.00 | 1.00 / 1.00 | 0.98 / 0.97 |
| long-scalars | 10,301 / 102,444 / 1,024,061 | 22,525,089 | 0.95 / 0.95 | 0.97 / 0.96 | 0.97 / 0.97 | 0.99 / 0.99 | 0.98 / 0.97 |
| block-scalars | 10,329 / 102,508 / 1,024,352 | 13,671,453 | 0.96 / 0.93 | 0.99 / 0.97 | 0.98 / 0.96 | 0.99 / 0.99 | 0.97 / 0.95 |
| flow-heavy | 10,240 / 102,495 / 1,024,082 | 369,711,943 | 0.99 / 0.98 | 0.99 / 0.99 | 0.99 / 0.99 | 0.99 / 0.99 | 0.98 / 0.97 |
| comment-heavy | 10,356 / 102,400 / 1,024,179 | 59,614,679 | 0.99 / 0.98 | 0.99 / 0.99 | 0.99 / 0.99 | 0.99 / 1.00 | 0.97 / 0.96 |
| anchors | 10,268 / 102,474 / 1,024,001 | 236,871,723 | 0.90 / 0.82 | 0.95 / 0.92 | 0.90 / 0.83 | 0.93 / 0.88 | 0.91 / 0.82 |
| whitespace | 10,276 / 102,456 / 1,024,028 | 125,509,238 | 0.97 / 0.95 | 0.98 / 0.96 | 0.98 / 0.96 | rejects at 1× | 0.97 / 0.94 |
Wall-clock (informational only)
Real-world inputs run at 1× and stress shapes at 100× (about 1 MB). Higher MB/s is better. The ratio columns are time ratios, so lower is better for oneyaml.
Host: AMD Ryzen 9 7950X3D 16-Core Processor; load average (1/5/15 min) at start: 5.72 / 7.78 / 8.81. MB/s = 10⁶ bytes/s, median of ≥ 5 runs.
| Case | Bytes | oneyaml MB/s | saphyr MB/s | yaml-rust2 MB/s | libyaml MB/s | yaml-edit MB/s | oneyaml time ÷ fastest event parser | oneyaml time ÷ yaml-edit |
|---|---|---|---|---|---|---|---|---|
| k8s-manifest@1× | 5,633 | 124 | 56 | 66 | 85 | 22 | 0.69× | 0.18× |
| gha-workflow@1× | 3,078 | 164 | 75 | 90 | 98 | 30 | 0.59× | 0.18× |
| compose-merge@1× | 2,442 | 105 | 54 | 62 | 95 | 23 | 0.90× | 0.22× |
| k8s-multi@1× | 1,050,323 | 126 | 55 | 67 | 90 | 20 | 0.71× | 0.16× |
| openapi-100k@1× | 102,778 | 122 | 54 | 65 | 95 | 22 | 0.77× | 0.18× |
| openapi-10m@1× | 10,486,088 | 121 | 55 | 64 | 95 | 19 | 0.79× | 0.16× |
| helm-values@1× | 102,418 | 353 | 198 | 215 | 194 | 60 | 0.61× | 0.17× |
| deep-block@100× | 1,028,894 | 1709 | 622 | 649 | 327 | rejects | 0.38× | – |
| deep-flow@100× | 1,024,002 | 20 | rejects | rejects | 1 | timeout (> 75 s) | 0.045× | – |
| wide-map@100× | 1,024,009 | 131 | 55 | 68 | 86 | 19 | 0.66× | 0.15× |
| long-scalars@100× | 1,024,061 | 1334 | 473 | 364 | 175 | 109 | 0.35× | 0.081× |
| block-scalars@100× | 1,024,352 | 1557 | 441 | 421 | 211 | 60 | 0.28× | 0.038× |
| flow-heavy@100× | 1,024,082 | 49 | 24 | 30 | 48 | 10 | 0.99× | 0.20× |
| comment-heavy@100× | 1,024,179 | 290 | 161 | 178 | 192 | 47 | 0.66× | 0.16× |
| anchors@100× | 1,024,001 | 71 | 21 | 37 | 77 | 10 | 1.08× | 0.15× |
| whitespace@100× | 1,024,028 | 144 | 85 | 107 | rejects | 26 | 0.74× | 0.18× |
Each stress shape's throughput at 1×, 10× and 100× (10 KB, 100 KB and 1 MB):
Host: AMD Ryzen 9 7950X3D 16-Core Processor; load average (1/5/15 min) at start: 5.60 / 7.09 / 8.44. MB/s = 10⁶ bytes/s, median of ≥ 5 runs.
| Shape | oneyaml MB/s 1× / 10× / 100× | saphyr MB/s 1× / 10× / 100× | yaml-rust2 MB/s 1× / 10× / 100× | libyaml MB/s 1× / 10× / 100× | yaml-edit MB/s 1× / 10× / 100× |
|---|---|---|---|---|---|
| deep-block | 463 / 985 / 1712 | 175 / 379 / 598 | 199 / 410 / 637 | 213 / 303 / 335 | 16 / rejects at 10× |
| deep-flow | 12 / 16 / 20 | rejects at 1× | rejects at 1× | 1 / 1 / 1 | rejects at 1× |
| wide-map | 127 / 132 / 129 | 55 / 55 / 53 | 69 / 68 / 69 | 85 / 85 / 82 | 26 / 23 / 16 |
| long-scalars | 1128 / 1285 / 1276 | 407 / 454 / 449 | 323 / 356 / 363 | 167 / 173 / 170 | 110 / 93 / 112 |
| block-scalars | 882 / 852 / 985 | 388 / 418 / 442 | 359 / 413 / 419 | 207 / 207 / 206 | 64 / 67 / 64 |
| flow-heavy | 46 / 48 / 47 | 24 / 25 / 24 | 29 / 28 / 30 | 49 / 50 / 48 | 11 / 11 / 11 |
| comment-heavy | 287 / 300 / 294 | 158 / 160 / 155 | 181 / 183 / 184 | 193 / 195 / 185 | 58 / 53 / 47 |
| anchors | 61 / 66 / 70 | 26 / 28 / 29 | 34 / 36 / 39 | 68 / 59 / 78 | 12 / 13 / 11 |
| whitespace | 136 / 142 / 140 | 83 / 82 / 86 | 99 / 100 / 107 | rejects at 1× | 26 / 23 / 25 |
Serde read
Comparators, pinned in crates/oracles/Cargo.toml:
| Library | Version | Documents | Merge keys (<<) | Dynamic value type |
|---|---|---|---|---|
| oneyaml | this tree | Deserializer::from_str_with iterator | Settings::merge_keys(true), on compose-merge only (off by default) | oneyaml::Value |
| serde_yaml_ng | 0.10.0 | Deserializer::from_str iterator | not in its deserializer; only Value::apply_merge, so typed reads go Value → apply_merge → from_value (the extra work it needs to be equivalent) | serde_yaml_ng::Value |
| serde_norway | 0.9.42 | as serde_yaml_ng | as serde_yaml_ng | serde_norway::Value |
| serde_yaml2 | 0.1.3 | none: reads the first document only | none | yaml_rust2::Yaml |
| serde-saphyr | 1.3.0 | from_multiple | expanded by default | none of its own: serde_json::Value |
| serde_json | 1.0.151 | the JSON is an array of the documents | already expanded in the JSON | serde_json::Value |
serde-saphyr's default resource budget rejects k8s-multi (more than
1,024 documents) and openapi-10m (more than 250,000 nodes and 1,000,000
events), inputs oneyaml's Limits::default() accepts, so it runs with
those three caps lifted and every other budget check on. The saphyr
project's own saphyr-serde crate is still a placeholder; serde-saphyr is
a third-party crate built on a fork of saphyr-parser.
Workloads, each on every real-world corpus input at 1×:
value: every document into the library's dynamic value type;typed: every document into realistic derived structs (crates/bench/src/models.rs: Kubernetes objects as akind-tagged enum with an untaggedIntOrString; a GitHub Actions workflow; a compose file whose services getrestart/logging/networksfrom<<: *common; an OpenAPI document with nested schemas; Helm values read through#[serde(flatten)]);sparse: every document into a struct that reads a few fields and skips the rest.
oneyaml runs with Settings::new() (so Limits::default(), unlike the
parser tables), plus merge_keys on compose-merge.
serde_json reads the equivalent JSON (oneyaml's Value of each
document, merges expanded, serialized compactly and cached so the probe
reads a file). It is a reference ceiling, not a YAML comparator: JSON has
no plain scalars to resolve, no indentation and no anchors. Its Ir/B and
MB/s are per byte of the YAML input.
Metrics: callgrind instructions inside the measured function (deserialize every document, then drop the result); DHAT totals minus a baseline probe that only prepares the input; the whole-process DHAT peak ÷ input beside the baseline's; and wall-clock medians.
Correctness guard
Before anything is measured, bench serde-check <lib> <workload> <case>
runs oneyaml and the competitor, converts every document of both results
to serde_json::Value, and compares them. A competitor that differs,
rejects or can't do the equivalent work is not measured on that row; the
reason is listed under the table. Two notes on the reasons:
serde_yaml2 reads the quoted scalar "1.85" as a float, where YAML 1.2
(and 1.1) make it a string; and serde_json rejects gha-workflow typed
because in the JSON equivalent RUST_BACKTRACE: 1 is a number, while
every YAML library reads the plain scalar 1 into a String from its
source text. The second is a property of JSON as a reference, not a
disagreement between YAML libraries.
| Case | Workload | oneyaml | serde_yaml_ng | serde_norway | serde_yaml2 | serde-saphyr | serde_json |
|---|---|---|---|---|---|---|---|
| k8s-manifest | value | agrees | agrees | agrees | n/a [1] | agrees | agrees |
| k8s-manifest | typed | agrees | agrees | agrees | n/a [2] | agrees | agrees |
| k8s-manifest | sparse | agrees | agrees | agrees | n/a [3] | agrees | agrees |
| gha-workflow | value | agrees | agrees | agrees | differs [4] | agrees | agrees |
| gha-workflow | typed | agrees | agrees | agrees | agrees | agrees | rejects [5] |
| gha-workflow | sparse | agrees | agrees | agrees | agrees | agrees | agrees |
| compose-merge | value | agrees | agrees | agrees | n/a [6] | agrees | agrees |
| compose-merge | typed | agrees | agrees | agrees | n/a [7] | agrees | agrees |
| compose-merge | sparse | agrees | agrees | agrees | n/a [8] | agrees | agrees |
| k8s-multi | value | agrees | agrees | agrees | n/a [9] | agrees | agrees |
| k8s-multi | typed | agrees | agrees | agrees | n/a [10] | agrees | agrees |
| k8s-multi | sparse | agrees | agrees | agrees | n/a [11] | agrees | agrees |
| openapi-100k | value | agrees | agrees | agrees | agrees | agrees | agrees |
| openapi-100k | typed | agrees | agrees | agrees | agrees | agrees | agrees |
| openapi-100k | sparse | agrees | agrees | agrees | agrees | agrees | agrees |
| openapi-10m | value | agrees | agrees | agrees | agrees | agrees | agrees |
| openapi-10m | typed | agrees | agrees | agrees | agrees | agrees | agrees |
| openapi-10m | sparse | agrees | agrees | agrees | agrees | agrees | agrees |
| helm-values | value | agrees | agrees | agrees | agrees | agrees | agrees |
| helm-values | typed | agrees | agrees | agrees | agrees | agrees | agrees |
| helm-values | sparse | agrees | agrees | agrees | agrees | agrees | agrees |
- serde_yaml2, k8s-manifest value: no multi-document API (reads the first document only)
- serde_yaml2, k8s-manifest typed: no multi-document API (reads the first document only)
- serde_yaml2, k8s-manifest sparse: no multi-document API (reads the first document only)
- serde_yaml2, gha-workflow value: doc 0 /jobs/bravo-2/strategy/matrix/toolchain/2: 1.85 (ours "1.85")
- serde_json, gha-workflow typed: invalid type: integer '1', expected a string at line 1 column 845
- serde_yaml2, compose-merge value: no merge-key support
- serde_yaml2, compose-merge typed: no merge-key support
- serde_yaml2, compose-merge sparse: no merge-key support
- serde_yaml2, k8s-multi value: no multi-document API (reads the first document only)
- serde_yaml2, k8s-multi typed: no multi-document API (reads the first document only)
- serde_yaml2, k8s-multi sparse: no multi-document API (reads the first document only)
Instructions
Lower is better. The ratio columns compare oneyaml with the fastest feature-complete library on that row (named in the next column; the target applies to this ratio), with serde_yaml2 (partial, no target), and with serde_json.
| Case | Workload | Bytes | oneyaml Ir/B | serde_yaml_ng Ir/B | serde_norway Ir/B | serde_yaml2 Ir/B | serde-saphyr Ir/B | serde_json Ir/B | oneyaml ÷ fastest feature-complete (target applies) | fastest feature-complete | target ≤ 1.25× | oneyaml ÷ serde_yaml2 (partial, no target) | oneyaml ÷ serde_json |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| k8s-manifest | value | 5,633 | 383.8 | 457.3 | 458.5 | n/a | 729.4 | 69.9 | 0.84× | serde_yaml_ng | met | – | 5.49× |
| k8s-manifest | typed | 5,633 | 344.2 | 423.5 | 423.4 | n/a | 819.4 | 65.0 | 0.81× | serde_norway | met | – | 5.30× |
| k8s-manifest | sparse | 5,633 | 306.2 | 309.9 | 309.9 | n/a | 723.0 | 16.1 | 0.99× | serde_yaml_ng | met | – | 18.99× |
| gha-workflow | value | 3,078 | 255.6 | 353.4 | 354.2 | differs | 499.1 | 46.1 | 0.72× | serde_yaml_ng | met | – | 5.55× |
| gha-workflow | typed | 3,078 | 231.1 | 292.2 | 292.2 | 178.8 | 440.5 | rejects | 0.79× | serde_yaml_ng | met | 1.29× | – |
| gha-workflow | sparse | 3,078 | 215.9 | 259.1 | 259.1 | 179.6 | 499.4 | 11.2 | 0.83× | serde_norway | met | 1.20× | 19.20× |
| compose-merge | value | 2,442 | 591.6 | 593.8 | 596.1 | n/a | 1116.9 | 114.8 | 1.00× | serde_yaml_ng | met | – | 5.15× |
| compose-merge | typed | 2,442 | 529.6 | 623.3 | 625.5 | n/a | 999.1 | 76.3 | 0.85× | serde_yaml_ng | met | – | 6.94× |
| compose-merge | sparse | 2,442 | 484.3 | 600.2 | 602.1 | n/a | 1070.2 | 29.6 | 0.81× | serde_yaml_ng | met | – | 16.35× |
| k8s-multi | value | 1,050,323 | 379.6 | 457.5 | 458.8 | n/a | 721.2 | 70.0 | 0.83× | serde_yaml_ng | met | – | 5.42× |
| k8s-multi | typed | 1,050,323 | 340.9 | 422.1 | 422.1 | n/a | 808.9 | 66.2 | 0.81× | serde_norway | met | – | 5.15× |
| k8s-multi | sparse | 1,050,323 | 299.8 | 354.0 | 354.0 | n/a | 726.8 | 16.2 | 0.85× | serde_yaml_ng | met | – | 18.52× |
| openapi-100k | value | 102,778 | 381.5 | 454.2 | 454.4 | 328.8 | 729.8 | 70.7 | 0.84× | serde_yaml_ng | met | 1.16× | 5.39× |
| openapi-100k | typed | 102,778 | 333.1 | 348.4 | 348.4 | 237.6 | 649.6 | 44.1 | 0.96× | serde_yaml_ng | met | 1.40× | 7.55× |
| openapi-100k | sparse | 102,778 | 304.9 | 297.3 | 297.3 | 240.9 | 752.4 | 12.9 | 1.03× | serde_norway | met | 1.27× | 23.60× |
| openapi-10m | value | 10,486,088 | 375.5 | 447.7 | 449.0 | 324.4 | 720.3 | 70.7 | 0.84× | serde_yaml_ng | met | 1.16× | 5.31× |
| openapi-10m | typed | 10,486,088 | 329.6 | 346.4 | 346.4 | 236.3 | 641.6 | 44.3 | 0.95× | serde_yaml_ng | met | 1.39× | 7.44× |
| openapi-10m | sparse | 10,486,088 | 300.4 | 294.7 | 294.7 | 238.1 | 741.5 | 12.8 | 1.02× | serde_norway | met | 1.26× | 23.55× |
| helm-values | value | 102,418 | 110.4 | 186.8 | 187.2 | 103.3 | 236.8 | 16.6 | 0.59× | serde_yaml_ng | met | 1.07× | 6.64× |
| helm-values | typed | 102,418 | 102.1 | 174.8 | 174.7 | 94.7 | 263.3 | 11.9 | 0.58× | serde_norway | met | 1.08× | 8.56× |
| helm-values | sparse | 102,418 | 94.8 | 151.3 | 151.3 | 83.2 | 245.2 | 3.1 | 0.63× | serde_norway | met | 1.14× | 30.16× |
Allocations
Allocation count (blocks, realloc included; baseline subtracted)
| Case | Workload | Bytes | oneyaml | serde_yaml_ng | serde_norway | serde_yaml2 | serde-saphyr | serde_json | oneyaml ÷ best feature-complete | best feature-complete | oneyaml ÷ serde_yaml2 (partial) |
|---|---|---|---|---|---|---|---|---|---|---|---|
| k8s-manifest | value | 5,633 | 1,038 | 3,099 | 3,099 | n/a | 1,422 | 567 | 0.73× | serde-saphyr | – |
| k8s-manifest | typed | 5,633 | 620 | 2,805 | 2,805 | n/a | 2,313 | 459 | 0.27× | serde-saphyr | – |
| k8s-manifest | sparse | 5,633 | 105 | 2,415 | 2,415 | n/a | 1,716 | 28 | 0.061× | serde-saphyr | – |
| gha-workflow | value | 3,078 | 365 | 1,155 | 1,155 | differs | 597 | 215 | 0.61× | serde-saphyr | – |
| gha-workflow | typed | 3,078 | 201 | 1,031 | 1,031 | 698 | 337 | rejects | 0.60× | serde-saphyr | 0.29× |
| gha-workflow | sparse | 3,078 | 58 | 914 | 914 | 802 | 718 | 10 | 0.081× | serde-saphyr | 0.072× |
| compose-merge | value | 2,442 | 764 | 1,584 | 1,584 | n/a | 1,496 | 401 | 0.51× | serde-saphyr | – |
| compose-merge | typed | 2,442 | 488 | 1,639 | 1,639 | n/a | 1,172 | 245 | 0.42× | serde-saphyr | – |
| compose-merge | sparse | 2,442 | 188 | 1,585 | 1,585 | n/a | 1,506 | 30 | 0.12× | serde-saphyr | – |
| k8s-multi | value | 1,050,323 | 187,496 | 569,202 | 569,202 | n/a | 259,380 | 103,425 | 0.72× | serde-saphyr | – |
| k8s-multi | typed | 1,050,323 | 110,628 | 515,008 | 515,008 | n/a | 423,621 | 83,517 | 0.26× | serde-saphyr | – |
| k8s-multi | sparse | 1,050,323 | 10,563 | 443,118 | 443,118 | n/a | 313,574 | 4,436 | 0.034× | serde-saphyr | – |
| openapi-100k | value | 102,778 | 17,151 | 54,250 | 54,250 | 48,318 | 24,687 | 9,965 | 0.69× | serde-saphyr | 0.35× |
| openapi-100k | typed | 102,778 | 6,583 | 45,636 | 45,636 | 31,934 | 12,385 | 4,189 | 0.53× | serde-saphyr | 0.21× |
| openapi-100k | sparse | 102,778 | 167 | 41,456 | 41,456 | 38,091 | 35,491 | 5 | 0.0047× | serde-saphyr | 0.0044× |
| openapi-10m | value | 10,486,088 | 1,718,287 | 5,461,198 | 5,461,198 | 4,860,265 | 2,479,934 | 1,002,271 | 0.69× | serde-saphyr | 0.35× |
| openapi-10m | typed | 10,486,088 | 659,270 | 4,597,784 | 4,597,784 | 3,214,454 | 1,243,104 | 421,511 | 0.53× | serde-saphyr | 0.21× |
| openapi-10m | sparse | 10,486,088 | 9,138 | 4,176,282 | 4,176,282 | 3,834,137 | 3,569,112 | 5 | 0.0026× | serde-saphyr | 0.0024× |
| helm-values | value | 102,418 | 3,466 | 12,639 | 12,639 | 11,060 | 7,282 | 1,992 | 0.48× | serde-saphyr | 0.31× |
| helm-values | typed | 102,418 | 1,254 | 10,977 | 10,977 | 9,793 | 11,419 | 840 | 0.11× | serde_yaml_ng | 0.13× |
| helm-values | sparse | 102,418 | 41 | 10,176 | 10,176 | 8,919 | 10,581 | 3 | 0.0040× | serde_yaml_ng | 0.0046× |
Bytes allocated ÷ YAML input bytes (baseline subtracted)
| Case | Workload | Bytes | oneyaml | serde_yaml_ng | serde_norway | serde_yaml2 | serde-saphyr | serde_json | oneyaml ÷ best feature-complete | best feature-complete | oneyaml ÷ serde_yaml2 (partial) |
|---|---|---|---|---|---|---|---|---|---|---|---|
| k8s-manifest | value | 5,633 | 38.84 | 78.76 | 78.76 | n/a | 48.37 | 15.37 | 0.80× | serde-saphyr | – |
| k8s-manifest | typed | 5,633 | 25.62 | 72.95 | 72.95 | n/a | 49.32 | 13.10 | 0.52× | serde-saphyr | – |
| k8s-manifest | sparse | 5,633 | 6.75 | 60.20 | 60.20 | n/a | 35.93 | 0.28 | 0.19× | serde-saphyr | – |
| gha-workflow | value | 3,078 | 23.63 | 71.14 | 71.14 | differs | 34.07 | 9.40 | 0.69× | serde-saphyr | – |
| gha-workflow | typed | 3,078 | 15.86 | 66.95 | 66.95 | 12.83 | 28.96 | rejects | 0.55× | serde-saphyr | 1.24× |
| gha-workflow | sparse | 3,078 | 6.51 | 61.23 | 61.23 | 8.13 | 26.78 | 0.22 | 0.24× | serde-saphyr | 0.80× |
| compose-merge | value | 2,442 | 66.94 | 110.24 | 110.24 | n/a | 163.34 | 15.95 | 0.61× | serde_norway | – |
| compose-merge | typed | 2,442 | 91.36 | 116.54 | 116.54 | n/a | 153.33 | 7.49 | 0.78× | serde_norway | – |
| compose-merge | sparse | 2,442 | 69.89 | 110.58 | 110.57 | n/a | 150.19 | 0.48 | 0.63× | serde_norway | – |
| k8s-multi | value | 1,050,323 | 35.67 | 66.09 | 66.09 | n/a | 44.57 | 15.28 | 0.80× | serde-saphyr | – |
| k8s-multi | typed | 1,050,323 | 23.14 | 60.90 | 60.90 | n/a | 46.18 | 13.70 | 0.50× | serde-saphyr | – |
| k8s-multi | sparse | 1,050,323 | 2.12 | 47.66 | 47.66 | n/a | 32.30 | 0.41 | 0.066× | serde-saphyr | – |
| openapi-100k | value | 102,778 | 30.29 | 56.83 | 56.83 | 21.11 | 43.34 | 14.55 | 0.70× | serde-saphyr | 1.44× |
| openapi-100k | typed | 102,778 | 18.72 | 48.83 | 48.83 | 18.09 | 36.55 | 9.00 | 0.51× | serde-saphyr | 1.03× |
| openapi-100k | sparse | 102,778 | 1.05 | 39.84 | 39.84 | 9.87 | 32.14 | 0.00 | 0.033× | serde-saphyr | 0.11× |
| openapi-10m | value | 10,486,088 | 29.97 | 63.53 | 63.53 | 20.80 | 42.54 | 14.38 | 0.70× | serde-saphyr | 1.44× |
| openapi-10m | typed | 10,486,088 | 18.28 | 55.87 | 55.87 | 17.91 | 35.89 | 8.96 | 0.51× | serde-saphyr | 1.02× |
| openapi-10m | sparse | 10,486,088 | 0.72 | 46.91 | 46.91 | 9.74 | 31.48 | 0.00 | 0.023× | serde-saphyr | 0.073× |
| helm-values | value | 102,418 | 7.55 | 14.48 | 14.48 | 4.91 | 14.26 | 2.53 | 0.53× | serde-saphyr | 1.54× |
| helm-values | typed | 102,418 | 4.36 | 12.45 | 12.45 | 4.40 | 14.68 | 1.94 | 0.35× | serde_norway | 0.99× |
| helm-values | sparse | 102,418 | 0.33 | 10.51 | 10.51 | 2.46 | 12.74 | 0.00 | 0.031× | serde_norway | 0.13× |
Whole-process peak heap ÷ YAML input bytes (the baseline column is YAML generation alone)
| Case | Workload | Bytes | baseline | oneyaml | serde_yaml_ng | serde_norway | serde_yaml2 | serde-saphyr | serde_json | oneyaml ÷ best feature-complete | best feature-complete | oneyaml ÷ serde_yaml2 (partial) |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| k8s-manifest | value | 5,633 | 1.94 | 26.04 | 32.37 | 32.37 | n/a | 21.58 | 16.27 | 1.21× | serde-saphyr | – |
| k8s-manifest | typed | 5,633 | 1.94 | 9.77 | 23.39 | 23.39 | n/a | 12.55 | 7.16 | 0.78× | serde-saphyr | – |
| k8s-manifest | sparse | 5,633 | 1.94 | 5.28 | 18.57 | 18.57 | n/a | 6.88 | 2.64 | 0.77× | serde-saphyr | – |
| gha-workflow | value | 3,078 | 3.50 | 17.42 | 49.35 | 49.35 | differs | 18.07 | 10.24 | 0.96× | serde-saphyr | – |
| gha-workflow | typed | 3,078 | 3.50 | 9.67 | 45.44 | 45.44 | 7.48 | 14.23 | rejects | 0.68× | serde-saphyr | 1.29× |
| gha-workflow | sparse | 3,078 | 3.50 | 5.02 | 40.35 | 40.35 | 3.50 | 9.41 | 3.50 | 0.53× | serde-saphyr | 1.43× |
| compose-merge | value | 2,442 | 4.59 | 36.32 | 73.41 | 73.41 | n/a | 31.43 | 17.60 | 1.16× | serde-saphyr | – |
| compose-merge | typed | 2,442 | 4.59 | 19.72 | 73.41 | 73.41 | n/a | 23.58 | 9.13 | 0.84× | serde-saphyr | – |
| compose-merge | sparse | 2,442 | 4.59 | 13.66 | 73.42 | 73.41 | n/a | 17.42 | 4.59 | 0.78× | serde-saphyr | – |
| k8s-multi | value | 1,050,323 | 1.00 | 22.94 | 17.21 | 17.21 | n/a | 16.18 | 16.01 | 1.42× | serde-saphyr | – |
| k8s-multi | typed | 1,050,323 | 1.00 | 7.02 | 7.08 | 7.08 | n/a | 7.02 | 6.85 | 1.00× | serde-saphyr | – |
| k8s-multi | sparse | 1,050,323 | 1.00 | 1.25 | 1.32 | 1.32 | n/a | 1.26 | 1.86 | 0.99× | serde-saphyr | – |
| openapi-100k | value | 102,778 | 1.71 | 22.73 | 32.71 | 32.71 | 12.91 | 16.21 | 15.39 | 1.40× | serde-saphyr | 1.76× |
| openapi-100k | typed | 102,778 | 1.71 | 10.57 | 26.86 | 26.86 | 11.04 | 10.65 | 9.83 | 0.99× | serde-saphyr | 0.96× |
| openapi-100k | sparse | 102,778 | 1.71 | 1.96 | 17.87 | 17.87 | 1.71 | 1.78 | 2.17 | 1.10× | serde-saphyr | 1.15× |
| openapi-10m | value | 10,486,088 | 1.67 | 22.52 | 35.67 | 35.67 | 12.73 | 15.82 | 15.21 | 1.42× | serde-saphyr | 1.77× |
| openapi-10m | typed | 10,486,088 | 1.67 | 10.45 | 30.06 | 30.06 | 10.98 | 10.40 | 9.79 | 1.00× | serde-saphyr | 0.95× |
| openapi-10m | sparse | 10,486,088 | 1.67 | 1.75 | 21.10 | 21.10 | 1.67 | 1.67 | 2.16 | 1.05× | serde-saphyr | 1.05× |
| helm-values | value | 102,418 | 3.03 | 6.22 | 9.61 | 9.61 | 4.51 | 4.78 | 3.03 | 1.30× | serde-saphyr | 1.38× |
| helm-values | typed | 102,418 | 3.03 | 3.68 | 8.20 | 8.20 | 3.69 | 3.91 | 3.03 | 0.94× | serde-saphyr | 1.00× |
| helm-values | sparse | 102,418 | 3.03 | 3.03 | 6.67 | 6.67 | 3.03 | 3.03 | 3.03 | 1.00× | serde-saphyr | 1.00× |
Wall-clock (informational only)
Higher MB/s is better (per byte of YAML input, also for serde_json); the ratio columns are time ratios, lower is better for oneyaml.
Host: AMD Ryzen 9 7950X3D 16-Core Processor; load average (1/5/15 min) at start: 7.57 / 7.76 / 8.36. MB/s = 10⁶ bytes/s, median of ≥ 5 runs.
| Case | Workload | Bytes | oneyaml MB/s | serde_yaml_ng MB/s | serde_norway MB/s | serde_yaml2 MB/s | serde-saphyr MB/s | serde_json MB/s | oneyaml ÷ fastest feature-complete | fastest feature-complete | oneyaml ÷ serde_yaml2 (partial) | oneyaml ÷ serde_json |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| k8s-manifest | value | 5,633 | 37 | 36 | 36 | n/a | 17 | 256 | 0.96× | serde_yaml_ng | – | 6.85× |
| k8s-manifest | typed | 5,633 | 42 | 38 | 39 | n/a | 15 | 229 | 0.92× | serde_norway | – | 5.48× |
| k8s-manifest | sparse | 5,633 | 50 | 54 | 58 | n/a | 18 | 1486 | 1.17× | serde_norway | – | 29.86× |
| gha-workflow | value | 3,078 | 58 | 48 | 51 | differs | 26 | 370 | 0.88× | serde_norway | – | 6.36× |
| gha-workflow | typed | 3,078 | 64 | 64 | 64 | 78 | 29 | rejects | 0.99× | serde_yaml_ng | 1.21× | – |
| gha-workflow | sparse | 3,078 | 69 | 71 | 72 | 86 | 26 | 2123 | 1.04× | serde_norway | 1.25× | 30.69× |
| compose-merge | value | 2,442 | 25 | 29 | 28 | n/a | 12 | 140 | 1.14× | serde_yaml_ng | – | 5.52× |
| compose-merge | typed | 2,442 | 27 | 27 | 26 | n/a | 8 | 141 | 1.00× | serde_yaml_ng | – | 5.25× |
| compose-merge | sparse | 2,442 | 19 | 28 | 27 | n/a | 13 | 731 | 1.50× | serde_yaml_ng | – | 39.19× |
| k8s-multi | value | 1,050,323 | 30 | 31 | 29 | n/a | 15 | 77 | 1.03× | serde_yaml_ng | – | 2.61× |
| k8s-multi | typed | 1,050,323 | 41 | 39 | 39 | n/a | 14 | 249 | 0.96× | serde_norway | – | 6.13× |
| k8s-multi | sparse | 1,050,323 | 50 | 54 | 55 | n/a | 17 | 1534 | 1.09× | serde_norway | – | 30.60× |
| openapi-100k | value | 102,778 | 42 | 42 | 43 | 45 | 17 | 283 | 1.03× | serde_norway | 1.07× | 6.77× |
| openapi-100k | typed | 102,778 | 44 | 55 | 54 | 59 | 20 | 294 | 1.26× | serde_yaml_ng | 1.34× | 6.71× |
| openapi-100k | sparse | 102,778 | 50 | 67 | 67 | 63 | 17 | 2109 | 1.36× | serde_yaml_ng | 1.27× | 42.47× |
| openapi-10m | value | 10,486,088 | 32 | 29 | 28 | 41 | 14 | 120 | 0.90× | serde_yaml_ng | 1.29× | 3.78× |
| openapi-10m | typed | 10,486,088 | 39 | 35 | 33 | 51 | 18 | 148 | 0.91× | serde_yaml_ng | 1.31× | 3.81× |
| openapi-10m | sparse | 10,486,088 | 51 | 47 | 50 | 65 | 18 | 2208 | 0.97× | serde_norway | 1.28× | 43.31× |
| helm-values | value | 102,418 | 139 | 118 | 118 | 151 | 57 | 1197 | 0.85× | serde_norway | 1.08× | 8.58× |
| helm-values | typed | 102,418 | 145 | 126 | 128 | 177 | 50 | 1241 | 0.88× | serde_norway | 1.22× | 8.54× |
| helm-values | sparse | 102,418 | 169 | 159 | 159 | 207 | 57 | 8485 | 0.94× | serde_norway | 1.22× | 50.13× |
Serde write
The same comparators, each through its own writer with its defaults:
| Library | One document | Several documents (k8s-manifest, k8s-multi) |
|---|---|---|
| oneyaml | value: Value::to_yaml_string; typed: oneyaml::to_string | value: to_yaml_string per document, --- between them (what Serializer writes); typed: one oneyaml::Serializer over a Vec<u8>, a serialize per document |
| serde_yaml_ng 0.10.0, serde_norway 0.9.42 | to_string | one Serializer over a Vec<u8>, a serialize per document (it writes the ---) |
| serde-saphyr 1.3.0 | to_string | to_string_multiple |
| serde_yaml2 0.1.3 | to_string | none: n/a |
| serde_json 1.0.151 | to_string | to_string of the array of documents |
to_writer is to_string plus one write_all of the result, so it is
not a separate row. Every side uses its default settings (oneyaml's
quotes every string that some YAML reader would read as something else).
Workloads, each on every real-world corpus input at 1× (every document):
value: every document as the library's own dynamic value, read beforehand by that library (serde_json: from the equivalent JSON), so each library writes its natural in-memory form;typed: every document as the derived structs ofcrates/bench/src/models.rs, read beforehand by oneyaml, so every library writes the same Rust values.
Reading the data is preparation, outside the measured function (write, then drop the output); each cell's DHAT baseline prepares the same data and writes nothing.
Correctness guard (write)
bench ser-check <lib> <workload> <case>: each library writes its data,
and the output is read back with oneyaml's reader (serde_json's for
serde_json), into Value for value (compared with oneyaml's reading of
the input) or into the same structs for typed (compared with ==). A
library whose output reads back as something else (a lost or extra
document, a changed scalar type, an escape that doesn't read back, an
empty collection turned into null) is differs and is not measured;
oneyaml is checked the same way. An agrees cell also shows the output
size ÷ the input size.
| Case | Workload | oneyaml | serde_yaml_ng | serde_norway | serde_yaml2 | serde-saphyr | serde_json |
|---|---|---|---|---|---|---|---|
| k8s-manifest | value | agrees (1.06×) | agrees (0.99×) | agrees (0.99×) | n/a [1] | agrees (0.99×) | agrees (0.86×) |
| k8s-manifest | typed | agrees (1.16×) | agrees (1.09×) | agrees (1.09×) | n/a [2] | agrees (1.09×) | agrees (0.94×) |
| gha-workflow | value | agrees (1.00×) | agrees (0.95×) | agrees (0.95×) | differs [3] | agrees (0.95×) | agrees (0.79×) |
| gha-workflow | typed | agrees (1.32×) | agrees (1.23×) | agrees (1.23×) | differs [4] | agrees (1.23×) | agrees (0.99×) |
| compose-merge | value | agrees (1.69×) | agrees (1.62×) | agrees (1.62×) | n/a [5] | agrees (1.62×) | agrees (1.37×) |
| compose-merge | typed | agrees (1.69×) | agrees (1.63×) | agrees (1.63×) | differs [6] | agrees (1.63×) | agrees (1.36×) |
| k8s-multi | value | agrees (1.06×) | agrees (0.99×) | agrees (0.99×) | n/a [7] | agrees (0.99×) | agrees (0.86×) |
| k8s-multi | typed | agrees (1.16×) | agrees (1.09×) | agrees (1.09×) | n/a [8] | agrees (1.09×) | agrees (0.94×) |
| openapi-100k | value | agrees (1.13×) | agrees (1.11×) | agrees (1.11×) | differs [9] | agrees (1.12×) | agrees (0.83×) |
| openapi-100k | typed | agrees (2.36×) | agrees (2.33×) | agrees (2.33×) | differs [10] | agrees (2.33×) | agrees (1.57×) |
| openapi-10m | value | agrees (1.13×) | agrees (1.11×) | agrees (1.11×) | differs [11] | agrees (1.11×) | agrees (0.83×) |
| openapi-10m | typed | agrees (2.34×) | agrees (2.31×) | agrees (2.31×) | differs [12] | agrees (2.31×) | agrees (1.56×) |
| helm-values | value | agrees (0.24×) | agrees (0.24×) | agrees (0.24×) | differs [13] | agrees (0.24×) | agrees (0.22×) |
| helm-values | typed | agrees (0.24×) | agrees (0.24×) | agrees (0.24×) | differs [14] | agrees (0.24×) | agrees (0.22×) |
- serde_yaml2, k8s-manifest value: no multi-document API (writes one document only)
- serde_yaml2, k8s-manifest typed: no multi-document API (writes one document only)
- serde_yaml2, gha-workflow value: output does not read back: a ''' inside a single-quoted scalar ends it early at jobs.delta-0.steps[2].with (81:48)
- serde_yaml2, gha-workflow typed: output does not read back: a ''' inside a single-quoted scalar ends it early at jobs.bravo-2.steps[2].with (95:48)
- serde_yaml2, compose-merge value: no merge-key support
- serde_yaml2, compose-merge typed: output does not read back: invalid type: unit value, expected a map at services.db.environment (75:18)
- serde_yaml2, k8s-multi value: no multi-document API (writes one document only)
- serde_yaml2, k8s-multi typed: no multi-document API (writes one document only)
- serde_yaml2, openapi-100k value: doc 0 /components/schemas/delta0/properties/ratio/example: 0 (ours 0.0)
- serde_yaml2, openapi-100k typed: output does not read back: invalid type: unit value, expected a map at paths./alphas/107/{id}.parameters[0].schema.properties (37:20)
- serde_yaml2, openapi-10m value: doc 0 /components/schemas/delta0/properties/ratio/example: 0 (ours 0.0)
- serde_yaml2, openapi-10m typed: output does not read back: invalid type: unit value, expected a map at paths./alphas/10011/{id}.parameters[0].schema.properties (37:20)
- serde_yaml2, helm-values value: doc 0 /alpha11/affinity: null (ours {})
- serde_yaml2, helm-values typed: output does not read back: invalid type: unit value, expected a map (1:1)
Instructions (write)
Lower is better; columns as in the read table.
| Case | Workload | Bytes | oneyaml Ir/B | serde_yaml_ng Ir/B | serde_norway Ir/B | serde_yaml2 Ir/B | serde-saphyr Ir/B | serde_json Ir/B | oneyaml ÷ fastest feature-complete (target applies) | fastest feature-complete | target ≤ 1.25× | oneyaml ÷ serde_yaml2 (partial, no target) | oneyaml ÷ serde_json |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| k8s-manifest | value | 5,633 | 124.0 | 230.0 | 230.0 | n/a | 195.0 | 16.4 | 0.64× | serde-saphyr | met | – | 7.54× |
| k8s-manifest | typed | 5,633 | 144.5 | 248.0 | 248.0 | n/a | 208.8 | 14.5 | 0.69× | serde-saphyr | met | – | 9.94× |
| gha-workflow | value | 3,078 | 88.6 | 184.3 | 184.3 | differs | 152.6 | 13.6 | 0.58× | serde-saphyr | met | – | 6.53× |
| gha-workflow | typed | 3,078 | 128.8 | 242.8 | 242.8 | differs | 186.6 | 15.0 | 0.69× | serde-saphyr | met | – | 8.61× |
| compose-merge | value | 2,442 | 192.5 | 369.1 | 369.1 | n/a | 335.5 | 26.4 | 0.57× | serde-saphyr | met | – | 7.29× |
| compose-merge | typed | 2,442 | 236.7 | 363.5 | 363.5 | differs | 330.2 | 22.8 | 0.72× | serde-saphyr | met | – | 10.38× |
| k8s-multi | value | 1,050,323 | 124.4 | 228.7 | 228.7 | n/a | 193.6 | 16.2 | 0.64× | serde-saphyr | met | – | 7.66× |
| k8s-multi | typed | 1,050,323 | 142.5 | 246.1 | 246.1 | n/a | 206.9 | 13.7 | 0.69× | serde-saphyr | met | – | 10.41× |
| openapi-100k | value | 102,778 | 106.7 | 225.8 | 225.8 | differs | 180.1 | 16.2 | 0.59× | serde-saphyr | met | – | 6.57× |
| openapi-100k | typed | 102,778 | 218.6 | 425.5 | 425.5 | differs | 305.6 | 24.7 | 0.72× | serde-saphyr | met | – | 8.86× |
| openapi-10m | value | 10,486,088 | 104.9 | 223.9 | 223.9 | differs | 178.2 | 15.7 | 0.59× | serde-saphyr | met | – | 6.69× |
| openapi-10m | typed | 10,486,088 | 215.4 | 420.5 | 420.5 | differs | 301.7 | 23.2 | 0.71× | serde-saphyr | met | – | 9.30× |
| helm-values | value | 102,418 | 26.5 | 56.1 | 56.1 | differs | 42.3 | 4.1 | 0.63× | serde-saphyr | met | – | 6.51× |
| helm-values | typed | 102,418 | 32.2 | 55.1 | 55.1 | differs | 42.4 | 3.5 | 0.76× | serde-saphyr | met | – | 9.32× |
Allocations (write)
Allocation count (blocks, realloc included; baseline subtracted)
| Case | Workload | Bytes | oneyaml | serde_yaml_ng | serde_norway | serde_yaml2 | serde-saphyr | serde_json | oneyaml ÷ best feature-complete | best feature-complete | oneyaml ÷ serde_yaml2 (partial) |
|---|---|---|---|---|---|---|---|---|---|---|---|
| k8s-manifest | value | 5,633 | 153 | 492 | 492 | n/a | 839 | 6 | 0.31× | serde_yaml_ng | – |
| k8s-manifest | typed | 5,633 | 126 | 537 | 537 | n/a | 899 | 6 | 0.23× | serde_yaml_ng | – |
| gha-workflow | value | 3,078 | 19 | 177 | 177 | differs | 334 | 5 | 0.11× | serde_yaml_ng | – |
| gha-workflow | typed | 3,078 | 59 | 285 | 285 | differs | 443 | 5 | 0.21× | serde_yaml_ng | – |
| compose-merge | value | 2,442 | 19 | 329 | 329 | n/a | 618 | 5 | 0.058× | serde_yaml_ng | – |
| compose-merge | typed | 2,442 | 95 | 324 | 324 | differs | 608 | 5 | 0.29× | serde_yaml_ng | – |
| k8s-multi | value | 1,050,323 | 27,718 | 88,499 | 88,499 | n/a | 152,646 | 13 | 0.31× | serde_yaml_ng | – |
| k8s-multi | typed | 1,050,323 | 21,033 | 96,794 | 96,794 | n/a | 163,707 | 13 | 0.22× | serde_yaml_ng | – |
| openapi-100k | value | 102,778 | 26 | 8,097 | 8,097 | differs | 15,171 | 10 | 0.0032× | serde_yaml_ng | – |
| openapi-100k | typed | 102,778 | 1,702 | 17,127 | 17,127 | differs | 25,516 | 11 | 0.099× | serde_yaml_ng | – |
| openapi-10m | value | 10,486,088 | 39 | 812,544 | 812,544 | differs | 1,528,642 | 17 | 0.000048× | serde_yaml_ng | – |
| openapi-10m | typed | 10,486,088 | 168,570 | 1,721,470 | 1,721,470 | differs | 2,569,995 | 17 | 0.098× | serde_yaml_ng | – |
| helm-values | value | 102,418 | 21 | 1,965 | 1,965 | differs | 3,083 | 8 | 0.011× | serde_yaml_ng | – |
| helm-values | typed | 102,418 | 90 | 1,965 | 1,965 | differs | 3,083 | 8 | 0.046× | serde_yaml_ng | – |
Bytes allocated ÷ YAML input bytes (baseline subtracted)
| Case | Workload | Bytes | oneyaml | serde_yaml_ng | serde_norway | serde_yaml2 | serde-saphyr | serde_json | oneyaml ÷ best feature-complete | best feature-complete | oneyaml ÷ serde_yaml2 (partial) |
|---|---|---|---|---|---|---|---|---|---|---|---|
| k8s-manifest | value | 5,633 | 11.35 | 13.75 | 13.75 | n/a | 9.17 | 2.88 | 1.24× | serde-saphyr | – |
| k8s-manifest | typed | 5,633 | 6.99 | 14.00 | 14.00 | n/a | 8.76 | 2.88 | 0.80× | serde-saphyr | – |
| gha-workflow | value | 3,078 | 4.18 | 18.83 | 18.83 | differs | 6.31 | 2.60 | 0.66× | serde-saphyr | – |
| gha-workflow | typed | 3,078 | 4.49 | 19.56 | 19.56 | differs | 8.08 | 2.60 | 0.56× | serde-saphyr | – |
| compose-merge | value | 2,442 | 9.46 | 24.88 | 24.88 | n/a | 12.93 | 3.28 | 0.73× | serde-saphyr | – |
| compose-merge | typed | 2,442 | 11.42 | 24.86 | 24.86 | differs | 12.71 | 3.28 | 0.90× | serde-saphyr | – |
| k8s-multi | value | 1,050,323 | 10.36 | 3.78 | 3.78 | n/a | 7.97 | 2.00 | 2.74× | serde_yaml_ng | – |
| k8s-multi | typed | 1,050,323 | 5.28 | 4.00 | 4.00 | n/a | 9.78 | 2.00 | 1.32× | serde_yaml_ng | – |
| openapi-100k | value | 102,778 | 3.86 | 4.16 | 4.16 | differs | 8.69 | 2.55 | 0.93× | serde_yaml_ng | – |
| openapi-100k | typed | 102,778 | 7.84 | 8.01 | 8.01 | differs | 13.51 | 5.10 | 0.98× | serde_yaml_ng | – |
| openapi-10m | value | 10,486,088 | 4.43 | 4.46 | 4.46 | differs | 9.44 | 3.20 | 0.99× | serde_yaml_ng | – |
| openapi-10m | typed | 10,486,088 | 9.01 | 8.94 | 8.94 | differs | 14.71 | 3.20 | 1.01× | serde_yaml_ng | – |
| helm-values | value | 102,418 | 0.93 | 1.31 | 1.31 | differs | 1.65 | 0.64 | 0.71× | serde_yaml_ng | – |
| helm-values | typed | 102,418 | 0.86 | 1.31 | 1.31 | differs | 1.65 | 0.64 | 0.66× | serde_yaml_ng | – |
Peak heap added by writing ÷ YAML input bytes (whole-process peak minus the baseline's, which holds the prepared data)
| Case | Workload | Bytes | oneyaml | serde_yaml_ng | serde_norway | serde_yaml2 | serde-saphyr | serde_json |
|---|---|---|---|---|---|---|---|---|
| k8s-manifest | value | 5,633 | ≤ prep | ≤ prep | ≤ prep | n/a | ≤ prep | 0.58 |
| k8s-manifest | typed | 5,633 | ≤ prep | 7.07 | 7.07 | n/a | ≤ prep | ≤ prep |
| gha-workflow | value | 3,078 | ≤ prep | ≤ prep | ≤ prep | differs | ≤ prep | 0.50 |
| gha-workflow | typed | 3,078 | ≤ prep | 13.97 | 13.97 | differs | ≤ prep | ≤ prep |
| compose-merge | value | 2,442 | ≤ prep | ≤ prep | ≤ prep | n/a | ≤ prep | 0.31 |
| compose-merge | typed | 2,442 | ≤ prep | 10.81 | 10.81 | differs | ≤ prep | ≤ prep |
| k8s-multi | value | 1,050,323 | 0.25 | 0.14 | 0.14 | n/a | 0.22 | 0.14 |
| k8s-multi | typed | 1,050,323 | 0.30 | 0.25 | 0.25 | n/a | 0.98 | ≤ prep |
| openapi-100k | value | 102,778 | 0.95 | ≤ prep | ≤ prep | differs | ≤ prep | 0.45 |
| openapi-100k | typed | 102,778 | 1.25 | 1.49 | 1.49 | differs | 0.99 | 0.99 |
| openapi-10m | value | 10,486,088 | 1.37 | ≤ prep | ≤ prep | differs | 0.56 | 0.77 |
| openapi-10m | typed | 10,486,088 | 1.95 | 1.72 | 1.72 | differs | 1.71 | 0.11 |
| helm-values | value | 102,418 | ≤ prep | ≤ prep | ≤ prep | differs | ≤ prep | ≤ prep |
| helm-values | typed | 102,418 | ≤ prep | ≤ prep | ≤ prep | differs | ≤ prep | ≤ prep |
The third table replaces the read path's whole-process peak: the
baseline's peak is the prepared data, so the difference is what writing
adds at its peak, the output plus the writer's working set. ≤ prep
means writing never rose above the preparation's peak, so DHAT can't tell
what it added. This table has no ratio columns: a hidden cell could be
below any visible one.
Wall-clock (write, informational only)
Higher MB/s is better (MB of YAML input, for every library); the ratio columns are time ratios.
Host: AMD Ryzen 9 7950X3D 16-Core Processor; load average (1/5/15 min) at start: 11.69 / 10.09 / 9.18. MB/s = 10⁶ bytes/s, median of ≥ 5 runs.
| Case | Workload | Bytes | oneyaml MB/s | serde_yaml_ng MB/s | serde_norway MB/s | serde_yaml2 MB/s | serde-saphyr MB/s | serde_json MB/s | oneyaml ÷ fastest feature-complete | fastest feature-complete | oneyaml ÷ serde_yaml2 (partial) | oneyaml ÷ serde_json |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| k8s-manifest | value | 5,633 | 168 | 85 | 86 | n/a | 106 | 1209 | 0.63× | serde-saphyr | – | 7.18× |
| k8s-manifest | typed | 5,633 | 127 | 81 | 82 | n/a | 97 | 1322 | 0.77× | serde-saphyr | – | 10.42× |
| gha-workflow | value | 3,078 | 151 | 112 | 109 | differs | 86 | 971 | 0.74× | serde_yaml_ng | – | 6.45× |
| gha-workflow | typed | 3,078 | 91 | 41 | 43 | differs | 59 | 816 | 0.65× | serde-saphyr | – | 8.97× |
| compose-merge | value | 2,442 | 58 | 28 | 27 | n/a | 60 | 806 | 1.04× | serde-saphyr | – | 13.93× |
| compose-merge | typed | 2,442 | 81 | 59 | 55 | differs | 61 | 969 | 0.76× | serde-saphyr | – | 11.98× |
| k8s-multi | value | 1,050,323 | 96 | 68 | 67 | n/a | 61 | 1005 | 0.71× | serde_yaml_ng | – | 10.43× |
| k8s-multi | typed | 1,050,323 | 119 | 79 | 78 | n/a | 88 | 1144 | 0.74× | serde-saphyr | – | 9.64× |
| openapi-100k | value | 102,778 | 201 | 81 | 67 | differs | 83 | 1227 | 0.41× | serde-saphyr | – | 6.12× |
| openapi-100k | typed | 102,778 | 85 | 46 | 46 | differs | 65 | 834 | 0.77× | serde-saphyr | – | 9.84× |
| openapi-10m | value | 10,486,088 | 99 | 77 | 76 | differs | 61 | 280 | 0.78× | serde_yaml_ng | – | 2.82× |
| openapi-10m | typed | 10,486,088 | 66 | 45 | 43 | differs | 58 | 401 | 0.87× | serde-saphyr | – | 6.03× |
| helm-values | value | 102,418 | 934 | 392 | 388 | differs | 484 | 5533 | 0.52× | serde-saphyr | – | 5.92× |
| helm-values | typed | 102,418 | 609 | 383 | 391 | differs | 470 | 6007 | 0.77× | serde-saphyr | – | 9.87× |
Document
yaml-edit 0.3.2 is the comparator: the other lossless YAML editor for
Rust. Both sides run on the real-world corpus at 1×; oneyaml under the
default settings with Limits::unlimited(), as in the parser tables.
- parse: oneyaml
Stream::parse_with(input, &settings), the strict entry point (a single-document input costs the same throughDocument::parse_with): the syntax tree plus the meaning check, so thatDocument::parsefails exactly wherefrom_str::<Value>fails (duplicate keys, merge values, aliases, tags). yaml-edit:YamlFile::parse, which checks syntax only. The result is dropped; neither side walks it. - emit:
to_string()of the parsed, untouched document (parsing is preparation, not measured). The emit = input column checks that the output is byte-identical to the input, for both libraries. - retained: parse, then keep the result, so DHAT's end-of-run heap (net of the baseline's) is what a parsed document keeps, including its own copy of the input. parse blocks are the allocations of a parse.
Instructions (Ir/B; lower is better)
| Case | Bytes | parse oneyaml | parse yaml-edit | parse ÷ yaml-edit | parity | emit oneyaml | emit yaml-edit | emit ÷ yaml-edit | parity | emit = input (oneyaml / yaml-edit) |
|---|---|---|---|---|---|---|---|---|---|---|
| k8s-manifest | 5,633 | 400.3 | 495.2 | 0.81× | met | 47.3 | 442.9 | 0.11× | met | = / = |
| gha-workflow | 3,078 | 286.0 | 417.5 | 0.68× | met | 35.0 | 322.8 | 0.11× | met | = / = |
| compose-merge | 2,442 | 444.0 | 512.9 | 0.87× | met | 54.9 | 441.6 | 0.12× | met | = / = |
| k8s-multi | 1,050,323 | 385.4 | 435.2 | 0.89× | met | 46.0 | 436.9 | 0.11× | met | = / = |
| openapi-100k | 102,778 | 399.2 | 449.0 | 0.89× | met | 49.2 | 485.4 | 0.10× | met | = / = |
| openapi-10m | 10,486,088 | 389.9 | 438.6 | 0.89× | met | 47.7 | 477.8 | 0.100× | met | = / = |
| helm-values | 102,418 | 135.8 | 196.0 | 0.69× | met | 24.1 | 146.7 | 0.16× | met | = / = |
Memory (DHAT; parse allocations net of the input-generation baseline; retained = the parsed stream/file kept, net of the baseline's end-of-run heap, including the parser's copy of the input where it keeps one)
| Case | Bytes | parse blocks oneyaml | parse blocks yaml-edit | retained ÷ input oneyaml | retained ÷ input yaml-edit | target: retained ≤ 8× (≥ 100 KB) |
|---|---|---|---|---|---|---|
| k8s-manifest | 5,633 | 104 | 2,454 | 4.79 | 10.76 | no target (< 100 KB) |
| gha-workflow | 3,078 | 97 | 1,062 | 4.06 | 8.31 | no target (< 100 KB) |
| compose-merge | 2,442 | 121 | 1,221 | 5.41 | 11.73 | no target (< 100 KB) |
| k8s-multi | 1,050,323 | 1,791 | 385,968 | 4.47 | 8.47 | met |
| openapi-100k | 102,778 | 225 | 40,298 | 4.38 | 9.12 | met |
| openapi-10m | 10,486,088 | 9,214 | 4,014,717 | 4.31 | 8.88 | met |
| helm-values | 102,418 | 94 | 16,284 | 2.44 | 3.16 | met |
Host: AMD Ryzen 9 7950X3D 16-Core Processor; load average (1/5/15 min) at start: 9.18 / 9.66 / 9.09. Median of ≥ 5 runs, in ms.
Wall-clock (informational only)
| Case | Bytes | parse ms oneyaml | parse ms yaml-edit | parse ÷ yaml-edit | emit ms oneyaml | emit ms yaml-edit | emit ÷ yaml-edit | emit ÷ parse (oneyaml) |
|---|---|---|---|---|---|---|---|---|
| k8s-manifest | 5,633 | 0.123 | 0.149 | 0.83× | 0.012 | 0.132 | 0.091× | 0.098× |
| gha-workflow | 3,078 | 0.049 | 0.066 | 0.75× | 0.005 | 0.053 | 0.095× | 0.10× |
| compose-merge | 2,442 | 0.061 | 0.063 | 0.98× | 0.006 | 0.056 | 0.11× | 0.10× |
| k8s-multi | 1,050,323 | 25.241 | 28.446 | 0.89× | 1.978 | 26.024 | 0.076× | 0.078× |
| openapi-100k | 102,778 | 2.445 | 2.571 | 0.95× | 0.201 | 2.666 | 0.076× | 0.082× |
| openapi-10m | 10,486,088 | 263.656 | 356.068 | 0.74× | 22.930 | 286.922 | 0.080× | 0.087× |
| helm-values | 102,418 | 0.731 | 1.115 | 0.65× | 0.108 | 0.759 | 0.14× | 0.15× |
Edits
yaml-edit has a comparable edit API (Document::set/remove on its
rowan CST).
- script: a fixed script on the first document's root mapping: insert
a new last entry
bench_edit_key: x, replace its value with'y', remove it, on a parsed document (parsing is preparation, not measured). oneyaml:Stream::document_mut(0),insert,set_path,remove; yaml-edit:Document::settwice,Document::remove. The instruction columns are totals for one script on a fresh parse, not per byte: a local edit should cost the same at any document size. The script leaves the document as it found it, so the wall-clock columns repeat it on one tree. - round: parse + script +
to_string, from the input text. - output:
=when the text after the round is byte-identical to the input.
Instructions (lower is better)
| Case | Bytes | script Ir oneyaml | script Ir yaml-edit | script ÷ yaml-edit | parity | round Ir/B oneyaml | round Ir/B yaml-edit | round ÷ yaml-edit | parity | output = input (oneyaml / yaml-edit) |
|---|---|---|---|---|---|---|---|---|---|---|
| k8s-manifest | 5,633 | 562,029 | 1,058,947 | 0.53× | met | 552.0 | 1126.2 | 0.49× | met | = / = |
| gha-workflow | 3,078 | 826,673 | 1,870,115 | 0.44× | met | 595.2 | 1348.6 | 0.44× | met | = / = |
| compose-merge | 2,442 | 503,520 | 302,397 | 1.67× | missed | 712.4 | 1079.5 | 0.66× | met | = / = |
| k8s-multi | 1,050,323 | 559,559 | 1,786,870 | 0.31× | met | 436.8 | 873.7 | 0.50× | met | = / = |
| openapi-100k | 102,778 | 584,641 | 37,349,309 | 0.016× | met | 459.1 | 1297.3 | 0.35× | met | = / = |
| openapi-10m | 10,486,088 | 589,638 | 3,717,124,522 | 0.00016× | met | 442.6 | 1270.6 | 0.35× | met | = / = |
| helm-values | 102,418 | 1,192,304 | 11,824,702 | 0.10× | met | 173.7 | 457.5 | 0.38× | met | = / = |
Host: AMD Ryzen 9 7950X3D 16-Core Processor; load average (1/5/15 min) at start: 10.72 / 9.90 / 9.20. Median of ≥ 5 runs, in ms.
Wall-clock (informational only)
| Case | Bytes | script ms oneyaml | script ms yaml-edit | script ÷ yaml-edit | round ms oneyaml | round ms yaml-edit | round ÷ yaml-edit |
|---|---|---|---|---|---|---|---|
| k8s-manifest | 5,633 | 0.039 | 0.064 | 0.61× | 0.203 | 0.371 | 0.55× |
| gha-workflow | 3,078 | 0.065 | 0.164 | 0.40× | 0.220 | 0.364 | 0.60× |
| compose-merge | 2,442 | 0.059 | 0.027 | 2.22× | 0.201 | 0.245 | 0.82× |
| k8s-multi | 1,050,323 | 0.041 | 0.172 | 0.24× | 29.375 | 58.134 | 0.51× |
| openapi-100k | 102,778 | 0.041 | 2.175 | 0.019× | 2.869 | 8.138 | 0.35× |
| openapi-10m | 10,486,088 | 0.040 | 224.429 | 0.00018× | 284.433 | 866.035 | 0.33× |
| helm-values | 102,418 | 0.097 | 1.628 | 0.060× | 1.323 | 4.634 | 0.29× |