PLAN-bench phase 08: docs¶
Prompt¶
Before responding to questions or discussion points in this
document, explore the instar codebase thoroughly. Read relevant
source files, understand existing patterns (docs/bitmap.md as
the per-subcommand guide template, the CHANGELOG/README/AGENTS
wiring the bitmap phase-10 commit 30d43e7 performed), and
ground your answers in what the code actually does today. The
factual content comes from the completed phases' capture
sections and the shipped code — never from memory of what was
planned. Do not speculate when you could read instead.
Phase plans live alongside the master plan
(PLAN-bench.md) in docs/plans/. This is the
eighth and final phase; completing it closes the master plan and
the 15/15 qemu-img parity roster.
I prefer one commit per logical change, and at minimum one commit per phase. Each commit should be self-contained: it should build, pass tests, and have a clear commit message explaining what changed and why.
Situation¶
Surveyed grounding:
- Template:
docs/bitmap.md(334 lines) is the newest per-subcommand guide — title + framing prose,## Synopsiswith an options block, content sections,## Refusals and errorsas host-side/guest-side tables with verbatim messages,## Known divergencesas prose bullets each naming itsKNOWN_*_DIVERGENCESregistry key,## Exampleswith$-prefixed transcripts including refusals,## Future work, and a closing see-also paragraph pointing at the code. - Wiring surface (the bitmap phase-10 pattern, commit
30d43e7):docs/index.mdguide row;docs/usage.md### benchunder## instar Operations(after### bitmap, ~line 1002; the Ceph invocation at:510—qemu-img bench -f qcow2 -w -c 65536 -d 16 --pattern 65 -s 4096— is the real-world example bench emulates);README.mdProject-Status prose list (~:29, bench absent) + guide-links block (~:34) + optionally a## Usageblock;AGENTS.mdops tree comment (~:20),### Operationsbullet, and the test-file list (~:314,tests/test_bench.pymissing);CHANGELOG.md### Addedentry in the bitmap style (bold lead naming the plan + phase range, feature prose, coverage, the VERSION bump, doc link). - ARCHITECTURE.md is stale in two ways this phase fixes:
the
### Guest Memory Map(~:623-646) still shows the pre-2026-07-06 layout (core 64 KiB at 0x10000, ops 384 KiB at 0x20000) — it must be rewritten for the lifted map (core 128 KiB [0x10000, 0x30000), ops 768 KiB [0x30000, 0xF0000), data pages at 0xF0000, guard gap to VQ_BASE_START 0x100000 — source of truth:src/shared/src/lib.rs's constants and the3a5e1e2commit message); and the call-table VERSION prose (~:571) stops at 19 — bench appendedsend_bench_start/send_bench_resultat VERSION 20 (8ca0dc6). Plus the missingoperations/bench/bullet (bench.bin 160224 B). - Close-out convention: the completed master-plan row in
docs/plans/index.mdreads like bitmap's (Complete (phases 1-10): ...summary + all phase links ✓); the master plan's own Execution row 8 and the Success criteria section are the final tick-through.order.ymlalready lists PLAN-bench.md — no edit. docs/openstack-announcement-email.mdis a frozen pre-release draft (its roster description predates dd) — it is NOT updated;docs/bench.mdcarries the reproducible sandbox-overhead methodology that email's claim lacked.
Mission¶
1. docs/bench.md (the substantive artifact)¶
Bitmap-template structure with bench-specific content. Required sections and their load-bearing points:
- Title + framing:
# instar bench — benchmark the sandboxed I/O path. The honest reframing up front: instar bench does NOT measure what qemu-img bench measures (qemu's block layer over the page cache); it measures instar's own end-to-end sandboxed path (guest format layer → virtio-block → ioeventfd → host I/O thread → file I/O), and running both tools on the same image with the same arguments IS the reproducible sandbox-overhead measurement. - Synopsis + options (fenced blocks; the full accepted
surface incl. the no-ops
-q/-Uand the dd-style-fposture). - What the number means (the section the master plan names): the timing bracket (host wall-clock between the guest's start marker — emitted after all setup, mirroring qemu's gettimeofday placement — and the result message; boot, image open, and table loads excluded), and the measurement caveats, each stated plainly:
-dis echoed for output parity but execution is serial (effective-depth: 1in JSON); the guest driver is synchronous single-buffer.- the virtio transport granularity is 64 KiB sectors — sub-sector requests RMW through covering sectors; one bench request may be several virtio round trips.
- write tests: per-touched-cluster metadata decisions read L1/L2 uncached from disk, and refcount write-back is concentrated at flush points and run end, all inside the bracket — qemu amortises metadata through its cache, so write timings are not like-for-like at fine grain.
- numbers are comparable across instar versions on the same host/image (the perf-regression use), and instar-vs-qemu on the same invocation is the sandbox-overhead measurement; nothing else is comparable.
- Sandbox-overhead methodology: a worked example (both
tools, same image,
-c/-schosen, repeat N times, compare medians), with the honest note about which caveats apply. - Write tests (
-w): the destructive warning (mutates the image in place with no confirmation, matching qemu); the raw+qcow2 v1 envelope and the guest-side gate table (verbatim refusal messages +error_detailgate reasons frommap_bench_error); the qcow2 write-back design in one paragraph (write-through L2/L1 data-first, refcounts staged and last — a crash mid-bench leaves repairable leaks, never dangling pointers); overlay COW correctness. - Refusals and errors: host-side table (the §2 validation messages incl. echo/range forms, cache/aio/image-opts postures) and guest-side table (unsupported format, parse failed, Failed request, the write gates) — verbatim strings from the phase-4/5 captures.
--output json: the schema table (all keys, the fixedeffective-depth, derived rates, µs-precision elapsed-seconds) and a note that the human path is byte-parity with qemu and json replaces it entirely.- Known divergences from qemu-img bench: prose bullets, one
per
KNOWN_BENCH_DIVERGENCESentry (11), each naming the registry key and quoting the messages, in the bitmap style. - Examples: default read run; the Ceph-style write
invocation (adapted from usage.md:510, with the
serialized-depth note);
--flush-interval;--output json; two refusal transcripts (a validation error and a write gate). - Future work: from the master plan — true queue depth
(guest driver rework), TSC-based per-request latencies,
-t none/O_DIRECT, vmdk/vhd/vhdx writes, the CI perf-tracking job consuming--output json, buffers above 2 MiB, an explicit host-path measurement mode. - Closing see-also:
src/crates/bench/,src/operations/bench/,tests/test_bench.py's registry,[usage](/components/instar/plans/usage/).
2. The wiring sweep¶
Per the Situation checklist: docs/index.md row;
docs/usage.md ### bench section (fenced synopsis, the Ceph
caller tie-in, the read-all-formats/write-raw-qcow2 restriction,
See [docs/bench.md](/components/instar/plans/bench/)...); README.md (prose list +
guide link + a short ## Usage block mirroring dd's); AGENTS.md
(tree comment, Operations bullet, test list); CHANGELOG.md
### Added (bitmap-style: plan + phases 1-8, the reframed
measurement in one sentence, read formats and the raw+qcow2
write envelope, VERSION 19→20 for the two appended senders, the
62-test suite + two fuzz surfaces, docs/bench.md link — plus
mention the memory-map lift and the vmdk-flat info fix if the
Unreleased section doesn't already carry them: CHECK and add
under the right heading if missing, they shipped on this
branch); ARCHITECTURE.md (the operations/bench/ bullet, the
VERSION-20 prose, and the memory-map rewrite — verify every
number against src/shared/src/lib.rs, not the plans).
3. Close-out¶
Walk the master plan's Success criteria list item by item,
verifying each against the shipped tree (suite counts, binary
sizes, the fuzz registrations, the doc files) — annotate the
master plan's Execution row 8 → Complete and flip the top-line
if any; docs/plans/index.md bench row → the bitmap-style
Complete (phases 1-8): ... summary ending with the
roster-closing sentence (instar now implements all 15 qemu-img
subcommands). order.yml needs no edit (verify). The email doc
stays frozen.
Steps¶
| Step | Effort | Model | Isolation | Brief for sub-agent |
|---|---|---|---|---|
| 8a | high | sonnet | none | Write docs/bench.md per Mission §1 (bitmap.md's structure; every message/number verified against the shipped code and the phase-4/5 capture sections — verbatim strings, real sizes, the actual JSON keys from render_bench_json) + the docs/index.md row + the docs/usage.md section. High effort for the "what the number means" honesty — this section is the plan's raison d'être and must be accurate about every caveat. pre-commit. Commit 1. |
| 8b | medium | sonnet | none | The wiring sweep per Mission §2: README, AGENTS, CHANGELOG (checking whether the memory-map lift and vmdk-flat fix already have entries), and the ARCHITECTURE.md updates including the stale memory-map rewrite verified against src/shared/src/lib.rs constants. pre-commit; make lint/test-rust unaffected but run to confirm. Commit 2. |
| 8c | low | sonnet | none | Close-out per Mission §3: success-criteria walk (report each criterion → evidence), master plan row 8 + index → Complete with the roster-closing summary, order.yml verified untouched. pre-commit. Commit 3 — the plan-closing commit. |
Success criteria verification (step 8c)¶
Walked PLAN-bench.md's "Success criteria" list (Administration
and logistics) against the shipped tree at 2c6f561, re-running
the deterministic surface live where cheap (build already done,
/dev/kvm present, local qemu-img 10.0.8) and citing captured
phase results for the suite-scale/fuzz-scale checks per the
close-out brief (no re-run of the full integration suite or long
fuzz campaigns).
| # | Criterion (abbreviated) | Result | Evidence |
|---|---|---|---|
| 1 | Default read bench through the guest on raw/qcow2(+chains)/vmdk/vhd/vhdx; header byte-identical to qemu-img bench, completion line format-identical |
PASS | Live run, this session: instar bench -c 100 -s 4096 -f raw <MBR-signed raw>, plain qcow2, a qcow2-over-qcow2 backing chain, vmdk, vhdx, and vpc(vhd) images all produced a header line byte-identical to the local qemu-img bench for the same arguments (Sending 100 read requests, 4096 bytes each, 64 in parallel (starting at offset 0, step size 4096)) and a Run completed in %3.3f seconds. completion line differing only in the timing value; exit 0 on all six invocations |
| 2 | -w --pattern 65 ... on raw and qcow2: pattern verifiable by read-back, qemu-img check clean, fresh qcow2 disk size grows |
PASS | Live run: instar bench -w -c 20 --pattern 65 --flush-interval 5 -d 1 -f raw wrote the pattern; read-back of the first 4096 bytes was all 0x41 (0x41 = decimal 65 = ASCII 'A'). instar bench -w -c 20 -s 4096 --pattern 66 on a fresh 8 MiB qcow2 grew the file from 196616 B to 458752 B; qemu-img check on the qcow2 afterwards reported "No errors were found on the image." (exit 0) |
| 3 | --flush-interval real flushes; refusal messages (--flush-interval w/o -w, flush_interval < depth, count/depth/bufsize < 1, pattern > 0xff) match Debian-10.0.8; --pattern w/o -w silently ignored; every failure exits 1 |
PASS | Live run, byte-identical to local qemu-img bench on all four refusal forms: "--flush-interval is only available in write tests", "Flush interval can't be smaller than depth", "Invalid request count specified. Must be between 1 and 2147483647.", "Invalid pattern byte specified. Must be between 0 and 255." — all exit 1 on both binaries. instar bench -c 5 --pattern 99 -f raw (no -w) ran the read test normally (pattern ignored), exit 0. Write test with --flush-interval 5 -d 1 completed with the Sending flush every 5 requests line present |
| 4 | instar-vs-qemu-img on identical args is a reproducible sandbox-overhead measurement; docs/bench.md documents methodology + caveats |
PASS | docs/bench.md exists (commit 90374d1) with a "Sandbox-overhead methodology" section and a "What the number means" section covering the serialized-depth (effective-depth: 1), 64 KiB transfer-granularity, and qcow2-metadata-caching caveats; live runs above confirm both binaries accept identical arguments and produce comparable, differently-timed output on the same image |
| 5 | --output json emits the agreed schema; default output is exactly qemu's lines |
PASS | Live run: instar bench -c 50 -f raw --output json emitted all 15 documented keys (filename, format, count, depth, effective-depth, buffer-size, step-size, offset, write, pattern, flush-interval, no-drain, flushes-issued, elapsed-seconds, requests-per-second, bytes-per-second); default-path runs above printed only qemu's two/three lines, nothing else |
| 6 | make instar builds; make lint is clean |
PASS | make lint → "All checks passed!" this session; make instar completed and listed bench.bin Bench operation (read/write throughput benchmark) - loaded at 0x30000 among the built operation binaries |
| 7 | Guest binaries pass make check-binary-sizes; bench.bin registered; core.bin fits 128 KiB after VERSION 19→20 |
PASS | bash scripts/check-binary-sizes.sh this session: OK: core (0x10000-0x30000) - 68KB / 128KB (53%, .bin=68416B) and OK: bench (0x30000-0xF0000) - 156KB / 768KB (20%, .bin=160224B); script exits "All binaries fit within their memory regions." src/shared/src/lib.rs:1404: pub const VERSION: u32 = 20; |
| 8 | All Rust unit tests pass (make test-rust), incl. crates/bench |
PASS | make test-rust run this session, exit 0, zero FAILED occurrences across the full workspace; bench-<hash> unit-test binary: test result: ok. 43 passed; 0 failed (matches the phase-1 capture of 43 tests) |
| 9 | All Python integration tests pass (make test-integration), incl. tests/test_bench.py |
PASS (cited, not re-run per brief) | Phase-6b captured full-suite result: 2476 ran / 1919 passed / 0 failed / 557 skipped, 13m0s wall-clock, 16-way (docs/plans/PLAN-bench-phase-06-integration.md, "Captured suite results (step 6b)"); tests/test_bench.py is 62 tests across seven classes, file-scoped 62/62 in ~2.4 s, scheduled across 10 workers with no cross-test interference in the full run. This session confirmed the file still exists and still declares 62 def test_ methods (grep -c "def test_" tests/test_bench.py → 62) |
| 10 | fuzz_bench_schedule clean in nightly tiers; differential op_bench clean over the deterministic surface |
PASS (cited, not re-run per brief) | Phase-7 captured results (docs/plans/PLAN-bench-phase-07-fuzz.md, "Captured fuzz results (step 7c)"): fuzz_bench_schedule smoke — 1,306,856 runs in 121 s, zero crashes, registered as the 30th coverage-fuzz target in FAST_TIER; op_bench differential — 100 iterations / 233.8 s, zero bench divergences across 22 bench-invoking iterations, wrap-avoidance steer-around verified over 30,000 generated plans with zero wrap leaks |
| 11 | pre-commit run --all-files passes |
PASS | Run this session: rustfmt and clippy / check binary sizes against memory layout / Lint GitHub Actions workflow files / shellcheck all report Passed |
| 12 | Docs updated: docs/bench.md, docs/usage.md, ARCHITECTURE.md, README.md, AGENTS.md, CHANGELOG.md, docs/plans/index.md, docs/plans/order.yml |
PASS | All eight present and bench-aware this session: docs/bench.md (new, 90374d1); docs/usage.md:1016 ### bench; ARCHITECTURE.md:588-692 (send_bench_start/send_bench_result, VERSION 20, the lifted 128 KiB/768 KiB memory map matching src/shared/src/lib.rs); README.md:31,39,490-503 (Project-Status list, guide link, ### I/O Benchmarking (bench) usage block); AGENTS.md:20,161-175,387-391 (ops tree, Operations bullet, test-file list); CHANGELOG.md:165-186 (### Added entry, phases 1-8, VERSION 19→20, 62-test suite, doc link); docs/plans/index.md bench row (updated by this step, see below); docs/plans/order.yml:28 already lists PLAN-bench.md (untouched, see below) |
| 13 | instar implements all 15 qemu-img subcommands; parity roster closed | PASS | With bench shipped, bitmap (merged, PR #386) was already the 14th; bench is the 15th and last (README.md:31, docs/plans/PLAN-bench.md Situation: "bench is the single remaining qemu-img subcommand instar does not implement") |
No criterion failed; the plan proceeds to close-out (master plan
Execution row 8 → Complete, docs/plans/index.md bench row →
Complete, docs/plans/order.yml left untouched).