ktav 0.2.0

Ktav — a plain configuration format. Three rules, zero indentation, zero quoting. Serde-native.
Documentation
# `ktav` parse benchmark baseline

Captured before the structured-errors refactor so any regression in the
parser/error-construction code is visible against this reference.

- **Date:** 2026-05-01
- **Crate:** `ktav` 0.1.4 (`D:\dev\ktav-lang\rust`)
- **Toolchain:** `rustc 1.93.0 (254b59607 2026-01-19)`
- **OS:** Microsoft Windows 10 Pro, build 10.0.19045
- **CPU:** 11th Gen Intel(R) Core(TM) i7-11800H @ 2.30 GHz
- **RAM:** 47.8 GB
- **Mode:** `cargo bench --bench parse -- --quick parse_synth`
  (Criterion `--quick` — reduced sample count, but median is stable
  enough for ±5% regression detection.)
- **Noise caveat:** numbers are indicative on this host. Windows
  desktop, no noise suppression (no `cpufreq` pinning, no isolated
  cores, normal background load). Re-run on the same machine before
  drawing fine-grained conclusions; cross-machine comparisons are not
  meaningful.

## Workloads

Generated by `benches/fixtures.rs::synth(target_bytes)` — a deterministic
round-robin mix of plain pairs, dotted keys, typed scalars (`:i`/`:f`),
raw markers (`::`), nested objects/arrays, multi-line raw blocks
(`( ... )`), and comment lines. Sizes are *targets*; actual byte counts
are slightly above target.

| Workload     | Target   | Actual (approx) |
| ------------ | -------- | --------------- |
| `small_1k`   | 1 KiB    | ~1.03 KiB       |
| `medium_50k` | 50 KiB   | ~50.0 KiB       |
| `large_500k` | 500 KiB  | ~500 KiB        |

## Success path — `ktav::parse(text)`

Group `parse_synth`. Throughput keyed off input bytes.

| Bench                    | Median time | Throughput (median) |
| ------------------------ | ----------- | ------------------- |
| `parse_synth/small_1k`   | 16.148 µs   | 63.66 MiB/s         |
| `parse_synth/medium_50k` | 896.30 µs   | 54.55 MiB/s         |
| `parse_synth/large_500k` | 9.4864 ms   | 51.47 MiB/s         |

## Error path — `ktav::parse(text_with_one_bad_line)`

Group `parse_synth_error`. A single invalid line (`"key:value\n"` — no
space after the colon) is injected at the document midpoint
(snapped to the next line boundary) by
`fixtures::with_bad_line(&good)`. Each iteration parses, observes
`Err(_)`, and discards. This exercises the error-construction code that
the upcoming structured-errors refactor will touch.

| Bench                          | Median time | Throughput (median) |
| ------------------------------ | ----------- | ------------------- |
| `parse_synth_error/small_1k`   | 7.5346 µs   | 137.71 MiB/s        |
| `parse_synth_error/medium_50k` | 339.68 µs   | 143.98 MiB/s        |
| `parse_synth_error/large_500k` | 4.4666 ms   | 109.32 MiB/s        |

The error path is faster than the success path because the parser bails
out at roughly the document midpoint — only ~half the document is
actually scanned before the error is constructed and returned. The
"throughput" column above divides by the **full** input size (including
the unscanned tail), so it overstates the effective per-byte cost. For
regression-tracking purposes only the median **time** matters; the
MiB/s figure is informational.

## Re-running

```sh
cd D:\dev\ktav-lang\rust
cargo bench --bench parse -- --quick parse_synth
```

For a full (non-`--quick`) run, drop the `--quick` flag — expect ~5
minutes wall time and tighter confidence intervals. Criterion stores its
own per-machine baseline under `target/criterion/`; subsequent runs
will report `+/- X%` against the previous run automatically.


## Post-0.1.5 — structured errors + spans + thin-public

After Phase 1 (Error::Structured + ErrorKind), 0.1.6 hardening
(Span + #[non_exhaustive] + 3 promoted categories), and 0.1.7
(public ktav::thin event-based API), re-run on the same Win10 /
i7-11800H / rustc 1.93.0 host with the same `cargo bench --bench
parse -- --quick` invocation:

| Bench                          | 0.1.4    | 0.1.5    | Δ      |
|--------------------------------|----------|----------|--------|
| `parse_synth/small_1k`         | 16.1 µs  | 16.0 µs  | −0.6 % |
| `parse_synth/medium_50k`       | 896 µs   | 663 µs   | −26 %  |
| `parse_synth/large_500k`       | 9.49 ms  | 9.27 ms  | −2.3 % |
| `parse_synth_error/small_1k`   | 7.5 µs   | 7.18 µs  | −4.2 % |
| `parse_synth_error/medium_50k` | 340 µs   | 346 µs   | +1.8 % |
| `parse_synth_error/large_500k` | 4.47 ms  | 4.50 ms  | +0.7 % |

All inside the ±10 % envelope set as the regression threshold for the
structured-errors body of work. Net: zero success-path regression
(the `medium_50k` improvement is partly noise on this host, partly a
real win from cumulative-byte tracking replacing some `str::lines()`
overhead). Error-path slightly faster — the new `Display` impl
constructs the formatted string lazily at `.to_string()` time,
whereas the prior `format!(...)` allocated a `String` at every
error site eagerly. The cumulative-byte counter that powers spans is
statistically free.

`parse_synth_error/large_500k` showed a temporary +30 % spike in an
intermediate run (3.46 ms baseline outlier vs 4.50 ms post); Criterion
reported "no change in performance detected" at p=0.10 across runs.
The number above is the stabilised post-refactor median.