diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 30e014997..b26ac3f6e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -73,6 +73,8 @@ jobs: steps: - uses: actions/checkout@v4 - uses: ./.github/actions/setup-build-env + - name: Install hyperfine for benchmark integration checks + run: cargo install hyperfine --version 2.0.0 --locked - run: just lint-py - run: just test-scripts - run: just test-py @@ -245,9 +247,9 @@ jobs: - name: Record isolated core budget run: | just build release - # Host copy-up scheduling and VM admission, including contract tests, - # bring the runtime closure to ~189,544 lines; retain ~1,456 lines of headroom. - just check-core-budget target/release/pvisor --max-dependencies 235 --max-workspace-lines 191000 --max-core-items 301 > target/pvisor-core-budget.json + # Queue dispatch, read-only DAX and their contract tests bring the + # runtime closure to 194,578 lines; retain 1,422 lines of headroom. + just check-core-budget target/release/pvisor --max-dependencies 235 --max-workspace-lines 196000 --max-core-items 301 > target/pvisor-core-budget.json - uses: actions/upload-artifact@v4 with: name: pvisor-core-budget-linux diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 67bf027d9..eeece2e60 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -3,10 +3,28 @@ name: Documentation on: push: branches: [main, develop, next] - paths: ['docs/**', 'scripts/*docs*.py', 'scripts/check-reference.py', 'tests/test_docs.py', 'justfile', '.github/actions/setup-build-env/**', '.github/workflows/docs.yml'] + paths: + - 'docs/**' + - 'benchmark.new/**' + - 'tools/semspec/**' + - 'scripts/*docs*.py' + - 'scripts/check-reference.py' + - 'tests/test_docs.py' + - 'justfile' + - '.github/actions/setup-build-env/**' + - '.github/workflows/docs.yml' pull_request: branches: [main, develop, next] - paths: ['docs/**', 'scripts/*docs*.py', 'scripts/check-reference.py', 'tests/test_docs.py', 'justfile', '.github/actions/setup-build-env/**', '.github/workflows/docs.yml'] + paths: + - 'docs/**' + - 'benchmark.new/**' + - 'tools/semspec/**' + - 'scripts/*docs*.py' + - 'scripts/check-reference.py' + - 'tests/test_docs.py' + - 'justfile' + - '.github/actions/setup-build-env/**' + - '.github/workflows/docs.yml' workflow_dispatch: permissions: @@ -24,6 +42,12 @@ jobs: steps: - uses: actions/checkout@v4 - uses: ./.github/actions/setup-build-env + with: + rust: "true" + - uses: Swatinem/rust-cache@v2 + with: + shared-key: docs-semspec + workspaces: tools/semspec -> ../../target/semspec - name: Test documentation checks run: just test-py tests/test_docs.py - name: Build and check documentation diff --git a/.github/workflows/pvisor-benchmark.yml b/.github/workflows/pvisor-benchmark.yml index 3fb13f045..6c845e7c2 100644 --- a/.github/workflows/pvisor-benchmark.yml +++ b/.github/workflows/pvisor-benchmark.yml @@ -10,6 +10,8 @@ on: - 'crates/**' - 'vendor/**' - 'benchmark/**' + - 'benchmark.new/**' + - 'tools/semspec/**' - 'justfile' - '.github/actions/setup-build-env/**' - '.github/workflows/pvisor-benchmark.yml' @@ -22,6 +24,8 @@ on: - 'crates/**' - 'vendor/**' - 'benchmark/**' + - 'benchmark.new/**' + - 'tools/semspec/**' - 'justfile' - '.github/actions/setup-build-env/**' - '.github/workflows/pvisor-benchmark.yml' @@ -55,6 +59,9 @@ jobs: with: shared-key: pvisor-benchmark + - name: Install hyperfine + run: cargo install hyperfine --version 2.0.0 --locked + - name: Resolve main baseline id: baseline env: @@ -74,7 +81,7 @@ jobs: git worktree add --detach "$baseline_repo" "$baseline_sha" echo "repo=$baseline_repo" >> "$GITHUB_OUTPUT" echo "sha=$baseline_sha" >> "$GITHUB_OUTPUT" - if [[ -f "$baseline_repo/benchmark/pvisor/bench.py" ]]; then + if [[ -f "$baseline_repo/justfile" ]]; then echo "supported=true" >> "$GITHUB_OUTPUT" else echo "supported=false" >> "$GITHUB_OUTPUT" @@ -110,8 +117,8 @@ jobs: shell: bash run: | set -euo pipefail - candidate="${RUNNER_TEMP}/.data/pvisor-benchmark/candidate/smoke/raw-report.json" - baseline="${RUNNER_TEMP}/.data/pvisor-benchmark/main/smoke/raw-report.json" + candidate="${RUNNER_TEMP}/.data/pvisor-benchmark/candidate/smoke/in.bench.json" + baseline="${RUNNER_TEMP}/.data/pvisor-benchmark/main/smoke/in.bench.json" output="${RUNNER_TEMP}/.data/pvisor-benchmark/comparison" if [[ -f "$baseline" ]]; then just benchmark-compare "$candidate" "$baseline" "$output" diff --git a/Cargo.toml b/Cargo.toml index 49276a1c8..91381b847 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -133,6 +133,15 @@ strip = "symbols" codegen-units = 8 panic = "abort" +# File-operation dispatch and checked path walks run on every virtio-fs request. +# Keep the distribution size policy elsewhere, but optimize these hot crates +# for execution speed. +[profile.release.package.pvisor-vm] +opt-level = 3 + +[profile.release.package.pvisor-overlay-core] +opt-level = 3 + # Build scripts and proc macros do not need release stripping. Keeping their # symbols avoids invoking a host rust-objcopy that may be unavailable when # cross-compiling from macOS, while the final application remains stripped. diff --git a/README.md b/README.md index af3164eeb..4f992e8f3 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ controls, with platform-dependent container/VM support. See [capabilities and ev - [Security](https://deeplink-org.github.io/pvisor/en/security/) — threat model, executor boundaries, and known limitations - [Benchmarks](https://deeplink-org.github.io/pvisor/en/benchmarks/) — methods and current data - [Design and research](https://deeplink-org.github.io/pvisor/en/design/) — architecture, isolation, and research directions -- [Experimental features](experimental/README.md) — proposed capabilities, implementation stages, and validation gates +- [Experimental features](docs/src/en/design/experimental/index.md) — proposed capabilities, implementation stages, and validation gates - [Community](https://deeplink-org.github.io/pvisor/en/community/) — contributing, testing, and roadmap - [中文文档](https://deeplink-org.github.io/pvisor/zh/start/) @@ -127,6 +127,11 @@ The remaining crates are `pvisor-core`, `pvisor-journal`, `pvisor-gateway`, dependencies and targeted tests (`just test pvisor` for the runtime, `just test cli` for application frontends). +Release builds optimize the virtio-fs adapter (`pvisor-vm`) and shared filesystem +mechanics (`pvisor-overlay-core`) for execution speed. Other crates retain the +distribution profile's size optimization; the `performance` profile optimizes +the complete application for speed. + ## Guest firmware The customized libkrunfw sources, guest kernel configurations, patches and diff --git a/benchmark.new/README.md b/benchmark.new/README.md new file mode 100644 index 000000000..70e3f34b4 --- /dev/null +++ b/benchmark.new/README.md @@ -0,0 +1,168 @@ +# JSON benchmark tables + +这里回答的问题:怎样用固定、可审查的 hyperfine 命令生成表格,并保留每次实验的来源? +需求、角色和正确性门禁见 [benchmark/README.md](../benchmark/README.md#registry)。 +[registry.csv](registry.csv) 把活动实验映射到按主题组织的 JSON 模板。综合页无独立实验,B-CLUSTER 已退役。 + +## 模板与执行 + +每个条件对应一个行命令。hyperfine 行命令固定 +样本数、预热、被测命令和 hooks,执行一次实验后直接展开为 N、Median ms、Mean ms、 +SD ms、Min ms、Max ms 六列。各列取同一份原始导出,不再重复声明命令: + +```json +{ + "Native": "$ hyperfine --shell=none --runs 60 --warmup 3 --command-name native '/bin/true'" +} +``` + +事件计时使用 Python 行命令,脚本一次返回全部指标。`startup/timing.json` 包含八个 +后端,每行同次测量 Ready / Exit;预热不进入统计,正确性失败不发布结果。 +旧 hyperfine 启动模板已删除,实际 argv 固定在 `startup/subjects.py`。 + +```sh +just benchmark startup/timing +just benchmark startup/timing --row Docker +just benchmark startup/timing "pvisor vm" +just benchmark docker_timing +just benchmark smoke +just benchmark filesystem/tools +just benchmark replay --binary target/release/pvisor-replay +``` + +`just benchmark` 直接输出渲染后的 Markdown 表格,并在模板旁生成同名 `.render.json`: +例如 `docker_timing.json` → `docker_timing.render.json`。默认使用自动清理的临时目录, +只保留最终表格;不追加 CSV、不自动更新用户文档。部分行失败时不覆盖已有结果。 +第二个位置参数或 `--row` 可指定启动或文件系统表的一行;行名忽略大小写和多余空格。 +复测仅执行该行,更新同名结果文件中的该行并输出合并表格,已有其他行保留,顺序跟随模板。 +没有旧结果时只含已测行;整表执行会替换全部结果。拼错行名会在构建和准备前报错并列出可选行。 +启动默认选当前可用的前两个 CPU,可用 `--cpu-affinity` 指定;pVisor、镜像与内核需要预先准备。 +process smoke/nightly 保留自动构建;其他表使用预构建制品。 + +需要审计、CI 对比或发布时,显式保留原始证据: + +```sh +just benchmark startup/timing --output benchmark.new/startup/.data/measurement +just benchmark smoke --output benchmark.new/process/.data/measurement +``` + +启动每行保存在 `row-0-/`、`row-1-/` 等子目录中;其他表直接使用指定目录。 +`PVISOR_BENCHMARK_OUTPUT` 仍按表名创建子目录,供 CI 保留 `smoke/in.bench.json`。 +可对保留批次单独执行 `update.py`;文档发布必须使用完整审计证据。 + +安装 Python 3.10+ 并构建独立的 semspec CLI;process/replay 另需 hyperfine 2.0+。 +准备脚本只冻结输入、展开私有路径和做预检;真正执行实验的入口是 **semspec bench**。 +例如 process smoke(2 次预热、10 次样本;nightly 为 10 / 50): + +```sh +cargo build --locked --manifest-path tools/semspec/Cargo.toml --target-dir target/semspec +export SEMSPEC_BIN="$PWD/target/semspec/debug/semspec" +batch=$(python3 benchmark.new/process/prepare.py benchmark.new/process/smoke.json --binary target/release/pvisor) +"$SEMSPEC_BIN" bench --cwd "$batch/workspace" "$batch/in.json" +python3 benchmark.new/update.py "$batch" +"$SEMSPEC_BIN" render benchmark.new/process/results/smoke.semspec.md +``` + +独立 filesystem 原生负载使用 Python 行命令,见 [filesystem/README.md](filesystem/README.md)。 +两行共用一份输入,解释器与负载校验计入完整命令时间,默认无需 hyperfine。手动保留批次: + +```sh +batch=$(python3 benchmark.new/filesystem/prepare.py benchmark.new/filesystem/tools.json) +"$SEMSPEC_BIN" bench --cwd "$batch" "$batch/in.json" +python3 benchmark.new/update.py "$batch" +``` + +启动页八个固定条件见 [startup/README.md](startup/README.md)。`@REPO@`、`@BATCH@`、 +`@BINARY@` 等标记由准备脚本展开成实际路径,生成本批次的 `in.json`;原模板保持不变。 +`common.py` 共享制品摘要和准备逻辑,`update.py` 校验并发布。process/replay 的完整命令实验由 hyperfine 计时和统计。pVisor VM Ready / Exit 使用独立的 Python `perf_counter_ns` 脚本, +固定参数写在脚本内,返回整行 JSON;它的采样和标准库统计单独标记,不作为 hyperfine 数据。 +启动条件的 Python 观察者直接创建被测进程。filesystem 的观察者也直接执行固定 Python 负载, +用 perf_counter_ns 测量完整命令;输入准备、观察者与后续审计不计时。process/replay 继续使用 hyperfine。 + +`run.py` 统一解析表名、行名和选项;Just 只负责按解析结果构建 process 二进制与 semspec。 +准备和发布只接受整行命令,旧的逐单元格命令与 `| json ... * ...` 提取分支已删除。 +历史展示 JSON 仍可直接渲染;旧逐单元格批次不再由当前发布器发布,需要按当前模板重新测量。 + +## 证据与发布 + +每次准备创建入口目录 `.data/--/`,拒绝复用已有目录: + +- `in.json`:展开路径后的实际命令模板。 +- `in.render.json`:保留完整精度的结果表格。 +- `in.bench.json`:原始测量导出、命令和表格行/单元格映射。 +- `in.bench.logs/`:每个命令的日志;失败也保留。 +- `manifest.json`、`harness/`:版本、CPU 亲和性、制品和冻结代码摘要、状态。 + +`update.py` 检查全部正式样本零退出、条件和提取值与原始导出一致、输入与代码来源未变, +再追加 `results/.semspec.csv`(启动和文件系统表使用 `.data/results/`),在模板旁生成 +`.render.json` 和引用它的结果 Markdown。 +CSV 保留完整精度及批次来源;展示 JSON 将毫秒格式化为两位小数。重复发布同批次不重复追加; +不同 CSV schema 拒绝混合。可以通过 `--csv` / `--markdown` 指定新目标。 +Markdown 单独一行写 `{{ table("../tools.render.json") }}`;文档构建只读取已生成 JSON, +不会运行实验。历史 results 文件和包装器计时 CSV 保留原口径,不混入新批次。 + +`--prepare` / `--conclude` 重置并验证每次输入输出;失败不发布。构建、rootfs、镜像和服务 +预先准备,不能将旧多样本 campaign 当成单次被测命令。hyperfine 按条件顺序独立采样, +不宣称随机配对、因果改善比例或置信区间。Ready、内部阶段、密度和整体 cgroup 内存等 +不同指标不能用完整命令时间替换。工程和诊断实验保持 registry 中各自的角色。 + +## 迁移范围与验证 + +注册入口已核对行命令规则;尚无单次负载的模板保持未准备。 +全部活动 ID 已改为 JSON 入口。启动、process、filesystem/tools、replay 有具体命令;其他条目的 +`requires-single-condition-command` 模板显式使用 null,准备时拒绝测量。它们还需从旧 +`benchmark/pvisor/` 拆出单次负载及正确性 hooks,不能用历史统计或任意命令填充。 + +`just benchmark [smoke|nightly]` 在构建后走 prepare → semspec bench → audit → render;CI 使用同一 +新 harness 测量候选和基线,并从原始导出执行现有 15% 回归门禁。 +验证:`just test-semspec` 和 `just test-py discover -s benchmark.new/tests`。 + +## 文档表格 + +[docs.json](docs.json) 登记中英文 benchmark 用户页及相关分析页的 benchmark ID、角色、 +来源 CSV,以及动态表格的 JSON 引用和发布绑定。固定历史表格直接写在 Markdown 中, +保留原来的数字、统计口径、链接和对齐;构建时不再依赖逐张历史 JSON。 +复测发布的结果与自动生成的覆盖范围表继续通过 `semspec render` 读取 JSON。 +重复表头可使用 `headers`,链接、代码和对齐通过显式渲染选项保留。 + +```sh +python3 benchmark.new/docs.py check +python3 benchmark.new/docs.py publish "$batch" +``` + +`python3 benchmark.new/measurement_coverage.py` 更新 14 个用户页的复测范围清单。没有匹配负载的页面保留历史数字并明确标识。 + +`check` 检查文章覆盖、动态 JSON 引用、表结构、未执行命令与 CSV 来源。 +公开的新表还必须逐格等于各条件最新 CSV 的展示值,样本数和计时范围不匹配会阻止构建。 +`publish` 仅接受 `docs.json` 的 live binding,校验对应时钟的原始样本与正确性 hooks, +追加两种语言同字节的完整精度 CSV,更新展示 JSON 与批次 provenance,原 MD 无需改写。 +provenance 摘要统一保存在 `docs/overrides/assets/benchmarks/`,中英文共用一份; +保留批次、计时口径、运行配置、输入/二进制与证据摘要。完整 manifest、逐文件 harness +清单、宿主路径和软件包收据留在批次 `.data/`。启动仅发布文档引用的 Ready / Exit +两张 JSON 表,单行复测保留其他条件的来源配置。 +发布器验证 `metric_scope` 与绑定声明的负载配置;中位数与标准差同一行,批次名称放在来源记录,不挤入数值表。 +没有 live binding 的条件会拒绝发布;工程 A/B 不写入用户表。filesystem 的历史 hyperfine 结果 +保留在本地归档,其用户表、下载和发布绑定已删除;新 Python 时钟数据需单独登记文档表与 CSV。改变历史表数据时需同步 +登记新来源与摘要,不能让旧 CSV 或旧结论自动冒充新实验。原文章归档留在 +`.data/docs-tables-20261011/`;原 CSV 未覆盖。 +整理前的完整文档 provenance 归档于 `.data/docs-provenance-20261011/`。 + +## Replay + +B-REPLAY 使用七个固定适配器,每个一条 task 0 合成轨迹,60 次采样、零预热。 +它是独立的完整 CLI 时间数据,历史二十条轨迹的分布与一致性结果继续保留。 +准备脚本冻结 replay 二进制及轨迹并完成预检;每次 hooks 校验源摘要、精确参数、 +完整批次边界、历史观测、零工具执行和真实工作区未变。失败保留证据并阻止发布。 +未提供 source-build receipt 时,不能声称冻结二进制来自当前源码。 + +```sh +export PATH="/path/to/hyperfine/bin:$PATH" +export SEMSPEC_BIN="$PWD/target/semspec/debug/semspec" +batch=$(taskset -c 0,1 python3 benchmark.new/replay/prepare.py benchmark.new/replay/replay.json --binary target/release/pvisor-replay) +taskset -c 0,1 "$SEMSPEC_BIN" bench --cwd "$batch" "$batch/in.json" +python3 benchmark.new/update.py "$batch" +python3 benchmark.new/docs.py publish "$batch" +``` + +亲和性在准备与 semspec 外层设置,manifest 记录实际选择;被测命令没有 Python 或 +`taskset` 包裹。所有适配器按独立条件采样,不声称随机配对改善比例。 diff --git a/benchmark.new/agent/agent.json b/benchmark.new/agent/agent.json new file mode 100644 index 000000000..95834f8b2 --- /dev/null +++ b/benchmark.new/agent/agent.json @@ -0,0 +1,10 @@ +{ + "unprepared": { + "N": null, + "Median ms": null, + "Mean ms": null, + "SD ms": null, + "Min ms": null, + "Max ms": null + } +} diff --git a/benchmark.new/agent/macos.json b/benchmark.new/agent/macos.json new file mode 100644 index 000000000..95834f8b2 --- /dev/null +++ b/benchmark.new/agent/macos.json @@ -0,0 +1,10 @@ +{ + "unprepared": { + "N": null, + "Median ms": null, + "Mean ms": null, + "SD ms": null, + "Min ms": null, + "Max ms": null + } +} diff --git a/benchmark.new/agent/macos_ab.json b/benchmark.new/agent/macos_ab.json new file mode 100644 index 000000000..95834f8b2 --- /dev/null +++ b/benchmark.new/agent/macos_ab.json @@ -0,0 +1,10 @@ +{ + "unprepared": { + "N": null, + "Median ms": null, + "Mean ms": null, + "SD ms": null, + "Min ms": null, + "Max ms": null + } +} diff --git a/benchmark.new/common.py b/benchmark.new/common.py new file mode 100644 index 000000000..4c760f527 --- /dev/null +++ b/benchmark.new/common.py @@ -0,0 +1,263 @@ +"""Prepare auditable JSON inputs for semspec; no timer or sampling loop.""" + +import csv +import datetime as dt +import hashlib +import json +import os +import platform +import re +import shlex +import shutil +import subprocess +import sys +import tempfile +from pathlib import Path + +ROOT = Path(__file__).resolve().parent +REPO = ROOT.parent +METRIC_SCOPE = "direct subject command; hyperfine hooks and semspec excluded" +READY_SCOPE = "Popen launch to receipt of exact REFERENCE_READY line; perf_counter_ns; preparation and teardown excluded" + +READY_EXIT_SCOPE = "same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded" + +FILESYSTEM_SCOPE = "Popen launch through normal zero exit of native Python metadata/read command; perf_counter_ns; interpreter, pipes and subject validation included; fixture preparation and observer excluded" + + +def digest(path): + value = hashlib.sha256() + with Path(path).open("rb") as stream: + for block in iter(lambda: stream.read(1024 * 1024), b""): + value.update(block) + return value.hexdigest() + + +def save(path, value): + path = Path(path) + path.parent.mkdir(parents=True, exist_ok=True) + with tempfile.NamedTemporaryFile( + mode="w", encoding="utf-8", dir=path.parent, delete=False + ) as stream: + temporary = Path(stream.name) + json.dump(value, stream, ensure_ascii=False, indent=2) + stream.write("\n") + temporary.replace(path) + + +def artifact_summary(artifacts): + """Keep input/binary identities; the local manifest owns full harness records.""" + summary = {} + for name, sha256 in artifacts.items(): + path = Path(name) + if ( + path.suffix in (".py", ".rs") + or path.suffix == ".json" + and "benchmark.new" in path.parts + and ".data" not in path.parts + or path.name in ("semspec", "hyperfine") + or re.fullmatch(r"python3(?:\.\d+)?", path.name) + ): + continue + if ".data" in path.parts: + relative = path.parts[path.parts.index(".data") + 2 :] + label = "/".join(relative) or path.name + else: + label = path.name if path.is_absolute() else path.as_posix() + if label in summary and summary[label] != sha256: + raise ValueError("ambiguous public artifact identity: " + label) + summary[label] = sha256 + return summary + + +def registered(template): + entry = str(Path(template).resolve().relative_to(ROOT)) + with (ROOT / "registry.csv").open() as stream: + matches = [row for row in csv.DictReader(stream) if row["entry"] == entry] + if len(matches) != 1: + raise ValueError("template must have exactly one benchmark registry entry: " + entry) + return matches[0] + + +def create_batch(template, output=None): + template = Path(template).resolve(strict=True) + registered(template) + if output: + batch = Path(output).resolve() + if ".data" not in batch.parts: + raise ValueError("raw output must live under an ignored .data directory") + batch.mkdir(parents=True, exist_ok=False) + else: + parent = template.parent / ".data" + parent.mkdir(exist_ok=True) + prefix = ( + template.stem + "-" + dt.datetime.now(dt.timezone.utc).strftime("%Y%m%dT%H%M%SZ") + "-" + ) + batch = Path(tempfile.mkdtemp(prefix=prefix, dir=parent)) + return batch + + +def expand(expression, variables): + """Substitute explicit preparation paths while retaining argv quoting.""" + + def token(value): + for name, replacement in variables.items(): + value = value.replace("@" + name + "@", str(replacement)) + if re.search(r"@[A-Z_]+@", value): + raise ValueError("unresolved preparation placeholder: " + value) + return value + + argv = shlex.split(expression.removeprefix("$ ")) + for i in range(len(argv)): + nested = i == len(argv) - 1 or ( + i and argv[i - 1] in ("--setup", "--prepare", "--conclude", "--cleanup", "--env") + ) + if nested and "@" in argv[i]: + argv[i] = shlex.join([token(part) for part in shlex.split(argv[i])]) + else: + argv[i] = token(argv[i]) + return "$ " + shlex.join(argv) + + +def materialize( + template, batch, variables=None, artifacts=(), cwd=None, helpers=(), row_config=None +): + template, batch = Path(template).resolve(), Path(batch).resolve() + metadata = registered(template) + variables = dict(variables or {}) | {"REPO": REPO, "BATCH": batch} + table = json.loads(template.read_text()) + if row_config is not None: + table = {name: table[name] for name in row_config["rows"]} + if metadata.get("status") == "requires-single-condition-command": + raise ValueError("template has no prepared single-condition command") + commands = [] + for name, row in table.items(): + if row_config is not None: + if not isinstance(row, str) or not row.startswith("$ python3 "): + raise ValueError("script template must contain Python row commands") + argv = shlex.split(row[2:]) + for i, arg in enumerate(argv): + for key, replacement in variables.items(): + arg = arg.replace("@" + key + "@", str(replacement)) + if re.search(r"@[A-Z_]+@", arg): + raise ValueError("unresolved row placeholder") + argv[i] = arg + table[name] = "$ " + shlex.join(argv) + commands.append( + (name, row_config["rows"][name], row_config["runs"], row_config["warmup"]) + ) + continue + if not isinstance(row, str) or not row.startswith("$ hyperfine "): + raise ValueError("benchmark requires one hyperfine command per row") + expanded = expand(row, variables) + argv = shlex.split(expanded[2:]) + if "|" in argv: + raise ValueError("cell selectors are obsolete; use a whole row command") + table[name] = expanded + runs = int(argv[argv.index("--runs") + 1]) + warmup = int(argv[argv.index("--warmup") + 1]) + if runs < 2 or warmup < 0: + raise ValueError("runs >= 2 and warmup >= 0 required") + condition = argv[argv.index("--command-name") + 1] + commands.append((name, condition, runs, warmup)) + if not commands: + raise ValueError("template has no prepared single-condition command") + configurations = {(runs, warmup) for _, _, runs, warmup in commands} + if len(configurations) != 1: + raise ValueError("a batch must use one runs/warmup configuration") + runs, warmup = configurations.pop() + if row_config is not None: + startup = ( + metadata["benchmark_id"] == "B-STARTUP" + and ( + metadata["entry"] == "startup/timing.json" + or metadata["entry"].startswith("startup/") + and metadata["entry"].endswith("_timing.json") + ) + and len(row_config["rows"]) == 1 + and row_config.get("metric_scope", READY_EXIT_SCOPE) == READY_EXIT_SCOPE + ) + filesystem = ( + metadata["benchmark_id"] == "B-FS-TOOLS" + and metadata["entry"] == "filesystem/tools.json" + and row_config.get("metric_scope") == FILESYSTEM_SCOPE + and 1 <= len(row_config["rows"]) <= 2 + ) + if not (startup or filesystem): + raise ValueError( + "only registered startup/filesystem scripts have row publication support" + ) + executable, version = sys.executable, None + else: + executable = shutil.which("hyperfine") + if not executable: + raise ValueError("hyperfine 2.0+ is required") + version = subprocess.check_output([executable, "--version"], text=True).strip() + if int(version.split()[1].split(".")[0]) < 2: + raise ValueError("benchmark.new requires hyperfine 2.0+") + sources = (template, ROOT / "common.py", ROOT / "update.py", *helpers) + semspec = os.environ.get("SEMSPEC_BIN") + inputs = { + str(Path(path).resolve(strict=True)): digest(path) + for path in (*artifacts, *sources, executable, *([semspec] if semspec else [])) + } + harness = batch / "harness" + harness.mkdir() + for source in sources: + source = Path(source).resolve() + destination = harness / (source.parent.name + "-" + source.name) + shutil.copy2(source, destination) + commit = subprocess.run( + ["git", "rev-parse", "HEAD"], cwd=REPO, capture_output=True, text=True + ).stdout.strip() + manifest = dict( + **{key: metadata[key] for key in ("benchmark_id", "role", "entry")}, + batch=batch.name, + recorded_at=dt.datetime.now(dt.timezone.utc).isoformat(), + template_sha256=digest(template), + metric_scope=row_config.get("metric_scope", READY_EXIT_SCOPE) + if row_config + else METRIC_SCOPE, + order=( + "sequential independent script samples; no randomized paired improvement claim" + if row_config + else "separate hyperfine conditions; no randomized paired improvement claim" + ), + runs=runs, + warmup=warmup, + conditions=list(dict.fromkeys(condition for _, condition, _, _ in commands)), + rows={row: condition for row, condition, _, _ in commands}, + cwd=str(Path(cwd or template.parent).resolve()), + hyperfine_version=version, + hyperfine_sha256=None if row_config else digest(executable), + source_commit=commit, + source_commit_scope="harness worktree; binary digest does not prove source-build correspondence", + platform=platform.platform(), + kernel=platform.release(), + cpu_affinity=sorted(os.sched_getaffinity(0)) if hasattr(os, "sched_getaffinity") else None, + artifacts=inputs, + harness={p.name: digest(p) for p in harness.iterdir()}, + status="prepared", + ) + if row_config is not None: + manifest.update( + measurement_engine="python-perf_counter_ns", python_version=platform.python_version() + ) + save(batch / "in.json", table) + manifest["input_sha256"] = digest(batch / "in.json") + save(batch / "manifest.json", manifest) + return batch / "in.json" + + +if __name__ == "__main__": + import argparse + + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("template", type=Path) + p.add_argument("--output", type=Path) + args = p.parse_args() + try: + batch = create_batch(args.template, args.output) + materialize(args.template, batch) + print(batch) + except (ValueError, OSError, subprocess.SubprocessError) as error: + p.exit(1, f"{error}\n") diff --git a/benchmark.new/density/density.json b/benchmark.new/density/density.json new file mode 100644 index 000000000..95834f8b2 --- /dev/null +++ b/benchmark.new/density/density.json @@ -0,0 +1,10 @@ +{ + "unprepared": { + "N": null, + "Median ms": null, + "Mean ms": null, + "SD ms": null, + "Min ms": null, + "Max ms": null + } +} diff --git a/benchmark.new/docs.json b/benchmark.new/docs.json new file mode 100644 index 000000000..80793089d --- /dev/null +++ b/benchmark.new/docs.json @@ -0,0 +1,886 @@ +{ + "format": 1, + "scope": "all bilingual benchmark articles and benchmark design/evidence articles", + "preservation": "Fixed historical tables live directly in Markdown with their original statistics, scope and source links. This inventory tracks dynamic JSON tables and publication bindings. Raw evidence remains under ignored .data.", + "pages": [ + { + "locale": "zh", + "page": "benchmarks/agent-tasks.md", + "benchmark_ids": [ + "B-AGENT-TASK", + "B-COMPARE" + ], + "role": "user-facing", + "tables": [], + "source_csv": [ + "agent-tasks.csv", + "runtime-comparisons.csv", + "runtime-provenance.csv", + "runtime-summary.csv", + "task-scenarios.csv" + ] + }, + { + "locale": "zh", + "page": "benchmarks/cluster-scalability.md", + "benchmark_ids": [ + "B-CLUSTER" + ], + "role": "user-facing", + "tables": [], + "source_csv": [ + "cluster-comparisons.csv", + "cluster-provenance.csv", + "cluster-summary.csv", + "controller-history-provenance.csv", + "controller-history-summary.csv" + ] + }, + { + "locale": "zh", + "page": "benchmarks/compare-rl-infra.md", + "benchmark_ids": [ + "B-COMPARE" + ], + "role": "user-facing", + "tables": [], + "source_csv": [ + "agent-tasks.csv", + "compare-rl-infra.csv", + "density-summary.csv", + "isolation-tests.csv", + "lazy-startup-summary.csv", + "replay-fidelity.csv", + "startup.csv", + "vm-memory/memory-choices.csv" + ] + }, + { + "locale": "zh", + "page": "benchmarks/density.md", + "benchmark_ids": [ + "B-DENSITY" + ], + "role": "user-facing", + "tables": [], + "source_csv": [ + "density-provenance.csv", + "density-summary.csv" + ] + }, + { + "locale": "zh", + "page": "benchmarks/filesystem.md", + "benchmark_ids": [ + "B-FS-TOOLS", + "B-MACOS" + ], + "role": "user-facing", + "tables": [], + "source_csv": [ + "filesystem-cache-policy.csv", + "filesystem-comparisons.csv", + "filesystem-provenance.csv", + "filesystem.csv", + "runtime-provenance.csv", + "runtime-summary.csv" + ] + }, + { + "locale": "zh", + "page": "benchmarks/index.md", + "benchmark_ids": [ + "B-OVERVIEW" + ], + "role": "user-facing", + "tables": [ + { + "json": "benchmarks/measurement-coverage.render.json", + "kind": "coverage", + "rows": 14, + "columns": [ + "数据状态", + "实测范围" + ], + "options": { + "markdown": true + } + } + ], + "source_csv": [ + "runtime-comparisons.csv", + "runtime-provenance.csv", + "runtime-summary.csv", + "workflow-comparisons.csv", + "workflow-provenance.csv", + "workflow-summary.csv", + "measurement-coverage.csv", + "filesystem.csv", + "filesystem-comparisons.csv", + "filesystem-provenance.csv" + ] + }, + { + "locale": "zh", + "page": "benchmarks/isolation-tests.md", + "benchmark_ids": [ + "B-ISOLATION" + ], + "role": "user-facing", + "tables": [], + "source_csv": [ + "evidence-sources.csv", + "isolation-provenance.csv", + "isolation-tests.csv" + ] + }, + { + "locale": "zh", + "page": "benchmarks/lazy-image-startup.md", + "benchmark_ids": [ + "B-LAZY-STARTUP" + ], + "role": "user-facing", + "tables": [], + "source_csv": [ + "lazy-numpy-details.csv", + "lazy-numpy-provenance.csv", + "lazy-numpy-summary.csv", + "lazy-startup-details.csv", + "lazy-startup-provenance.csv", + "lazy-startup-summary.csv" + ] + }, + { + "locale": "zh", + "page": "benchmarks/methodology.md", + "benchmark_ids": [ + "B-METHOD" + ], + "role": "user-facing", + "tables": [], + "source_csv": [ + "evidence-sources.csv", + "methodology.csv" + ] + }, + { + "locale": "zh", + "page": "benchmarks/network.md", + "benchmark_ids": [ + "B-NETWORK" + ], + "role": "user-facing", + "tables": [], + "source_csv": [ + "network-comparisons.csv", + "network-provenance.csv", + "network-summary.csv" + ] + }, + { + "locale": "zh", + "page": "benchmarks/replay-fidelity.md", + "benchmark_ids": [ + "B-REPLAY" + ], + "role": "user-facing", + "tables": [ + { + "json": "benchmarks/replay-semspec.render.json", + "kind": "published", + "columns": [ + "N", + "Median ms", + "Mean ms", + "SD ms", + "Min ms", + "Max ms" + ], + "rows": 7, + "options": {} + } + ], + "source_csv": [ + "evidence-sources.csv", + "replay-fidelity.csv", + "replay-provenance.csv" + ] + }, + { + "locale": "zh", + "page": "benchmarks/startup.md", + "benchmark_ids": [ + "B-STARTUP", + "B-MACOS" + ], + "role": "user-facing", + "tables": [ + { + "json": "benchmarks/startup-timing.ready.render.json", + "kind": "published", + "columns": [ + "N", + "Ready 中位数 ms", + "Ready 均值 ms", + "Ready 标准差 ms", + "Ready 最小 ms", + "Ready 最大 ms" + ], + "rows": 8, + "options": {} + }, + { + "json": "benchmarks/startup-timing.exit.render.json", + "kind": "published", + "columns": [ + "N", + "Exit 中位数 ms", + "Exit 均值 ms", + "Exit 标准差 ms", + "Exit 最小 ms", + "Exit 最大 ms" + ], + "rows": 8, + "options": {} + } + ], + "source_csv": [ + "startup-hyperfine.csv", + "startup-semspec.csv", + "startup-stock-comparisons.csv", + "startup-stock-provenance.csv", + "startup.csv", + "startup-ready.csv", + "startup-ready-exit.csv", + "startup-timing.csv" + ] + }, + { + "locale": "zh", + "page": "benchmarks/supervision-cost.md", + "benchmark_ids": [ + "B-WORKFLOW", + "B-SUPERVISION", + "B-APPLY" + ], + "role": "user-facing", + "tables": [], + "source_csv": [ + "apply-comparisons.csv", + "apply-concurrent-conflicts.csv", + "apply-provenance.csv", + "apply-recovery.csv", + "apply.csv", + "evidence-sources.csv", + "supervision-comparisons.csv", + "supervision-cost.csv", + "supervision-provenance.csv", + "supervision-summary.csv", + "workflow-comparisons.csv", + "workflow-provenance.csv", + "workflow-summary.csv" + ] + }, + { + "locale": "zh", + "page": "benchmarks/vm-memory/index.md", + "benchmark_ids": [ + "B-VM-MEMORY" + ], + "role": "user-facing", + "tables": [], + "source_csv": [ + "memory-choices-comparisons.csv", + "memory-choices-provenance.csv", + "memory-choices.csv", + "memory-sharing-comparisons.csv", + "memory-sharing-provenance.csv", + "memory-sharing.csv" + ] + }, + { + "locale": "zh", + "page": "design/vm-startup-performance-analysis.md", + "benchmark_ids": [ + "B-STARTUP-ENG", + "B-KERNEL-ENG", + "B-STARTUP-DIAG" + ], + "role": "engineering evidence", + "tables": [], + "source_csv": [ + "kernel-capabilities.csv", + "kernel-config-capabilities.csv", + "concurrency-startup-summary.csv", + "concurrency-retest-provenance.csv" + ] + }, + { + "locale": "zh", + "page": "design/vm-memory-performance-analysis.md", + "benchmark_ids": [ + "B-MEMORY-SCALE", + "B-MEMORY-DIAG", + "B-COLD-RUNTIME-ENG" + ], + "role": "engineering evidence", + "tables": [], + "source_csv": [] + }, + { + "locale": "zh", + "page": "design/filesystem-performance-analysis.md", + "benchmark_ids": [ + "B-FS-ENG", + "B-FS-DIAG" + ], + "role": "engineering evidence", + "tables": [], + "source_csv": [ + "apply-engineering-comparisons.csv", + "apply-engineering-provenance.csv", + "apply-engineering-summary.csv", + "apply-sync-counts.csv", + "filesystem-counters.csv", + "filesystem-durability-counters.csv", + "filesystem-exec-provenance.csv", + "filesystem-exec-summary.csv", + "filesystem-executor-scratch.csv", + "filesystem-fuse-counters.csv", + "filesystem-node-cache.csv", + "host-copy-up-summary.csv", + "host-idle-dispatch-provenance.csv", + "host-idle-dispatch-summary.csv", + "host-read-summary.csv", + "immutable-lower-cache-counters.csv", + "immutable-lower-cache-summary.csv", + "journal-lock-summary.csv", + "kernel-cache-counters.csv", + "kernel-cache-summary.csv", + "vm-completion-dispatch-provenance.csv", + "vm-completion-dispatch-summary.csv", + "concurrency-filesystem-summary.csv", + "concurrency-retest-provenance.csv", + "direct-queue-filesystem-summary.csv", + "direct-queue-provenance.csv", + "readonly-dax-summary.csv", + "readonly-dax-provenance.csv", + "writeback-filesystem-summary.csv", + "writeback-provenance.csv", + "filesystem-evidence-overview.csv", + "filesystem-measurement-status.csv", + "filesystem-optimization-n30-summary.csv", + "filesystem-optimization-n30-provenance.csv" + ] + }, + { + "locale": "zh", + "page": "design/agent-task-performance-analysis.md", + "benchmark_ids": [ + "B-AGENT-TASK" + ], + "role": "engineering evidence", + "tables": [], + "source_csv": [] + }, + { + "locale": "zh", + "page": "design/cluster-performance-analysis.md", + "benchmark_ids": [ + "B-CLUSTER" + ], + "role": "engineering evidence", + "tables": [], + "source_csv": [] + }, + { + "locale": "zh", + "page": "design/cluster-benchmark-plan.md", + "benchmark_ids": [ + "B-CLUSTER" + ], + "role": "engineering evidence", + "tables": [], + "source_csv": [] + }, + { + "locale": "zh", + "page": "design/benchmark-methodology-evidence.md", + "benchmark_ids": [ + "B-METHOD" + ], + "role": "engineering evidence", + "tables": [], + "source_csv": [] + }, + { + "locale": "en", + "page": "benchmarks/agent-tasks.md", + "benchmark_ids": [ + "B-AGENT-TASK", + "B-COMPARE" + ], + "role": "user-facing", + "tables": [], + "source_csv": [ + "agent-tasks.csv", + "runtime-comparisons.csv", + "runtime-provenance.csv", + "runtime-summary.csv", + "task-scenarios.csv" + ] + }, + { + "locale": "en", + "page": "benchmarks/cluster-scalability.md", + "benchmark_ids": [ + "B-CLUSTER" + ], + "role": "user-facing", + "tables": [], + "source_csv": [ + "cluster-comparisons.csv", + "cluster-provenance.csv", + "cluster-summary.csv", + "controller-history-provenance.csv", + "controller-history-summary.csv" + ] + }, + { + "locale": "en", + "page": "benchmarks/compare-rl-infra.md", + "benchmark_ids": [ + "B-COMPARE" + ], + "role": "user-facing", + "tables": [], + "source_csv": [ + "agent-tasks.csv", + "compare-rl-infra.csv", + "density-summary.csv", + "isolation-tests.csv", + "lazy-startup-summary.csv", + "replay-fidelity.csv", + "startup.csv", + "vm-memory/memory-choices.csv" + ] + }, + { + "locale": "en", + "page": "benchmarks/density.md", + "benchmark_ids": [ + "B-DENSITY" + ], + "role": "user-facing", + "tables": [], + "source_csv": [ + "density-provenance.csv", + "density-summary.csv" + ] + }, + { + "locale": "en", + "page": "benchmarks/filesystem.md", + "benchmark_ids": [ + "B-FS-TOOLS", + "B-MACOS" + ], + "role": "user-facing", + "tables": [], + "source_csv": [ + "filesystem-cache-policy.csv", + "filesystem-comparisons.csv", + "filesystem-provenance.csv", + "filesystem.csv", + "runtime-provenance.csv", + "runtime-summary.csv" + ] + }, + { + "locale": "en", + "page": "benchmarks/index.md", + "benchmark_ids": [ + "B-OVERVIEW" + ], + "role": "user-facing", + "tables": [ + { + "json": "benchmarks/measurement-coverage.render.json", + "kind": "coverage", + "rows": 14, + "columns": [ + "Data status", + "Measured scope" + ], + "options": { + "markdown": true + } + } + ], + "source_csv": [ + "runtime-comparisons.csv", + "runtime-provenance.csv", + "runtime-summary.csv", + "workflow-comparisons.csv", + "workflow-provenance.csv", + "workflow-summary.csv", + "measurement-coverage.csv", + "filesystem.csv", + "filesystem-comparisons.csv", + "filesystem-provenance.csv" + ] + }, + { + "locale": "en", + "page": "benchmarks/isolation-tests.md", + "benchmark_ids": [ + "B-ISOLATION" + ], + "role": "user-facing", + "tables": [], + "source_csv": [ + "evidence-sources.csv", + "isolation-provenance.csv", + "isolation-tests.csv" + ] + }, + { + "locale": "en", + "page": "benchmarks/lazy-image-startup.md", + "benchmark_ids": [ + "B-LAZY-STARTUP" + ], + "role": "user-facing", + "tables": [], + "source_csv": [ + "lazy-numpy-details.csv", + "lazy-numpy-provenance.csv", + "lazy-numpy-summary.csv", + "lazy-startup-details.csv", + "lazy-startup-provenance.csv", + "lazy-startup-summary.csv" + ] + }, + { + "locale": "en", + "page": "benchmarks/methodology.md", + "benchmark_ids": [ + "B-METHOD" + ], + "role": "user-facing", + "tables": [], + "source_csv": [ + "evidence-sources.csv", + "methodology.csv" + ] + }, + { + "locale": "en", + "page": "benchmarks/network.md", + "benchmark_ids": [ + "B-NETWORK" + ], + "role": "user-facing", + "tables": [], + "source_csv": [ + "network-comparisons.csv", + "network-provenance.csv", + "network-summary.csv" + ] + }, + { + "locale": "en", + "page": "benchmarks/replay-fidelity.md", + "benchmark_ids": [ + "B-REPLAY" + ], + "role": "user-facing", + "tables": [ + { + "json": "benchmarks/replay-semspec.render.json", + "kind": "published", + "columns": [ + "N", + "Median ms", + "Mean ms", + "SD ms", + "Min ms", + "Max ms" + ], + "rows": 7, + "options": {} + } + ], + "source_csv": [ + "evidence-sources.csv", + "replay-fidelity.csv", + "replay-provenance.csv" + ] + }, + { + "locale": "en", + "page": "benchmarks/startup.md", + "benchmark_ids": [ + "B-STARTUP", + "B-MACOS" + ], + "role": "user-facing", + "tables": [ + { + "json": "benchmarks/startup-timing.ready.render.json", + "kind": "published", + "columns": [ + "N", + "Ready median ms", + "Ready mean ms", + "Ready SD ms", + "Ready min ms", + "Ready max ms" + ], + "rows": 8, + "options": {} + }, + { + "json": "benchmarks/startup-timing.exit.render.json", + "kind": "published", + "columns": [ + "N", + "Exit median ms", + "Exit mean ms", + "Exit SD ms", + "Exit min ms", + "Exit max ms" + ], + "rows": 8, + "options": {} + } + ], + "source_csv": [ + "startup-hyperfine.csv", + "startup-semspec.csv", + "startup-stock-comparisons.csv", + "startup-stock-provenance.csv", + "startup.csv", + "startup-ready.csv", + "startup-ready-exit.csv", + "startup-timing.csv" + ] + }, + { + "locale": "en", + "page": "benchmarks/supervision-cost.md", + "benchmark_ids": [ + "B-WORKFLOW", + "B-SUPERVISION", + "B-APPLY" + ], + "role": "user-facing", + "tables": [], + "source_csv": [ + "apply-comparisons.csv", + "apply-concurrent-conflicts.csv", + "apply-provenance.csv", + "apply-recovery.csv", + "apply.csv", + "evidence-sources.csv", + "supervision-comparisons.csv", + "supervision-cost.csv", + "supervision-provenance.csv", + "supervision-summary.csv", + "workflow-comparisons.csv", + "workflow-provenance.csv", + "workflow-summary.csv" + ] + }, + { + "locale": "en", + "page": "benchmarks/vm-memory/index.md", + "benchmark_ids": [ + "B-VM-MEMORY" + ], + "role": "user-facing", + "tables": [], + "source_csv": [ + "memory-choices-comparisons.csv", + "memory-choices-provenance.csv", + "memory-choices.csv", + "memory-sharing-comparisons.csv", + "memory-sharing-provenance.csv", + "memory-sharing.csv" + ] + }, + { + "locale": "en", + "page": "design/vm-startup-performance-analysis.md", + "benchmark_ids": [ + "B-STARTUP-ENG", + "B-KERNEL-ENG", + "B-STARTUP-DIAG" + ], + "role": "engineering evidence", + "tables": [], + "source_csv": [ + "kernel-capabilities.csv", + "kernel-config-capabilities.csv", + "concurrency-startup-summary.csv", + "concurrency-retest-provenance.csv" + ] + }, + { + "locale": "en", + "page": "design/vm-memory-performance-analysis.md", + "benchmark_ids": [ + "B-MEMORY-SCALE", + "B-MEMORY-DIAG", + "B-COLD-RUNTIME-ENG" + ], + "role": "engineering evidence", + "tables": [], + "source_csv": [] + }, + { + "locale": "en", + "page": "design/filesystem-performance-analysis.md", + "benchmark_ids": [ + "B-FS-ENG", + "B-FS-DIAG" + ], + "role": "engineering evidence", + "tables": [], + "source_csv": [ + "apply-engineering-comparisons.csv", + "apply-engineering-provenance.csv", + "apply-engineering-summary.csv", + "apply-sync-counts.csv", + "filesystem-counters.csv", + "filesystem-durability-counters.csv", + "filesystem-exec-provenance.csv", + "filesystem-exec-summary.csv", + "filesystem-executor-scratch.csv", + "filesystem-fuse-counters.csv", + "filesystem-node-cache.csv", + "host-copy-up-summary.csv", + "host-idle-dispatch-provenance.csv", + "host-idle-dispatch-summary.csv", + "host-read-summary.csv", + "immutable-lower-cache-counters.csv", + "immutable-lower-cache-summary.csv", + "journal-lock-summary.csv", + "kernel-cache-counters.csv", + "kernel-cache-summary.csv", + "vm-completion-dispatch-provenance.csv", + "vm-completion-dispatch-summary.csv", + "concurrency-filesystem-summary.csv", + "concurrency-retest-provenance.csv", + "direct-queue-filesystem-summary.csv", + "direct-queue-provenance.csv", + "readonly-dax-summary.csv", + "readonly-dax-provenance.csv", + "writeback-filesystem-summary.csv", + "writeback-provenance.csv", + "filesystem-evidence-overview.csv", + "filesystem-measurement-status.csv", + "filesystem-optimization-n30-summary.csv", + "filesystem-optimization-n30-provenance.csv" + ] + }, + { + "locale": "en", + "page": "design/agent-task-performance-analysis.md", + "benchmark_ids": [ + "B-AGENT-TASK" + ], + "role": "engineering evidence", + "tables": [], + "source_csv": [] + }, + { + "locale": "en", + "page": "design/cluster-performance-analysis.md", + "benchmark_ids": [ + "B-CLUSTER" + ], + "role": "engineering evidence", + "tables": [], + "source_csv": [] + }, + { + "locale": "en", + "page": "design/cluster-benchmark-plan.md", + "benchmark_ids": [ + "B-CLUSTER" + ], + "role": "engineering evidence", + "tables": [], + "source_csv": [] + }, + { + "locale": "en", + "page": "design/benchmark-methodology-evidence.md", + "benchmark_ids": [ + "B-METHOD" + ], + "role": "engineering evidence", + "tables": [], + "source_csv": [] + } + ], + "bindings": { + "replay/replay.json": { + "benchmark_id": "B-REPLAY", + "page": "benchmarks/replay-fidelity.md", + "table": "benchmarks/replay-semspec.render.json", + "csv": "replay-semspec.csv", + "show_batch": false + }, + "startup/native_timing.json": { + "kind": "startup", + "benchmark_id": "B-STARTUP", + "page": "benchmarks/startup.md", + "metric_scope": "same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded" + }, + "startup/pvisor_host_timing.json": { + "kind": "startup", + "benchmark_id": "B-STARTUP", + "page": "benchmarks/startup.md", + "metric_scope": "same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded" + }, + "startup/pvisor_staged_timing.json": { + "kind": "startup", + "benchmark_id": "B-STARTUP", + "page": "benchmarks/startup.md", + "metric_scope": "same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded" + }, + "startup/pvisor_vm_timing.json": { + "kind": "startup", + "benchmark_id": "B-STARTUP", + "page": "benchmarks/startup.md", + "metric_scope": "same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded" + }, + "startup/docker_timing.json": { + "kind": "startup", + "benchmark_id": "B-STARTUP", + "page": "benchmarks/startup.md", + "metric_scope": "same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded" + }, + "startup/fc_system_timing.json": { + "kind": "startup", + "benchmark_id": "B-STARTUP", + "page": "benchmarks/startup.md", + "metric_scope": "same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded" + }, + "startup/qemu_timing.json": { + "kind": "startup", + "benchmark_id": "B-STARTUP", + "page": "benchmarks/startup.md", + "metric_scope": "same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded" + }, + "startup/qemu_microvm_timing.json": { + "kind": "startup", + "benchmark_id": "B-STARTUP", + "page": "benchmarks/startup.md", + "metric_scope": "same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded" + } + } +} diff --git a/benchmark.new/docs.py b/benchmark.new/docs.py new file mode 100644 index 000000000..689fdfc90 --- /dev/null +++ b/benchmark.new/docs.py @@ -0,0 +1,264 @@ +"""Check dynamic documentation tables and publish audited user benchmark results. + +Fixed historical tables live in Markdown. Publication only updates +explicit live bindings, never historical tables or narrative conclusions. +""" + +import argparse +import csv +import json +import math +import re +import shlex +from pathlib import Path + +from common import METRIC_SCOPE, READY_EXIT_SCOPE, REPO, ROOT, artifact_summary, digest, save +from update import _publish_csv, display_table, validate_batch + +INVENTORY = ROOT / "docs.json" +MACRO = re.compile(r"\{\{ table\((.*?)\) \}\}") + + +def article_tables(path): + # Ignore illustrative fenced macros, like semspec render itself. + text = re.sub(r"(?ms)^(`{3,}|~{3,})[^\n]*\n.*?^\1[^\n]*$", "", path.read_text()) + return [json.loads("[" + match[1] + "]") for match in MACRO.finditer(text)] + + +def csv_table(csv_path, names, columns, scope, benchmark_id): + """Rebuild display cells from each condition's latest successful CSV row.""" + latest = {} + with Path(csv_path).open() as stream: + for row in csv.DictReader(stream): + if ( + row["metric_scope"] != scope + or row["benchmark_id"] != benchmark_id + or row["role"] != "user-facing" + ): + raise ValueError("public CSV has a different metric or benchmark scope") + latest[row["condition"]] = row + table = {} + for label, condition in names.items(): + source = latest.get(condition) + values = {} + for display, field in columns.items(): + if source is None: + values[display] = None + elif field == "Batch": + values[display] = source["batch"] + elif field == "N": + values[display] = int(source["N"]) + if values[display] != int(source["runs"]): + raise ValueError("public CSV sample count differs from configured runs") + else: + value = float(source[field]) + if not math.isfinite(value): + raise ValueError("non-finite public CSV statistic") + values[display] = f"{value:.2f}" + table[label] = values + return table + + +def check_live_tables(inventory): + from importlib.util import module_from_spec, spec_from_file_location + + spec = spec_from_file_location("startup_public_data", ROOT / "startup/subjects.py") + startup = module_from_spec(spec) + spec.loader.exec_module(startup) + for page in inventory["pages"]: + base = REPO / "docs/src" / page["locale"] + for descriptor in page["tables"]: + if descriptor["kind"] != "published": + continue + target = base / descriptor["json"] + columns = dict(zip(descriptor["columns"], descriptor["columns"])) + if page["page"] == "benchmarks/startup.md": + scope = READY_EXIT_SCOPE + names = {startup.LABELS[name]: name for name in startup.NAMES} + if page["locale"] == "zh": + for display in columns: + for zh, en in ( + ("中位数", "median"), + ("均值", "mean"), + ("标准差", "SD"), + ("最小", "min"), + ("最大", "max"), + ): + columns[display] = columns[display].replace(zh, en) + csv_path = target.with_name("startup-timing.csv") + benchmark_id = "B-STARTUP" + else: + binding = next( + ( + b + for b in inventory["bindings"].values() + if b.get("table") == descriptor["json"] + ), + None, + ) + if binding is None: + raise ValueError("published table has no auditable binding: " + str(target)) + entry = next(e for e, b in inventory["bindings"].items() if b is binding) + names = binding.get("conditions", {}) + if not names: + for label, expression in json.loads((ROOT / entry).read_text()).items(): + argv = shlex.split(expression[2:]) + names[label] = argv[argv.index("--command-name") + 1] + csv_path = target.with_name(binding["csv"]) + scope = binding.get("metric_scope", METRIC_SCOPE) + benchmark_id = binding["benchmark_id"] + expected = csv_table(csv_path, names, columns, scope, benchmark_id) + if json.loads(target.read_text()) != expected: + raise ValueError("public JSON differs from latest audited CSV: " + str(target)) + + +def check_inventory(): + inventory = json.loads(INVENTORY.read_text()) + if inventory["format"] != 1: + raise ValueError("unsupported documentation inventory") + paths = set() + for page in inventory["pages"]: + path = REPO / "docs/src" / page["locale"] / page["page"] + if path in paths: + raise ValueError("duplicate article") + paths.add(path) + tables = page["tables"] + macros = article_tables(path) + actual = [(path.parent / args[0]).resolve() for args in macros] + expected = [(REPO / "docs/src" / page["locale"] / t["json"]).resolve() for t in tables] + if sorted(actual) != sorted(expected) or len(actual) != len(set(actual)): + raise ValueError(f"table bindings differ: {path}") + for table, target in zip(tables, expected): + if table["kind"] not in ("published", "coverage"): + raise ValueError(f"fixed historical tables belong in Markdown: {target}") + data = json.loads(target.read_text()) + if not data or len(data) != table["rows"]: + raise ValueError(f"row inventory differs: {target}") + columns = table["columns"] + if any(list(row) != columns for row in data.values()): + raise ValueError(f"column inventory differs: {target}") + for row in data.values(): + for value in row.values(): + if ( + isinstance(value, (list, dict)) + or isinstance(value, str) + and value.startswith("$ ") + ): + raise ValueError(f"unresolved/non-scalar public data: {target}") + args = macros[actual.index(target)] + if (args[1] if len(args) == 2 else {}) != table["options"]: + raise ValueError(f"render options differ: {path}") + for source in page["source_csv"]: + if not (path.parent / source).is_file(): + raise ValueError(f"missing source CSV: {source}") + for locale in ("zh", "en"): + if not set((REPO / f"docs/src/{locale}/benchmarks").rglob("*.md")) <= paths: + raise ValueError("benchmark article missing from inventory") + check_live_tables(inventory) + return len(paths), sum(len(p["tables"]) for p in inventory["pages"]) + + +def publish_docs(batch): + inventory = json.loads(INVENTORY.read_text()) + metadata = json.loads((batch / "manifest.json").read_text()) + binding = inventory["bindings"].get(metadata["entry"]) + if ( + binding is None + or metadata["benchmark_id"] != binding["benchmark_id"] + or metadata["role"] != "user-facing" + ): + raise ValueError("batch has no matching user-facing documentation binding") + if metadata["metric_scope"] != binding.get("metric_scope", METRIC_SCOPE): + raise ValueError("measurement scope differs from documentation binding") + if "fixture_scope" in binding and metadata.get("fixture_scope") != binding["fixture_scope"]: + raise ValueError("workload configuration differs from documentation binding") + if binding.get("kind") == "startup": + import sys + + sys.path.insert(0, str(ROOT / "startup")) + from publish import publish_startup + + publish_startup(batch, REPO / "docs/src") + from measurement_coverage import coverage + + coverage() + return [ + REPO / "docs/src" / locale / f"benchmarks/startup-timing.{metric}.render.json" + for locale in ("zh", "en") + for metric in ("ready", "exit") + ] + manifest, resolved, results = validate_batch(batch) + table = { + name: ({"Batch": manifest["batch"]} if binding.get("show_batch", True) else {}) | row + for name, row in display_table(resolved).items() + } + targets = [] + for locale in ("zh", "en"): + page = next( + p for p in inventory["pages"] if p["locale"] == locale and p["page"] == binding["page"] + ) + target = next(t for t in page["tables"] if t["json"] == binding["table"]) + if ( + target["kind"] != "published" + or target["columns"] != list(next(iter(table.values()))) + or target["rows"] != len(table) + ): + raise ValueError("public table schema differs; create a new binding") + targets.append(REPO / "docs/src" / locale / target["json"]) + provenance = { + key: manifest.get(key) + for key in ( + "batch", + "benchmark_id", + "role", + "entry", + "recorded_at", + "runs", + "warmup", + "metric_scope", + "platform", + "kernel", + "cpu_affinity", + "source_commit", + "source_commit_scope", + "fixture_scope", + "hyperfine_version", + "exports", + ) + } + provenance["manifest_sha256"] = digest(batch / "manifest.json") + provenance["artifacts"] = artifact_summary(manifest["artifacts"]) + provenance["publisher_sha256"] = digest(Path(__file__)) + # Validate both CSV schemas and batch collisions before changing any table. + csv_paths = [path.with_name(binding["csv"]) for path in targets] + for path in csv_paths: + _publish_csv(batch, path, manifest, resolved, results, dry_run=True) + for path in csv_paths: + _publish_csv(batch, path, manifest, resolved, results) + provenance_path = ( + REPO / "docs/overrides/assets/benchmarks" / (Path(binding["csv"]).stem + ".provenance.json") + ) + save(provenance_path, provenance) + for path in targets: + save(path, table) + from measurement_coverage import coverage + + coverage() + return targets + + +if __name__ == "__main__": + p = argparse.ArgumentParser(description=__doc__) + sub = p.add_subparsers(dest="action", required=True) + sub.add_parser("check") + pub = sub.add_parser("publish") + pub.add_argument("batch", type=Path) + args = p.parse_args() + try: + if args.action == "check": + pages, tables = check_inventory() + print(f"{pages} articles, {tables} dynamic JSON tables verified") + else: + print("\n".join(str(path) for path in publish_docs(args.batch.resolve()))) + except (ValueError, KeyError, OSError) as error: + p.exit(1, f"{error}\n") diff --git a/benchmark.new/filesystem/README.md b/benchmark.new/filesystem/README.md new file mode 100644 index 000000000..14c864fa1 --- /dev/null +++ b/benchmark.new/filesystem/README.md @@ -0,0 +1,80 @@ +# 文件系统:一张表,一个 JSON + +这里回答的问题:准备好的热缓存输入上,原生 metadata/read 完整 Python 命令需要多久? +注册条目为 [B-FS-TOOLS](../../benchmark/README.md#b-fs-tools)。当前入口只覆盖两项原生探针; +七工具内部阶段、跨后端任务及工程 A/B 的范围仍由各自注册条目约束。 + +[tools.json](tools.json) 是一张两行表: + +```json +{ + "native-metadata": "$ python3 @REPO@/benchmark.new/filesystem/metadata_timing.py @BATCH@", + "native-read": "$ python3 @REPO@/benchmark.new/filesystem/read_timing.py @BATCH@" +} +``` + +```sh +just benchmark filesystem/tools +just benchmark filesystem/tools --row native-read +just benchmark filesystem/tools native-read +just benchmark filesystem/tools --output benchmark.new/filesystem/.data/measurement +``` + +命令直接输出渲染表格,并生成同名 `tools.render.json`。第二个位置参数或 `--row` 只执行选中的行, +更新该行并输出合并后的表格,其他已测行保留。行名忽略大小写和多余空格,未知行会列出可选行。 +当前可选行为 `native-metadata`、`native-read`;`pVisor VM` 属于 `startup/timing`,文件系统 VM 行尚未接入。 +默认临时目录自动清理,只保留最终表格;显式 `--output` 保留失败诊断和成功审计证据,拒绝复用目录。 + +## 输入与计时 + +两行共用一份固定输入:32 个目录、2,048 个小文件和一个 64 MiB 数据文件。 +准备时流式生成数据文件,不创建每样本副本。metadata 遍历并统计文件数和大小; +read 读取完整数据并校验 SHA256。输入大小、目录结构及所有内容在计时外核对。 + +每行固定 3 次预热、30 次正式样本。观察者用 `perf_counter_ns` 在创建子进程前开始, +正常零退出并收集输出后停表。解释器启动、管道收集和负载自身的校验包含在完整命令时间内; +输入准备、观察者启动和后续审计不计时。各条件顺序独立采样,报告样本标准差,不推导配对改善比例。 +每次输出必须是对应操作的成功 JSON;失败或超时阻止更新最终结果。 + +## 代码与证据 + +- `prepare.py`:核对固定行命令,创建共享输入并执行逐行预检。 +- `workload.py`:输入生成、完整校验与两种单次负载。 +- `fs_timing.py`:共同采样、时钟和统计;`*_timing.py` 固定操作。 +- `audit.py`:校验逐次命令、零退出、单调时钟、样本统计与 semspec 行映射。 + +保留批次含 `in.json`、`in.render.json`、`in.bench.json`、manifest 和冻结 harness, +逐次日志在 `trials//`,正式样本在 `.samples.json`。 +本地 CSV / Markdown 可通过 `update.py` 显式发布到 `.data/results/`。 +旧 results 已归档到 `.data/history/`;旧 workload CLI 与 hyperfine 模板不再参与执行。 + +Python 时钟数据与旧 hyperfine 数据的来源不同。旧数据和撤下的公开文件保留在本地 +历史归档,用户文件系统页只展示七工具对照;`docs.py publish` 会拒绝时钟范围不匹配的发布。 +需要发布新口径时应登记独立文档表与 CSV,不覆盖或拼接旧样本。 + +## 不可变 share 的 DAX 工程 A/B + +`dax_ab.py` 服务 [B-FS-ENG](../../benchmark/README.md#b-fs-eng)。这是独立的 +phase-clock 配对实验,不写入 `tools.render.json`,不代表 CLI 可写 workspace。 +准备命令冻结源码、优化二进制、固件、静态 C workload 和不可变输入,并提前保存 +30 对平衡随机顺序。两组使用相同的只读设备,仅切换 guest 的 DAX 挂载选项。 + +```sh +python3 benchmark.new/filesystem/dax_ab.py --prepare benchmark.new/filesystem/.data/dax-UNIQUE \ + --rootfs PREPARED_ROOTFS --firmware PREPARED_FIRMWARE +tools/semspec/target/debug/semspec bench benchmark.new/filesystem/.data/dax-UNIQUE/in.json +``` + +真实测量需要 Linux x86_64 的 `/dev/kvm` 和可安装 CPU/memory 限制的 user systemd。 +完整 campaign 位于同一个两核/2 GiB/零 swap cgroup,监测线程在组外的 CPU 2,3。 +CPU 0,1 通过继承的 taskset affinity 限定;可用时同时设置 cpuset,缺少控制器时 +记录该项为空。宿主 `RLIMIT_NOFILE` 为 65,536,避免 metadata inode 句柄耗尽。 +每组 3 次预热、30 个正式新 VM;各阶段完整字节验证、零退出、DAX 属性、预算与 +宿主输入不变都是成功门禁。首次访问和重复访问独立统计;重复 read/parallel 为 +4 遍的每遍均值,mmap 为 16 遍的每遍均值,metadata/small 各一遍。 +小文件 open/read/close 包含正常窗口回收;大文件 read 排除 OPEN,mmap 首次包含 +建立映射和逐页首次访问。阶段校验和准备不计时,双线程阶段包含创建和回收线程。 +失败拒绝整个批次,不删除有效慢样本。预先登记的分离分布规则、5,000 次配对 +bootstrap 和所有计数见 `protocol.json`;干扰门禁是 0.5 秒的外部构建/测试扫描, +不等于排除了全部宿主噪声。原始证据和完整逐次日志保留在批次目录, +`summary.json`/`summary.csv` 与 `in.render.json` 从同一批数据生成。 diff --git a/benchmark.new/filesystem/ab.json b/benchmark.new/filesystem/ab.json new file mode 100644 index 000000000..95834f8b2 --- /dev/null +++ b/benchmark.new/filesystem/ab.json @@ -0,0 +1,10 @@ +{ + "unprepared": { + "N": null, + "Median ms": null, + "Mean ms": null, + "SD ms": null, + "Min ms": null, + "Max ms": null + } +} diff --git a/benchmark.new/filesystem/audit.py b/benchmark.new/filesystem/audit.py new file mode 100644 index 000000000..dc0f70e6a --- /dev/null +++ b/benchmark.new/filesystem/audit.py @@ -0,0 +1,109 @@ +"""Recompute current native filesystem rows from retained trials and check inputs.""" + +import json +import shlex +from pathlib import Path + +from common import ROOT, save +from fs_timing import ( + FIELDS, + FIXTURE_SCOPE, + ROWS, + RUNS, + SCOPE, + TIMEOUT, + WARMUP, + command, + summarize, + validate_result, +) +from workload import validate_fixture + + +def audit_filesystem(batch, manifest, evidence, resolved, template): + batch = Path(batch) + if ( + manifest["entry"] != "filesystem/tools.json" + or manifest["benchmark_id"] != "B-FS-TOOLS" + or manifest["metric_scope"] != SCOPE + or manifest["fixture_scope"] != FIXTURE_SCOPE + or manifest["runs"] != RUNS + or manifest["warmup"] != WARMUP + or manifest.get("measurement_engine") != "python-perf_counter_ns" + or not template + or not set(template) <= set(ROWS) + or manifest["rows"] != {row: row for row in template} + or manifest["conditions"] != list(template) + or set(resolved) != set(template) + ): + raise ValueError("filesystem timing/fixture configuration differs") + fixture = validate_fixture(batch / "fixture") + if evidence["cells"] or len(evidence["commands"]) != len(template): + raise ValueError("filesystem evidence requires one script command per row") + phases = ( + [("preflight", 0)] + + [("warmup", i) for i in range(WARMUP)] + + [("sample", i) for i in range(RUNS)] + ) + rows, results = [], {} + for (label, expression), record in zip(template.items(), evidence["commands"]): + mode = ROWS[label] + expected_argv = ["python3", str(ROOT / "filesystem" / f"{mode}_timing.py"), str(batch)] + if ( + not isinstance(expression, str) + or not expression.startswith("$ ") + or shlex.split(expression[2:]) != expected_argv + or record.get("kind") != "script-row" + or record["command"] != expression[2:] + ): + raise ValueError("filesystem row command differs from the fixed probe") + rows.append( + dict(row=label, command=expression[2:], expression=expression[2:], columns=list(FIELDS)) + ) + expected_paths = [ + batch / "trials" / mode / f"{phase}-{index:03d}.json" for phase, index in phases + ] + if set((batch / "trials" / mode).glob("*.json")) != set(expected_paths): + raise ValueError("missing filesystem preflight/warmup/sample evidence") + previous_end = 0 + samples = [] + for path, (phase, index) in zip(expected_paths, phases): + trial = json.loads(path.read_text()) + if ( + trial["command"] != command(batch, mode) + or (trial["phase"], trial["index"]) != (phase, index) + or "error" in trial + ): + raise ValueError("filesystem trial command, order or outcome differs") + validate_result(trial, mode) + start, end, duration = (trial[key] for key in ("start_ns", "end_ns", "duration_ns")) + if ( + any(type(value) is not int for value in (start, end, duration)) + or not previous_end < start < end + or duration != end - start + or duration > TIMEOUT * 1_000_000_000 + ): + raise ValueError("invalid filesystem monotonic duration/timeout") + previous_end = end + if phase == "sample": + samples.append(duration) + result = summarize(samples) + raw = json.loads((batch / f"{mode}.samples.json").read_text()) + if raw != dict( + engine="python-perf_counter_ns", unit="nanosecond", samples=samples, result=result + ): + raise ValueError("filesystem raw samples or statistics changed") + if ( + list(resolved[label]) != list(FIELDS) + or resolved[label] != result + or record["raw"] != result + or record["value"] != result + ): + raise ValueError("filesystem resolved row differs from recorded samples") + results[label] = dict(command=shlex.join(command(batch, mode))) + if evidence.get("rows") != rows: + raise ValueError("incomplete filesystem row mapping") + save( + batch / "filesystem.audit.json", dict(status="passed", fixture=fixture, metric_scope=SCOPE) + ) + return results diff --git a/benchmark.new/filesystem/dax_ab.json b/benchmark.new/filesystem/dax_ab.json new file mode 100644 index 000000000..83378025d --- /dev/null +++ b/benchmark.new/filesystem/dax_ab.json @@ -0,0 +1,3 @@ +{ + "immutable-share-paired": "$ python3 @HARNESS@/dax_ab.py --measure @BATCH@" +} diff --git a/benchmark.new/filesystem/dax_ab.py b/benchmark.new/filesystem/dax_ab.py new file mode 100644 index 000000000..12456c1f1 --- /dev/null +++ b/benchmark.new/filesystem/dax_ab.py @@ -0,0 +1,388 @@ +#!/usr/bin/env python3 +"""Prepare and run the explicit immutable-share DAX experiment. + +Benchmark: B-FS-ENG (benchmark/README.md#b-fs-eng), role engineering A/B. +Motivation: decide whether the new immutable share's DAX path helps readers. +Conclusion sought: paired median differences and 95% CIs by access pattern. +Design: one frozen optimized subject, mount toggle only, host-hot/fresh guests, +2 CPU/2 GiB complete VM group, 3 warmups/30 balanced random pairs, full checks. +This is a phase-clock experiment, not hyperfine or the writable CLI workload. +""" +import argparse +import csv +import hashlib +import json +import os +import platform +import random +import resource +import shutil +import statistics +import subprocess +import sys +import threading +import time +import uuid +from pathlib import Path + +HERE = Path(__file__).resolve().parent +REPO = HERE.parents[1] +METRICS = [f"{kind}_{phase}" for kind in ("metadata", "small", "read", "mmap", "parallel") + for phase in ("first", "repeat")] +BUILDERS = {"cargo", "rustc", "pytest", "nextest", "hyperfine", "ninja", "make", "cargo-nextest"} + + +def save(path, value): + path.write_text(json.dumps(value, indent=2, ensure_ascii=False) + "\n") + + +def digest(path): + h = hashlib.sha256() + with path.open("rb") as f: + for block in iter(lambda: f.read(1 << 20), b""): + h.update(block) + return h.hexdigest() + + +def tree(root): + """Content/namespace/permission inventory, excluding read-induced atime.""" + result = {} + for p in sorted(root.rglob("*")): + s = p.lstat() + item = {"mode": s.st_mode, "size": s.st_size if p.is_file() else None, + "mtime_ns": s.st_mtime_ns, "ctime_ns": s.st_ctime_ns} + if p.is_symlink(): + item["link"] = os.readlink(p) + elif p.is_file(): + item["sha256"] = digest(p) + result[str(p.relative_to(root))] = item + return result + + +def pattern(seed, length=1 << 20): + out = bytearray(length) + x = seed + for i in range(0, length, 4): + x ^= (x << 13) & 0xffffffff + x ^= x >> 17 + x ^= (x << 5) & 0xffffffff + out[i:i + 4] = x.to_bytes(4, "little") + return out + + +def prepare(args): + batch = args.prepare.resolve() + if ".data" not in batch.parts: + raise ValueError("raw batch must be under .data") + batch.mkdir(parents=True, exist_ok=False) + harness = batch / "harness" + harness.mkdir() + for name in ("dax_ab.py", "dax_driver.rs", "dax_workload.c"): + shutil.copy2(HERE / name, harness / name) + (batch / "bin").mkdir() + data = batch / "immutable" + data.mkdir() + for name, seed in (("sequential", 101), ("mapped", 202), ("parallel", 303)): + block = pattern(seed) + with (data / name).open("wb") as f: + for _ in range(64): + f.write(block) + (data / "small").mkdir() + for i in range(128): + (data / "small" / f"f{i:03}").write_bytes(pattern(1000 + i, 65536)) + for i in range(2048): + d = data / "meta" / f"d{i // 64:02}" + d.mkdir(parents=True, exist_ok=True) + (d / f"f{i:04}").write_bytes(bytes(range(256))) + with (batch / "guest-build.log").open("w") as log: + subprocess.run(["cc", "-O3", "-static", "-pthread", "-Wall", "-Wextra", "-Werror", + str(harness / "dax_workload.c"), "-o", str(data / "workload")], + stdout=log, stderr=subprocess.STDOUT, check=True) + driver = batch / "driver" + driver.mkdir() + (driver / "src").mkdir() + shutil.copy2(harness / "dax_driver.rs", driver / "src/main.rs") + (driver / "Cargo.toml").write_text( + '[workspace]\n[package]\nname="pvisor-dax-ab-subject"\nversion="0.0.0"\nedition="2024"\n' + '[dependencies]\npvisor-vm={path=' + json.dumps(str(REPO / "crates/pvisor-vm")) + '}\n' + 'serde_json="1"\n[profile.release]\nopt-level=3\nlto="thin"\nstrip="symbols"\n' + 'codegen-units=8\npanic="abort"\n[profile.release.build-override]\nstrip="none"\n' + '[profile.performance]\ninherits="release"\n[profile.performance.build-override]\nstrip="none"\n' + '[patch.crates-io]\nfuser={path=' + json.dumps(str(REPO / "vendor/fuser")) + '}\n') + shutil.copy2(REPO / "Cargo.lock", driver / "Cargo.lock") + # Preserve the exact worktree bytes used for this build, including untracked + # implementation files. No checkout or workspace-wide build is performed. + source = batch / "source" + source.mkdir() + for name in ("Cargo.toml", "Cargo.lock"): + shutil.copy2(REPO / name, source / name) + shutil.copytree(REPO / "crates", source / "crates", + ignore=shutil.ignore_patterns(".data", "target", "__pycache__")) + if (REPO / ".cargo").is_dir(): + shutil.copytree(REPO / ".cargo", source / ".cargo") + shutil.copytree(REPO / "vendor/fuser", source / "vendor/fuser", + ignore=shutil.ignore_patterns(".data", "target", "__pycache__")) + source_hashes = {str(p.relative_to(source)): digest(p) for p in source.rglob("*") if p.is_file()} + with (batch / "build.log").open("w") as log: + subprocess.run(["cargo", "build", "--offline", "--profile", "performance", + "--manifest-path", str(driver / "Cargo.toml"), + "--target-dir", str(REPO / "target")], + stdout=log, stderr=subprocess.STDOUT, check=True, cwd=REPO) + for relative, expected in source_hashes.items(): + if digest(REPO / relative) != expected: + raise ValueError("build input changed: " + relative) + subject = batch / "bin/subject" + shutil.copy2(REPO / "target/performance/pvisor-dax-ab-subject", subject) + firmware = batch / "bin/libkrunfw.so" + shutil.copy2(args.firmware.resolve(), firmware) + orders = [["off", "on"] for _ in range(15)] + [["on", "off"] for _ in range(15)] + random.Random(20261011).shuffle(orders) + protocol = { + "benchmark_id": "B-FS-ENG", "role": "engineering A/B", "batch": batch.name, + "entry": "filesystem/dax_ab.json", "template_sha256": digest(HERE / "dax_ab.json"), + "question": "Does immutable-share DAX reduce verified read and mmap phase latency?", + "rootfs": str(args.rootfs.resolve()), "firmware_source": str(args.firmware.resolve()), + "subject": str(subject), "guest_cpus": 2, "guest_memory_mib": 512, + "window_bytes": 128 << 20, "cpu_affinity": [0, 1], "cpu_quota": "200000 100000", + "memory_max": 2 << 30, "swap_max": 0, "nofile_limit": 65536, + "warmups_per_arm": 3, "pairs": 30, + "orders": orders, "seed": 20261011, "bootstrap_samples": 5000, + "cache": "warm host backing; new guest per trial; first data access vs same-guest repeat", + "timer": "guest CLOCK_MONOTONIC_RAW; verification excluded; parallel includes thread create/join", + "repeat_normalization": {"read": 4, "parallel": 4, "mmap": 16, "small": 1, "metadata": 1}, + "distribution_rule": "sorted gap >=1.5x, >=3 samples on each side, cluster medians >=1.7x => separated; no overall ranking", + "failure_rule": "any validation/exit/input/artifact/budget/OOM/build-or-test interference error rejects entire batch; no slow-sample exclusion", + "artifacts": {str(p.relative_to(batch)): digest(p) for p in ( + subject, firmware, data / "workload", *harness.iterdir(), driver / "Cargo.toml", driver / "Cargo.lock")}, + "source_hashes": source_hashes, + "host": {"platform": platform.platform(), "cpuinfo": Path("/proc/cpuinfo").read_text(), + "cc": subprocess.check_output(["cc", "--version"], text=True), + "rustc": subprocess.check_output(["rustc", "-Vv"], text=True)}, + "input_before": tree(data), "rootfs_before": tree(args.rootfs.resolve()), + } + save(batch / "protocol.json", protocol) + template = json.loads((HERE / "dax_ab.json").read_text()) + save(batch / "in.json", {name: command.replace("@HARNESS@", str(harness)).replace("@BATCH@", str(batch)) + for name, command in template.items()}) + print(batch, flush=True) + + +def verify_artifacts(batch, protocol): + for relative, expected in protocol["artifacts"].items(): + if digest(batch / relative) != expected: + raise ValueError("artifact changed: " + relative) + + +def budget(): + relative = Path("/proc/self/cgroup").read_text().strip().split("::", 1)[1] + root = Path("/sys/fs/cgroup") / relative.lstrip("/") + state = {name: (root / name).read_text().strip() for name in ( + "cpu.max", "memory.max", "memory.swap.max", "memory.events")} + state["cpuset.cpus.effective"] = ((root / "cpuset.cpus.effective").read_text().strip() + if (root / "cpuset.cpus.effective").exists() else None) + state["cgroup"] = str(root) + state["affinity"] = sorted(os.sched_getaffinity(0)) + state["nofile"] = list(resource.getrlimit(resource.RLIMIT_NOFILE)) + if not (state["cpu.max"] == "200000 100000" + and state["memory.max"] == str(2 << 30) and state["memory.swap.max"] == "0" + and state["affinity"] == [0, 1] and state["nofile"] == [65536, 65536]): + raise ValueError("budget not installed: " + json.dumps(state)) + return state + + +def trial(batch, protocol, group, index, mode): + folder = batch / "trials" / group / f"{index:02}-{mode}" + folder.mkdir(parents=True) + stage = folder / "stage" + stage.mkdir() + (stage / "upper").mkdir() + (stage / "work").mkdir() + command = [protocol["subject"], protocol["rootfs"], str(batch / "bin/libkrunfw.so"), + str(batch / "immutable"), str(stage), mode] + started = time.monotonic_ns() + with (folder / "stdout.log").open("wb") as out, (folder / "stderr.log").open("wb") as err: + result = subprocess.run(command, stdin=subprocess.DEVNULL, stdout=out, stderr=err, + timeout=60, check=False) + ended = time.monotonic_ns() + lines = [line.split("DAX_BENCH_RESULT ", 1)[1] for line in (folder / "stdout.log").read_text().splitlines() + if "DAX_BENCH_RESULT " in line] + record = {"group": group, "index": index, "mode": mode, "command": command, + "host_start_ns": started, "host_end_ns": ended, "returncode": result.returncode} + save(folder / "host.json", record) + if result.returncode != 0 or len(lines) != 1: + raise ValueError("failed guest trial: " + str(folder)) + observed = json.loads(lines[0]) + if (observed["mode"] != mode or observed["dax"] != (mode == "on") + or observed["verified_bytes"] != 3 * (64 << 20) + 128 * (64 << 10) + or observed["metadata_files"] != 2048 or set(observed["metrics_ns"]) != set(METRICS) + or not all(isinstance(v, int) and v > 0 for v in observed["metrics_ns"].values())): + raise ValueError("guest validation missing: " + str(folder)) + record.update(observed) + save(folder / "result.json", record) + shutil.rmtree(stage) # the parent owns cleanup, including normal process exit + return record + + +def separated(values): + xs = sorted(values) + candidates = [(xs[i] / xs[i - 1], i) for i in range(3, len(xs) - 2)] + gap, split = max(candidates) + if gap >= 1.5 and statistics.median(xs[split:]) / statistics.median(xs[:split]) >= 1.7: + return [{"n": len(part), "median_ms": statistics.median(part)} + for part in (xs[:split], xs[split:])] + return [] + + +def analyze(batch, records): + samples = [r for r in records if r["group"] == "samples"] + rows = {} + for metric in METRICS: + off = [next(r for r in samples if r["index"] == i and r["mode"] == "off")["metrics_ns"][metric] / 1e6 for i in range(30)] + on = [next(r for r in samples if r["index"] == i and r["mode"] == "on")["metrics_ns"][metric] / 1e6 for i in range(30)] + a, b = statistics.median(off), statistics.median(on) + diffs, ratios = [], [] + rng = random.Random(20261011) + for _ in range(5000): + indices = [rng.randrange(30) for _ in range(30)] + x = statistics.median(off[i] for i in indices) + y = statistics.median(on[i] for i in indices) + diffs.append(y - x) + ratios.append((y / x - 1) * 100) + diffs.sort() + ratios.sort() + off_clusters, on_clusters = separated(off), separated(on) + split = bool(off_clusters or on_clusters) + rows[metric] = {"pairs": 30, "off_median_ms": None if off_clusters else a, + "on_median_ms": None if on_clusters else b, + "delta_ms": None if split else b - a, + "delta_percent": None if split else (b / a - 1) * 100, + "ci95_delta_ms": None if split else [diffs[124], diffs[4874]], + "ci95_percent": None if split else [ratios[124], ratios[4874]], + "off_clusters": off_clusters, "on_clusters": on_clusters, + "off_sorted_ms": sorted(off), "on_sorted_ms": sorted(on), + "verdict": "separated distribution" if split else "not detected" if diffs[124] <= 0 <= diffs[4874] + else "faster" if diffs[4874] < 0 else "slower"} + save(batch / "summary.json", rows) + with (batch / "summary.csv").open("w") as f: + writer = csv.writer(f) + writer.writerow(["batch", "metric", "pairs", "off_ms", "on_ms", "delta_percent", "ci95_low_percent", "ci95_high_percent", "verdict"]) + for name, r in rows.items(): + ci = r["ci95_percent"] or [None, None] + writer.writerow([batch.name, name, 30, r["off_median_ms"], r["on_median_ms"], r["delta_percent"], *ci, r["verdict"]]) + return rows + + +def campaign(batch): + protocol = json.loads((batch / "protocol.json").read_text()) + verify_artifacts(batch, protocol) + before = budget() + save(batch / "budget-before.json", before) + if tree(batch / "immutable") != protocol["input_before"]: + raise ValueError("immutable inputs changed") + records = [] + for i in range(3): + for mode in ("off", "on"): + records.append(trial(batch, protocol, "warmups", i, mode)) + for i, order in enumerate(protocol["orders"]): + for mode in order: + records.append(trial(batch, protocol, "samples", i, mode)) + save(batch / "records.partial.json", records) + print(f"completed pair {i + 1}/30", file=sys.stderr, flush=True) + after = budget() + save(batch / "budget-after.json", after) + def parse_events(value): + return dict(line.split() for line in value.splitlines()) + + prior_events, final_events = parse_events(before["memory.events"]), parse_events(after["memory.events"]) + if any(prior_events[key] != final_events[key] for key in ("oom", "oom_kill", "oom_group_kill")): + raise ValueError("OOM events occurred") + if tree(batch / "immutable") != protocol["input_before"] or tree(Path(protocol["rootfs"])) != protocol["rootfs_before"]: + raise ValueError("host input changed") + verify_artifacts(batch, protocol) + save(batch / "records.json", records) + rows = analyze(batch, records) + flat = {"Pairs": 30, "Measured VMs": 60, "Failed": 0, "Excluded": 0} + for name, row in rows.items(): + flat[name + " off ms"] = row["off_median_ms"] + flat[name + " on ms"] = row["on_median_ms"] + flat[name + " delta %"] = row["delta_percent"] + flat[name + " CI low %"], flat[name + " CI high %"] = row["ci95_percent"] or [None, None] + print(json.dumps(flat), flush=True) + + +def measure(batch): + if (batch / "records.json").exists(): + raise ValueError("refusing to reuse a measured batch") + stops = threading.Event() + interference = [] + ticks = [] + + def monitor(): + while not stops.is_set(): + found = [] + for process in Path("/proc").glob("[0-9]*"): + try: + name = (process / "comm").read_text().strip() + if name in BUILDERS: + found.append({"pid": int(process.name), "comm": name, + "cmdline": (process / "cmdline").read_bytes().replace(b"\0", b" ").decode(errors="replace")}) + except (FileNotFoundError, ProcessLookupError, PermissionError): + continue + tick = {"monotonic_ns": time.monotonic_ns(), "builders": found, + "loadavg": Path("/proc/loadavg").read_text().strip()} + ticks.append(tick) + interference.extend(found) + stops.wait(0.5) + + # The controller and monitor stay off the VM's two CPUs and outside its + # resource cgroup. The whole campaign and its children share one budget. + os.sched_setaffinity(0, {2, 3}) + worker = threading.Thread(target=monitor, daemon=True) + worker.start() + unit = "pvisor-dax-ab-" + uuid.uuid4().hex[:12] + command = ["systemd-run", "--user", "--quiet", "--wait", "--pipe", "--collect", "--unit", unit, + "-p", "CPUQuota=200%", "-p", "AllowedCPUs=0,1", "-p", "MemoryMax=2G", + "-p", "MemorySwapMax=0", "-p", "LimitNOFILE=65536", "taskset", "-c", "0,1", sys.executable, + str(batch / "harness/dax_ab.py"), "--campaign", str(batch)] + save(batch / "campaign-command.json", command) + try: + with (batch / "campaign.stdout.log").open("wb") as out, (batch / "campaign.stderr.log").open("wb") as err: + result = subprocess.run(command, stdout=out, stderr=err, stdin=subprocess.DEVNULL, timeout=600) + except BaseException: + subprocess.run(["systemctl", "--user", "stop", unit], check=False, timeout=20, + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + raise + finally: + stops.set() + worker.join() + save(batch / "interference.json", {"ticks": ticks, "interference": interference}) + if result.returncode != 0 or interference: + save(batch / "status.json", {"valid": False, "returncode": result.returncode, "interference": interference}) + raise ValueError("campaign invalid; inspect " + str(batch / "campaign.stderr.log")) + rows = json.loads((batch / "campaign.stdout.log").read_text()) + save(batch / "status.json", {"valid": True, "failures": 0, "excluded": 0, "pairs": 30, + "monitor_ticks": len(ticks), "interference": 0}) + print(json.dumps(rows), flush=True) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + modes = parser.add_mutually_exclusive_group(required=True) + modes.add_argument("--prepare", type=Path) + modes.add_argument("--measure", type=Path) + modes.add_argument("--campaign", type=Path) + parser.add_argument("--rootfs", type=Path) + parser.add_argument("--firmware", type=Path) + args = parser.parse_args() + if args.prepare: + if not args.rootfs or not args.firmware: + parser.error("--prepare needs --rootfs and --firmware") + prepare(args) + elif args.measure: + measure(args.measure.resolve()) + else: + campaign(args.campaign.resolve()) + + +if __name__ == "__main__": + main() diff --git a/benchmark.new/filesystem/dax_driver.rs b/benchmark.new/filesystem/dax_driver.rs new file mode 100644 index 000000000..e8c6b86e9 --- /dev/null +++ b/benchmark.new/filesystem/dax_driver.rs @@ -0,0 +1,56 @@ +//! A separately built, public-API VM subject for B-FS-ENG. +//! The parent owns the stage: normal guest shutdown exits this process. +use pvisor_vm::api::{OverlayConfig, PermissionSemantics, VmBuilder, VmConfiguration, VmRuntime}; +use std::path::PathBuf; + +fn main() -> Result<(), Box> { + let args: Vec<_> = std::env::args_os().skip(1).collect(); + if args.len() != 5 { + return Err("expected ROOTFS FIRMWARE IMMUTABLE_DIRECTORY STAGE MODE".into()); + } + let mode = args[4].to_str().ok_or("non UTF-8 mode")?; + if !matches!(mode, "off" | "on") { + return Err("mode must be off or on".into()); + } + let stage = PathBuf::from(&args[3]); + let mut vm = VmBuilder::new(2, 512)?; + vm.set_firmware_path(PathBuf::from(&args[1]))?; + vm.overlay( + "/dev/root", + OverlayConfig { + lower_dirs: vec![PathBuf::from(&args[0])], + lower_mutability: vec![], + upper_dir: stage.join("upper"), + work_dir: Some(stage.join("work")), + preimage_dir: None, + apply_target: None, + baseline_lower: None, + baseline_content_index: None, + excluded_paths: vec![], + access_policy: Default::default(), + semantics: PermissionSemantics::LinuxComplete, + }, + 0, + )?; + vm.immutable_filesystem("pvisor-dax", &PathBuf::from(&args[2]), 128 << 20)?; + // The common static workload is a virtual file in rootfs, so its own + // executable pages use the same ordinary data path in both conditions. + vm.virtual_file( + "/dev/root", + "/.dax-workload", + std::fs::read(PathBuf::from(&args[2]).join("workload"))?, + 0o500, + true, + )?; + vm.virtual_file( + "/dev/root", + "/.pvisor-guest.json", + serde_json::to_vec(&serde_json::json!({ + "argv": ["/.dax-workload", mode], "env": {}, "cwd": "/" + }))?, + 0o400, + true, + )?; + vm.run(|_| Ok(()))?; + Ok(()) +} diff --git a/benchmark.new/filesystem/dax_workload.c b/benchmark.new/filesystem/dax_workload.c new file mode 100644 index 000000000..ae58d8453 --- /dev/null +++ b/benchmark.new/filesystem/dax_workload.c @@ -0,0 +1,190 @@ +/* B-FS-ENG guest phase clocks. Validation is outside every phase timer. */ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BLOCK (1U << 20) +#define LARGE (64U << 20) +#define SMALL (64U << 10) +#define COUNT 128 +static unsigned char expected[BLOCK]; +static const char *names[12]; +static uint64_t elapsed[12]; +static unsigned metrics; + +static void require(int ok, const char *what) { + if (!ok) { fprintf(stderr, "DAX_BENCH_FAIL %s: %s\n", what, strerror(errno)); exit(2); } +} +static uint64_t now(void) { + struct timespec t; + require(clock_gettime(CLOCK_MONOTONIC_RAW, &t) == 0, "clock"); + return (uint64_t)t.tv_sec * 1000000000 + (uint64_t)t.tv_nsec; +} +static void metric(const char *name, uint64_t start, unsigned repetitions) { + names[metrics] = name; + elapsed[metrics++] = (now() - start) / repetitions; +} +static void pattern(uint32_t seed) { + uint32_t x = seed; + for (unsigned i = 0; i < BLOCK; i += 4) { + x ^= x << 13; x ^= x >> 17; x ^= x << 5; + memcpy(expected + i, &x, 4); + } +} +static void verify(const unsigned char *buf, size_t len, uint32_t seed) { + pattern(seed); + for (size_t i = 0; i < len; i += BLOCK) + require(memcmp(buf + i, expected, len - i < BLOCK ? len - i : BLOCK) == 0, "content"); +} +static int opened(const char *name) { + char path[128]; + snprintf(path, sizeof(path), "/tmp/data/%s", name); + int fd = open(path, O_RDONLY | O_CLOEXEC); + require(fd >= 0, "open"); + return fd; +} +static void read_range(int fd, unsigned char *buf, size_t offset, size_t len) { + for (size_t i = 0; i < len; i += SMALL) + require(pread(fd, buf + i, SMALL, (off_t)(offset + i)) == SMALL, "pread"); +} +static unsigned char *buffer(size_t n) { + unsigned char *p = malloc(n); + require(p != NULL, "malloc"); + memset(p, 0, n); /* pre-fault destination outside the timer */ + volatile unsigned char *resident = p; + for (size_t i = 0; i < n; i += 4096) resident[i] = 0xa5; + resident[n - 1] = 0xa5; + __asm__ volatile("" : : "r"(p) : "memory"); + return p; +} +static void sequential(void) { + int fd = opened("sequential"); + unsigned char *buf = buffer(LARGE); + uint64_t t = now(); + read_range(fd, buf, 0, LARGE); + metric("read_first", t, 1); + verify(buf, LARGE, 101); + t = now(); + for (unsigned i = 0; i < 4; ++i) read_range(fd, buf, 0, LARGE); + metric("read_repeat", t, 4); + verify(buf, LARGE, 101); + require(close(fd) == 0, "close"); free(buf); +} +static uint64_t scan(volatile unsigned char *p) { + uint64_t sum = 0; + for (size_t i = 0; i < LARGE; i += 4096) sum += p[i]; + return sum; +} +static void mapped(void) { + int fd = opened("mapped"); + uint64_t t = now(); + unsigned char *p = mmap(NULL, LARGE, PROT_READ, MAP_PRIVATE, fd, 0); + require(p != MAP_FAILED, "mmap"); + uint64_t sum = scan(p); + metric("mmap_first", t, 1); + verify(p, LARGE, 202); + uint64_t expected_sum = 0; + for (size_t i = 0; i < LARGE; i += 4096) expected_sum += expected[i % BLOCK]; + require(sum == expected_sum, "mmap first sum"); + sum = 0; t = now(); + for (unsigned i = 0; i < 16; ++i) sum += scan(p); + metric("mmap_repeat", t, 16); + require(sum == expected_sum * 16, "mmap repeat sum"); + require(munmap(p, LARGE) == 0 && close(fd) == 0, "unmap/close"); +} +struct reader { int fd; unsigned char *buf; size_t offset; }; +static void *reader(void *input) { + struct reader *r = input; + read_range(r->fd, r->buf, r->offset, LARGE / 2); + return NULL; +} +static void parallel_pass(int fd, unsigned char *buf) { + pthread_t workers[2]; + struct reader args[2] = {{fd, buf, 0}, {fd, buf + LARGE / 2, LARGE / 2}}; + for (unsigned i = 0; i < 2; ++i) + require(pthread_create(&workers[i], NULL, reader, &args[i]) == 0, "thread create"); + for (unsigned i = 0; i < 2; ++i) + require(pthread_join(workers[i], NULL) == 0, "thread join"); +} +static void parallel(void) { + int fd = opened("parallel"); + unsigned char *buf = buffer(LARGE); + uint64_t t = now(); + parallel_pass(fd, buf); + metric("parallel_first", t, 1); + verify(buf, LARGE, 303); + t = now(); + for (unsigned i = 0; i < 4; ++i) parallel_pass(fd, buf); + metric("parallel_repeat", t, 4); + verify(buf, LARGE, 303); + require(close(fd) == 0, "close"); free(buf); +} +static void small_pass(unsigned char *buf) { + for (unsigned i = 0; i < COUNT; ++i) { + char name[64]; snprintf(name, sizeof(name), "small/f%03u", i); + int fd = opened(name); + read_range(fd, buf + i * SMALL, 0, SMALL); + require(close(fd) == 0, "small close"); + } +} +static void small_verify(unsigned char *buf) { + for (unsigned i = 0; i < COUNT; ++i) verify(buf + i * SMALL, SMALL, 1000 + i); +} +static void small(void) { + unsigned char *buf = buffer(COUNT * SMALL); + uint64_t t = now(); small_pass(buf); + metric("small_first", t, 1); small_verify(buf); + t = now(); small_pass(buf); + metric("small_repeat", t, 1); small_verify(buf); + free(buf); +} +static void metadata_pass(void) { + uint64_t size = 0; + for (unsigned i = 0; i < 2048; ++i) { + char path[128]; struct stat s; + snprintf(path, sizeof(path), "/tmp/data/meta/d%02u/f%04u", i / 64, i); + require(stat(path, &s) == 0 && S_ISREG(s.st_mode), "metadata"); + size += (uint64_t)s.st_size; + } + require(size == 2048U * 256, "metadata size"); +} +static void metadata(void) { + uint64_t t = now(); metadata_pass(); metric("metadata_first", t, 1); + t = now(); metadata_pass(); metric("metadata_repeat", t, 1); +} +int main(int argc, char **argv) { + require(argc == 2 && (!strcmp(argv[1], "off") || !strcmp(argv[1], "on")), "mode"); + int dax = !strcmp(argv[1], "on"); + require(mount("tmpfs", "/tmp", "tmpfs", 0, "size=16m") == 0, "tmpfs"); + require(mkdir("/tmp/data", 0700) == 0, "mountpoint"); + require(mount("pvisor-dax", "/tmp/data", "virtiofs", MS_RDONLY, + dax ? "dax=inode" : "dax=never") == 0, "virtiofs mount"); + struct statx s; + require(statx(AT_FDCWD, "/tmp/data/sequential", 0, STATX_BASIC_STATS, &s) == 0, "statx"); + require((s.stx_attributes_mask & STATX_ATTR_DAX) != 0 && + !!(s.stx_attributes & STATX_ATTR_DAX) == dax, "DAX selection"); + struct utsname kernel; require(uname(&kernel) == 0, "uname"); + /* Independent files avoid priming the later class through the earlier one. + A 128 MiB DAX window deliberately includes normal mapping reclamation. */ + metadata(); small(); sequential(); mapped(); parallel(); + require(umount("/tmp/data") == 0, "unmount"); + printf("DAX_BENCH_RESULT {\"mode\":\"%s\",\"dax\":%s,\"guest_kernel\":\"%s\",\"metrics_ns\":{", + argv[1], dax ? "true" : "false", kernel.release); + for (unsigned i = 0; i < metrics; ++i) + printf("%s\"%s\":%" PRIu64, i ? "," : "", names[i], elapsed[i]); + printf("},\"verified_bytes\":%u,\"metadata_files\":2048}\n", 3 * LARGE + COUNT * SMALL); + fflush(stdout); + return 0; +} diff --git a/benchmark.new/filesystem/diagnostic.json b/benchmark.new/filesystem/diagnostic.json new file mode 100644 index 000000000..95834f8b2 --- /dev/null +++ b/benchmark.new/filesystem/diagnostic.json @@ -0,0 +1,10 @@ +{ + "unprepared": { + "N": null, + "Median ms": null, + "Mean ms": null, + "SD ms": null, + "Min ms": null, + "Max ms": null + } +} diff --git a/benchmark.new/filesystem/fs_timing.py b/benchmark.new/filesystem/fs_timing.py new file mode 100644 index 000000000..3f6d432dd --- /dev/null +++ b/benchmark.new/filesystem/fs_timing.py @@ -0,0 +1,140 @@ +"""B-FS-TOOLS: complete native Python commands, 3 warmups and 30 samples. + +Motivation: quantify native metadata/read command costs on a prepared warm tree. +Design: 2048 files / 32 directories, one shared 64 MiB payload, perf_counter_ns +around process launch and normal exit. Interpreter, pipes and subject validation +are included; fixture preparation, observer startup and later audits are excluded. +These probes are distinct from seven-tool internal timings and cross-backend tasks. +""" + +import argparse +import json +import statistics +import subprocess +import sys +import time +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) +from common import FILESYSTEM_SCOPE as SCOPE +from common import ROOT, digest, save +from workload import MODES, validate_fixture + +RUNS = 30 +WARMUP = 3 +TIMEOUT = 30 +FIXTURE_SCOPE = "native Python whole-command probes; 2048 files in 32 directories; 64 MiB read with SHA256; warm cache; distinct from seven-tool internal timings" +FIELDS = ("N", "Median ms", "Mean ms", "SD ms", "Min ms", "Max ms") +ROWS = {"native-" + mode: mode for mode in MODES} + + +def command(batch, mode): + return [sys.executable, str(ROOT / "filesystem/workload.py"), mode, str(batch / "fixture")] + + +def summarize(samples): + if len(samples) < 2 or any(type(n) is not int or n <= 0 for n in samples): + raise ValueError("at least two positive integer durations required") + values = [n / 1_000_000 for n in samples] + return dict( + zip( + FIELDS, + ( + len(values), + statistics.median(values), + statistics.mean(values), + statistics.stdev(values), + min(values), + max(values), + ), + ) + ) + + +def validate_result(record, mode): + if record["exit_code"] != 0 or json.loads(record["stdout"]) != dict( + workload=mode, correctness="passed" + ): + raise ValueError("filesystem subject failed or returned incorrect output") + + +def run_trial(batch, mode, phase, index): + argv = command(batch, mode) + record = dict( + command=argv, + phase=phase, + index=index, + start_ns=None, + end_ns=None, + duration_ns=None, + exit_code=None, + stdout="", + stderr="", + ) + log = batch / "trials" / mode / f"{phase}-{index:03d}.json" + try: + record["start_ns"] = time.perf_counter_ns() + result = subprocess.run(argv, cwd=batch, capture_output=True, text=True, timeout=TIMEOUT) + record["end_ns"] = time.perf_counter_ns() + record.update( + duration_ns=record["end_ns"] - record["start_ns"], + exit_code=result.returncode, + stdout=result.stdout, + stderr=result.stderr, + ) + validate_result(record, mode) + return record["duration_ns"] + except subprocess.TimeoutExpired as error: + record.update( + error=str(error), + stdout=(error.stdout or b"").decode(errors="replace"), + stderr=(error.stderr or b"").decode(errors="replace"), + ) + raise + except BaseException as error: + record["error"] = str(error) + raise + finally: + save(log, record) + + +def measure(batch, mode): + manifest = json.loads((batch / "manifest.json").read_text()) + if ( + manifest["status"] != "prepared" + or manifest["metric_scope"] != SCOPE + or "native-" + mode not in manifest["rows"] + ): + raise ValueError("expected a freshly prepared filesystem row") + if list((batch / "trials" / mode).glob("*.json")) != [ + batch / "trials" / mode / "preflight-000.json" + ]: + raise ValueError("do not reuse a measured filesystem row") + for path, expected in manifest["artifacts"].items(): + if digest(path) != expected: + raise ValueError("prepared artifact changed: " + path) + validate_fixture(batch / "fixture") + for index in range(WARMUP): + run_trial(batch, mode, "warmup", index) + samples = [run_trial(batch, mode, "sample", index) for index in range(RUNS)] + validate_fixture(batch / "fixture") + result = summarize(samples) + save( + batch / f"{mode}.samples.json", + dict(engine="python-perf_counter_ns", unit="nanosecond", samples=samples, result=result), + ) + return result + + +def main(mode): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("batch", type=Path) + args = parser.parse_args() + batch = args.batch.resolve() + try: + print(json.dumps(measure(batch, mode))) + except (ValueError, OSError, subprocess.SubprocessError) as error: + manifest = json.loads((batch / "manifest.json").read_text()) + manifest.update(status="failed", error=str(error)) + save(batch / "manifest.json", manifest) + parser.exit(1, f"{error}\n") diff --git a/benchmark.new/filesystem/lazy_image.json b/benchmark.new/filesystem/lazy_image.json new file mode 100644 index 000000000..95834f8b2 --- /dev/null +++ b/benchmark.new/filesystem/lazy_image.json @@ -0,0 +1,10 @@ +{ + "unprepared": { + "N": null, + "Median ms": null, + "Mean ms": null, + "SD ms": null, + "Min ms": null, + "Max ms": null + } +} diff --git a/benchmark.new/filesystem/metadata_timing.py b/benchmark.new/filesystem/metadata_timing.py new file mode 100644 index 000000000..b7aac78b3 --- /dev/null +++ b/benchmark.new/filesystem/metadata_timing.py @@ -0,0 +1,6 @@ +"""B-FS-TOOLS: native metadata whole-command samples on the prepared tree.""" + +from fs_timing import main + +if __name__ == "__main__": + main("metadata") diff --git a/benchmark.new/filesystem/prepare.py b/benchmark.new/filesystem/prepare.py new file mode 100644 index 000000000..e4facdf4e --- /dev/null +++ b/benchmark.new/filesystem/prepare.py @@ -0,0 +1,83 @@ +"""B-FS-TOOLS: prepare the current native metadata/read table and its shared fixture.""" + +import argparse +import json +import subprocess +import sys +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) +from common import ROOT, create_batch, materialize, save +from fs_timing import FIXTURE_SCOPE, ROWS, RUNS, SCOPE, WARMUP, run_trial +from workload import prepare_fixture, validate_fixture + + +def validate_template(template): + table = json.loads(Path(template).read_text()) + expected = { + label: f"$ python3 @REPO@/benchmark.new/filesystem/{mode}_timing.py @BATCH@" + for label, mode in ROWS.items() + } + if table != expected or list(table) != list(expected): + raise ValueError("filesystem table requires the fixed metadata/read timing rows") + return table + + +def prepare(template, output=None, row=None): + table = validate_template(template) + if row is not None: + if row not in table: + raise ValueError("unknown filesystem row") + table = {row: table[row]} + batch = create_batch(template, output) + try: + prepare_fixture(batch / "fixture") + validate_fixture(batch / "fixture") + materialize( + template, + batch, + artifacts=[batch / "fixture/fixture.json"], + cwd=batch, + helpers=[ + Path(__file__), + *[ + ROOT / "filesystem" / name + for name in ( + "fs_timing.py", + "audit.py", + "workload.py", + "metadata_timing.py", + "read_timing.py", + ) + ], + ], + row_config=dict( + rows={name: name for name in table}, runs=RUNS, warmup=WARMUP, metric_scope=SCOPE + ), + ) + manifest = json.loads((batch / "manifest.json").read_text()) + manifest["fixture_scope"] = FIXTURE_SCOPE + save(batch / "manifest.json", manifest) + for label in table: + run_trial(batch, ROWS[label], "preflight", 0) + return batch + except BaseException as error: + save(batch / "failure.json", dict(error=str(error))) + if (batch / "manifest.json").exists(): + manifest = json.loads((batch / "manifest.json").read_text()) + manifest.update(status="failed", error=str(error)) + save(batch / "manifest.json", manifest) + raise + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("template", type=Path) + parser.add_argument("--output", type=Path) + parser.add_argument("--row") + args = parser.parse_args() + try: + print(prepare(args.template, args.output, args.row)) + except (ValueError, OSError, subprocess.SubprocessError) as error: + parser.exit(1, f"{error}\n") diff --git a/benchmark.new/filesystem/read_timing.py b/benchmark.new/filesystem/read_timing.py new file mode 100644 index 000000000..96b3eac8a --- /dev/null +++ b/benchmark.new/filesystem/read_timing.py @@ -0,0 +1,6 @@ +"""B-FS-TOOLS: native read-and-SHA256 whole-command samples on the prepared payload.""" + +from fs_timing import main + +if __name__ == "__main__": + main("read") diff --git a/benchmark.new/filesystem/tools.json b/benchmark.new/filesystem/tools.json new file mode 100644 index 000000000..1c865722d --- /dev/null +++ b/benchmark.new/filesystem/tools.json @@ -0,0 +1,4 @@ +{ + "native-metadata": "$ python3 @REPO@/benchmark.new/filesystem/metadata_timing.py @BATCH@", + "native-read": "$ python3 @REPO@/benchmark.new/filesystem/read_timing.py @BATCH@" +} diff --git a/benchmark.new/filesystem/tools.render.json b/benchmark.new/filesystem/tools.render.json new file mode 100644 index 000000000..70a9a8577 --- /dev/null +++ b/benchmark.new/filesystem/tools.render.json @@ -0,0 +1,18 @@ +{ + "native-metadata": { + "N": 30, + "Median ms": "27.30", + "Mean ms": "27.43", + "SD ms": "0.81", + "Min ms": "25.76", + "Max ms": "29.15" + }, + "native-read": { + "N": 30, + "Median ms": "55.44", + "Mean ms": "55.72", + "SD ms": "0.99", + "Min ms": "54.27", + "Max ms": "58.04" + } +} diff --git a/benchmark.new/filesystem/workload.py b/benchmark.new/filesystem/workload.py new file mode 100644 index 000000000..e8e1cffa7 --- /dev/null +++ b/benchmark.new/filesystem/workload.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 +"""Deterministic native metadata/read subjects and their shared input fixture.""" + +import argparse +import hashlib +import json +from pathlib import Path + +FILES = 2048 +DIRECTORIES = 32 +PAYLOAD_MIB = 64 +PATTERN = b"pvisor-fixture-needle\n" +CHUNK = bytes(range(256)) * 4096 +MODES = ("metadata", "read") + + +def expected_fixture(): + checksum = hashlib.sha256() + for _ in range(PAYLOAD_MIB): + checksum.update(CHUNK) + return dict( + directories=DIRECTORIES, + files=FILES, + tree_bytes=FILES * len(PATTERN), + payload_bytes=PAYLOAD_MIB * len(CHUNK), + payload_sha256=checksum.hexdigest(), + ) + + +def prepare_fixture(root): + """Create one fixed tree and payload, without allocating a full payload in RAM.""" + root.mkdir(parents=True, exist_ok=False) + tree = root / "tree" + tree.mkdir() + for directory in range(DIRECTORIES): + (tree / f"d{directory:02d}").mkdir() + for index in range(FILES): + (tree / f"d{index % DIRECTORIES:02d}" / f"f{index:06d}.txt").write_bytes(PATTERN) + with (root / "payload.bin").open("wb") as stream: + for _ in range(PAYLOAD_MIB): + stream.write(CHUNK) + (root / "fixture.json").write_text(json.dumps(expected_fixture()) + "\n") + + +def validate_fixture(root): + """Check the exact inventory, file contents and payload against fixed inputs.""" + if {path.name for path in root.iterdir()} != {"tree", "payload.bin", "fixture.json"}: + raise ValueError("filesystem fixture inventory changed") + if (root / "fixture.json").is_symlink() or not (root / "fixture.json").is_file(): + raise ValueError("filesystem fixture configuration changed") + expected = expected_fixture() + if json.loads((root / "fixture.json").read_text()) != expected: + raise ValueError("filesystem fixture configuration changed") + tree = root / "tree" + names = {f"d{i:02d}" for i in range(DIRECTORIES)} | { + f"d{i % DIRECTORIES:02d}/f{i:06d}.txt" for i in range(FILES) + } + paths = list(tree.rglob("*")) + if ( + root.is_symlink() + or tree.is_symlink() + or {p.relative_to(tree).as_posix() for p in paths} != names + ): + raise ValueError("filesystem tree inventory changed") + for path in paths: + if path.is_symlink(): + raise ValueError("filesystem tree contains a symlink") + if "/" not in path.relative_to(tree).as_posix(): + if not path.is_dir(): + raise ValueError("filesystem directory changed") + elif not path.is_file() or path.read_bytes() != PATTERN: + raise ValueError("filesystem tree content changed") + payload = root / "payload.bin" + checksum = hashlib.sha256() + if ( + payload.is_symlink() + or not payload.is_file() + or payload.stat().st_size != expected["payload_bytes"] + ): + raise ValueError("filesystem payload changed") + with payload.open("rb") as stream: + for chunk in iter(lambda: stream.read(len(CHUNK)), b""): + checksum.update(chunk) + if checksum.hexdigest() != expected["payload_sha256"]: + raise ValueError("filesystem payload changed") + return expected + + +def execute(mode, root): + """Run one whole-command probe, including its result validation.""" + fixture = json.loads((root / "fixture.json").read_text()) + if mode == "metadata": + paths = list((root / "tree").rglob("*.txt")) + if ( + len(list((root / "tree").iterdir())) != fixture["directories"] + or len(paths) != fixture["files"] + or sum(path.stat().st_size for path in paths) != fixture["tree_bytes"] + ): + raise ValueError("metadata differs from fixture") + elif mode == "read": + payload = (root / "payload.bin").read_bytes() + if ( + len(payload) != fixture["payload_bytes"] + or hashlib.sha256(payload).hexdigest() != fixture["payload_sha256"] + ): + raise ValueError("payload differs from fixture") + else: + raise ValueError("unknown filesystem probe") + return dict(workload=mode, correctness="passed") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("mode", choices=MODES) + parser.add_argument("fixture", type=Path) + args = parser.parse_args() + print(json.dumps(execute(args.mode, args.fixture))) diff --git a/benchmark.new/isolation/isolation.json b/benchmark.new/isolation/isolation.json new file mode 100644 index 000000000..95834f8b2 --- /dev/null +++ b/benchmark.new/isolation/isolation.json @@ -0,0 +1,10 @@ +{ + "unprepared": { + "N": null, + "Median ms": null, + "Mean ms": null, + "SD ms": null, + "Min ms": null, + "Max ms": null + } +} diff --git a/benchmark.new/measurement_coverage.py b/benchmark.new/measurement_coverage.py new file mode 100644 index 000000000..2e867f2be --- /dev/null +++ b/benchmark.new/measurement_coverage.py @@ -0,0 +1,143 @@ +"""Publish the measured scope of every user page without inventing missing results.""" + +import csv +import io +import json +from pathlib import Path + +from common import REPO, ROOT, save +from update import replace + + +def filesystem_publication(): + """Recognize an accepted reference cohort, separate from row-clock bindings.""" + import hashlib + + base = REPO / "docs/src/zh/benchmarks" + receipt = REPO / "docs/overrides/assets/benchmarks/filesystem-publication-audit.json" + if not receipt.exists(): + return None + evidence = json.loads(receipt.read_text()) + if (evidence.get("state") != "passed" or evidence.get("benchmark_id") != "B-FS-TOOLS" + or evidence.get("formal_rows") != 240 or evidence.get("samples_per_backend") != 30): + raise ValueError("invalid accepted filesystem publication receipt") + for name in ("filesystem.csv", "filesystem-comparisons.csv", "filesystem-provenance.csv"): + if hashlib.sha256((base / name).read_bytes()).hexdigest() != evidence["csv_sha256"][name]: + raise ValueError("filesystem export changed after publication audit: " + name) + return evidence + + +def coverage(): + inventory = json.loads((ROOT / "docs.json").read_text()) + entries = list(csv.DictReader((ROOT / "registry.csv").open())) + filesystem_evidence = filesystem_publication() + rows = [] + for page in inventory["pages"]: + if page["locale"] != "zh" or not page["page"].startswith("benchmarks/"): + continue + bindings = { + entry: binding + for entry, binding in inventory["bindings"].items() + if binding["page"] == page["page"] + } + missing = [ + entry["entry"] + for entry in entries + if entry["role"] == "user-facing" + and entry["benchmark_id"] in page["benchmark_ids"] + and entry["status"] == "requires-single-condition-command" + ] + current = [] + for entry, binding in bindings.items(): + path = ( + REPO + / "docs/src/zh" + / (binding.get("table") or "benchmarks/startup-timing.ready.render.json") + ) + if not path.exists(): + continue + data = json.loads(path.read_text()) + if binding.get("kind") == "startup": + import sys + + sys.path.insert(0, str(ROOT / "startup")) + from publish import LABELS + + name = Path(entry).stem.removesuffix("_timing").replace("_", "-") + measured = data.get(LABELS[name], {}).get("N") is not None + else: + measured = any(row.get("N") is not None for row in data.values()) + if measured: + current.append(entry) + full_filesystem = page["page"] == "benchmarks/filesystem.md" and filesystem_evidence is not None + if full_filesystem: + current = ["B-FS-TOOLS accepted reference cohort " + filesystem_evidence["batch"]] + rows.append( + dict( + page=page["page"], + available_bindings=";".join(current), + missing_metric_adapters=";".join(missing), + coverage="full" if full_filesystem else "partial" if current else "historical", + ) + ) + output = io.StringIO() + writer = csv.DictWriter(output, fieldnames=list(rows[0])) + writer.writeheader() + writer.writerows(rows) + for locale in ("zh", "en"): + base = REPO / "docs/src" / locale / "benchmarks" + replace(base / "measurement-coverage.csv", output.getvalue()) + table = {} + for row in rows: + page = row["page"].removeprefix("benchmarks/") + if page == "startup.md": + description = ( + "同次启动 Ready / Exit;完整命令另列" + if locale == "zh" + else "Same-launch Ready / Exit; whole-command time is separate" + ) + elif page == "replay-fidelity.md": + description = ( + "固定 task 0 的完整 CLI 时间;20 任务历史分布另列" + if locale == "zh" + else "Whole CLI time for fixed task 0; historical 20-task distributions are separate" + ) + elif page == "filesystem.md": + description = ( + ("八后端七工具内部阶段与启动至退出;原生完整命令探针另列" if locale == "zh" + else "Eight-backend seven-tool phases and launch through exit; native whole-command probes are separate") + if row["coverage"] == "full" else + ("原生 metadata/read 完整命令;七工具跨后端任务未复测" if locale == "zh" + else "Native metadata/read whole commands; seven-tool cross-backend task not rerun") + ) + else: + description = ( + "保留原始指标与来源;暂无匹配的新测量" + if locale == "zh" + else "Original metrics and sources retained; no matching new measurements" + ) + label = "总览" if locale == "zh" else "Overview" + name = ( + label + if page == "index.md" + else f"[{page.removesuffix('/index.md').removesuffix('.md')}]({page})" + ) + table[name] = { + "数据状态" if locale == "zh" else "Data status": ( + "完整复测" if locale == "zh" else "Fully rerun" + ) if row["coverage"] == "full" else ( + "部分复测" if locale == "zh" else "Partially rerun" + ) + if row["coverage"] == "partial" + else ("历史数据" if locale == "zh" else "Historical data"), + "实测范围" if locale == "zh" else "Measured scope": description, + } + save(base / "measurement-coverage.render.json", table) + return rows + + +if __name__ == "__main__": + rows = coverage() + print( + f"{len(rows)} user pages: {sum(row['coverage'] == 'partial' for row in rows)} with new matched measurements" + ) diff --git a/benchmark.new/memory/cold_runtime.json b/benchmark.new/memory/cold_runtime.json new file mode 100644 index 000000000..95834f8b2 --- /dev/null +++ b/benchmark.new/memory/cold_runtime.json @@ -0,0 +1,10 @@ +{ + "unprepared": { + "N": null, + "Median ms": null, + "Mean ms": null, + "SD ms": null, + "Min ms": null, + "Max ms": null + } +} diff --git a/benchmark.new/memory/cold_storage.json b/benchmark.new/memory/cold_storage.json new file mode 100644 index 000000000..95834f8b2 --- /dev/null +++ b/benchmark.new/memory/cold_storage.json @@ -0,0 +1,10 @@ +{ + "unprepared": { + "N": null, + "Median ms": null, + "Mean ms": null, + "SD ms": null, + "Min ms": null, + "Max ms": null + } +} diff --git a/benchmark.new/memory/diagnostic.json b/benchmark.new/memory/diagnostic.json new file mode 100644 index 000000000..95834f8b2 --- /dev/null +++ b/benchmark.new/memory/diagnostic.json @@ -0,0 +1,10 @@ +{ + "unprepared": { + "N": null, + "Median ms": null, + "Mean ms": null, + "SD ms": null, + "Min ms": null, + "Max ms": null + } +} diff --git a/benchmark.new/memory/memory.json b/benchmark.new/memory/memory.json new file mode 100644 index 000000000..95834f8b2 --- /dev/null +++ b/benchmark.new/memory/memory.json @@ -0,0 +1,10 @@ +{ + "unprepared": { + "N": null, + "Median ms": null, + "Mean ms": null, + "SD ms": null, + "Min ms": null, + "Max ms": null + } +} diff --git a/benchmark.new/memory/scale.json b/benchmark.new/memory/scale.json new file mode 100644 index 000000000..95834f8b2 --- /dev/null +++ b/benchmark.new/memory/scale.json @@ -0,0 +1,10 @@ +{ + "unprepared": { + "N": null, + "Median ms": null, + "Mean ms": null, + "SD ms": null, + "Min ms": null, + "Max ms": null + } +} diff --git a/benchmark.new/memory/vcpu_idle.json b/benchmark.new/memory/vcpu_idle.json new file mode 100644 index 000000000..95834f8b2 --- /dev/null +++ b/benchmark.new/memory/vcpu_idle.json @@ -0,0 +1,10 @@ +{ + "unprepared": { + "N": null, + "Median ms": null, + "Mean ms": null, + "SD ms": null, + "Min ms": null, + "Max ms": null + } +} diff --git a/benchmark.new/network/network.json b/benchmark.new/network/network.json new file mode 100644 index 000000000..95834f8b2 --- /dev/null +++ b/benchmark.new/network/network.json @@ -0,0 +1,10 @@ +{ + "unprepared": { + "N": null, + "Median ms": null, + "Mean ms": null, + "SD ms": null, + "Min ms": null, + "Max ms": null + } +} diff --git a/benchmark.new/process/check.py b/benchmark.new/process/check.py new file mode 100644 index 000000000..b2e142f78 --- /dev/null +++ b/benchmark.new/process/check.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +"""Validate complete Run Bundles outside hyperfine's measured command.""" + +import json +import sys +from pathlib import Path + + +def validate(path): + run = json.loads(path.read_text())["run"] + if run["state"] != "completed" or run["exit_code"] != 0: + raise ValueError("Run Bundle must describe successful completion") + + +if __name__ == "__main__": + mode, value = sys.argv[1:] + path = Path(value) + if mode == "bundle": + validate(path) + elif mode == "new-bundle": + # A failed command never reaches this hook. Track every completed Run, + # rather than silently revalidating an old Bundle when no new Run exists. + current = {str(p) for p in path.glob("run-*/run-bundle.json")} + previous_path = path / ".checked.json" + previous = set(json.loads(previous_path.read_text())) if previous_path.exists() else set() + fresh = current - previous + if len(fresh) != 1: + raise ValueError("expected exactly one new completed Run Bundle") + for name in fresh: + validate(Path(name)) + previous_path.write_text(json.dumps(sorted(current))) + else: + raise ValueError("unknown validation mode") diff --git a/benchmark.new/process/compare.py b/benchmark.new/process/compare.py new file mode 100644 index 000000000..e248715e8 --- /dev/null +++ b/benchmark.new/process/compare.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +"""CI threshold gate using validated semspec cells and hyperfine's median.""" + +import argparse +import importlib.util +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT)) +_spec = importlib.util.spec_from_file_location("benchmark_update", ROOT / "update.py") +UPDATE = importlib.util.module_from_spec(_spec) +_spec.loader.exec_module(UPDATE) + + +def read(path): + path = Path(path).resolve() + directory = path.parent + if path != directory / "in.bench.json": + raise ValueError("expected the batch in.bench.json evidence file") + manifest, table, results = UPDATE.validate_batch(directory) + if manifest["benchmark_id"] != "B-PROCESS" or set(results) != { + "native", + "host-run", + "status", + "review", + }: + raise ValueError("expected a successful complete B-PROCESS semspec batch") + return { + name: result["summary"]["time_wall_clock"]["median"] for name, result in results.items() + }, manifest + + +def compare(candidate, baseline, threshold): + rows, manifest = read(candidate) + old = None + if baseline: + old, previous = read(baseline) + for key in ( + "runs", + "warmup", + "hyperfine_version", + "platform", + "cpu_affinity", + "conditions", + "metric_scope", + ): + if manifest[key] != previous[key]: + raise ValueError("incompatible measurement configuration: " + key) + # Both cohorts must have used the exact same candidate harness/templates. + if manifest["harness"] != previous["harness"]: + raise ValueError("candidate and baseline use different benchmark harnesses") + lines = [ + "# Process benchmark threshold gate", + "", + "Hyperfine median direct-command times in seconds. Separate cohorts; this threshold is not a paired improvement claim.", + "", + "| Condition | Candidate (s) | Baseline (s) | Gate |", + "| --- | ---: | ---: | --- |", + ] + regressions = 0 + for name, value in rows.items(): + status = "candidate-only" + previous_value = "" + if old is not None: + previous_value = f"{old[name]:.9f}" + status = ( + "regression" if value > old[name] * (1 + threshold / 100) else "within threshold" + ) + regressions += status == "regression" + lines.append(f"| {name} | {value:.9f} | {previous_value} | {status} |") + return "\n".join(lines) + "\n", regressions + + +if __name__ == "__main__": + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("--candidate", type=Path, required=True) + p.add_argument("--baseline", default="") + p.add_argument("--output", type=Path, required=True) + p.add_argument("--threshold", type=float, default=15) + args = p.parse_args() + if args.threshold < 0: + p.error("threshold must be nonnegative") + try: + text, regressions = compare( + args.candidate, Path(args.baseline) if args.baseline else None, args.threshold + ) + args.output.mkdir(parents=True, exist_ok=True) + (args.output / "report.md").write_text(text) + except (ValueError, KeyError, OSError) as error: + p.exit(1, f"{error}\n") + raise SystemExit(1 if regressions else 0) diff --git a/benchmark.new/process/nightly.json b/benchmark.new/process/nightly.json new file mode 100644 index 000000000..de296f4be --- /dev/null +++ b/benchmark.new/process/nightly.json @@ -0,0 +1,6 @@ +{ + "native": "$ hyperfine --shell=none --runs 50 --warmup 10 --style basic --env 'PVISOR_RUN_HOME=@BATCH@/runs PVISOR_STARTUP_TIMING=0 PVISOR_FS_PROFILE=0' --conclude true --command-name native /usr/bin/true", + "host-run": "$ hyperfine --shell=none --runs 50 --warmup 10 --style basic --env 'PVISOR_RUN_HOME=@BATCH@/runs PVISOR_STARTUP_TIMING=0 PVISOR_FS_PROFILE=0' --conclude 'python3 @REPO@/benchmark.new/process/check.py new-bundle @BATCH@/runs' --command-name host-run '@BINARY@ run --no-agent-defaults --overlaynet off --stdio capture -- /usr/bin/true'", + "status": "$ hyperfine --shell=none --runs 50 --warmup 10 --style basic --env 'PVISOR_RUN_HOME=@BATCH@/runs PVISOR_STARTUP_TIMING=0 PVISOR_FS_PROFILE=0' --conclude 'python3 @REPO@/benchmark.new/process/check.py bundle @JOB@/run-bundle.json' --command-name status '@BINARY@ status --json @JOB@'", + "review": "$ hyperfine --shell=none --runs 50 --warmup 10 --style basic --env 'PVISOR_RUN_HOME=@BATCH@/runs PVISOR_STARTUP_TIMING=0 PVISOR_FS_PROFILE=0' --conclude 'python3 @REPO@/benchmark.new/process/check.py bundle @JOB@/run-bundle.json' --command-name review '@BINARY@ review --json @JOB@'" +} diff --git a/benchmark.new/process/prepare.py b/benchmark.new/process/prepare.py new file mode 100644 index 000000000..6d2b8b714 --- /dev/null +++ b/benchmark.new/process/prepare.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +"""Prepare B-PROCESS's JSON suite and validate an untimed completed Run. + +Benchmark: B-PROCESS (benchmark/README.md#b-process), role engineering A/B. +Motivation: catch minimal Run and Bundle read regressions. +Conclusion sought: direct-command native/run/status/review levels for CI. +Design: frozen CLI; independent storage; completed Bundle validation; 2/10 or 10/50. +""" + +import argparse +import importlib.util +import json +import os +import subprocess +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +_spec = importlib.util.spec_from_file_location("benchmark_common", ROOT / "common.py") +COMMON = importlib.util.module_from_spec(_spec) +_spec.loader.exec_module(COMMON) + + +def prepare_template(template, binary, output=None): + binary = Path(binary).resolve(strict=True) + batch = COMMON.create_batch(template, output) + try: + (batch / "workspace").mkdir() + env = os.environ.copy() | dict( + PVISOR_RUN_HOME=str(batch / "runs"), PVISOR_STARTUP_TIMING="0", PVISOR_FS_PROFILE="0" + ) + command = [ + str(binary), + "run", + "--no-agent-defaults", + "--overlaynet", + "off", + "--stdio", + "capture", + "--", + "/usr/bin/true", + ] + result = subprocess.run( + command, cwd=batch / "workspace", env=env, capture_output=True, text=True + ) + (batch / "preflight.stdout").write_text(result.stdout) + (batch / "preflight.stderr").write_text(result.stderr) + if result.returncode: + raise ValueError("process preflight failed; see " + str(batch / "preflight.stderr")) + bundles = list((batch / "runs").glob("run-*/run-bundle.json")) + if len(bundles) != 1: + raise ValueError("preflight must create exactly one completed Run Bundle") + checker = Path(__file__).with_name("check.py") + subprocess.run(["python3", str(checker), "bundle", str(bundles[0])], check=True) + (batch / "runs/.checked.json").write_text(json.dumps([str(bundles[0])])) + COMMON.materialize( + template, + batch, + dict(BINARY=binary, JOB=bundles[0].parent), + [binary, checker], + batch / "workspace", + [Path(__file__), checker, Path(__file__).with_name("compare.py")], + ) + return batch + except BaseException as error: + COMMON.save(batch / "failure.json", dict(error=str(error))) + raise + + +if __name__ == "__main__": + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("template", type=Path) + p.add_argument("--binary", type=Path, required=True) + p.add_argument("--output", type=Path) + args = p.parse_args() + try: + print(prepare_template(args.template, args.binary, args.output)) + except (ValueError, OSError, subprocess.SubprocessError) as error: + p.exit(1, f"{error}\n") diff --git a/benchmark.new/process/results/smoke.semspec.csv b/benchmark.new/process/results/smoke.semspec.csv new file mode 100644 index 000000000..4adf807b5 --- /dev/null +++ b/benchmark.new/process/results/smoke.semspec.csv @@ -0,0 +1,5 @@ +batch,benchmark_id,role,runs,warmup,hyperfine_version,metric_scope,evidence_sha256,manifest_sha256,condition,command,N,Median ms,Mean ms,SD ms,Min ms,Max ms +smoke-20261010T171614Z-1q2jbmo3,B-PROCESS,engineering A/B,10,2,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,1b9c24b0adfafd344376e14b559ee9251b02d5978874d77158266be49389c5d1,82ba20a6fcfd38b887964a220e1b5af1a48c98d4b438947386e67eb612a002bd,native,/usr/bin/true,10,0.20607,0.21542509999999998,0.05784206197041119,0.15310600000000002,0.336989 +smoke-20261010T171614Z-1q2jbmo3,B-PROCESS,engineering A/B,10,2,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,1b9c24b0adfafd344376e14b559ee9251b02d5978874d77158266be49389c5d1,82ba20a6fcfd38b887964a220e1b5af1a48c98d4b438947386e67eb612a002bd,host-run,/home/reiase/workspace/pvisor/target/release/pvisor run --no-agent-defaults --overlaynet off --stdio capture -- /usr/bin/true,10,120.254139,123.1086853,6.722076734142427,118.00314300000001,140.49963300000002 +smoke-20261010T171614Z-1q2jbmo3,B-PROCESS,engineering A/B,10,2,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,1b9c24b0adfafd344376e14b559ee9251b02d5978874d77158266be49389c5d1,82ba20a6fcfd38b887964a220e1b5af1a48c98d4b438947386e67eb612a002bd,status,/home/reiase/workspace/pvisor/target/release/pvisor status --json /home/reiase/workspace/pvisor/benchmark.new/process/.data/smoke-20261010T171614Z-1q2jbmo3/runs/run-721aab9e-3b01-4ed0-b6ea-6e51911616c3,10,106.227908,106.3096036,0.8245010140204252,105.00578300000001,108.07133400000001 +smoke-20261010T171614Z-1q2jbmo3,B-PROCESS,engineering A/B,10,2,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,1b9c24b0adfafd344376e14b559ee9251b02d5978874d77158266be49389c5d1,82ba20a6fcfd38b887964a220e1b5af1a48c98d4b438947386e67eb612a002bd,review,/home/reiase/workspace/pvisor/target/release/pvisor review --json /home/reiase/workspace/pvisor/benchmark.new/process/.data/smoke-20261010T171614Z-1q2jbmo3/runs/run-721aab9e-3b01-4ed0-b6ea-6e51911616c3,10,105.49941849999999,105.4418469,0.8148286604693782,103.97156100000001,106.63894 diff --git a/benchmark.new/process/results/smoke.semspec.md b/benchmark.new/process/results/smoke.semspec.md new file mode 100644 index 000000000..6ae850902 --- /dev/null +++ b/benchmark.new/process/results/smoke.semspec.md @@ -0,0 +1,7 @@ +# B-PROCESS: complete commands + +engineering A/B; N=10, warmup=2; separate conditions. + +Batch: `smoke-20261010T171614Z-1q2jbmo3`. + +{{ table("smoke.semspec.render.json") }} diff --git a/benchmark.new/process/results/smoke.semspec.render.json b/benchmark.new/process/results/smoke.semspec.render.json new file mode 100644 index 000000000..572070c2c --- /dev/null +++ b/benchmark.new/process/results/smoke.semspec.render.json @@ -0,0 +1,34 @@ +{ + "native": { + "N": 10, + "Median ms": "0.21", + "Mean ms": "0.22", + "SD ms": "0.06", + "Min ms": "0.15", + "Max ms": "0.34" + }, + "host-run": { + "N": 10, + "Median ms": "120.25", + "Mean ms": "123.11", + "SD ms": "6.72", + "Min ms": "118.00", + "Max ms": "140.50" + }, + "status": { + "N": 10, + "Median ms": "106.23", + "Mean ms": "106.31", + "SD ms": "0.82", + "Min ms": "105.01", + "Max ms": "108.07" + }, + "review": { + "N": 10, + "Median ms": "105.50", + "Mean ms": "105.44", + "SD ms": "0.81", + "Min ms": "103.97", + "Max ms": "106.64" + } +} diff --git a/benchmark.new/process/smoke.json b/benchmark.new/process/smoke.json new file mode 100644 index 000000000..72bdf010d --- /dev/null +++ b/benchmark.new/process/smoke.json @@ -0,0 +1,6 @@ +{ + "native": "$ hyperfine --shell=none --runs 10 --warmup 2 --style basic --env 'PVISOR_RUN_HOME=@BATCH@/runs PVISOR_STARTUP_TIMING=0 PVISOR_FS_PROFILE=0' --conclude true --command-name native /usr/bin/true", + "host-run": "$ hyperfine --shell=none --runs 10 --warmup 2 --style basic --env 'PVISOR_RUN_HOME=@BATCH@/runs PVISOR_STARTUP_TIMING=0 PVISOR_FS_PROFILE=0' --conclude 'python3 @REPO@/benchmark.new/process/check.py new-bundle @BATCH@/runs' --command-name host-run '@BINARY@ run --no-agent-defaults --overlaynet off --stdio capture -- /usr/bin/true'", + "status": "$ hyperfine --shell=none --runs 10 --warmup 2 --style basic --env 'PVISOR_RUN_HOME=@BATCH@/runs PVISOR_STARTUP_TIMING=0 PVISOR_FS_PROFILE=0' --conclude 'python3 @REPO@/benchmark.new/process/check.py bundle @JOB@/run-bundle.json' --command-name status '@BINARY@ status --json @JOB@'", + "review": "$ hyperfine --shell=none --runs 10 --warmup 2 --style basic --env 'PVISOR_RUN_HOME=@BATCH@/runs PVISOR_STARTUP_TIMING=0 PVISOR_FS_PROFILE=0' --conclude 'python3 @REPO@/benchmark.new/process/check.py bundle @JOB@/run-bundle.json' --command-name review '@BINARY@ review --json @JOB@'" +} diff --git a/benchmark.new/registry.csv b/benchmark.new/registry.csv new file mode 100644 index 000000000..2c50dcbd7 --- /dev/null +++ b/benchmark.new/registry.csv @@ -0,0 +1,40 @@ +benchmark_id,role,entry,status +B-LAZY-STARTUP,user-facing,startup/lazy_startup.json,requires-single-condition-command +B-STARTUP-ENG,engineering A/B,startup/guest_init.json,requires-single-condition-command +B-KERNEL-ENG,engineering A/B,startup/kernel.json,requires-single-condition-command +B-STARTUP-DIAG,diagnostic,startup/diagnostic.json,requires-single-condition-command +B-FS-TOOLS,user-facing,filesystem/tools.json,prepared-python-filesystem +B-FS-ENG,engineering A/B,filesystem/ab.json,requires-single-condition-command +B-FS-ENG,engineering A/B,filesystem/dax_ab.json,explicit-python-paired-dax +B-FS-DIAG,diagnostic,filesystem/diagnostic.json,requires-single-condition-command +B-LAZY-ENG,engineering A/B,filesystem/lazy_image.json,requires-single-condition-command +B-APPLY,user-facing,workflow/apply.json,requires-single-condition-command +B-APPLY-ENG,engineering A/B,workflow/apply_ab.json,requires-single-condition-command +B-APPLY-DIAG,diagnostic,workflow/apply_diagnostic.json,requires-single-condition-command +B-SUPERVISION,user-facing,workflow/review.json,requires-single-condition-command +B-WORKFLOW,user-facing,workflow/workflow.json,requires-single-condition-command +B-NETWORK,user-facing,network/network.json,requires-single-condition-command +B-DENSITY,user-facing,density/density.json,requires-single-condition-command +B-ISOLATION,user-facing,isolation/isolation.json,requires-single-condition-command +B-AGENT-TASK,user-facing,agent/agent.json,requires-single-condition-command +B-REPLAY,user-facing,replay/replay.json,prepared-binary-required +B-VM-MEMORY,user-facing,memory/memory.json,requires-single-condition-command +B-MEMORY-SCALE,engineering A/B,memory/scale.json,requires-single-condition-command +B-MEMORY-DIAG,diagnostic,memory/diagnostic.json,requires-single-condition-command +B-COLD-RUNTIME-ENG,engineering A/B,memory/cold_runtime.json,requires-single-condition-command +B-VCPU-IDLE-ENG,engineering A/B,memory/vcpu_idle.json,requires-single-condition-command +B-COLD-STORAGE-DIAG,diagnostic,memory/cold_storage.json,requires-single-condition-command +B-MACOS,user-facing,agent/macos.json,requires-single-condition-command +B-MACOS-ENG,engineering A/B,agent/macos_ab.json,requires-single-condition-command +B-STARTUP,user-facing,startup/pvisor_vm_timing.json,prepared-python-ready-exit +B-PROCESS,engineering A/B,process/smoke.json,prepared-binary-required +B-PROCESS,engineering A/B,process/nightly.json,prepared-binary-required +B-STARTUP,user-facing,startup/native_timing.json,prepared-python-ready-exit +B-STARTUP,user-facing,startup/pvisor_host_timing.json,prepared-python-ready-exit +B-STARTUP,user-facing,startup/pvisor_staged_timing.json,prepared-python-ready-exit +B-STARTUP,user-facing,startup/docker_timing.json,prepared-python-ready-exit +B-STARTUP,user-facing,startup/fc_system_timing.json,prepared-python-ready-exit +B-STARTUP,user-facing,startup/qemu_timing.json,prepared-python-ready-exit +B-STARTUP,user-facing,startup/qemu_microvm_timing.json,prepared-python-ready-exit +B-STARTUP,user-facing,startup/timing.json,prepared-python-ready-exit +B-STARTUP-REVISION-ENG,engineering A/B,startup/revision.json,requires-single-condition-command diff --git a/benchmark.new/replay/prepare.py b/benchmark.new/replay/prepare.py new file mode 100644 index 000000000..e09a41879 --- /dev/null +++ b/benchmark.new/replay/prepare.py @@ -0,0 +1,88 @@ +"""B-REPLAY: freeze inputs and preflight; hyperfine measures the replay CLI. + +One fixed synthetic task per pinned adapter, no tools/model, 60 runs, no +warmups. Exact source, native prefix, arguments, historical observation and +unchanged workspace are verified outside timing for every execution. +""" + +import argparse +import json +import shlex +import shutil +import subprocess +import sys +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) +from common import ROOT, create_batch, digest, materialize, save +from replay_fixtures import trajectory +from replay_hooks import SENTINEL, conclude, prepare + + +def prepare_batch(template, binary, output=None): + batch = create_batch(template, output) + frozen = batch / "pvisor-replay" + shutil.copy2(binary.resolve(strict=True), frozen) + agents = json.loads(template.read_text()) + protocol = {} + inputs = [] + for agent in agents: + fixture = batch / "fixtures" / agent + (fixture / "workspace").mkdir(parents=True) + (fixture / "workspace/existing").write_text(SENTINEL) + command = "printf fixture-0 > marker-0.txt" + source, jsonl = trajectory(agent, 0, command) + path = fixture / "trajectory.json" + path.write_text( + ("\n".join(json.dumps(item) for item in source) if jsonl else json.dumps(source)) + "\n" + ) + protocol[agent] = { + "task": 0, + "command": command, + "next_command": "cat marker-0.txt", + "sha256": digest(path), + } + inputs.append(path) + save(batch / "protocol.json", protocol) + materialize( + template, + batch, + {"BINARY": frozen}, + artifacts=[frozen, *inputs, batch / "protocol.json"], + cwd=batch, + helpers=[ + ROOT / "replay" / name + for name in ("prepare.py", "replay_fixtures.py", "replay_hooks.py") + ], + ) + manifest_path = batch / "manifest.json" + manifest = json.loads(manifest_path.read_text()) + manifest["protocol_sha256"] = digest(batch / "protocol.json") + manifest["fixture_scope"] = ( + "one fixed synthetic task 0 per adapter; distinct from historical twenty-task cohort" + ) + save(manifest_path, manifest) + table = json.loads((batch / "in.json").read_text()) + for agent, row in table.items(): + argv = shlex.split(row[2:] if isinstance(row, str) else row["Median ms"][2:]) + end = argv.index("|") if "|" in argv else len(argv) + prepare(batch, agent) + with (batch / f"preflight-{agent}.log").open("w") as log: + subprocess.run( + shlex.split(argv[end - 1]), + cwd=batch, + check=True, + timeout=30, + stdout=log, + stderr=subprocess.STDOUT, + ) + conclude(batch, agent) + return batch + + +if __name__ == "__main__": + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("template", type=Path) + p.add_argument("--binary", type=Path, required=True) + args = p.parse_args() + print(prepare_batch(args.template.resolve(), args.binary)) diff --git a/benchmark.new/replay/replay.json b/benchmark.new/replay/replay.json new file mode 100644 index 000000000..90163b69a --- /dev/null +++ b/benchmark.new/replay/replay.json @@ -0,0 +1,9 @@ +{ + "claude-code": "$ hyperfine --shell=none --runs 60 --warmup 0 --command-name claude-code --prepare 'python3 @REPO@/benchmark.new/replay/replay_hooks.py prepare @BATCH@ claude-code' --conclude 'python3 @REPO@/benchmark.new/replay/replay_hooks.py conclude @BATCH@ claude-code' '@BINARY@ --agent claude-code --trajectory @BATCH@/fixtures/claude-code/trajectory.json --after-step 1 --prepare-only --workspace @BATCH@/fixtures/claude-code/workspace --state-dir @BATCH@/fixtures/claude-code/state --output-dir @BATCH@/fixtures/claude-code/output'", + "codex": "$ hyperfine --shell=none --runs 60 --warmup 0 --command-name codex --prepare 'python3 @REPO@/benchmark.new/replay/replay_hooks.py prepare @BATCH@ codex' --conclude 'python3 @REPO@/benchmark.new/replay/replay_hooks.py conclude @BATCH@ codex' '@BINARY@ --agent codex --trajectory @BATCH@/fixtures/codex/trajectory.json --after-step 1 --prepare-only --workspace @BATCH@/fixtures/codex/workspace --state-dir @BATCH@/fixtures/codex/state --output-dir @BATCH@/fixtures/codex/output'", + "opencode": "$ hyperfine --shell=none --runs 60 --warmup 0 --command-name opencode --prepare 'python3 @REPO@/benchmark.new/replay/replay_hooks.py prepare @BATCH@ opencode' --conclude 'python3 @REPO@/benchmark.new/replay/replay_hooks.py conclude @BATCH@ opencode' '@BINARY@ --agent opencode --trajectory @BATCH@/fixtures/opencode/trajectory.json --after-step 1 --prepare-only --workspace @BATCH@/fixtures/opencode/workspace --state-dir @BATCH@/fixtures/opencode/state --output-dir @BATCH@/fixtures/opencode/output'", + "mini-swe-agent": "$ hyperfine --shell=none --runs 60 --warmup 0 --command-name mini-swe-agent --prepare 'python3 @REPO@/benchmark.new/replay/replay_hooks.py prepare @BATCH@ mini-swe-agent' --conclude 'python3 @REPO@/benchmark.new/replay/replay_hooks.py conclude @BATCH@ mini-swe-agent' '@BINARY@ --agent mini-swe-agent --trajectory @BATCH@/fixtures/mini-swe-agent/trajectory.json --after-step 1 --prepare-only --workspace @BATCH@/fixtures/mini-swe-agent/workspace --state-dir @BATCH@/fixtures/mini-swe-agent/state --output-dir @BATCH@/fixtures/mini-swe-agent/output'", + "openhands": "$ hyperfine --shell=none --runs 60 --warmup 0 --command-name openhands --prepare 'python3 @REPO@/benchmark.new/replay/replay_hooks.py prepare @BATCH@ openhands' --conclude 'python3 @REPO@/benchmark.new/replay/replay_hooks.py conclude @BATCH@ openhands' '@BINARY@ --agent openhands --trajectory @BATCH@/fixtures/openhands/trajectory.json --after-step 1 --prepare-only --workspace @BATCH@/fixtures/openhands/workspace --state-dir @BATCH@/fixtures/openhands/state --output-dir @BATCH@/fixtures/openhands/output'", + "pi-agent": "$ hyperfine --shell=none --runs 60 --warmup 0 --command-name pi-agent --prepare 'python3 @REPO@/benchmark.new/replay/replay_hooks.py prepare @BATCH@ pi-agent' --conclude 'python3 @REPO@/benchmark.new/replay/replay_hooks.py conclude @BATCH@ pi-agent' '@BINARY@ --agent pi-agent --trajectory @BATCH@/fixtures/pi-agent/trajectory.json --after-step 1 --prepare-only --workspace @BATCH@/fixtures/pi-agent/workspace --state-dir @BATCH@/fixtures/pi-agent/state --output-dir @BATCH@/fixtures/pi-agent/output'", + "swe-agent": "$ hyperfine --shell=none --runs 60 --warmup 0 --command-name swe-agent --prepare 'python3 @REPO@/benchmark.new/replay/replay_hooks.py prepare @BATCH@ swe-agent' --conclude 'python3 @REPO@/benchmark.new/replay/replay_hooks.py conclude @BATCH@ swe-agent' '@BINARY@ --agent swe-agent --trajectory @BATCH@/fixtures/swe-agent/trajectory.json --after-step 1 --prepare-only --workspace @BATCH@/fixtures/swe-agent/workspace --state-dir @BATCH@/fixtures/swe-agent/state --output-dir @BATCH@/fixtures/swe-agent/output'" +} diff --git a/benchmark.new/replay/replay_fixtures.py b/benchmark.new/replay/replay_fixtures.py new file mode 100644 index 000000000..49d396416 --- /dev/null +++ b/benchmark.new/replay/replay_fixtures.py @@ -0,0 +1,307 @@ +"""B-REPLAY: pinned synthetic native prefixes; no timing or sampling. + +Fixture contracts preserved from benchmark/pvisor/v1/replay.py. This cohort +uses one fixed task (0) per adapter; it does not replace twenty-task fidelity. +""" + +import json + + +def trajectory(agent, task, command): + next_command = f"cat marker-{task}.txt" + if agent == "swe-agent": + steps = [] + history = [dict(role="user", content=f"fixture {task}")] + for i, cmd in enumerate((command, next_command)): + steps.append( + dict( + action=cmd, + observation="historical observation", + thought=f"fixture-text-{task}-{i}", + state={}, + ) + ) + history.extend( + [ + dict( + role="assistant", + content=f"fixture-text-{task}-{i}", + tool_calls=[dict(id=f"call-{i}")], + ), + dict(role="tool", content="historical observation", tool_call_id=f"call-{i}"), + ] + ) + return dict(trajectory=steps, history=history, replay_config={}), False + if agent == "claude-code": + events = [ + { + "type": "user", + "uuid": "user-1", + "parentUuid": None, + "isSidechain": False, + "sessionId": "session-1", + "version": "2.1.220", + "message": {"role": "user", "content": f"fixture {task}"}, + } + ] + parent = "user-1" + for i, cmd in enumerate((command, next_command)): + assistant = f"assistant-{i}" + result = f"result-{i}" + events.append( + { + "type": "assistant", + "uuid": assistant, + "parentUuid": parent, + "isSidechain": False, + "sessionId": "session-1", + "version": "2.1.220", + "message": { + "id": f"message-{i}", + "role": "assistant", + "content": [ + {"type": "text", "text": f"fixture-text-{task}-{i}"}, + { + "type": "tool_use", + "id": f"tool-{i}", + "name": "Bash", + "input": {"command": cmd}, + }, + ], + }, + } + ) + events.append( + { + "type": "user", + "uuid": result, + "parentUuid": assistant, + "sourceToolAssistantUUID": assistant, + "isSidechain": False, + "sessionId": "session-1", + "version": "2.1.220", + "message": { + "role": "user", + "content": [ + { + "type": "tool_result", + "tool_use_id": f"tool-{i}", + "content": "historical observation", + } + ], + }, + } + ) + parent = result + return events, True + if agent == "codex": + events = [ + {"type": "session_meta", "payload": {"id": "sess-benchmark"}}, + { + "type": "response_item", + "payload": { + "type": "message", + "role": "user", + "content": [{"type": "input_text", "text": f"fixture {task}"}], + }, + }, + ] + for i, cmd in enumerate((command, next_command)): + events.append( + { + "type": "response_item", + "payload": { + "type": "message", + "role": "assistant", + "content": [{"type": "output_text", "text": f"fixture-text-{task}-{i}"}], + }, + } + ) + events += [ + { + "type": "response_item", + "payload": { + "type": "function_call", + "call_id": f"call-{i}", + "name": "exec_command", + "arguments": json.dumps({"cmd": cmd}), + }, + }, + { + "type": "response_item", + "payload": { + "type": "function_call_output", + "call_id": f"call-{i}", + "output": "historical observation", + }, + }, + ] + return events, True + if agent == "opencode": + events = [ + { + "type": "user", + "sessionID": "ses-benchmark", + "parts": [{"type": "text", "text": f"fixture {task}"}], + } + ] + for i, cmd in enumerate((command, next_command)): + events += [ + {"type": "step_start", "sessionID": "ses-benchmark"}, + { + "type": "tool_use", + "sessionID": "ses-benchmark", + "part": { + "type": "tool", + "tool": "bash", + "callID": f"call-{i}", + "state": { + "status": "completed", + "input": {"command": cmd}, + "output": "historical observation", + }, + }, + }, + { + "type": "step_finish", + "sessionID": "ses-benchmark", + "part": {"reason": "tool-calls"}, + }, + ] + return events, True + if agent == "mini-swe-agent": + messages = [{"role": "user", "content": f"fixture {task}", "extra": {}}] + for i, cmd in enumerate((command, next_command)): + messages += [ + { + "role": "assistant", + "content": f"fixture-text-{task}-{i}", + "extra": { + "response": {}, + "actions": [{"tool_call_id": f"call-{i}", "command": cmd}], + }, + }, + {"role": "tool", "content": "historical observation", "extra": {"returncode": 0}}, + ] + return { + "trajectory_format": "mini-swe-agent-1.1", + "info": { + "mini_version": "2.4.6", + "config": {"model": {}, "agent": {}, "environment": {}}, + }, + "messages": messages, + }, False + if agent == "openhands": + events = [ + {"id": 0, "source": "user", "action": "message", "args": {"content": f"fixture {task}"}} + ] + for i, cmd in enumerate((command, next_command)): + events += [ + { + "id": 1 + i * 2, + "source": "agent", + "action": "run", + "args": {"command": cmd, "thought": f"fixture-text-{task}-{i}"}, + }, + { + "id": 2 + i * 2, + "source": "environment", + "observation": "run", + "cause": 1 + i * 2, + "message": "historical observation", + "args": {"command": cmd, "metadata": {"exit_code": 0}}, + }, + ] + return events, False + events = [ + { + "type": "message_end", + "message": {"role": "user", "content": f"fixture {task}", "timestamp": 1}, + } + ] + for i, cmd in enumerate((command, next_command)): + events.append( + { + "type": "turn_end", + "message": { + "role": "assistant", + "provider": "benchmark", + "model": "fixture", + "api": "openai-completions", + "content": [ + {"type": "text", "text": f"fixture-text-{task}-{i}"}, + { + "type": "toolCall", + "id": f"call-{i}", + "name": "bash", + "arguments": {"command": cmd}, + }, + ], + "stopReason": "toolUse", + "usage": {"input": 1, "output": 1}, + "timestamp": 2 + i * 2, + }, + "toolResults": [ + { + "role": "toolResult", + "toolCallId": f"call-{i}", + "toolName": "bash", + "content": [{"type": "text", "text": "historical observation"}], + "isError": False, + "timestamp": 3 + i * 2, + } + ], + } + ) + return events, True + + +def nested_strings(value): + if isinstance(value, dict): + return [s for child in value.values() for s in nested_strings(child)] + if isinstance(value, list): + return [s for child in value for s in nested_strings(child)] + if isinstance(value, str): + try: + parsed = json.loads(value) + if isinstance(parsed, (dict, list)): + return [value, *nested_strings(parsed)] + except (ValueError, TypeError): + pass + return [value] + return [] + + +def validate_prefix(manifest, output, agent, command, next_command, source_sha): + boundary = manifest["boundary"] + if ( + manifest["agent"]["name"] != agent + or manifest["source"]["sha256"] != source_sha + or boundary["after_step"] != 1 + or boundary["tool_calls"] != 1 + or boundary["complete_tool_batch"] is not True + ): + raise ValueError("wrong source, adapter or complete prefix boundary") + batches = manifest["batches"] + if len(batches) != 1 or len(batches[0]["tool_calls"]) != 1: + raise ValueError("wrong prefix structure") + call = batches[0]["tool_calls"][0] + arguments = call["arguments"] + if arguments.get("command", arguments.get("cmd", arguments.get("raw_action"))) != command: + raise ValueError("prefix tool arguments differ") + filename = { + "mini-swe-agent": "prepared-prefix.json", + "openhands": "prepared-replay-events.json", + "swe-agent": "prepared-prefix.traj", + }.get(agent, "prepared-prefix.jsonl") + raw = (output / "native" / filename).read_text() + native = ( + [json.loads(line) for line in raw.splitlines() if line.strip()] + if filename.endswith(".jsonl") + else json.loads(raw) + ) + strings = nested_strings(native) + if command not in strings or "historical observation" not in strings or next_command in strings: + raise ValueError( + "prepared native prefix lost tool/observation or included action beyond boundary" + ) diff --git a/benchmark.new/replay/replay_hooks.py b/benchmark.new/replay/replay_hooks.py new file mode 100644 index 000000000..a2f0df201 --- /dev/null +++ b/benchmark.new/replay/replay_hooks.py @@ -0,0 +1,117 @@ +"""B-REPLAY: untimed reset and fidelity audit for each direct CLI execution.""" + +import argparse +import json +import shutil +import sys +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) +from common import digest, save +from replay_fixtures import validate_prefix + +SENTINEL = "existing workspace must survive preparation\n" + + +def prepare(batch, agent): + fixture = batch / "fixtures" / agent + protocol = json.loads((batch / "protocol.json").read_text())[agent] + if digest(fixture / "trajectory.json") != protocol["sha256"]: + raise ValueError("trajectory changed") + for name in ("state", "output"): + if (fixture / name).exists(): + raise ValueError("previous trial was not audited") + if sorted(p.name for p in (fixture / "workspace").iterdir()) != ["existing"]: + raise ValueError("prepare-only changed workspace inventory") + if (fixture / "workspace/existing").read_text() != SENTINEL: + raise ValueError("prepare-only changed workspace contents") + + +def verify(root, protocol, agent): + results = list((root / "output").glob("*/result.json")) + if len(results) != 1: + raise ValueError("expected one replay result") + result = json.loads(results[0].read_text()) + manifest = json.loads(results[0].with_name("manifest.json").read_text()) + if ( + result["failure"] is not None + or result["replayed_tool_calls"] != 0 + or result["agent_status"] != "not_started" + or result["phase"] != "prepared" + or result["quality"] != "verified" + or result["continued_steps"] != 0 + or result["after_step"] != 1 + ): + raise ValueError("prepare-only executed tools, started an agent or failed") + validate_prefix( + manifest, + results[0].parent, + agent, + protocol["command"], + protocol["next_command"], + protocol["sha256"], + ) + return manifest["agent"]["profile"] + + +def conclude(batch, agent): + prepare_workspace = batch / "fixtures" / agent + protocol = json.loads((batch / "protocol.json").read_text())[agent] + profile = verify(prepare_workspace, protocol, agent) + # Both the result and the real host workspace must pass before archival. + workspace = prepare_workspace / "workspace" + if ( + sorted(p.name for p in workspace.iterdir()) != ["existing"] + or (workspace / "existing").read_text() != SENTINEL + ): + raise ValueError("prepare-only modified the workspace") + if digest(prepare_workspace / "trajectory.json") != protocol["sha256"]: + raise ValueError("trajectory changed during execution") + parent = batch / "trials" / agent + parent.mkdir(parents=True, exist_ok=True) + trial = parent / f"{len(list(parent.iterdir())) + 1:04d}" + trial.mkdir() + for name in ("output", "state"): + shutil.move(str(prepare_workspace / name), str(trial / name)) + save(trial / "audit.json", {"profile": profile, "correctness": "passed", "executed_tools": 0}) + + +def audit_replay(batch, manifest, results): + protocol = json.loads((batch / "protocol.json").read_text()) + if digest(batch / "protocol.json") != manifest["protocol_sha256"]: + raise ValueError("replay protocol changed") + if set(protocol) != set(results): + raise ValueError("incomplete adapter set") + for agent in results: + prepare(batch, agent) + trials = sorted((batch / "trials" / agent).iterdir()) + if len(trials) != manifest["runs"] + manifest["warmup"] + 1: + raise ValueError("missing replay preflight/warmup/sample fidelity audits") + for trial in trials: + profile = verify(trial, protocol[agent], agent) + if json.loads((trial / "audit.json").read_text()) != { + "profile": profile, + "correctness": "passed", + "executed_tools": 0, + }: + raise ValueError("invalid archived audit") + + +if __name__ == "__main__": + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("action", choices=("prepare", "conclude")) + p.add_argument("batch", type=Path) + p.add_argument( + "agent", + choices=( + "claude-code", + "codex", + "opencode", + "mini-swe-agent", + "openhands", + "pi-agent", + "swe-agent", + ), + ) + args = p.parse_args() + globals()[args.action](args.batch.resolve(), args.agent) diff --git a/benchmark.new/replay/results/replay.semspec.csv b/benchmark.new/replay/results/replay.semspec.csv new file mode 100644 index 000000000..409ba9ce6 --- /dev/null +++ b/benchmark.new/replay/results/replay.semspec.csv @@ -0,0 +1,15 @@ +batch,benchmark_id,role,runs,warmup,hyperfine_version,metric_scope,evidence_sha256,manifest_sha256,condition,command,N,Median ms,Mean ms,SD ms,Min ms,Max ms +replay-20261010T173216Z-gbh7pkr9,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,5556cb33a1e853ac3074fff9dd34c2fdf722abd2fc9f8f0391bfd89131dedac0,808c779fce917fb38c7845481fb2162f7b2e2b11c387dc60611421caf2532f92,claude-code,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/pvisor-replay --agent claude-code --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/claude-code/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/claude-code/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/claude-code/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/claude-code/output,60,5.813657,5.8942094166666665,0.5230506143534632,5.176242,8.040147000000001 +replay-20261010T173216Z-gbh7pkr9,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,5556cb33a1e853ac3074fff9dd34c2fdf722abd2fc9f8f0391bfd89131dedac0,808c779fce917fb38c7845481fb2162f7b2e2b11c387dc60611421caf2532f92,codex,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/pvisor-replay --agent codex --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/codex/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/codex/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/codex/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/codex/output,60,5.668345500000001,5.793974399999999,0.5887070394240217,5.288943,9.142757 +replay-20261010T173216Z-gbh7pkr9,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,5556cb33a1e853ac3074fff9dd34c2fdf722abd2fc9f8f0391bfd89131dedac0,808c779fce917fb38c7845481fb2162f7b2e2b11c387dc60611421caf2532f92,opencode,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/pvisor-replay --agent opencode --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/opencode/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/opencode/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/opencode/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/opencode/output,60,5.6216435,5.662359433333333,0.25105121611453857,5.226376,6.6708620000000005 +replay-20261010T173216Z-gbh7pkr9,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,5556cb33a1e853ac3074fff9dd34c2fdf722abd2fc9f8f0391bfd89131dedac0,808c779fce917fb38c7845481fb2162f7b2e2b11c387dc60611421caf2532f92,mini-swe-agent,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/pvisor-replay --agent mini-swe-agent --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/mini-swe-agent/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/mini-swe-agent/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/mini-swe-agent/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/mini-swe-agent/output,60,5.660796000000001,5.7662035833333345,0.41099182149198304,5.286918000000001,6.981852 +replay-20261010T173216Z-gbh7pkr9,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,5556cb33a1e853ac3074fff9dd34c2fdf722abd2fc9f8f0391bfd89131dedac0,808c779fce917fb38c7845481fb2162f7b2e2b11c387dc60611421caf2532f92,openhands,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/pvisor-replay --agent openhands --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/openhands/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/openhands/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/openhands/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/openhands/output,60,5.6294830000000005,5.856039483333334,1.2306524438359545,5.282350000000001,14.935868999999999 +replay-20261010T173216Z-gbh7pkr9,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,5556cb33a1e853ac3074fff9dd34c2fdf722abd2fc9f8f0391bfd89131dedac0,808c779fce917fb38c7845481fb2162f7b2e2b11c387dc60611421caf2532f92,pi-agent,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/pvisor-replay --agent pi-agent --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/pi-agent/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/pi-agent/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/pi-agent/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/pi-agent/output,60,5.831475000000001,5.8543261,0.3711018192554459,4.919222,7.126382 +replay-20261010T173216Z-gbh7pkr9,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,5556cb33a1e853ac3074fff9dd34c2fdf722abd2fc9f8f0391bfd89131dedac0,808c779fce917fb38c7845481fb2162f7b2e2b11c387dc60611421caf2532f92,swe-agent,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/pvisor-replay --agent swe-agent --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/swe-agent/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/swe-agent/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/swe-agent/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/swe-agent/output,60,5.645287000000001,5.8619556500000005,0.8858928147291519,5.243638,11.045348 +replay-20261010T194213Z-9zpco2sv,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,34f0c4a59ad1612860c382951158167a4fb3a2f45e47dbf635d0dca793105eca,202c53d97959bbdf76bb10fd5fb071d22eb2c435f194f48e3372800b02dd1c8e,claude-code,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/pvisor-replay --agent claude-code --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/claude-code/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/claude-code/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/claude-code/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/claude-code/output,60,5.7403095,5.8633256833333345,0.8778318015393844,5.114597,12.135782 +replay-20261010T194213Z-9zpco2sv,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,34f0c4a59ad1612860c382951158167a4fb3a2f45e47dbf635d0dca793105eca,202c53d97959bbdf76bb10fd5fb071d22eb2c435f194f48e3372800b02dd1c8e,codex,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/pvisor-replay --agent codex --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/codex/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/codex/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/codex/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/codex/output,60,5.589363,5.668820266666667,0.31781873367499147,5.052701000000001,6.9522770000000005 +replay-20261010T194213Z-9zpco2sv,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,34f0c4a59ad1612860c382951158167a4fb3a2f45e47dbf635d0dca793105eca,202c53d97959bbdf76bb10fd5fb071d22eb2c435f194f48e3372800b02dd1c8e,opencode,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/pvisor-replay --agent opencode --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/opencode/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/opencode/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/opencode/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/opencode/output,60,5.563975500000001,5.6183589833333345,0.2956679622357836,4.985716,6.622702 +replay-20261010T194213Z-9zpco2sv,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,34f0c4a59ad1612860c382951158167a4fb3a2f45e47dbf635d0dca793105eca,202c53d97959bbdf76bb10fd5fb071d22eb2c435f194f48e3372800b02dd1c8e,mini-swe-agent,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/pvisor-replay --agent mini-swe-agent --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/mini-swe-agent/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/mini-swe-agent/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/mini-swe-agent/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/mini-swe-agent/output,60,5.608388000000001,5.723988500000002,0.3963316399933439,5.279214,7.547869 +replay-20261010T194213Z-9zpco2sv,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,34f0c4a59ad1612860c382951158167a4fb3a2f45e47dbf635d0dca793105eca,202c53d97959bbdf76bb10fd5fb071d22eb2c435f194f48e3372800b02dd1c8e,openhands,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/pvisor-replay --agent openhands --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/openhands/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/openhands/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/openhands/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/openhands/output,60,5.675914000000001,5.734144033333331,0.4020236849784177,4.805099,7.400434000000001 +replay-20261010T194213Z-9zpco2sv,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,34f0c4a59ad1612860c382951158167a4fb3a2f45e47dbf635d0dca793105eca,202c53d97959bbdf76bb10fd5fb071d22eb2c435f194f48e3372800b02dd1c8e,pi-agent,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/pvisor-replay --agent pi-agent --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/pi-agent/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/pi-agent/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/pi-agent/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/pi-agent/output,60,5.582430500000001,5.629462633333333,0.33750797150701284,4.734617,6.9348540000000005 +replay-20261010T194213Z-9zpco2sv,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,34f0c4a59ad1612860c382951158167a4fb3a2f45e47dbf635d0dca793105eca,202c53d97959bbdf76bb10fd5fb071d22eb2c435f194f48e3372800b02dd1c8e,swe-agent,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/pvisor-replay --agent swe-agent --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/swe-agent/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/swe-agent/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/swe-agent/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/swe-agent/output,60,5.732465000000001,5.763901283333333,0.39466482164589084,4.707587,7.244041 diff --git a/benchmark.new/replay/results/replay.semspec.md b/benchmark.new/replay/results/replay.semspec.md new file mode 100644 index 000000000..717317402 --- /dev/null +++ b/benchmark.new/replay/results/replay.semspec.md @@ -0,0 +1,7 @@ +# B-REPLAY: complete commands + +user-facing; N=60, warmup=0; separate conditions. + +Batch: `replay-20261010T194213Z-9zpco2sv`. + +{{ table("replay.semspec.render.json") }} diff --git a/benchmark.new/replay/results/replay.semspec.render.json b/benchmark.new/replay/results/replay.semspec.render.json new file mode 100644 index 000000000..be43ae10f --- /dev/null +++ b/benchmark.new/replay/results/replay.semspec.render.json @@ -0,0 +1,58 @@ +{ + "claude-code": { + "N": 60, + "Median ms": "5.74", + "Mean ms": "5.86", + "SD ms": "0.88", + "Min ms": "5.11", + "Max ms": "12.14" + }, + "codex": { + "N": 60, + "Median ms": "5.59", + "Mean ms": "5.67", + "SD ms": "0.32", + "Min ms": "5.05", + "Max ms": "6.95" + }, + "opencode": { + "N": 60, + "Median ms": "5.56", + "Mean ms": "5.62", + "SD ms": "0.30", + "Min ms": "4.99", + "Max ms": "6.62" + }, + "mini-swe-agent": { + "N": 60, + "Median ms": "5.61", + "Mean ms": "5.72", + "SD ms": "0.40", + "Min ms": "5.28", + "Max ms": "7.55" + }, + "openhands": { + "N": 60, + "Median ms": "5.68", + "Mean ms": "5.73", + "SD ms": "0.40", + "Min ms": "4.81", + "Max ms": "7.40" + }, + "pi-agent": { + "N": 60, + "Median ms": "5.58", + "Mean ms": "5.63", + "SD ms": "0.34", + "Min ms": "4.73", + "Max ms": "6.93" + }, + "swe-agent": { + "N": 60, + "Median ms": "5.73", + "Mean ms": "5.76", + "SD ms": "0.39", + "Min ms": "4.71", + "Max ms": "7.24" + } +} diff --git a/benchmark.new/run.py b/benchmark.new/run.py new file mode 100644 index 000000000..96962040c --- /dev/null +++ b/benchmark.new/run.py @@ -0,0 +1,210 @@ +#!/usr/bin/env python3 +"""Run a registered table and print its rendered result; retain raw data on request.""" + +import argparse +import importlib.util +import json +import os +import subprocess +import sys +import tempfile +import uuid +from pathlib import Path + +from common import ROOT, registered, save +from update import display_table, validate_batch + + +def resolve_entry(value): + """Accept a registered relative path or an unambiguous short entry name.""" + candidate = Path(value) + if candidate.suffix != ".json": + candidate = Path(str(candidate) + ".json") + paths = [candidate, ROOT / candidate] + if len(candidate.parts) == 1: + paths.extend(ROOT.glob("*/" + candidate.name)) + matches = {path.resolve() for path in paths if path.is_file()} + if len(matches) != 1: + raise ValueError("unknown or ambiguous benchmark table: " + value) + template = matches.pop() + registered(template) + return template + + +def load_prepare(topic): + path = ROOT / topic / ("common.py" if topic == "startup" else "prepare.py") + sys.path.insert(0, str(path.parent)) + spec = importlib.util.spec_from_file_location("prepare_" + topic, path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +def resolve_row(template, value): + """Match a row exactly, or by case and whitespace when unambiguous.""" + table = json.loads(Path(template).read_text()) + if value in table: + return value + normalized = " ".join(value.casefold().split()) + matches = [name for name in table if " ".join(name.casefold().split()) == normalized] + if len(matches) == 1: + return matches[0] + raise ValueError(f"unknown or ambiguous table row: {value}; available: {', '.join(table)}") + + +def resolve_selection(values, row=None): + """Keep multiple table arguments, with an optional second positional row.""" + values = values or ["smoke"] + first = resolve_entry(values[0]) + if len(values) == 2 and row is None: + try: + second = resolve_entry(values[1]) + except ValueError: + return [first], resolve_row(first, values[1]) + return [first, second], None + templates = [first, *(resolve_entry(value) for value in values[1:])] + if row is not None: + if len(templates) != 1: + raise ValueError("row selection requires exactly one table") + row = resolve_row(first, row) + return templates, row + + +def run_table( + template, + semspec, + output=None, + binary=None, + row=None, + cpu_affinity=None, + assets=None, + receipt=None, +): + template = Path(template).resolve() + metadata = registered(template) + topic = template.parent.name + table = json.loads(template.read_text()) + row_names = list(table) + if row is not None: + row = resolve_row(template, row) + table = {row: table[row]} + rendered = template.with_suffix(".render.json") + previous = json.loads(rendered.read_text()) if row is not None and rendered.exists() else {} + if not isinstance(previous, dict): + raise ValueError("existing rendered table must be a JSON object") + if metadata["status"] == "requires-single-condition-command": + raise ValueError("table has no prepared single-condition command") + if metadata["status"] == "explicit-python-paired-dax": + raise ValueError("paired DAX needs dax_ab.py --prepare followed by semspec bench; see filesystem/README.md") + if row is not None and topic not in ("startup", "filesystem"): + raise ValueError("--row is supported only for startup/filesystem tables") + prepare = load_prepare(topic) + binary = binary or ROOT.parent / "target/release" / ( + "pvisor-replay" if topic == "replay" else "pvisor" + ) + # Temporary evidence is audited before removal. Explicit output retains it, + # including on failure, for CI comparison or a later publication. + temporary = tempfile.TemporaryDirectory(prefix="pvisor-benchmark-") if output is None else None + evidence_root = Path(temporary.name) / ".data" if temporary else Path(output).resolve() + token = uuid.uuid4().hex[:12] + try: + if topic == "startup": + if ".data" not in evidence_root.parts: + raise ValueError("raw output must live under an ignored .data directory") + evidence_root.mkdir(parents=True, exist_ok=False) + result = {} + names = list(table) if topic == "startup" else [None] + for index, name in enumerate(names): + destination = ( + evidence_root / f"row-{index}-{token}" if topic == "startup" else evidence_root + ) + if topic == "startup": + batch = prepare.prepare_template( + template, destination, assets, binary, receipt, cpu_affinity, row=name + ) + elif topic == "process": + batch = prepare.prepare_template(template, binary, destination) + elif topic == "replay": + batch = prepare.prepare_batch(template, binary, destination) + elif topic == "filesystem": + batch = prepare.prepare(template, output=destination, row=row) + else: + raise ValueError("unsupported prepared benchmark topic: " + topic) + manifest = json.loads((batch / "manifest.json").read_text()) + subprocess.run( + [str(semspec), "bench", "--cwd", manifest["cwd"], str(batch / "in.json")], + check=True, + stdout=subprocess.DEVNULL, + ) + _, resolved, _ = validate_batch(batch) + result.update(resolved) + previous.update(display_table(result)) + save(rendered, {name: previous[name] for name in row_names if name in previous}) + subprocess.run( + [str(semspec), "render", str(template.with_suffix(".md")), "--stdin"], + input='{{ table("' + rendered.name + '") }}\n', + text=True, + check=True, + ) + return rendered + finally: + if temporary: + temporary.cleanup() + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("tables", nargs="*", default=["smoke"]) + parser.add_argument("--row") + parser.add_argument("--binary", type=Path, default=None) + parser.add_argument("--assets", type=Path) + parser.add_argument("--kernel-receipt", type=Path) + parser.add_argument("--cpu-affinity") + parser.add_argument("--output", type=Path) + parser.add_argument("--check", action="store_true", help=argparse.SUPPRESS) + args = parser.parse_args() + try: + templates, row = resolve_selection(args.tables, args.row) + if row is not None and templates[0].parent.name not in ("startup", "filesystem"): + raise ValueError("row selection is supported only for startup/filesystem tables") + if args.check: + print( + "process" + if args.binary is None + and any(template.parent.name == "process" for template in templates) + else "prepared" + ) + return + semspec = os.environ.get("SEMSPEC_BIN", ROOT.parent / "target/semspec/debug/semspec") + original = os.sched_getaffinity(0) if hasattr(os, "sched_getaffinity") else None + selected = args.cpu_affinity or ( + ",".join(map(str, sorted(original)[:2])) if original else None + ) + try: + if original and selected: + os.sched_setaffinity(0, {int(cpu) for cpu in selected.split(",")}) + for template in templates: + output = args.output + if output is None and os.environ.get("PVISOR_BENCHMARK_OUTPUT"): + output = Path(os.environ["PVISOR_BENCHMARK_OUTPUT"]) / template.stem + if output is not None and args.output is not None and len(templates) > 1: + output = output / template.parent.name / template.stem + run_table( + template, + semspec, + output, + args.binary, + row, + selected, + args.assets, + args.kernel_receipt, + ) + finally: + if original: + os.sched_setaffinity(0, original) + except (ValueError, KeyError, OSError, subprocess.SubprocessError) as error: + parser.exit(1, f"{error}\n") + + +if __name__ == "__main__": + main() diff --git a/benchmark.new/startup/README.md b/benchmark.new/startup/README.md new file mode 100644 index 000000000..af2cb13f9 --- /dev/null +++ b/benchmark.new/startup/README.md @@ -0,0 +1,63 @@ +# 启动时间:一张表,一个 JSON + +这里回答的问题:环境已准备好时,各执行模式到首条 Ready 输出与正常退出要等多久? +注册条目为 [B-STARTUP](../../benchmark/README.md#b-startup)。 + +[timing.json](timing.json) 是八行对比表,每行调用 `_timing.py`; +各后端也有独立的 `_timing.json`,输出与输入同名的 `.render.json`。 + +```sh +just benchmark startup/timing +just benchmark startup/timing --row Docker +just benchmark docker_timing +just benchmark pvisor_vm_timing +just benchmark native_timing +``` + +`--row` 只执行并渲染选中的行。默认使用自动清理的临时目录,只保存最终表格。 +需要保留原始证据或发布文档时: + +```sh +just benchmark startup/timing --output benchmark.new/startup/.data/measurement +python3 benchmark.new/update.py benchmark.new/startup/.data/measurement/row-0-* --startup-docs docs/src +``` + +每行证据保存在独立的 `row--/`,显式输出目录拒绝复用。 +`update.py` 的本地 CSV / Markdown 默认放在 `.data/results/`。 + +## 制品与输入 + +非原生后端通过 `--assets PATH` 或 `PVISOR_BENCHMARK_ASSETS` 指定共同输入; +FC/QEMU 通过 `--kernel-receipt PATH` 或 `PVISOR_BENCHMARK_KERNEL_RECEIPT` 指定 stock 内核收据。 +代码不绑定历史批次路径或日期。Docker 默认使用输入 `assets.json` 的不可变 `docker_image`, +也可通过 `PVISOR_BENCHMARK_DOCKER_IMAGE` 指定;镜像必须预先存在。 + +pVisor 默认使用 `target/release/pvisor`,可用 `--binary PATH` 指定。 +已有 Host listener 必须与二进制匹配。默认选当前可用的前两个 CPU; +`--cpu-affinity` 可指定准备和采样共同使用的亲和性。 + +## 当前运行链路 + +- `subjects.py`:八个后端的名称、标签和固定被测 argv。 +- `common.py`:展开输入、核对制品、准备工作区并执行预检。 +- `hooks.py`:重置每次输入,校验输出、canary、Run Bundle 和只读 stage。 +- `timing.py`:共享事件时钟、采样和统计;各 `*_timing.py` 固定后端。 +- `audit.py`:复算当前批次的配对样本并校验逐次证据。 +- `publish.py`:将成功批次发布为双语 Ready / Exit 表。 + +文档只保存 Ready / Exit 两张结果 JSON;来源摘要共用 +`docs/overrides/assets/benchmarks/startup-timing.provenance.json`,单行复测保留其他 +条件的批次和配置。完整 manifest、输入清单与内核收据留在试验 `.data/`。 + +每行固定 3 次预热、60 次正式样本,VM 为 2 vCPU / 128 MiB,热缓存。 +时钟在 Popen 前开始,收到唯一且精确的 `REFERENCE_READY` 行时记录 Ready, +正常零退出时记录 Exit。两个指标来自同次运行,使用标准库样本标准差。 +解释器启动和输入准备不计时;Popen、管道观察与调度包含在内。 + +预检、预热与正式样本都要求正确有序输出、输入未变和零退出。 +pVisor 还校验实际隔离模式与 Run Bundle;FC/QEMU 要求有序 Exit0 且无 panic。 +FC 使用 stock 内核/MMIO,受控停止不能算正常退出。任何失败都阻止更新结果。 +日志直接保存在试验目录;成功验证后丢弃可重建磁盘,保留纳秒记录、输出与 Bundle。 + +历史报告、CSV 和旧 results 已归档到 `.data/history/`,不参与当前采样、审计或发布。 +工程与诊断占位表仍由 registry 登记,尚未准备的负载拒绝执行。 diff --git a/benchmark.new/startup/audit.py b/benchmark.new/startup/audit.py new file mode 100644 index 000000000..013e00e0e --- /dev/null +++ b/benchmark.new/startup/audit.py @@ -0,0 +1,167 @@ +"""Audit the current paired event protocol, samples, output and prepared inputs.""" + +import json +import shlex +import sys +from pathlib import Path + +from hooks import validate_output +from subjects import LABELS, NAMES +from timing import COMMON, FIELDS, ROOT, RUNS, TIMEOUT, WARMUP, summarize_pairs + + +def audit_startup(directory, manifest, results): + sys.path.insert(0, str(ROOT.parent / "benchmark/pvisor")) + from firecracker_kernels import verify_kernel_receipt + from reference_inputs import verify_reference_inputs + + root = Path(directory) + if COMMON.digest(root / "protocol.json") != manifest["artifacts"][str(root / "protocol.json")]: + raise ValueError("startup protocol checksum mismatch") + protocol = json.loads((root / "protocol.json").read_text()) + if manifest["runs"] < 30 or protocol["backends"] != list(results): + raise ValueError("startup audit requires >=30 samples per condition") + after = verify_reference_inputs(Path(protocol["assets"])) if protocol["assets"] else None + if after != protocol["input_before"]: + raise ValueError("prepared inputs changed") + if ( + protocol["kernel"] + and verify_kernel_receipt(Path(protocol["kernel_receipt"]), "fc-system") + != protocol["kernel"] + ): + raise ValueError("stock kernel changed") + history = list((root / "trials").glob("*/condition.json")) + for backend in protocol["backends"]: + trials = [ + json.loads(p.read_text()) + for p in history + if json.loads(p.read_text())["backend"] == backend + ] + if len(trials) != 1 + manifest["warmup"] + manifest["runs"] or any( + trial != dict(backend=backend, validated=True) for trial in trials + ): + raise ValueError("missing preflight/warmup/measured validation evidence: " + backend) + COMMON.save( + root / "audit.json", + dict(status="passed", inputs_before=protocol["input_before"], inputs_after=after), + ) + + +def audit_timing(batch, manifest, evidence, resolved, template): + """Recompute the row from retained nanoseconds and recheck every trial.""" + + if len(manifest["conditions"]) != 1 or manifest["conditions"][0] not in NAMES: + raise ValueError("expected one known startup backend") + backend = manifest["conditions"][0] + label = LABELS[backend] + entry = "startup/" + backend.replace("-", "_") + "_timing.json" + if ( + manifest["entry"] not in (entry, "startup/timing.json") + or manifest["metric_scope"] != COMMON.READY_EXIT_SCOPE + or manifest["benchmark_id"] != "B-STARTUP" + or manifest.get("measurement_engine") != "python-perf_counter_ns" + or manifest["runs"] != RUNS + or manifest["warmup"] != WARMUP + or manifest["rows"] != {label: backend} + ): + raise ValueError("unexpected Ready measurement protocol") + fields = FIELDS + expression = template.get(label) + if not isinstance(expression, str) or not expression.startswith("$ "): + raise ValueError("expected one Ready script row") + script = backend.replace("-", "_") + "_timing.py" + expected_argv = ["python3", str(ROOT / "startup" / script), str(batch)] + if len(template) != 1 or shlex.split(expression[2:]) != expected_argv: + raise ValueError("Ready script command differs from the fixed entry") + commands = evidence["commands"] + if ( + len(commands) != 1 + or commands[0].get("kind") != "script-row" + or commands[0]["command"] != expression[2:] + or evidence["cells"] + ): + raise ValueError("timing evidence must be one script row") + if evidence.get("rows") != [ + { + "row": label, + "command": expression[2:], + "expression": expression[2:], + "columns": list(fields), + } + ]: + raise ValueError("Ready row mapping differs") + protocol = json.loads((batch / "protocol.json").read_text()) + if ( + protocol.get("scope") != manifest["metric_scope"] + or protocol.get("measurement_engine") != "python-perf_counter_ns" + or protocol.get("ready") != "exact REFERENCE_READY line received" + or protocol.get("guest_cpus") != 2 + or protocol.get("guest_memory_mib") != 128 + ): + raise ValueError("unexpected Ready protocol scope or VM configuration") + samples = [] + history = sorted((batch / "trials").glob("*/timing.json")) + phases = ( + [("preflight", 0)] + + [("warmup", i) for i in range(WARMUP)] + + [("sample", i) for i in range(RUNS)] + ) + if len(history) != len(phases): + raise ValueError("missing Ready trial evidence") + previous_end = 0 + for path, (phase, index) in zip(history, phases): + trial = json.loads(path.read_text()) + if ( + (trial.get("phase"), trial.get("index")) != (phase, index) + or trial["command"] != protocol["subject"] + or trial["exit_code"] != 0 + ): + raise ValueError("Ready trial command, order or exit differs") + start, ready, end = (trial[k] for k in ("start_ns", "ready_ns", "end_ns")) + if ( + any(type(n) is not int for n in (start, ready, end)) + or not previous_end < start < ready <= end + ): + raise ValueError("invalid Ready monotonic timestamps") + if trial["ready_duration_ns"] != ready - start or end - start > TIMEOUT * 1_000_000_000: + raise ValueError("invalid Ready duration/timeout") + if trial.get("exit_duration_ns") != end - start: + raise ValueError("invalid Exit duration") + previous_end = end + root = path.parent + validate_output( + (root / "stdout").read_text(), + (root / "stderr").read_text(), + guest=backend in ("fc-system", "qemu", "qemu-microvm"), + ) + if backend in ("pvisor-vm", "pvisor-staged"): + bundle = json.loads((root / "stage/run-bundle.json").read_text()) + if ( + bundle["run"]["state"] != "completed" + or bundle["run"]["exit_code"] != 0 + or bundle["run"]["executor"]["isolation"] + != ("virtual_machine" if backend == "pvisor-vm" else protocol["staged_isolation"]) + or bundle["safety"]["filesystem_changes_staged"] is not True + or any(p.is_file() for p in (root / "stage/upper").rglob("*")) + ): + raise ValueError("Ready VM output/Run Bundle did not pass") + if phase == "sample": + samples.append({"ready_ns": ready - start, "exit_ns": end - start}) + raw = json.loads((batch / "timing.samples.json").read_text()) + result = summarize_pairs(samples) + if raw != { + "engine": "python-perf_counter_ns", + "unit": "nanosecond", + "samples": samples, + "result": result, + }: + raise ValueError("Ready raw samples or summary changed") + if ( + resolved != {label: result} + or commands[0]["raw"] != result + or commands[0]["value"] != result + ): + raise ValueError("Ready resolved row differs from archived samples") + results = {backend: {"command": shlex.join(protocol["subject"])}} + audit_startup(batch, manifest, results) + return results diff --git a/benchmark.new/startup/common.py b/benchmark.new/startup/common.py new file mode 100644 index 000000000..4e594366e --- /dev/null +++ b/benchmark.new/startup/common.py @@ -0,0 +1,230 @@ +#!/usr/bin/env python3 +"""Prepare one B-STARTUP event row and validate an untimed preflight. + +Benchmark: B-STARTUP (benchmark/README.md#b-startup), role user-facing. +Motivation: choose an executor for prepared disposable environments. +Conclusion sought: same-launch Ready and normal Exit levels. +Design: fresh inputs; checked output and Run Bundles; warm caches; 2 vCPU/128 MiB. +""" + +import argparse +import importlib.util +import json +import os +import shutil +import subprocess +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +REPO = ROOT.parent +sys.path.insert(0, str(REPO / "benchmark/pvisor")) +_spec = importlib.util.spec_from_file_location("benchmark_common", ROOT / "common.py") +COMMON = importlib.util.module_from_spec(_spec) +_spec.loader.exec_module(COMMON) +from firecracker_kernels import verify_kernel_receipt # noqa: E402 +from hooks import cleanup_container, prepare # noqa: E402 +from reference_inputs import verify_reference_inputs # noqa: E402 +from subjects import LABELS, NAMES, subject # noqa: E402 + +BACKENDS = NAMES +BOOT = "console=ttyS0 reboot=k panic=1 root=/dev/vda rw init=/bench/init quiet pvbench.mode=ready pvbench.scratch=executor" + + +def preserve_listener(binary): + """Refuse to launch a different executable against a live Host listener.""" + generation = Path("/tmp") / f"pvisor-host-{os.geteuid()}" / "generation" + if not generation.exists(): + return + state = json.loads(generation.read_text()) + pid = state.get("pid") + if not isinstance(pid, int) or pid <= 0: + raise ValueError("existing Host listener cannot be verified") + try: + os.kill(pid, 0) + except ProcessLookupError: + return + try: + matches = COMMON.digest(Path("/proc") / str(pid) / "exe") == COMMON.digest(binary) + except OSError as error: + if not shutil.which("b3sum") or not state.get("digest"): + raise ValueError("cannot verify existing Host; b3sum is required") from error + selected = subprocess.check_output(["b3sum", "--no-names", str(binary)], text=True).strip() + matches = selected == state["digest"] + if not matches: + raise ValueError( + "selected binary differs from existing Host listener; use its paired binary or a private namespace" + ) + + +def prepare_template( + template, output=None, assets=None, binary=None, receipt=None, cpu_affinity="0,1", row=None +): + template = Path(template).resolve() + table = json.loads(template.read_text()) + if row is None and len(table) != 1: + raise ValueError("select one startup row during preparation") + row = row or next(iter(table)) + backend = next((key for key, label in LABELS.items() if label == row), None) + if backend is None or row not in table: + raise ValueError("one registered startup condition is required") + script = backend.replace("-", "_") + "_timing" + if table[row] != f"$ python3 @REPO@/benchmark.new/startup/{script}.py @BATCH@": + raise ValueError("startup row differs from its fixed timing script") + import timing as ready + + batch = COMMON.create_batch(template, output) + original_affinity = os.sched_getaffinity(0) + try: + selected = {int(cpu) for cpu in cpu_affinity.split(",")} + available = os.sched_getaffinity(0) + if not selected or not selected <= available: + raise ValueError("requested CPU affinity is unavailable") + os.sched_setaffinity(0, selected) + asset_path = assets or os.environ.get("PVISOR_BENCHMARK_ASSETS") + if backend != "native" and asset_path is None: + raise ValueError("prepared inputs required: use --assets or PVISOR_BENCHMARK_ASSETS") + assets = None if backend == "native" else Path(asset_path).resolve() + before = verify_reference_inputs(assets) if assets else None + artifacts = [] + if assets: + artifacts += [assets / "assets.json", assets / "input-manifest.json"] + binary = Path(binary or REPO / "target/release/pvisor").resolve() + if backend.startswith("pvisor-"): + preserve_listener(binary) + artifacts.append(binary) + kernel = None + if backend in ("fc-system", "qemu", "qemu-microvm"): + receipt_path = receipt or os.environ.get("PVISOR_BENCHMARK_KERNEL_RECEIPT") + if receipt_path is None: + raise ValueError( + "stock kernel receipt required: use --kernel-receipt or PVISOR_BENCHMARK_KERNEL_RECEIPT" + ) + receipt = Path(receipt_path).resolve() + kernel = verify_kernel_receipt(receipt, "fc-system") + if backend.startswith("qemu") and "initrd" not in kernel["paths"]: + raise ValueError( + "these QEMU templates require the prepared stock initrd; edit the template for an initrd-free condition" + ) + artifacts += [receipt, *map(Path, kernel["paths"].values())] + image = None + if backend == "docker": + image = os.environ.get("PVISOR_BENCHMARK_DOCKER_IMAGE") or json.loads( + (assets / "assets.json").read_text() + ).get("docker_image") + if not isinstance(image, str) or not image.startswith("sha256:"): + raise ValueError("Docker requires an immutable pre-prepared image ID") + actual = subprocess.check_output( + ["docker", "inspect", "--format", "{{.Id}}", image], text=True + ).strip() + if image != actual: + raise ValueError("prepared Docker image identity differs") + executable = ( + "firecracker" + if backend == "fc-system" + else "qemu-system-x86_64" + if backend.startswith("qemu") + else "docker" + if backend == "docker" + else None + ) + if executable: + resolved = shutil.which(executable) + if not resolved: + raise ValueError("missing executable: " + executable) + artifacts.append(Path(resolved)) + protocol = dict( + benchmark_id="B-STARTUP", + batch=batch.name, + backends=[backend], + assets=str(assets) if assets else None, + kernel=kernel, + kernel_receipt=str(receipt) if kernel else None, + input_before=before, + boot_args=BOOT if backend == "fc-system" else None, + device_transport="virtio-mmio" if backend == "fc-system" else None, + host_isolation="rootless_process", + staged_isolation="rootless_process", + cpu_affinity=cpu_affinity, + guest_cpus=2, + guest_memory_mib=128, + cache="warm; no host cache eviction", + docker_command=["docker"], + container_name="pvisor-startup-" + batch.name[-18:], + docker_image=image, + completion="normal zero exit; controlled Firecracker termination is never completion", + scope=COMMON.READY_EXIT_SCOPE, + measurement_engine="python-perf_counter_ns", + ready="exact REFERENCE_READY line received", + ) + COMMON.save(batch / "protocol.json", protocol) + artifacts.append(batch / "protocol.json") + helpers = [ + Path(__file__), + template.with_name("hooks.py"), + template.with_name("audit.py"), + REPO / "benchmark/pvisor/reference_inputs.py", + REPO / "benchmark/pvisor/firecracker_kernels.py", + ] + helpers += [ + template.with_name(name) for name in ("timing.py", "subjects.py", script + ".py") + ] + variables = dict( + BINARY=binary, + ASSETS=assets, + IMAGE=image, + CPU=cpu_affinity, + CONTAINER=protocol["container_name"], + BATCH_NAME=batch.name, + VMLINUX=kernel["paths"]["vmlinuz"] if kernel else "", + INITRD=kernel["paths"].get("initrd", "") if kernel else "", + ) + protocol["subject"] = subject(backend, variables | {"REPO": REPO, "BATCH": batch}) + COMMON.save(batch / "protocol.json", protocol) + prepare(batch, backend) + COMMON.materialize( + template, + batch, + variables, + artifacts, + batch / "workspace", + helpers, + row_config=dict(rows={row: backend}, runs=ready.RUNS, warmup=ready.WARMUP), + ) + ready.run_trial(batch, "preflight", 0, reset=False) + return batch + except BaseException as error: + COMMON.save(batch / "failure.json", dict(error=str(error), backend=backend)) + if (batch / "manifest.json").exists(): + manifest = json.loads((batch / "manifest.json").read_text()) + manifest["status"], manifest["error"] = "failed", str(error) + COMMON.save(batch / "manifest.json", manifest) + if (batch / "protocol.json").exists(): + cleanup_container(batch) + raise + finally: + os.sched_setaffinity(0, original_affinity) + + +if __name__ == "__main__": + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("template", type=Path) + p.add_argument("--output", type=Path) + p.add_argument("--assets", type=Path) + p.add_argument("--binary", type=Path) + p.add_argument("--kernel-receipt", type=Path) + p.add_argument("--cpu-affinity", default="0,1") + args = p.parse_args() + try: + print( + prepare_template( + args.template, + args.output, + args.assets, + args.binary, + args.kernel_receipt, + args.cpu_affinity, + ) + ) + except (ValueError, OSError, subprocess.SubprocessError) as error: + p.exit(1, f"{error}\n") diff --git a/benchmark.new/startup/diagnostic.json b/benchmark.new/startup/diagnostic.json new file mode 100644 index 000000000..95834f8b2 --- /dev/null +++ b/benchmark.new/startup/diagnostic.json @@ -0,0 +1,10 @@ +{ + "unprepared": { + "N": null, + "Median ms": null, + "Mean ms": null, + "SD ms": null, + "Min ms": null, + "Max ms": null + } +} diff --git a/benchmark.new/startup/docker_timing.json b/benchmark.new/startup/docker_timing.json new file mode 100644 index 000000000..ac5b0a073 --- /dev/null +++ b/benchmark.new/startup/docker_timing.json @@ -0,0 +1,3 @@ +{ + "Docker": "$ python3 @REPO@/benchmark.new/startup/docker_timing.py @BATCH@" +} diff --git a/benchmark.new/startup/docker_timing.py b/benchmark.new/startup/docker_timing.py new file mode 100644 index 000000000..fd2a2f5f5 --- /dev/null +++ b/benchmark.new/startup/docker_timing.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python3 +"""B-STARTUP: Docker, 3 warmups / 60 same-launch Ready and normal Exit samples. + +Prepared inputs, 2 CPU / 128 MiB for VMs, warm caches; preparation and Python +startup excluded. Correct output and normal exit gate both metrics. +""" + +from timing import main + +if __name__ == "__main__": + main("docker") diff --git a/benchmark.new/startup/fc_system_timing.json b/benchmark.new/startup/fc_system_timing.json new file mode 100644 index 000000000..3285ea86f --- /dev/null +++ b/benchmark.new/startup/fc_system_timing.json @@ -0,0 +1,3 @@ +{ + "Firecracker": "$ python3 @REPO@/benchmark.new/startup/fc_system_timing.py @BATCH@" +} diff --git a/benchmark.new/startup/fc_system_timing.py b/benchmark.new/startup/fc_system_timing.py new file mode 100644 index 000000000..11267cc7d --- /dev/null +++ b/benchmark.new/startup/fc_system_timing.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python3 +"""B-STARTUP: Firecracker, 3 warmups / 60 same-launch Ready and normal Exit samples. + +Prepared inputs, 2 CPU / 128 MiB for VMs, warm caches; preparation and Python +startup excluded. Correct output and normal exit gate both metrics. +""" + +from timing import main + +if __name__ == "__main__": + main("fc-system") diff --git a/benchmark.new/startup/guest_init.json b/benchmark.new/startup/guest_init.json new file mode 100644 index 000000000..95834f8b2 --- /dev/null +++ b/benchmark.new/startup/guest_init.json @@ -0,0 +1,10 @@ +{ + "unprepared": { + "N": null, + "Median ms": null, + "Mean ms": null, + "SD ms": null, + "Min ms": null, + "Max ms": null + } +} diff --git a/benchmark.new/startup/hooks.py b/benchmark.new/startup/hooks.py new file mode 100644 index 000000000..3da92df84 --- /dev/null +++ b/benchmark.new/startup/hooks.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python3 +"""Prepare fresh inputs and validate each paired startup trial.""" + +import json +import re +import shutil +import subprocess + + +def save(path, value): + path.write_text(json.dumps(value, indent=2) + "\n") + + +def archive(root): + current = root / "current" + if current.exists(): + history = root / "trials" + history.mkdir(exist_ok=True) + # The validated disk is disposable; retain output, bundles and timestamps. + if (current / "condition.json").is_file(): + condition = json.loads((current / "condition.json").read_text()) + if condition.get("validated") is True: + (current / "rootfs.ext4").unlink(missing_ok=True) + current.rename(history / f"{len(list(history.iterdir())):05d}") + + +def prepare(root, backend): + protocol = json.loads((root / "protocol.json").read_text()) + archive(root) + current = root / "current" + work = current / "workspace" + current.mkdir(parents=True) + stable = root / "workspace" + stable.mkdir(exist_ok=True) + for child in stable.iterdir(): + if child.is_dir() and not child.is_symlink(): + shutil.rmtree(child) + else: + child.unlink() + work.symlink_to(stable, target_is_directory=True) + (current / "stderr").touch() + if protocol["assets"]: + subprocess.run( + ["cp", "-a", "--reflink=auto", protocol["assets"] + "/rootfs/work/.", str(work)], + check=True, + ) + (work / "startup-canary").write_bytes(b"unchanged startup input\n") + (current / "runs").mkdir() + save(current / "condition.json", {"backend": backend, "validated": False}) + if backend in ("fc-system", "qemu", "qemu-microvm"): + subprocess.run( + [ + "cp", + "--reflink=auto", + protocol["assets"] + "/agent-env.ext4", + str(current / "rootfs.ext4"), + ], + check=True, + ) + if backend == "fc-system": + kernel = protocol["kernel"]["paths"] + boot = {"kernel_image_path": kernel["kernel"], "boot_args": protocol["boot_args"]} + if "initrd" in kernel: + boot["initrd_path"] = kernel["initrd"] + (current / "firecracker.log").touch() + save( + current / "firecracker.json", + { + "boot-source": boot, + "drives": [ + { + "drive_id": "rootfs", + "path_on_host": str(current / "rootfs.ext4"), + "is_root_device": True, + "is_read_only": False, + } + ], + "machine-config": {"vcpu_count": 2, "mem_size_mib": 128}, + "logger": { + "log_path": str(current / "firecracker.log"), + "level": "Warning", + "show_level": True, + "show_log_origin": True, + }, + }, + ) + + +def validate_output(stdout, stderr, guest=False): + if re.search(r"kernel panic", stdout + "\n" + stderr, re.I): + raise ValueError("guest kernel panic") + lines = stdout.splitlines() + ready = [i for i, line in enumerate(lines) if line.startswith("REFERENCE_READY")] + result = [i for i, line in enumerate(lines) if line.startswith("REFERENCE_RESULT")] + exits = [i for i, line in enumerate(lines) if line.startswith("REFERENCE_EXIT")] + if len(ready) != 1 or lines[ready[0]] != "REFERENCE_READY" or len(result) != 1: + raise ValueError("missing or duplicate startup markers") + if json.loads(lines[result[0]].removeprefix("REFERENCE_RESULT ")) != { + "mode": "ready", + "correctness": "passed", + }: + raise ValueError("incorrect startup result") + if not ready[0] < result[0]: + raise ValueError("startup markers out of order") + if guest: + if len(exits) != 1 or lines[exits[0]] != "REFERENCE_EXIT 0" or not result[0] < exits[0]: + raise ValueError("guest did not report a unique ordered successful exit") + elif exits: + raise ValueError("unexpected guest exit marker in process output") + + +def validate(root, backend): + protocol = json.loads((root / "protocol.json").read_text()) + current = root / "current" + condition = json.loads((current / "condition.json").read_text()) + if condition != {"backend": backend, "validated": False}: + raise ValueError("wrong trial condition or duplicate validation") + stdout, stderr = ((current / name).read_text(errors="replace") for name in ("stdout", "stderr")) + if backend == "fc-system": + stderr += (current / "firecracker.log").read_text(errors="replace") + validate_output(stdout, stderr, backend in ("fc-system", "qemu", "qemu-microvm")) + if (current / "workspace/startup-canary").read_bytes() != b"unchanged startup input\n": + raise ValueError("host workspace was modified") + if backend.startswith("pvisor-"): + bundles = ( + [current / "stage/run-bundle.json"] + if backend in ("pvisor-staged", "pvisor-vm") + else list((current / "runs").glob("run-*/run-bundle.json")) + ) + bundles = [path for path in bundles if path.is_file()] + if len(bundles) != 1: + raise ValueError("expected exactly one Run Bundle") + bundle = json.loads(bundles[0].read_text()) + run = bundle["run"] + expected = ( + "virtual_machine" + if backend == "pvisor-vm" + else protocol["staged_isolation"] + if backend == "pvisor-staged" + else protocol["host_isolation"] + ) + if ( + run["state"] != "completed" + or run["exit_code"] != 0 + or run["executor"]["isolation"] != expected + ): + raise ValueError("wrong completed Run or observed isolation") + staged = backend in ("pvisor-staged", "pvisor-vm") + if bundle["safety"]["filesystem_changes_staged"] != staged: + raise ValueError("wrong staging observation") + if staged and any(path.is_file() for path in (current / "stage/upper").rglob("*")): + raise ValueError("read-only startup payload produced staged file changes") + condition["validated"] = True + save(current / "condition.json", condition) + + +def cleanup_container(root): + protocol = json.loads((root / "protocol.json").read_text()) + if "docker" not in protocol["backends"]: + return + docker = protocol["docker_command"] + name = protocol["container_name"] + listed = subprocess.check_output( + [*docker, "ps", "--all", "--filter", "name=^" + name + "$", "--format", "{{.ID}}"], + text=True, + timeout=10, + ).strip() + if not listed: + return + result = subprocess.run( + [*docker, "inspect", "--format", '{{index .Config.Labels "pvisor.startup.batch"}}', name], + capture_output=True, + text=True, + timeout=10, + ) + if result.returncode != 0 or result.stdout.strip() != protocol["batch"]: + raise ValueError("refuse to remove a container without this benchmark label") + subprocess.run([*docker, "rm", "--force", name], check=True, capture_output=True, timeout=10) diff --git a/benchmark.new/startup/kernel.json b/benchmark.new/startup/kernel.json new file mode 100644 index 000000000..95834f8b2 --- /dev/null +++ b/benchmark.new/startup/kernel.json @@ -0,0 +1,10 @@ +{ + "unprepared": { + "N": null, + "Median ms": null, + "Mean ms": null, + "SD ms": null, + "Min ms": null, + "Max ms": null + } +} diff --git a/benchmark.new/startup/lazy_startup.json b/benchmark.new/startup/lazy_startup.json new file mode 100644 index 000000000..95834f8b2 --- /dev/null +++ b/benchmark.new/startup/lazy_startup.json @@ -0,0 +1,10 @@ +{ + "unprepared": { + "N": null, + "Median ms": null, + "Mean ms": null, + "SD ms": null, + "Min ms": null, + "Max ms": null + } +} diff --git a/benchmark.new/startup/native_timing.json b/benchmark.new/startup/native_timing.json new file mode 100644 index 000000000..7fd9f8236 --- /dev/null +++ b/benchmark.new/startup/native_timing.json @@ -0,0 +1,3 @@ +{ + "Native": "$ python3 @REPO@/benchmark.new/startup/native_timing.py @BATCH@" +} diff --git a/benchmark.new/startup/native_timing.py b/benchmark.new/startup/native_timing.py new file mode 100644 index 000000000..e8b3b66c6 --- /dev/null +++ b/benchmark.new/startup/native_timing.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python3 +"""B-STARTUP: Native, 3 warmups / 60 same-launch Ready and normal Exit samples. + +Prepared inputs, 2 CPU / 128 MiB for VMs, warm caches; preparation and Python +startup excluded. Correct output and normal exit gate both metrics. +""" + +from timing import main + +if __name__ == "__main__": + main("native") diff --git a/benchmark.new/startup/native_timing.render.json b/benchmark.new/startup/native_timing.render.json new file mode 100644 index 000000000..75e110d58 --- /dev/null +++ b/benchmark.new/startup/native_timing.render.json @@ -0,0 +1,15 @@ +{ + "Native": { + "N": 60, + "Ready median ms": "0.40", + "Ready mean ms": "0.41", + "Ready SD ms": "0.07", + "Ready min ms": "0.35", + "Ready max ms": "0.89", + "Exit median ms": "0.44", + "Exit mean ms": "0.47", + "Exit SD ms": "0.16", + "Exit min ms": "0.39", + "Exit max ms": "1.54" + } +} diff --git a/benchmark.new/startup/publish.py b/benchmark.new/startup/publish.py new file mode 100644 index 000000000..734cf3c5a --- /dev/null +++ b/benchmark.new/startup/publish.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python3 +"""Publish audited paired startup timing tables and CSV.""" + +import argparse +import csv +import importlib.util +import json +import sys +from pathlib import Path + +from subjects import LABELS, NAMES + +ROOT = Path(__file__).resolve().parents[1] +_spec = importlib.util.spec_from_file_location("benchmark_common", ROOT / "common.py") +COMMON = importlib.util.module_from_spec(_spec) +_spec.loader.exec_module(COMMON) + + +def publish_startup(directory, source): + # Explicit module loading avoids the startup/common.py import name collision. + spec = importlib.util.spec_from_file_location("benchmark_update", ROOT / "update.py") + module = importlib.util.module_from_spec(spec) + sys.path.insert(0, str(ROOT)) + spec.loader.exec_module(module) + source = Path(source) + metadata = json.loads((Path(directory) / "manifest.json").read_text()) + scope = metadata["metric_scope"] + if scope != COMMON.READY_EXIT_SCOPE: + raise ValueError("startup publication requires paired Ready / Exit data") + stem = "startup-timing" + filename = stem + ".csv" + csvs = [source / locale / "benchmarks" / filename for locale in ("zh", "en")] + if (csvs[0].read_bytes() if csvs[0].exists() else None) != ( + csvs[1].read_bytes() if csvs[1].exists() else None + ): + raise ValueError("bilingual startup CSV histories differ") + validated = module.validate_batch(directory) + for path in csvs: + module._publish_csv(directory, path, *validated, dry_run=True) + if path.exists(): + with path.open() as stream: + if any(row["metric_scope"] != scope for row in csv.DictReader(stream)): + raise ValueError("mixed Ready metric scopes") + provenance_path = source.parent / "overrides/assets/benchmarks" / (stem + ".provenance.json") + previous = json.loads(provenance_path.read_text()) if provenance_path.exists() else {} + # Validate the new configuration before writing CSVs. + current_configuration = configuration( + directory, {"batch": metadata["batch"], "condition": metadata["conditions"][0]}, {} + ) + for locale, path in zip(("zh", "en"), csvs): + module._publish_csv(directory, path, *validated) + with path.open() as stream: + latest = {row["condition"]: row for row in csv.DictReader(stream)} + if any(r["metric_scope"] != scope for r in latest.values()): + raise ValueError("mixed Ready metric scopes") + from timing import FIELDS + + fields = FIELDS + translations = { + "median": "中位数", + "mean": "均值", + "SD": "标准差", + "min": "最小", + "max": "最大", + } + labels = [ + "N", + *[ + key.replace(key.split()[1], translations[key.split()[1]]) if locale == "zh" else key + for key in fields[1:] + ], + ] + table = {} + for name in NAMES: + row = latest.get(name) + values = ( + [int(row["runs"]), *[f"{float(row[key]):.2f}" for key in fields[1:]]] + if row + else [None] * len(fields) + ) + table[LABELS[name]] = dict(zip(labels, values)) + for metric in ("Ready", "Exit"): + projection = { + name: {k: v for k, v in columns.items() if k == "N" or k.startswith(metric + " ")} + for name, columns in table.items() + } + COMMON.save(path.with_name(stem + "." + metric.lower() + ".render.json"), projection) + provenance = { + LABELS[name]: dict( + batch=row["batch"], + metric_scope=row["metric_scope"], + measurement_engine="python-perf_counter_ns", + evidence_sha256=row["evidence_sha256"], + manifest_sha256=row["manifest_sha256"], + configuration=( + current_configuration + if row["batch"] == metadata["batch"] + else configuration(directory, row, previous.get(LABELS[name], {})) + ), + ) + for name, row in latest.items() + } + COMMON.save(provenance_path, provenance) + if source.resolve() == (ROOT.parent / "docs/src").resolve(): + from measurement_coverage import coverage + + coverage() + + +def configuration(directory, row, previous): + """Retain the configuration of each independent latest successful cohort.""" + root = Path(directory) + if root.name != row["batch"]: + return previous.get("configuration") if previous.get("batch") == row["batch"] else None + protocol = json.loads((root / "protocol.json").read_text()) + manifest = json.loads((root / "manifest.json").read_text()) + guest = row["condition"] in ("pvisor-vm", "fc-system", "qemu", "qemu-microvm") + return compact_configuration( + { + "cpu_affinity": manifest["cpu_affinity"], + "guest_cpus": protocol["guest_cpus"] if guest else None, + "guest_memory_mib": protocol["guest_memory_mib"] if guest else None, + "device_transport": protocol.get("device_transport"), + "kernel": protocol["kernel"], + "cache": protocol["cache"], + "source_commit_scope": manifest["source_commit_scope"], + "artifacts": manifest["artifacts"], + "publisher_sha256": COMMON.digest(Path(__file__)), + } + ) + + +def compact_configuration(config): + """Publish configuration facts and hashes; full receipts remain in the batch.""" + result = {key: value for key, value in config.items() if value is not None} + result["artifacts"] = COMMON.artifact_summary(config.get("artifacts", {})) + kernel = config.get("kernel") + if kernel and "record" in kernel: + record = kernel["record"] + result["kernel"] = { + "receipt_sha256": kernel["receipt_sha256"], + "variant": record["variant"], + "provenance": { + key: value + for key, value in record["provenance"].items() + if key not in ("rpm_info", "rpm_file_digest_checks") + }, + "artifacts": {name: item["sha256"] for name, item in record["artifacts"].items()}, + } + return result + + +if __name__ == "__main__": + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("directory", type=Path) + p.add_argument("--docs-source", type=Path, default=ROOT.parent / "docs/src") + args = p.parse_args() + try: + publish_startup(args.directory, args.docs_source) + except (ValueError, KeyError, OSError) as error: + p.exit(1, f"{error}\n") diff --git a/benchmark.new/startup/pvisor_host_timing.json b/benchmark.new/startup/pvisor_host_timing.json new file mode 100644 index 000000000..c9e33ff9f --- /dev/null +++ b/benchmark.new/startup/pvisor_host_timing.json @@ -0,0 +1,3 @@ +{ + "pVisor host": "$ python3 @REPO@/benchmark.new/startup/pvisor_host_timing.py @BATCH@" +} diff --git a/benchmark.new/startup/pvisor_host_timing.py b/benchmark.new/startup/pvisor_host_timing.py new file mode 100644 index 000000000..78769515e --- /dev/null +++ b/benchmark.new/startup/pvisor_host_timing.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python3 +"""B-STARTUP: pVisor host, 3 warmups / 60 same-launch Ready and normal Exit samples. + +Prepared inputs, 2 CPU / 128 MiB for VMs, warm caches; preparation and Python +startup excluded. Correct output and normal exit gate both metrics. +""" + +from timing import main + +if __name__ == "__main__": + main("pvisor-host") diff --git a/benchmark.new/startup/pvisor_staged_timing.json b/benchmark.new/startup/pvisor_staged_timing.json new file mode 100644 index 000000000..7ba27f922 --- /dev/null +++ b/benchmark.new/startup/pvisor_staged_timing.json @@ -0,0 +1,3 @@ +{ + "pVisor staged": "$ python3 @REPO@/benchmark.new/startup/pvisor_staged_timing.py @BATCH@" +} diff --git a/benchmark.new/startup/pvisor_staged_timing.py b/benchmark.new/startup/pvisor_staged_timing.py new file mode 100644 index 000000000..c2c4bc004 --- /dev/null +++ b/benchmark.new/startup/pvisor_staged_timing.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python3 +"""B-STARTUP: pVisor staged, 3 warmups / 60 same-launch Ready and normal Exit samples. + +Prepared inputs, 2 CPU / 128 MiB for VMs, warm caches; preparation and Python +startup excluded. Correct output and normal exit gate both metrics. +""" + +from timing import main + +if __name__ == "__main__": + main("pvisor-staged") diff --git a/benchmark.new/startup/pvisor_vm_timing.json b/benchmark.new/startup/pvisor_vm_timing.json new file mode 100644 index 000000000..44927ee0f --- /dev/null +++ b/benchmark.new/startup/pvisor_vm_timing.json @@ -0,0 +1,3 @@ +{ + "pVisor VM": "$ python3 @REPO@/benchmark.new/startup/pvisor_vm_timing.py @BATCH@" +} diff --git a/benchmark.new/startup/pvisor_vm_timing.py b/benchmark.new/startup/pvisor_vm_timing.py new file mode 100644 index 000000000..f1465dc17 --- /dev/null +++ b/benchmark.new/startup/pvisor_vm_timing.py @@ -0,0 +1,6 @@ +"""B-STARTUP: pVisor VM paired Ready / Exit, 3 warmups and 60 samples.""" + +from timing import main + +if __name__ == "__main__": + main("pvisor-vm") diff --git a/benchmark.new/startup/qemu_microvm_timing.json b/benchmark.new/startup/qemu_microvm_timing.json new file mode 100644 index 000000000..3090aaa76 --- /dev/null +++ b/benchmark.new/startup/qemu_microvm_timing.json @@ -0,0 +1,3 @@ +{ + "QEMU microvm": "$ python3 @REPO@/benchmark.new/startup/qemu_microvm_timing.py @BATCH@" +} diff --git a/benchmark.new/startup/qemu_microvm_timing.py b/benchmark.new/startup/qemu_microvm_timing.py new file mode 100644 index 000000000..f1fddbac6 --- /dev/null +++ b/benchmark.new/startup/qemu_microvm_timing.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python3 +"""B-STARTUP: QEMU microvm, 3 warmups / 60 same-launch Ready and normal Exit samples. + +Prepared inputs, 2 CPU / 128 MiB for VMs, warm caches; preparation and Python +startup excluded. Correct output and normal exit gate both metrics. +""" + +from timing import main + +if __name__ == "__main__": + main("qemu-microvm") diff --git a/benchmark.new/startup/qemu_timing.json b/benchmark.new/startup/qemu_timing.json new file mode 100644 index 000000000..3232e0bec --- /dev/null +++ b/benchmark.new/startup/qemu_timing.json @@ -0,0 +1,3 @@ +{ + "QEMU": "$ python3 @REPO@/benchmark.new/startup/qemu_timing.py @BATCH@" +} diff --git a/benchmark.new/startup/qemu_timing.py b/benchmark.new/startup/qemu_timing.py new file mode 100644 index 000000000..4e1fb9cb7 --- /dev/null +++ b/benchmark.new/startup/qemu_timing.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python3 +"""B-STARTUP: QEMU, 3 warmups / 60 same-launch Ready and normal Exit samples. + +Prepared inputs, 2 CPU / 128 MiB for VMs, warm caches; preparation and Python +startup excluded. Correct output and normal exit gate both metrics. +""" + +from timing import main + +if __name__ == "__main__": + main("qemu") diff --git a/benchmark.new/startup/revision.json b/benchmark.new/startup/revision.json new file mode 100644 index 000000000..a97ac1251 --- /dev/null +++ b/benchmark.new/startup/revision.json @@ -0,0 +1,4 @@ +{ + "baseline": {"Ready median ms": null, "Exit median ms": null}, + "candidate": {"Ready median ms": null, "Exit median ms": null} +} diff --git a/benchmark.new/startup/subjects.py b/benchmark.new/startup/subjects.py new file mode 100644 index 000000000..873d3466b --- /dev/null +++ b/benchmark.new/startup/subjects.py @@ -0,0 +1,191 @@ +"""Fixed startup argv; preparation and timing live outside the subject command.""" + +import re + +NAMES = ( + "native", + "pvisor-host", + "pvisor-staged", + "pvisor-vm", + "docker", + "fc-system", + "qemu", + "qemu-microvm", +) +LABELS = dict( + zip( + NAMES, + ( + "Native", + "pVisor host", + "pVisor staged", + "pVisor VM", + "Docker", + "Firecracker", + "QEMU", + "QEMU microvm", + ), + ) +) +SUBJECTS = { + "native": [ + "/bin/sh", + "-c", + 'printf \'REFERENCE_READY\\nREFERENCE_RESULT {"mode":"ready","correctness":"passed"}\\n\'', + ], + "pvisor-host": [ + "@BINARY@", + "run", + "--no-agent-defaults", + "--overlaynet", + "off", + "--stdio", + "inherit", + "--timeout", + "30s", + "--filesystem", + "host", + "--", + "/bin/sh", + "-c", + 'printf \'REFERENCE_READY\\nREFERENCE_RESULT {"mode":"ready","correctness":"passed"}\\n\'', + ], + "pvisor-staged": [ + "@BINARY@", + "run", + "--no-agent-defaults", + "--overlaynet", + "off", + "--stdio", + "inherit", + "--timeout", + "30s", + "--stage", + "@BATCH@/current/stage", + "--", + "/bin/sh", + "-c", + 'printf \'REFERENCE_READY\\nREFERENCE_RESULT {"mode":"ready","correctness":"passed"}\\n\'', + ], + "pvisor-vm": [ + "@BINARY@", + "run", + "--no-agent-defaults", + "--overlaynet", + "off", + "--stdio", + "inherit", + "--timeout", + "30s", + "--stage", + "@BATCH@/current/stage", + "--vm", + "--rootfs", + "@ASSETS@/rootfs", + "--cpu", + "2", + "--memory", + "128MiB", + "--", + "/bin/sh", + "-c", + 'printf \'REFERENCE_READY\\nREFERENCE_RESULT {"mode":"ready","correctness":"passed"}\\n\'', + ], + "docker": [ + "docker", + "run", + "--rm", + "--pull", + "never", + "--network", + "none", + "--name", + "@CONTAINER@", + "--label", + "pvisor.startup.batch=@BATCH_NAME@", + "--mount", + "type=bind,source=@BATCH@/workspace,target=/work", + "--workdir", + "/work", + "--entrypoint", + "/bench/affinity", + "@IMAGE@", + "@CPU@", + "/bin/sh", + "-c", + 'printf \'REFERENCE_READY\\nREFERENCE_RESULT {"mode":"ready","correctness":"passed"}\\n\'', + ], + "fc-system": ["firecracker", "--no-api", "--config-file", "@BATCH@/current/firecracker.json"], + "qemu": [ + "qemu-system-x86_64", + "-machine", + "q35", + "-accel", + "kvm", + "-cpu", + "host", + "-smp", + "2", + "-m", + "128", + "-nodefaults", + "-display", + "none", + "-serial", + "stdio", + "-no-reboot", + "-kernel", + "@VMLINUX@", + "-append", + "console=ttyS0 reboot=k panic=1 root=/dev/vda rw init=/bench/init quiet " + "pvbench.mode=ready pvbench.scratch=executor", + "-drive", + "file=@BATCH@/current/rootfs.ext4,format=raw,if=none,id=root", + "-device", + "virtio-blk-pci,drive=root", + "-initrd", + "@INITRD@", + ], + "qemu-microvm": [ + "qemu-system-x86_64", + "-machine", + "microvm,acpi=off,x-option-roms=off,pit=off,pic=off,rtc=on", + "-accel", + "kvm", + "-cpu", + "host", + "-smp", + "2", + "-m", + "128", + "-nodefaults", + "-display", + "none", + "-serial", + "stdio", + "-no-reboot", + "-kernel", + "@VMLINUX@", + "-append", + "console=ttyS0 reboot=t panic=1 root=/dev/vda rw init=/bench/init quiet " + "pvbench.mode=ready pvbench.scratch=executor pci=off", + "-drive", + "file=@BATCH@/current/rootfs.ext4,format=raw,if=none,id=root", + "-device", + "virtio-blk-device,drive=root", + "-initrd", + "@INITRD@", + ], +} + + +def subject(backend, variables): + """Expand prepared paths in each argv element without invoking a shell.""" + argv = [] + for value in SUBJECTS[backend]: + for name, replacement in variables.items(): + value = value.replace("@" + name + "@", str(replacement)) + if re.search(r"@[A-Z_]+@", value): + raise ValueError("unresolved subject path") + argv.append(value) + return argv diff --git a/benchmark.new/startup/timing.json b/benchmark.new/startup/timing.json new file mode 100644 index 000000000..65b691f9d --- /dev/null +++ b/benchmark.new/startup/timing.json @@ -0,0 +1,10 @@ +{ + "Native": "$ python3 @REPO@/benchmark.new/startup/native_timing.py @BATCH@", + "pVisor host": "$ python3 @REPO@/benchmark.new/startup/pvisor_host_timing.py @BATCH@", + "pVisor staged": "$ python3 @REPO@/benchmark.new/startup/pvisor_staged_timing.py @BATCH@", + "pVisor VM": "$ python3 @REPO@/benchmark.new/startup/pvisor_vm_timing.py @BATCH@", + "Docker": "$ python3 @REPO@/benchmark.new/startup/docker_timing.py @BATCH@", + "Firecracker": "$ python3 @REPO@/benchmark.new/startup/fc_system_timing.py @BATCH@", + "QEMU": "$ python3 @REPO@/benchmark.new/startup/qemu_timing.py @BATCH@", + "QEMU microvm": "$ python3 @REPO@/benchmark.new/startup/qemu_microvm_timing.py @BATCH@" +} diff --git a/benchmark.new/startup/timing.py b/benchmark.new/startup/timing.py new file mode 100644 index 000000000..abaf0998f --- /dev/null +++ b/benchmark.new/startup/timing.py @@ -0,0 +1,212 @@ +#!/usr/bin/env python3 +"""B-STARTUP: observe paired Ready and normal Exit for a prepared backend. + +Motivation: waiting for a usable prepared VM determines interaction latency. +Conclusion sought: launch-to-first-valid-output latency at 2 vCPU / 128 MiB. +Design: 3 warmups, 60 samples, fresh stage/workspace, warm host caches; output, +normal exit and any pVisor Run Bundle must pass after timing stops. Interpreter +startup, input preparation and teardown are excluded; Popen and pipe observation +are included. Exit uses the same start and includes work through normal exit. +Measurements use the Python perf_counter_ns clock. +""" + +import argparse +import importlib.util +import json +import os +import selectors +import signal +import statistics +import subprocess +import time +from pathlib import Path + +from hooks import archive, cleanup_container, prepare, validate + +ROOT = Path(__file__).resolve().parents[1] +_spec = importlib.util.spec_from_file_location("ready_common", ROOT / "common.py") +COMMON = importlib.util.module_from_spec(_spec) +_spec.loader.exec_module(COMMON) +RUNS = 60 +WARMUP = 3 +TIMEOUT = 30 +READY_FIELDS = ( + "N", + "Ready median ms", + "Ready mean ms", + "Ready SD ms", + "Ready min ms", + "Ready max ms", +) +EXIT_FIELDS = tuple(field.replace("Ready", "Exit") for field in READY_FIELDS) +FIELDS = (*READY_FIELDS, *EXIT_FIELDS[1:]) + + +def summarize(samples, label="Ready"): + if len(samples) < 2 or any( + not isinstance(n, int) or isinstance(n, bool) or n <= 0 for n in samples + ): + raise ValueError("at least two positive integer Ready durations are required") + ms = [n / 1_000_000 for n in samples] + return dict( + zip( + tuple(field.replace("Ready", label) for field in READY_FIELDS), + [ + len(ms), + statistics.median(ms), + statistics.mean(ms), + statistics.stdev(ms), + min(ms), + max(ms), + ], + ) + ) + + +def summarize_pairs(samples): + if any( + set(s) != {"ready_ns", "exit_ns"} + or type(s["ready_ns"]) is not int + or type(s["exit_ns"]) is not int + or not 0 < s["ready_ns"] <= s["exit_ns"] + for s in samples + ): + raise ValueError("Ready and Exit must share one valid trial") + return summarize([s["ready_ns"] for s in samples]) | summarize( + [s["exit_ns"] for s in samples], "Exit" + ) + + +def capture(argv, cwd, env, log, timeout=TIMEOUT): + """Start the clock immediately before Popen; stop at the exact line receipt. + + Drain output and wait for normal exit afterwards. Timeout/failure kills only + this trial's owned process group, preserving output and diagnostic timing. + """ + record = { + "command": argv, + "start_ns": None, + "ready_ns": None, + "end_ns": None, + "exit_code": None, + } + process = None + try: + with log.open("ab", buffering=0) as output, selectors.DefaultSelector() as selector: + start = time.perf_counter_ns() + record["start_ns"] = start + process = subprocess.Popen( + argv, + cwd=cwd, + env=env, + stdin=subprocess.DEVNULL, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + bufsize=0, + start_new_session=True, + ) + selector.register(process.stdout, selectors.EVENT_READ) + pending = b"" + deadline = start / 1e9 + timeout + while selector.get_map(): + remaining = deadline - time.perf_counter() + if remaining <= 0: + raise TimeoutError("Ready trial did not finish normally within timeout") + for key, _ in selector.select(min(remaining, 0.1)): + block = os.read(key.fd, 65536) + received = time.perf_counter_ns() + if not block: + selector.unregister(key.fileobj) + continue + output.write(block) + pending += block + while b"\n" in pending: + line, pending = pending.split(b"\n", 1) + if line.rstrip(b"\r") == b"REFERENCE_READY": + if record["ready_ns"] is not None: + raise ValueError("duplicate Ready marker") + record["ready_ns"] = received + record["exit_code"] = process.wait(timeout=max(0.001, deadline - time.perf_counter())) + record["end_ns"] = time.perf_counter_ns() + if record["exit_code"] != 0 or record["ready_ns"] is None: + raise ValueError("trial failed or did not emit an exact Ready line") + record["ready_duration_ns"] = record["ready_ns"] - record["start_ns"] + record["exit_duration_ns"] = record["end_ns"] - record["start_ns"] + return record + except BaseException as error: + record["error"] = str(error) + raise + finally: + if process is not None: + try: + os.killpg(process.pid, signal.SIGKILL) + except ProcessLookupError: + pass + process.wait() + process.stdout.close() + COMMON.save(log.with_suffix(".timing.json"), record) + + +def run_trial(batch, phase, index, reset=True): + protocol = json.loads((batch / "protocol.json").read_text()) + backend = protocol["backends"][0] + if reset: + prepare(batch, backend) + env = os.environ.copy() | { + "PVISOR_RUN_HOME": str(batch / "current/runs"), + "PVISOR_STARTUP_TIMING": "0", + "PVISOR_FS_PROFILE": "0", + } + record = capture(protocol["subject"], batch / "workspace", env, batch / "current/stdout") + record.update(phase=phase, index=index) + COMMON.save(batch / "current/timing.json", record) + (batch / "current/stdout.timing.json").unlink() + validate(batch, backend) + archive(batch) + return {"ready_ns": record["ready_duration_ns"], "exit_ns": record["exit_duration_ns"]} + + +def measure(batch): + manifest = json.loads((batch / "manifest.json").read_text()) + if manifest["status"] != "prepared" or manifest["metric_scope"] != COMMON.READY_EXIT_SCOPE: + raise ValueError("expected a freshly prepared Ready / Exit batch") + if len(list((batch / "trials").glob("*/timing.json"))) != 1: + raise ValueError("Ready batch must have only its preflight; do not reuse a measured batch") + for path, sha in manifest["artifacts"].items(): + if COMMON.digest(path) != sha: + raise ValueError("prepared artifact changed: " + path) + for i in range(WARMUP): + run_trial(batch, "warmup", i) + samples = [run_trial(batch, "sample", i) for i in range(RUNS)] + result = summarize_pairs(samples) + COMMON.save( + batch / "timing.samples.json", + { + "engine": "python-perf_counter_ns", + "unit": "nanosecond", + "samples": samples, + "result": result, + }, + ) + return result + + +def main(backend): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("batch", type=Path) + args = parser.parse_args() + try: + batch = args.batch.resolve() + protocol = json.loads((batch / "protocol.json").read_text()) + if protocol["backends"] != [backend]: + raise ValueError("script backend differs from prepared protocol") + print(json.dumps(measure(batch))) + except (ValueError, OSError, subprocess.SubprocessError) as error: + if (args.batch / "manifest.json").exists(): + manifest = json.loads((args.batch / "manifest.json").read_text()) + if manifest.get("status") == "prepared" and manifest.get("conditions") == [backend]: + manifest.update(status="failed", error=str(error)) + COMMON.save(args.batch / "manifest.json", manifest) + if (args.batch / "protocol.json").exists(): + cleanup_container(args.batch) + parser.exit(1, f"{error}\n") diff --git a/benchmark.new/startup/timing.render.json b/benchmark.new/startup/timing.render.json new file mode 100644 index 000000000..74e65b154 --- /dev/null +++ b/benchmark.new/startup/timing.render.json @@ -0,0 +1,15 @@ +{ + "Native": { + "N": 60, + "Ready median ms": "0.37", + "Ready mean ms": "0.38", + "Ready SD ms": "0.05", + "Ready min ms": "0.35", + "Ready max ms": "0.73", + "Exit median ms": "0.40", + "Exit mean ms": "0.43", + "Exit SD ms": "0.15", + "Exit min ms": "0.39", + "Exit max ms": "1.49" + } +} diff --git a/benchmark.new/tests/test_docs.py b/benchmark.new/tests/test_docs.py new file mode 100644 index 000000000..43f9fe079 --- /dev/null +++ b/benchmark.new/tests/test_docs.py @@ -0,0 +1,296 @@ +"""Dynamic table publication and inline historical documentation tables.""" + +import csv +import importlib.util +import json +import sys +import unittest +from pathlib import Path +from unittest.mock import patch + +from tests.fixtures import workspace + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT)) +spec = importlib.util.spec_from_file_location("benchmark_docs", ROOT / "docs.py") +DOCS = importlib.util.module_from_spec(spec) +spec.loader.exec_module(DOCS) + + +class DocumentationTests(unittest.TestCase): + def test_all_benchmark_articles_and_bound_data_are_consistent(self): + pages, tables = DOCS.check_inventory() + self.assertGreaterEqual(pages, 42) + self.assertEqual(tables, 8) + manifest = json.loads((ROOT / "docs.json").read_text()) + pairs = {} + for page in manifest["pages"]: + self.assertTrue(all(t["kind"] in ("published", "coverage") for t in page["tables"])) + pairs.setdefault(page["page"], []).append(page) + for pair in pairs.values(): + self.assertEqual(len(pair), 2) + self.assertEqual(pair[0]["benchmark_ids"], pair[1]["benchmark_ids"]) + self.assertEqual( + [t["rows"] for t in pair[0]["tables"]], [t["rows"] for t in pair[1]["tables"]] + ) + self.assertEqual( + [len(t["columns"]) for t in pair[0]["tables"]], + [len(t["columns"]) for t in pair[1]["tables"]], + ) + + def test_inline_historical_tables_need_no_json_binding(self): + with workspace() as (root, resources): + pages = [] + for locale in ("zh", "en"): + article = root / "docs/src" / locale / "benchmarks/example.md" + article.parent.mkdir(parents=True) + article.write_text( + "# Historical data\n\n| Backend | Median ms |\n| --- | ---: |\n| Native | 1.27 |\n" + ) + pages.append( + dict(locale=locale, page="benchmarks/example.md", tables=[], source_csv=[]) + ) + inventory = root / "docs.json" + inventory.write_text(json.dumps(dict(format=1, pages=pages))) + with patch.object(DOCS, "REPO", root), patch.object(DOCS, "INVENTORY", inventory): + self.assertEqual(DOCS.check_inventory(), (2, 0)) + + def test_publication_writes_one_shared_source_and_preserves_raw_records(self): + with workspace() as (root, resources): + batch = root / ".data/replay-test" + batch.mkdir(parents=True) + manifest = dict( + entry="replay/replay.json", + batch=batch.name, + benchmark_id="B-REPLAY", + role="user-facing", + metric_scope=DOCS.METRIC_SCOPE, + runs=60, + warmup=0, + hyperfine_version="hyperfine 2.0.0", + rows={"codex": "codex"}, + exports={"in.bench.json": "a" * 64}, + artifacts={ + str(ROOT / "replay/replay_hooks.py"): "b" * 64, + str(batch / "pvisor-replay"): "c" * 64, + str(batch / "fixtures/codex/trajectory.json"): "d" * 64, + }, + source_commit_scope="binary source is not attested", + ) + raw_path = batch / "manifest.json" + raw_path.write_text(json.dumps(manifest)) + original = raw_path.read_bytes() + resolved = { + "codex": dict( + zip( + ["N", "Median ms", "Mean ms", "SD ms", "Min ms", "Max ms"], + [60, 1.2, 1.3, 0.1, 1.0, 1.6], + ) + ) + } + pages = [ + dict( + locale=locale, + page="benchmarks/replay-fidelity.md", + tables=[ + dict( + json="benchmarks/replay-semspec.render.json", + kind="published", + rows=1, + columns=list(resolved["codex"]), + ) + ], + ) + for locale in ("zh", "en") + ] + inventory = root / "docs.json" + inventory.write_text( + json.dumps( + dict( + pages=pages, + bindings={ + manifest["entry"]: dict( + benchmark_id="B-REPLAY", + page="benchmarks/replay-fidelity.md", + table="benchmarks/replay-semspec.render.json", + csv="replay-semspec.csv", + show_batch=False, + ) + }, + ) + ) + ) + with ( + patch.object(DOCS, "REPO", root), + patch.object(DOCS, "INVENTORY", inventory), + patch.object( + DOCS, + "validate_batch", + return_value=( + manifest, + resolved, + {"codex": {"command": "pvisor-replay fixture"}}, + ), + ), + patch("measurement_coverage.coverage"), + ): + targets = DOCS.publish_docs(batch) + shared = root / "docs/overrides/assets/benchmarks/replay-semspec.provenance.json" + provenance = json.loads(shared.read_text()) + self.assertEqual(provenance["manifest_sha256"], DOCS.digest(raw_path)) + self.assertEqual(provenance["exports"], manifest["exports"]) + self.assertEqual(provenance["source_commit_scope"], manifest["source_commit_scope"]) + self.assertEqual( + provenance["artifacts"], + { + "pvisor-replay": "c" * 64, + "fixtures/codex/trajectory.json": "d" * 64, + }, + ) + self.assertEqual(raw_path.read_bytes(), original) + self.assertEqual(targets[0].read_bytes(), targets[1].read_bytes()) + for target in targets: + self.assertFalse(target.with_suffix(".provenance.json").exists()) + + def test_scope_and_fixture_mismatch_cannot_overwrite_documentation(self): + binding = dict( + benchmark_id="B-FS-TOOLS", + metric_scope="expected timer", + fixture_scope="expected workload", + ) + metadata = dict( + entry="filesystem/tools.json", + benchmark_id="B-FS-TOOLS", + role="user-facing", + metric_scope="wrong timer", + fixture_scope="wrong workload", + ) + for scope, message in ( + ("wrong timer", "measurement scope"), + ("expected timer", "workload configuration"), + ): + metadata["metric_scope"] = scope + with ( + patch.object( + DOCS.Path, + "read_text", + side_effect=[ + json.dumps(dict(bindings={metadata["entry"]: binding})), + json.dumps(metadata), + ], + ), + patch.object(DOCS, "validate_batch") as validate, + ): + with self.assertRaisesRegex(ValueError, message): + DOCS.publish_docs(Path("/not-published")) + validate.assert_not_called() + + def test_live_bindings_declare_event_and_whole_command_scopes(self): + inventory = json.loads((ROOT / "docs.json").read_text()) + for entry, binding in inventory["bindings"].items(): + if binding.get("kind") == "startup": + self.assertIn("same Popen launch", binding["metric_scope"]) + self.assertEqual(binding["page"], "benchmarks/startup.md") + self.assertNotIn("filesystem/tools.json", inventory["bindings"]) + self.assertFalse(inventory["bindings"]["replay/replay.json"]["show_batch"]) + + def test_live_json_must_equal_latest_csv_and_sample_counts(self): + with workspace() as (root, resources): + base = root / "docs/src/en/benchmarks" + base.mkdir(parents=True) + columns = ["N", "Median ms", "Mean ms", "SD ms", "Min ms", "Max ms"] + header = ["condition", "role", "benchmark_id", "metric_scope", "runs", *columns] + csv_path = base / "results.csv" + with csv_path.open("w") as stream: + writer = csv.writer(stream) + writer.writerow(header) + for name in ("native-metadata", "native-read"): + writer.writerow( + [ + name, + "user-facing", + "B-FS-TOOLS", + DOCS.METRIC_SCOPE, + 30, + 30, + 1.23456, + 2.34567, + 0.12345, + 1.11111, + 3.99999, + ] + ) + target = base / "results.render.json" + expected = DOCS.csv_table( + csv_path, + {name: name for name in ("native-metadata", "native-read")}, + dict(zip(columns, columns)), + DOCS.METRIC_SCOPE, + "B-FS-TOOLS", + ) + target.write_text(json.dumps(expected)) + inventory = dict( + pages=[ + dict( + locale="en", + page="benchmarks/filesystem.md", + tables=[ + dict( + json="benchmarks/results.render.json", + kind="published", + columns=columns, + ) + ], + ) + ], + bindings={ + "filesystem/tools.json": dict( + table="benchmarks/results.render.json", + csv="results.csv", + benchmark_id="B-FS-TOOLS", + conditions={name: name for name in ("native-metadata", "native-read")}, + ) + }, + ) + with patch.object(DOCS, "REPO", root): + DOCS.check_live_tables(inventory) + expected["native-read"]["Median ms"] = "1.24" + target.write_text(json.dumps(expected)) + with self.assertRaisesRegex(ValueError, "differs from latest audited CSV"): + DOCS.check_live_tables(inventory) + with self.assertRaisesRegex(ValueError, "different metric"): + DOCS.csv_table(csv_path, {}, {}, "wrong timer", "B-FS-TOOLS") + csv_path.write_text(csv_path.read_text().replace(",30,30,", ",30,29,")) + with self.assertRaisesRegex(ValueError, "sample count"): + DOCS.csv_table( + csv_path, + {"native-read": "native-read"}, + {"N": "N"}, + DOCS.METRIC_SCOPE, + "B-FS-TOOLS", + ) + + def test_engineering_batch_cannot_publish_to_user_tables(self): + with ( + patch.object( + DOCS.Path, + "read_text", + return_value=json.dumps( + { + "format": 1, + "bindings": {}, + "entry": "process/smoke.json", + "benchmark_id": "B-PROCESS", + "role": "engineering A/B", + } + ), + ), + patch.object(DOCS, "validate_batch") as validate, + ): + with self.assertRaisesRegex(ValueError, "user-facing"): + DOCS.publish_docs(Path("/unmeasured")) + validate.assert_not_called() + + +if __name__ == "__main__": + unittest.main() diff --git a/benchmark.new/tests/test_filesystem.py b/benchmark.new/tests/test_filesystem.py new file mode 100644 index 000000000..54d838527 --- /dev/null +++ b/benchmark.new/tests/test_filesystem.py @@ -0,0 +1,201 @@ +"""Native filesystem rows validate fixed inputs, trials and Python timing evidence.""" + +import csv +import importlib.util +import json +import os +import subprocess +import sys +import unittest +from pathlib import Path +from unittest.mock import patch + +from tests.fixtures import workspace + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT)) +sys.path.insert(0, str(ROOT / "filesystem")) + + +def module(name, path): + spec = importlib.util.spec_from_file_location(name, path) + result = importlib.util.module_from_spec(spec) + spec.loader.exec_module(result) + return result + + +FILESYSTEM = module("filesystem_prepare_tests", ROOT / "filesystem/prepare.py") +from fs_timing import ( # noqa: E402 + FIELDS, + FIXTURE_SCOPE, + RUNS, + SCOPE, + WARMUP, + measure, + run_trial, + summarize, +) +from update import publish, validate_batch # noqa: E402 +from workload import execute, prepare_fixture, validate_fixture # noqa: E402 + +SEMSPEC = Path(os.environ.get("SEMSPEC_BIN", ROOT.parent / "target/semspec/debug/semspec")) + + +class FilesystemContracts(unittest.TestCase): + def test_modified_command_or_extra_arguments_are_rejected_before_preparation(self): + table = json.loads((ROOT / "filesystem/tools.json").read_text()) + for expression in ( + table["native-read"] + " --payload-mib 16", + "$ python3 arbitrary.py @BATCH@", + "$ hyperfine --runs 30 true", + ): + with self.subTest(expression=expression), workspace() as (root, resources): + path = root / "in.json" + path.write_text(json.dumps(table | {"native-read": expression})) + with self.assertRaisesRegex(ValueError, "fixed metadata/read"): + FILESYSTEM.validate_template(path) + + def test_fixture_validation_rejects_modified_sizes_contents_and_inventory(self): + with workspace() as (root, resources): + fixture = root / "fixture" + prepare_fixture(fixture) + validate_fixture(fixture) + self.assertEqual( + execute("metadata", fixture), dict(workload="metadata", correctness="passed") + ) + self.assertEqual(execute("read", fixture), dict(workload="read", correctness="passed")) + path = fixture / "fixture.json" + original = path.read_bytes() + for key, value in (("files", 1000), ("payload_bytes", 16 * 1024**2)): + declared = json.loads(original) + declared[key] = value + path.write_text(json.dumps(declared)) + with self.assertRaisesRegex(ValueError, "configuration changed"): + validate_fixture(fixture) + path.write_bytes(original) + tree_file = fixture / "tree/d00/f000000.txt" + original = tree_file.read_bytes() + tree_file.write_bytes(b"x" * len(original)) + with self.assertRaisesRegex(ValueError, "content changed"): + validate_fixture(fixture) + tree_file.write_bytes(original) + extra = fixture / "tree/extra" + extra.touch() + with self.assertRaisesRegex(ValueError, "inventory changed"): + validate_fixture(fixture) + extra.unlink() + with (fixture / "payload.bin").open("r+b") as stream: + stream.write(b"corrupted") + with self.assertRaisesRegex(ValueError, "payload changed"): + validate_fixture(fixture) + with self.assertRaisesRegex(ValueError, "payload differs"): + execute("read", fixture) + + def test_statistics_use_nanoseconds_and_sample_standard_deviation(self): + self.assertEqual( + summarize([1_000_000, 3_000_000, 5_000_000]), dict(zip(FIELDS, (3, 3, 3, 2, 1, 5))) + ) + for samples in ([1], [True, 2], [0, 2], [1.5, 2]): + with self.subTest(samples=samples), self.assertRaises(ValueError): + summarize(samples) + + def test_failed_sample_keeps_diagnostics_and_cannot_write_a_summary(self): + with workspace() as (root, resources): + batch = FILESYSTEM.prepare( + ROOT / "filesystem/tools.json", root / ".data/batch", row="native-read" + ) + with patch( + "fs_timing.run_trial", side_effect=[1, 1, 1, ValueError("incorrect output")] + ): + with self.assertRaisesRegex(ValueError, "incorrect output"): + measure(batch, "read") + self.assertFalse((batch / "read.samples.json").exists()) + with patch( + "fs_timing.subprocess.run", + return_value=subprocess.CompletedProcess([], 7, "failed", "diagnostic"), + ): + with self.assertRaisesRegex(ValueError, "subject failed"): + run_trial(batch, "read", "sample", 0) + failed = json.loads((batch / "trials/read/sample-000.json").read_text()) + self.assertEqual(failed["exit_code"], 7) + self.assertEqual(failed["stderr"], "diagnostic") + self.assertIn("error", failed) + + def test_timeout_retains_captured_output(self): + with workspace() as (root, resources): + timeout = subprocess.TimeoutExpired( + ["subject"], 30, output=b"partial", stderr=b"failed" + ) + with patch("fs_timing.subprocess.run", side_effect=timeout): + with self.assertRaises(subprocess.TimeoutExpired): + run_trial(root, "metadata", "preflight", 0) + record = json.loads((root / "trials/metadata/preflight-000.json").read_text()) + self.assertEqual(record["stdout"], "partial") + self.assertIn("error", record) + + +@unittest.skipUnless(SEMSPEC.is_file(), "built semspec required") +class FilesystemIntegration(unittest.TestCase): + def bench(self, batch): + result = subprocess.run( + [str(SEMSPEC), "bench", "--cwd", str(batch), str(batch / "in.json")], + capture_output=True, + text=True, + ) + self.assertEqual(result.returncode, 0, result.stderr) + + def test_two_rows_share_one_fixture_and_publish_only_audited_samples(self): + with workspace() as (root, resources): + batch = FILESYSTEM.prepare(ROOT / "filesystem/tools.json", root / ".data/batch") + self.bench(batch) + for mode in ("metadata", "read"): + self.assertEqual( + len(list((batch / "trials" / mode).glob("*.json"))), 1 + WARMUP + RUNS + ) + self.assertEqual(len(list(batch.rglob("payload.bin"))), 1) + with self.assertRaisesRegex(ValueError, "reuse a measured"): + measure(batch, "read") + manifest, table, results = validate_batch(batch) + self.assertEqual(manifest["metric_scope"], SCOPE) + self.assertEqual(manifest["fixture_scope"], FIXTURE_SCOPE) + self.assertEqual(manifest["measurement_engine"], "python-perf_counter_ns") + self.assertEqual(set(results), {"native-metadata", "native-read"}) + self.assertTrue(all(row["N"] == RUNS for row in table.values())) + csv_path, markdown = root / "result.csv", root / "table.md" + render = publish(batch, csv_path, markdown) + before = {path: path.read_bytes() for path in (csv_path, render)} + publish(batch, csv_path, markdown) + self.assertEqual(csv_path.read_bytes(), before[csv_path]) + with csv_path.open() as stream: + rows = list(csv.DictReader(stream)) + self.assertTrue( + all(row["measurement_engine"] == "python-perf_counter_ns" for row in rows) + ) + sample = batch / "trials/read/sample-000.json" + record = json.loads(sample.read_text()) + record["duration_ns"] += 1 + sample.write_text(json.dumps(record)) + with self.assertRaisesRegex(ValueError, "monotonic duration"): + publish(batch, csv_path, markdown) + self.assertEqual({path: path.read_bytes() for path in before}, before) + + def test_single_row_preserves_fixed_workload_and_rejects_tampered_statistics(self): + with workspace() as (root, resources): + batch = FILESYSTEM.prepare( + ROOT / "filesystem/tools.json", root / ".data/batch", row="native-metadata" + ) + self.assertEqual(list(json.loads((batch / "in.json").read_text())), ["native-metadata"]) + self.bench(batch) + path = batch / "metadata.samples.json" + raw = json.loads(path.read_text()) + raw["result"]["Median ms"] += 1 + path.write_text(json.dumps(raw)) + with self.assertRaisesRegex(ValueError, "raw samples or statistics"): + validate_batch(batch) + self.assertEqual( + json.loads((batch / "manifest.json").read_text())["status"], "prepared" + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/benchmark.new/tests/test_hyperfine.py b/benchmark.new/tests/test_hyperfine.py new file mode 100644 index 000000000..0a378558b --- /dev/null +++ b/benchmark.new/tests/test_hyperfine.py @@ -0,0 +1,462 @@ +"""JSON registry, one-run/many-column extraction, audit and publication contracts.""" + +import csv +import importlib.util +import io +import json +import os +import shlex +import shutil +import subprocess +import sys +import tempfile +import unittest +from pathlib import Path +from unittest.mock import patch + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT)) +import common as COMMON # noqa: E402 +from update import display_table, publish, validate_batch # noqa: E402 + +SEMSPEC = Path(os.environ.get("SEMSPEC_BIN", ROOT.parent / "target/semspec/debug/semspec")) +LIVE = shutil.which("hyperfine") and SEMSPEC.is_file() + + +class RegistryTests(unittest.TestCase): + def test_every_active_id_has_a_json_template_and_retired_ids_do_not(self): + registry = (ROOT.parent / "benchmark/README.md").read_text() + expected = { + line.split(":")[0].removeprefix("### ") + for line in registry.splitlines() + if line.startswith("### B-") + } + expected -= {"B-OVERVIEW / B-METHOD", "B-COMPARE", "B-CLUSTER"} + with (ROOT / "registry.csv").open() as stream: + rows = list(csv.DictReader(stream)) + self.assertEqual({row["benchmark_id"] for row in rows}, expected) + for row in rows: + path = ROOT / row["entry"] + self.assertEqual(path.suffix, ".json") + table = json.loads(path.read_text()) + self.assertTrue(table) + if row["status"].startswith("prepared-python-ready"): + from subjects import LABELS + + self.assertEqual(row["benchmark_id"], "B-STARTUP") + backends = {label: backend for backend, label in LABELS.items()} + for label, command in table.items(): + backend = backends[label] + script = backend.replace("-", "_") + "_timing" + self.assertEqual( + command, f"$ python3 @REPO@/benchmark.new/startup/{script}.py @BATCH@" + ) + self.assertTrue(path.with_name(script + ".py").is_file()) + continue + if row["status"] == "prepared-python-filesystem": + self.assertEqual(row["benchmark_id"], "B-FS-TOOLS") + self.assertEqual( + table, + { + "native-" + + mode: f"$ python3 @REPO@/benchmark.new/filesystem/{mode}_timing.py @BATCH@" + for mode in ("metadata", "read") + }, + ) + continue + if row["status"] == "explicit-python-paired-dax": + self.assertEqual(row["benchmark_id"], "B-FS-ENG") + self.assertEqual(row["role"], "engineering A/B") + self.assertEqual(row["entry"], "filesystem/dax_ab.json") + self.assertEqual(table, { + "immutable-share-paired": "$ python3 @HARNESS@/dax_ab.py --measure @BATCH@" + }) + self.assertTrue(path.with_suffix(".py").is_file()) + continue + if isinstance(next(iter(table.values())), str): + for command in table.values(): + self.assertTrue(command.startswith("$ hyperfine ")) + argv = shlex.split(command[2:]) + self.assertNotIn("|", argv) + self.assertIn("--runs", argv) + self.assertIn("--warmup", argv) + continue + first = set(next(iter(table.values()))) + for cells in table.values(): + self.assertEqual(first, set(cells)) + for value in cells.values(): + self.assertFalse(isinstance(value, (dict, list))) + if isinstance(value, str) and value.startswith("$ "): + self.assertEqual(shlex.split(value[2:])[0], "hyperfine") + if row["status"] == "requires-single-condition-command": + self.assertIsNone(value) + + def test_no_executable_template_duplicates_commands_across_statistic_cells(self): + with (ROOT / "registry.csv").open() as stream: + entries = list(csv.DictReader(stream)) + for entry in entries: + table = json.loads((ROOT / entry["entry"]).read_text()) + if entry["status"] == "requires-single-condition-command": + self.assertTrue( + all( + isinstance(row, dict) and all(value is None for value in row.values()) + for row in table.values() + ) + ) + with self.assertRaisesRegex(ValueError, "no prepared single-condition command"): + COMMON.materialize(ROOT / entry["entry"], ROOT / ".data/never-created") + else: + self.assertTrue( + all(isinstance(row, str) and row.startswith("$ ") for row in table.values()), + entry["entry"], + ) + + def test_path_substitution_preserves_nested_subject_and_hook_quoting(self): + variables = {"BINARY": "/a path/it's-pvisor", "BATCH": "/data/a path"} + expression = "$ " + shlex.join( + [ + "hyperfine", + "--runs", + "2", + "--warmup", + "0", + "--prepare", + "python3 @BINARY@ @BATCH@/input", + "--command-name", + "test", + "@BINARY@ run @BATCH@/input", + ] + ) + argv = shlex.split(COMMON.expand(expression, variables)[2:]) + self.assertEqual( + shlex.split(argv[argv.index("--prepare") + 1]), + ["python3", variables["BINARY"], variables["BATCH"] + "/input"], + ) + self.assertEqual( + shlex.split(argv[-1]), + [variables["BINARY"], "run", variables["BATCH"] + "/input"], + ) + + def test_display_format_changes_only_presentation(self): + table = {"native": {"N": 60, "Median ms": 1.234567, "note": "unchanged", "Min ms": None}} + self.assertEqual( + display_table(table), + {"native": {"N": 60, "Median ms": "1.23", "note": "unchanged", "Min ms": None}}, + ) + self.assertEqual(table["native"]["Median ms"], 1.234567) + + def test_preparation_rejects_legacy_cell_commands(self): + with tempfile.TemporaryDirectory() as directory: + entry = Path(directory) / "fixture.json" + command = "$ hyperfine --runs 2 --warmup 0 --command-name checked /bin/true" + for table in ( + {"checked": {"N": command}}, + {"checked": command + " | json /results/0/count * 1"}, + ): + entry.write_text(json.dumps(table)) + with patch.object(COMMON, "registered", return_value={}): + with self.assertRaisesRegex(ValueError, "per row|obsolete"): + COMMON.materialize(entry, Path(directory) / "batch") + + +class RowEvidenceTests(unittest.TestCase): + """Audit row data even on hosts without hyperfine installed.""" + + def test_valid_row_and_invalid_measurements_or_mapping(self): + from update import FIELDS + + with tempfile.TemporaryDirectory() as directory: + batch = Path(directory) + command = "hyperfine --runs 2 --warmup 1 --command-name checked /bin/true" + summary = {field: 2 if field == "count" else 0.001 for field in FIELDS.values()} + summary["unit"] = "second" + resolved = { + "checked": { + field: summary[key] * (1 if field == "N" else 1000) + for field, key in FIELDS.items() + } + } + template = {"checked": "$ " + command} + COMMON.save(batch / "in.json", template) + manifest = dict( + status="prepared", + metric_scope=COMMON.METRIC_SCOPE, + benchmark_id="B-PROCESS", + entry="process/fixture.json", + input_sha256=COMMON.digest(batch / "in.json"), + artifacts={}, + harness={}, + cwd=directory, + runs=2, + warmup=1, + rows={"checked": "checked"}, + conditions=["checked"], + hyperfine_version="hyperfine 2.0.0", + ) + evidence = dict( + format=1, + cwd=directory, + template=str(batch / "in.json"), + cells=[], + commands=[ + dict( + command=command, + kind="hyperfine-row", + value=resolved["checked"], + raw=dict( + schema_version=2, + metadata={"hyperfine_version": "2.0.0"}, + results=[ + dict( + name="checked", + command="/bin/true", + measurements=[{"exit_code": 0}, {"exit_code": 0}], + summary={"time_wall_clock": summary}, + ) + ], + ), + ) + ], + rows=[ + dict(row="checked", command=command, expression=command, columns=list(FIELDS)) + ], + ) + COMMON.save(batch / "in.render.json", resolved) + for damage in (None, "exit", "statistic", "mapping", "cell"): + with self.subTest(damage=damage): + changed = json.loads(json.dumps(evidence)) + result = changed["commands"][0]["raw"]["results"][0] + if damage == "exit": + result["measurements"][0]["exit_code"] = 7 + elif damage == "statistic": + result["summary"]["time_wall_clock"]["median"] = 9 + elif damage == "mapping": + changed["rows"][0]["row"] = "other" + elif damage == "cell": + changed["cells"] = [{"row": "checked", "column": "N"}] + COMMON.save(batch / "manifest.json", manifest) + COMMON.save(batch / "in.bench.json", changed) + if damage: + with self.assertRaises(ValueError): + validate_batch(batch) + else: + checked, table, results = validate_batch(batch) + self.assertEqual(checked["status"], "passed") + self.assertEqual(table, resolved) + self.assertEqual(set(results), {"checked"}) + + +@unittest.skipUnless(LIVE, "real hyperfine 2.0+ and built semspec required") +class HyperfineTests(unittest.TestCase): + def setUp(self): + self.temp = tempfile.TemporaryDirectory(prefix="semspec benchmark ") + self.addCleanup(self.temp.cleanup) + self.root = Path(self.temp.name) + self.entry = self.root / "fixture.json" + self.batch = self.root / ".data" / "batch" + self.csv = self.root / "results.csv" + self.md = self.root / "results.md" + + def measure(self, code="pass", extra=(), artifacts=(), names=("checked",)): + argv = [ + "hyperfine", + "--shell=none", + "--runs", + "2", + "--warmup", + "1", + "--style", + "basic", + *extra, + ] + table = { + name: "$ " + + shlex.join([*argv, "--command-name", name, shlex.join([sys.executable, "-c", code])]) + for name in names + } + self.entry.write_text(json.dumps(table)) + metadata = dict( + benchmark_id="B-PROCESS", + role="engineering A/B", + entry="process/smoke.json" if len(names) == 4 else "process/fixture.json", + ) + with patch.object(COMMON, "registered", return_value=metadata): + batch = COMMON.create_batch(self.entry, self.batch) + COMMON.materialize(self.entry, batch, artifacts=artifacts, cwd=self.root) + return subprocess.run( + [str(SEMSPEC), "bench", "--cwd", str(self.root), str(batch / "in.json")], + capture_output=True, + text=True, + ) + + def test_all_statistics_share_one_export_and_csv_uses_exact_selected_values(self): + calls = self.root / "calls" + code = f'from pathlib import Path; p=Path({str(calls)!r}); p.write_text(p.read_text()+"run\\n" if p.exists() else "run\\n")' + result = self.measure(code) + self.assertEqual(result.returncode, 0, result.stderr) + self.assertEqual(calls.read_text(), "run\n" * 3) + manifest, resolved, results = validate_batch(self.batch) + evidence = json.loads((self.batch / "in.bench.json").read_text()) + self.assertEqual(len(evidence["commands"]), 1) + self.assertEqual(evidence["cells"], []) + self.assertEqual(resolved["checked"]["N"], 2) + self.assertEqual( + resolved["checked"]["Median ms"], + results["checked"]["summary"]["time_wall_clock"]["median"] * 1000, + ) + publish(self.batch, self.csv, self.md) + exported = list(csv.DictReader(io.StringIO(self.csv.read_text()))) + self.assertEqual(float(exported[0]["Median ms"]), resolved["checked"]["Median ms"]) + self.assertIn('{{ table("results.render.json") }}', self.md.read_text()) + original = self.csv.read_bytes(), self.md.read_bytes() + publish(self.batch, self.csv, self.md) + self.assertEqual(original, (self.csv.read_bytes(), self.md.read_bytes())) + + def test_one_hyperfine_row_runs_once_and_all_columns_share_the_raw_export(self): + calls = self.root / "calls" + code = f'from pathlib import Path; p=Path({str(calls)!r}); p.write_text(p.read_text()+"run\\n" if p.exists() else "run\\n")' + result = self.measure(code) + self.assertEqual(result.returncode, 0, result.stderr) + self.assertEqual(calls.read_text(), "run\n" * 3) + manifest, resolved, results = validate_batch(self.batch) + evidence = json.loads((self.batch / "in.bench.json").read_text()) + self.assertEqual(len(evidence["commands"]), 1) + self.assertEqual(evidence["commands"][0]["kind"], "hyperfine-row") + self.assertEqual(evidence["cells"], []) + self.assertEqual(len(evidence["rows"]), 1) + self.assertEqual(list(resolved["checked"]), list(__import__("update").FIELDS)) + publish(self.batch, self.csv, self.md) + before = self.csv.read_bytes() + data = json.loads((self.batch / "in.render.json").read_text()) + data["checked"]["SD ms"] += 10 + (self.batch / "in.render.json").write_text(json.dumps(data)) + with self.assertRaisesRegex(ValueError, "checksum"): + publish(self.batch, self.csv, self.md) + self.assertEqual(self.csv.read_bytes(), before) + + def test_process_threshold_gate_accepts_one_command_per_condition(self): + spec = importlib.util.spec_from_file_location( + "process_compare_rows", ROOT / "process/compare.py" + ) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + result = self.measure(names=("native", "host-run", "status", "review")) + self.assertEqual(result.returncode, 0, result.stderr) + path = self.batch / "in.bench.json" + table, regressions = module.compare(path, path, 15) + self.assertEqual(regressions, 0) + self.assertEqual(table.count("within threshold"), 4) + evidence = json.loads(path.read_text()) + self.assertEqual(len(evidence["commands"]), 4) + self.assertEqual(len(evidence["rows"]), 4) + self.assertEqual(evidence["cells"], []) + + def test_failed_payload_and_failed_correctness_hook_cannot_publish(self): + for index, (code, extra) in enumerate( + [("raise SystemExit(7)", ()), ("pass", ("--conclude", "exit 1"))] + ): + self.batch = self.root / ".data" / f"failure-{index}" + result = self.measure(code, extra) + self.assertNotEqual(result.returncode, 0) + self.assertFalse((self.batch / "in.render.json").exists()) + self.assertTrue(list((self.batch / "in.bench.logs").glob("*.log"))) + with self.assertRaises((ValueError, OSError)): + publish(self.batch, self.csv, self.md) + self.assertFalse(self.csv.exists()) + + def test_prepare_and_conclude_cover_warmups_and_samples(self): + evidence = self.root / "state" + + def command(code): + return shlex.join([sys.executable, "-c", code]) + + prepare = command( + f'from pathlib import Path; Path({str(evidence)!r}).write_text("prepared")' + ) + conclude = command( + f'from pathlib import Path; assert Path({str(evidence)!r}).read_text()=="checked"' + ) + result = self.measure( + f'from pathlib import Path; p=Path({str(evidence)!r}); assert p.read_text()=="prepared"; p.write_text("checked")', + ("--prepare", prepare, "--conclude", conclude), + ) + self.assertEqual(result.returncode, 0, result.stderr) + publish(self.batch, self.csv, self.md) + + def test_artifact_change_is_rejected_before_publication(self): + artifact = self.root / "artifact" + artifact.write_text("before") + result = self.measure( + f'from pathlib import Path; Path({str(artifact)!r}).write_text("after")', + artifacts=[artifact], + ) + self.assertEqual(result.returncode, 0, result.stderr) + with self.assertRaisesRegex(ValueError, "artifact changed"): + publish(self.batch, self.csv, self.md) + self.assertFalse(self.csv.exists()) + + def test_tampered_resolved_cell_or_export_is_rejected(self): + self.assertEqual(self.measure().returncode, 0) + path = self.batch / "in.render.json" + original = path.read_bytes() + table = json.loads(original) + table["checked"]["Median ms"] = 0 + path.write_text(json.dumps(table)) + with self.assertRaisesRegex(ValueError, "resolved cell"): + publish(self.batch, self.csv, self.md) + path.write_bytes(original) + publish(self.batch, self.csv, self.md) + with (self.batch / "in.bench.json").open("a") as stream: + stream.write(" ") + with self.assertRaisesRegex(ValueError, "checksum"): + publish(self.batch, self.csv, self.md) + + def test_schema_mismatch_preserves_public_results_and_raw_cannot_be_overwritten(self): + self.assertEqual(self.measure().returncode, 0) + self.csv.write_text("legacy,value\nold,1\n") + self.md.write_text("historical\n") + original = self.csv.read_bytes(), self.md.read_bytes() + with self.assertRaisesRegex(ValueError, "schema differs"): + publish(self.batch, self.csv, self.md) + self.assertEqual(original, (self.csv.read_bytes(), self.md.read_bytes())) + with self.assertRaisesRegex(ValueError, "outside raw evidence"): + publish(self.batch, self.root / "other.csv", self.batch / "in.json") + with ( + self.assertRaises(FileExistsError), + patch.object(COMMON, "registered", return_value={}), + ): + COMMON.create_batch(self.entry, self.batch) + + def test_new_batch_appends_without_rewriting_previous_rows(self): + self.assertEqual(self.measure().returncode, 0) + publish(self.batch, self.csv, self.md) + first = list(csv.DictReader(io.StringIO(self.csv.read_text()))) + self.batch = self.batch.with_name("second") + self.assertEqual(self.measure().returncode, 0) + publish(self.batch, self.csv, self.md) + combined = list(csv.DictReader(io.StringIO(self.csv.read_text()))) + self.assertEqual(combined[: len(first)], first) + self.assertEqual(len(combined), 2) + + def test_process_gate_requires_all_conditions_and_rejects_failed_manifests(self): + spec = importlib.util.spec_from_file_location( + "process_compare", ROOT / "process/compare.py" + ) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + result = self.measure(names=("native", "host-run", "status", "review")) + self.assertEqual(result.returncode, 0, result.stderr) + path = self.batch / "in.bench.json" + table, regressions = module.compare(path, path, 15) + self.assertEqual(regressions, 0) + self.assertEqual(table.count("within threshold"), 4) + manifest_path = self.batch / "manifest.json" + manifest = json.loads(manifest_path.read_text()) + manifest["status"] = "failed" + manifest_path.write_text(json.dumps(manifest)) + with self.assertRaisesRegex(ValueError, "successful"): + module.compare(path, None, 15) + + +if __name__ == "__main__": + unittest.main() diff --git a/benchmark.new/tests/test_ready.py b/benchmark.new/tests/test_ready.py new file mode 100644 index 000000000..20418baf1 --- /dev/null +++ b/benchmark.new/tests/test_ready.py @@ -0,0 +1,245 @@ +"""Ready timing stops on receipt, while correctness still requires normal exit.""" + +import json +import os +import sys +import unittest +from pathlib import Path +from unittest.mock import patch + +from tests.fixtures import workspace + +# The shared observer and its evidence auditor are tested separately. + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT / "startup")) +import audit as AUDIT # noqa: E402 +import timing as READY # noqa: E402 + + +class ReadyContracts(unittest.TestCase): + def capture(self, root, code, timeout=2): + return READY.capture( + [sys.executable, "-c", code], root, os.environ.copy(), root / "output.log", timeout + ) + + def test_ready_excludes_exit_delay_and_keeps_output(self): + with workspace() as (root, resources): + result = self.capture( + root, "import time; print('REFERENCE_READY', flush=True); time.sleep(.25)" + ) + self.assertGreater(result["end_ns"] - result["ready_ns"], 200_000_000) + self.assertEqual(result["ready_duration_ns"], result["ready_ns"] - result["start_ns"]) + self.assertEqual(result["exit_duration_ns"], result["end_ns"] - result["start_ns"]) + self.assertEqual((root / "output.log").read_text(), "REFERENCE_READY\n") + self.assertEqual(json.loads((root / "output.timing.json").read_text()), result) + + def test_missing_duplicate_partial_and_failed_exit_reject_data(self): + for code in ( + "print('not ready')", + "print('REFERENCE_READY\\nREFERENCE_READY')", + "print('REFERENCE_READY suffix')", + "print('REFERENCE_READY', end='')", + "print('REFERENCE_READY'); raise SystemExit(7)", + ): + with self.subTest(code=code), workspace() as (root, resources): + with self.assertRaises(ValueError): + self.capture(root, code) + self.assertTrue((root / "output.log").read_bytes()) + self.assertIn("error", json.loads((root / "output.timing.json").read_text())) + + def test_timeout_after_ready_kills_owned_child(self): + with workspace() as (root, resources): + code = ( + "import subprocess, time; " + "child=subprocess.Popen(['sleep','10']); " + f"open({str(root / 'child.pid')!r},'w').write(str(child.pid)); " + "print('REFERENCE_READY',flush=True); time.sleep(10)" + ) + with self.assertRaises(TimeoutError): + self.capture(root, code, timeout=0.2) + pid = int((root / "child.pid").read_text()) + path = Path(f"/proc/{pid}/stat") + if path.exists(): + self.assertEqual(path.read_text().split()[2], "Z") + self.assertIn("error", json.loads((root / "output.timing.json").read_text())) + + def test_statistics_use_formal_samples_and_sample_sd(self): + result = READY.summarize([1_000_000, 3_000_000, 5_000_000]) + self.assertEqual(result, dict(zip(READY.READY_FIELDS, [3, 3, 3, 2, 1, 5]))) + for invalid in ([1], [True, 2], [0, 2], [1.1, 2]): + with self.assertRaises(ValueError): + READY.summarize(invalid) + + def test_paired_summary_rejects_unpaired_and_reversed_durations(self): + valid = [ + {"ready_ns": 1_000_000, "exit_ns": 3_000_000}, + {"ready_ns": 3_000_000, "exit_ns": 5_000_000}, + ] + result = READY.summarize_pairs(valid) + self.assertEqual(result["N"], 2) + self.assertEqual(result["Ready median ms"], 2) + self.assertEqual(result["Exit median ms"], 4) + for bad in ( + [{"ready_ns": 1}], + [{"ready_ns": 2, "exit_ns": 1}], + [{"ready_ns": True, "exit_ns": 3}], + [valid[0]], + ): + with self.assertRaises(ValueError): + READY.summarize_pairs(bad) + + def test_row_returns_once_after_three_warmups_and_sixty_samples(self): + with workspace() as (root, resources): + (root / "trials/00000").mkdir(parents=True) + (root / "trials/00000/timing.json").write_text("{}") + (root / "manifest.json").write_text( + json.dumps( + dict( + status="prepared", metric_scope=READY.COMMON.READY_EXIT_SCOPE, artifacts={} + ) + ) + ) + with patch.object( + READY, + "run_trial", + side_effect=lambda batch, phase, index: { + "ready_ns": (index + 1) * 1_000_000, + "exit_ns": (index + 1) * 2_000_000, + }, + ) as trial: + result = READY.measure(root) + self.assertEqual(result["N"], 60) + self.assertEqual(result["Ready median ms"], 30.5) + self.assertEqual(result["Exit median ms"], 61) + self.assertEqual(len(result), 11) + self.assertEqual( + [(call.args[1], call.args[2]) for call in trial.call_args_list], + [("warmup", i) for i in range(3)] + [("sample", i) for i in range(60)], + ) + self.assertEqual( + len(json.loads((root / "timing.samples.json").read_text())["samples"]), 60 + ) + + def test_failed_sample_cannot_write_summary(self): + with workspace() as (root, resources): + (root / "trials/00000").mkdir(parents=True) + (root / "trials/00000/timing.json").write_text("{}") + (root / "manifest.json").write_text( + json.dumps( + dict( + status="prepared", metric_scope=READY.COMMON.READY_EXIT_SCOPE, artifacts={} + ) + ) + ) + with patch.object( + READY, "run_trial", side_effect=[1, 1, 1, 1, ValueError("incorrect output")] + ): + with self.assertRaisesRegex(ValueError, "incorrect output"): + READY.measure(root) + self.assertFalse((root / "timing.samples.json").exists()) + + def test_audit_recomputes_all_samples_and_rejects_tampering(self): + import shlex + from copy import deepcopy + + with workspace() as (root, resources): + subject = ["/frozen/pvisor", "run"] + protocol = dict( + subject=subject, + scope=READY.COMMON.READY_EXIT_SCOPE, + measurement_engine="python-perf_counter_ns", + ready="exact REFERENCE_READY line received", + guest_cpus=2, + guest_memory_mib=128, + ) + READY.COMMON.save(root / "protocol.json", protocol) + phases = ( + [("preflight", 0)] + + [("warmup", i) for i in range(3)] + + [("sample", i) for i in range(60)] + ) + samples = [] + for i, (phase, index) in enumerate(phases): + trial = root / f"trials/{i:05d}" + duration = (index + 1) * 1_000_000 + start = (i + 1) * 1_000_000_000 + READY.COMMON.save( + trial / "timing.json", + dict( + command=subject, + phase=phase, + index=index, + start_ns=start, + ready_ns=start + duration, + end_ns=start + duration + 1_000_000, + ready_duration_ns=duration, + exit_duration_ns=duration + 1_000_000, + exit_code=0, + ), + ) + (trial / "stdout").write_text( + 'REFERENCE_READY\nREFERENCE_RESULT {"mode":"ready","correctness":"passed"}\n' + ) + (trial / "stderr").touch() + READY.COMMON.save( + trial / "stage/run-bundle.json", + dict( + run=dict( + state="completed", + exit_code=0, + executor=dict(isolation="virtual_machine"), + ), + safety=dict(filesystem_changes_staged=True), + ), + ) + if phase == "sample": + samples.append({"ready_ns": duration, "exit_ns": duration + 1_000_000}) + result = READY.summarize_pairs(samples) + READY.COMMON.save( + root / "timing.samples.json", + dict( + engine="python-perf_counter_ns", + unit="nanosecond", + samples=samples, + result=result, + ), + ) + command = shlex.join(["python3", str(ROOT / "startup/pvisor_vm_timing.py"), str(root)]) + evidence = dict( + commands=[dict(kind="script-row", command=command, raw=result, value=result)], + cells=[], + rows=[ + dict( + row="pVisor VM", + command=command, + expression=command, + columns=list(READY.FIELDS), + ) + ], + ) + manifest = dict( + metric_scope=READY.COMMON.READY_EXIT_SCOPE, + entry="startup/pvisor_vm_timing.json", + conditions=["pvisor-vm"], + benchmark_id="B-STARTUP", + measurement_engine="python-perf_counter_ns", + runs=60, + warmup=3, + rows={"pVisor VM": "pvisor-vm"}, + ) + template = {"pVisor VM": "$ " + command} + resolved = {"pVisor VM": result} + resources.enter_context(patch("audit.audit_startup")) + AUDIT.audit_timing(root, manifest, evidence, resolved, template) + changed = deepcopy(resolved) + changed["pVisor VM"]["Ready mean ms"] += 0.01 + with self.assertRaisesRegex(ValueError, "resolved row"): + AUDIT.audit_timing(root, manifest, evidence, changed, template) + (root / "trials/00001/timing.json").unlink() + with self.assertRaisesRegex(ValueError, "missing Ready"): + AUDIT.audit_timing(root, manifest, evidence, resolved, template) + + +if __name__ == "__main__": + unittest.main() diff --git a/benchmark.new/tests/test_replay.py b/benchmark.new/tests/test_replay.py new file mode 100644 index 000000000..d128954a8 --- /dev/null +++ b/benchmark.new/tests/test_replay.py @@ -0,0 +1,66 @@ +"""Direct replay preparation requires real native-prefix and host-state fidelity.""" + +import importlib.util +import json +import shlex +import shutil +import sys +import tempfile +import unittest +from pathlib import Path +from unittest.mock import patch + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT / "replay")) +sys.path.insert(0, str(ROOT)) +spec = importlib.util.spec_from_file_location("replay_prepare", ROOT / "replay/prepare.py") +PREPARE = importlib.util.module_from_spec(spec) +spec.loader.exec_module(PREPARE) +from replay_hooks import prepare, verify # noqa: E402 + +BINARY = ROOT.parent / "target/release/pvisor-replay" + + +@unittest.skipUnless( + BINARY.exists() and shutil.which("hyperfine"), "built replay and hyperfine required" +) +class ReplayTests(unittest.TestCase): + def test_all_native_prefixes_pass_and_tampering_is_rejected(self): + with tempfile.TemporaryDirectory() as tmp: + batch = Path(tmp) / ".data" + batch.mkdir() + (batch / "exports").mkdir() + with patch.object(PREPARE, "create_batch", return_value=batch): + PREPARE.prepare_batch(ROOT / "replay/replay.json", BINARY) + protocol = json.loads((batch / "protocol.json").read_text()) + self.assertEqual(len(protocol), 7) + for agent in protocol: + prepare(batch, agent) + archived = batch / "trials" / agent / "0001" + self.assertTrue(verify(archived, protocol[agent], agent)) + sentinel = batch / "fixtures/codex/workspace/existing" + sentinel.write_text("unexpected") + with self.assertRaisesRegex(ValueError, "contents"): + prepare(batch, "codex") + sentinel.write_text(PREPARE.SENTINEL) + source = batch / "fixtures/codex/trajectory.json" + source.write_text(source.read_text() + " ") + with self.assertRaisesRegex(ValueError, "trajectory"): + prepare(batch, "codex") + # All archived outcomes still have to match the exact expected prefix. + result = next((batch / "trials/claude-code/0001/output").glob("*/result.json")) + data = json.loads(result.read_text()) + data["replayed_tool_calls"] = 1 + result.write_text(json.dumps(data)) + with self.assertRaisesRegex(ValueError, "executed tools"): + verify(result.parents[2], protocol["claude-code"], "claude-code") + template = json.loads((batch / "in.json").read_text()) + for agent, row in template.items(): + argv = shlex.split(row[2:] if isinstance(row, str) else row["Median ms"][2:]) + end = argv.index("|") if "|" in argv else len(argv) + self.assertEqual(Path(shlex.split(argv[end - 1])[0]).name, "pvisor-replay") + self.assertIn("--shell=none", argv) + + +if __name__ == "__main__": + unittest.main() diff --git a/benchmark.new/tests/test_run.py b/benchmark.new/tests/test_run.py new file mode 100644 index 000000000..adc8549c4 --- /dev/null +++ b/benchmark.new/tests/test_run.py @@ -0,0 +1,117 @@ +"""Table execution publishes only complete audited results and owns temporary evidence.""" + +import importlib.util +import json +import sys +import unittest +from pathlib import Path +from types import SimpleNamespace +from unittest.mock import patch + +from tests.fixtures import workspace + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT)) +SPEC = importlib.util.spec_from_file_location("benchmark_run", ROOT / "run.py") +RUN = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(RUN) + + +class TableRunnerContracts(unittest.TestCase): + def execute(self, root, fail=False, output=None, row=None): + template = root / "startup/timing.json" + RUN.save(template, {"Docker": "$ docker", "Firecracker": "$ fc"}) + rendered = template.with_suffix(".render.json") + previous = {"Docker": {"N": 12}, "Firecracker": {"N": 13}, "obsolete": {"N": 60}} + RUN.save(rendered, previous) + batches = [] + + def prepare(template, destination, *args, row=None): + destination.mkdir(parents=True) + RUN.save(destination / "manifest.json", {"cwd": str(destination), "row": row}) + batches.append(destination) + return destination + + def audit(batch): + row = json.loads((batch / "manifest.json").read_text())["row"] + if fail and row == "Firecracker": + raise ValueError("correctness failed") + return {}, {row: {"N": 60, "Ready median ms": 1.234}}, {} + + with ( + patch.object(RUN, "registered", return_value={"status": "prepared"}), + patch.object( + RUN, "load_prepare", return_value=SimpleNamespace(prepare_template=prepare) + ), + patch.object(RUN, "validate_batch", side_effect=audit), + patch.object(RUN.subprocess, "run"), + ): + if fail: + with self.assertRaisesRegex(ValueError, "correctness failed"): + RUN.run_table(template, "/semspec", output=output, row=row) + self.assertEqual(json.loads(rendered.read_text()), previous) + else: + RUN.run_table(template, "/semspec", output=output, row=row) + self.assertEqual( + json.loads(rendered.read_text()), + { + name: {"N": 60, "Ready median ms": "1.23"} + if row is None or name == RUN.resolve_row(template, row) else previous[name] + for name in ("Docker", "Firecracker") + }, + ) + return batches + + def test_multirow_success_and_failure_clean_temporary_evidence(self): + for fail in (False, True): + with self.subTest(fail=fail), workspace() as (root, resources): + batches = self.execute(root, fail=fail) + self.assertEqual(len(batches), 2) + self.assertTrue(all(not batch.exists() for batch in batches)) + + def test_explicit_output_retains_failed_evidence(self): + with workspace() as (root, resources): + batches = self.execute(root, fail=True, output=root / ".data/retained") + self.assertTrue(all(batch.exists() for batch in batches)) + + def test_single_row_preserves_other_rows_and_failure_preserves_table(self): + for fail in (False, True): + with self.subTest(fail=fail), workspace() as (root, resources): + batches = self.execute(root, fail=fail, row=" firecracker ") + self.assertEqual(len(batches), 1) + self.assertTrue(all(not batch.exists() for batch in batches)) + + def test_selection_accepts_positional_row_and_keeps_multiple_tables(self): + templates, row = RUN.resolve_selection(["startup/timing", " pvisor vm "]) + self.assertEqual(templates, [ROOT / "startup/timing.json"]) + self.assertEqual(row, "pVisor VM") + self.assertEqual( + RUN.resolve_selection(["filesystem/tools"], "NATIVE-READ"), + ([ROOT / "filesystem/tools.json"], "native-read"), + ) + templates, row = RUN.resolve_selection(["startup/timing", "filesystem/tools"]) + self.assertEqual(len(templates), 2) + self.assertIsNone(row) + for values, selected in ((["startup/timing", "missing"], None), + (["startup/timing", "filesystem/tools"], "Docker")): + with self.subTest(values=values), self.assertRaises(ValueError): + RUN.resolve_selection(values, selected) + + def test_unknown_or_ambiguous_row_does_not_prepare(self): + with workspace() as (root, resources): + template = root / "startup/timing.json" + RUN.save(template, {"VM": "$ vm", "vm": "$ other"}) + self.assertEqual(RUN.resolve_row(template, "VM"), "VM") + with patch.object(RUN, "registered", return_value={"status": "prepared"}), \ + patch.object(RUN, "load_prepare") as prepare: + for value in ("missing", "Vm"): + with self.assertRaisesRegex(ValueError, "available: VM, vm"): + RUN.run_table(template, "/semspec", row=value) + prepare.assert_not_called() + + def test_entry_resolution_rejects_results_and_unknown_names(self): + self.assertEqual(RUN.resolve_entry("startup/timing"), ROOT / "startup/timing.json") + self.assertEqual(RUN.resolve_entry("docker_timing"), ROOT / "startup/docker_timing.json") + for value in ("missing", "startup/timing.render"): + with self.subTest(value=value), self.assertRaises(ValueError): + RUN.resolve_entry(value) diff --git a/benchmark.new/tests/test_startup.py b/benchmark.new/tests/test_startup.py new file mode 100644 index 000000000..1f0908699 --- /dev/null +++ b/benchmark.new/tests/test_startup.py @@ -0,0 +1,320 @@ +"""Fixed JSON subjects, fresh trial evidence and paired document data publication.""" + +import csv +import importlib.util +import io +import json +import os +import subprocess +import sys +import tempfile +import unittest +from pathlib import Path +from unittest.mock import patch + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT)) +sys.path.insert(0, str(ROOT / "startup")) +SPEC = importlib.util.spec_from_file_location("startup_prepare", ROOT / "startup/common.py") +STARTUP = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(STARTUP) +from hooks import archive, prepare, validate, validate_output # noqa: E402 +from publish import compact_configuration, publish_startup # noqa: E402 + +OUTPUT = 'REFERENCE_READY\nREFERENCE_RESULT {"mode":"ready","correctness":"passed"}\n' +SEMSPEC = Path(os.environ.get("SEMSPEC_BIN", ROOT.parent / "target/semspec/debug/semspec")) + + +class StartupContracts(unittest.TestCase): + def test_public_configuration_preserves_identities_without_full_local_receipts(self): + config = { + "cpu_affinity": [0, 1], + "guest_cpus": 2, + "guest_memory_mib": 128, + "device_transport": "virtio-mmio", + "source_commit_scope": "binary source is not attested", + "artifacts": { + str(ROOT / "startup/hooks.py"): "a" * 64, + str(ROOT / "startup/timing.json"): "b" * 64, + "/local/.data/cohort/pvisor": "c" * 64, + "/local/.data/cohort/fixtures/a/trajectory.json": "d" * 64, + "/local/.data/cohort/fixtures/b/trajectory.json": "e" * 64, + }, + "kernel": { + "receipt_sha256": "f" * 64, + "paths": {"kernel": "/local/kernel"}, + "record": { + "variant": "fc-system", + "provenance": { + "origin": "official-distro-stock", + "package_version": "test-version", + "pvisor_firmware": False, + "rpm_info": "full local RPM output", + "rpm_file_digest_checks": {}, + }, + "artifacts": {"kernel": {"file": "kernel", "sha256": "1" * 64}}, + }, + }, + } + original = json.dumps(config) + public = compact_configuration(config) + self.assertEqual(json.dumps(config), original) + self.assertEqual(public["cpu_affinity"], config["cpu_affinity"]) + self.assertEqual(public["source_commit_scope"], config["source_commit_scope"]) + self.assertEqual( + public["artifacts"], + { + "pvisor": "c" * 64, + "fixtures/a/trajectory.json": "d" * 64, + "fixtures/b/trajectory.json": "e" * 64, + }, + ) + self.assertEqual(public["kernel"]["receipt_sha256"], "f" * 64) + self.assertEqual(public["kernel"]["artifacts"], {"kernel": "1" * 64}) + self.assertFalse(public["kernel"]["provenance"]["pvisor_firmware"]) + self.assertNotIn("/local", json.dumps(public)) + self.assertNotIn("rpm_info", json.dumps(public)) + self.assertEqual(compact_configuration(public), public) + + def test_output_requires_unique_correct_ordered_markers_and_no_panic(self): + validate_output(OUTPUT, "") + validate_output(OUTPUT + "REFERENCE_EXIT 0\n", "", guest=True) + for stdout, stderr, guest in [ + (OUTPUT + OUTPUT, "", False), + (OUTPUT, "Kernel panic - not syncing", False), + (OUTPUT.replace("passed", "failed"), "", False), + (OUTPUT, "", True), + (OUTPUT + "REFERENCE_EXIT 1\n", "", True), + ("REFERENCE_EXIT 0\n" + OUTPUT, "", True), + ]: + with self.subTest(stdout=stdout, stderr=stderr), self.assertRaises(ValueError): + validate_output(stdout, stderr, guest) + + def test_publisher_rejects_retired_measurement_protocols_before_writing(self): + for scope in (STARTUP.COMMON.METRIC_SCOPE, STARTUP.COMMON.READY_SCOPE): + with self.subTest(scope=scope), tempfile.TemporaryDirectory() as temporary: + root = Path(temporary) + batch = root / "batch" + batch.mkdir() + (batch / "manifest.json").write_text(json.dumps({"metric_scope": scope})) + with self.assertRaisesRegex(ValueError, "paired Ready / Exit"): + publish_startup(batch, root / "docs") + self.assertFalse((root / "docs").exists()) + + def test_non_native_preparation_requires_explicit_current_inputs(self): + with tempfile.TemporaryDirectory() as temporary, patch.dict(os.environ, {}, clear=True): + destination = Path(temporary) / ".data/batch" + with self.assertRaisesRegex(ValueError, "prepared inputs required"): + STARTUP.prepare_template( + ROOT / "startup/pvisor_vm_timing.json", + destination, + cpu_affinity=str(min(os.sched_getaffinity(0))), + ) + self.assertFalse((destination / "protocol.json").exists()) + + def test_eight_subjects_exclude_python_taskset_and_timeout_wrappers(self): + from subjects import SUBJECTS + + subjects = SUBJECTS + self.assertEqual(set(subjects), set(STARTUP.BACKENDS)) + for subject in subjects.values(): + self.assertNotIn(subject[0], ("python3", "taskset", "timeout")) + self.assertIn("128MiB", subjects["pvisor-vm"]) + self.assertIn("@ASSETS@/rootfs", subjects["pvisor-vm"]) + self.assertIn("--pull", subjects["docker"]) + self.assertIn("/bench/affinity", subjects["docker"]) + self.assertIn("@BATCH@/current/firecracker.json", subjects["fc-system"]) + for name in ("qemu", "qemu-microvm"): + self.assertIn("@VMLINUX@", subjects[name]) + self.assertIn("@INITRD@", subjects[name]) + self.assertIn("128", subjects[name]) + self.assertIn( + "microvm,acpi=off,x-option-roms=off,pit=off,pic=off,rtc=on", subjects["qemu-microvm"] + ) + + def test_archive_discards_only_validated_scratch_disks(self): + with tempfile.TemporaryDirectory() as temporary: + root = Path(temporary) + for validated in (False, True): + current = root / "current" + current.mkdir() + (current / "rootfs.ext4").write_bytes(b"scratch disk") + (current / "condition.json").write_text(json.dumps({"validated": validated})) + archive(root) + trial = root / "trials" / ("00001" if validated else "00000") + self.assertEqual((trial / "rootfs.ext4").exists(), not validated) + self.assertTrue((trial / "condition.json").is_file()) + + def test_listener_mismatch_is_refused_before_launch(self): + with ( + patch.object(STARTUP.Path, "exists", return_value=True), + patch.object( + STARTUP.Path, + "read_text", + return_value=json.dumps(dict(pid=123, capability="never-print-this")), + ), + patch.object(STARTUP.os, "kill", return_value=None), + patch.object(STARTUP.COMMON, "digest", side_effect=["listener", "different"]), + ): + with self.assertRaisesRegex(ValueError, "differs from existing Host"): + STARTUP.preserve_listener(Path("/candidate")) + + def test_each_trial_has_fresh_staging_and_checks_host_content(self): + with tempfile.TemporaryDirectory() as temporary: + root = Path(temporary) + (root / "protocol.json").write_text(json.dumps(dict(assets=None, backends=["native"]))) + prepare(root, "native") + (root / "current/stdout").write_text(OUTPUT) + (root / "current/stderr").write_text("") + validate(root, "native") + archive(root) + prepare(root, "native") + self.assertFalse((root / "current/stage").exists()) + (root / "current/stdout").write_text(OUTPUT) + (root / "current/stderr").write_text("") + (root / "current/workspace/startup-canary").write_text("changed") + with self.assertRaisesRegex(ValueError, "workspace was modified"): + validate(root, "native") + self.assertEqual(len(list((root / "trials").iterdir())), 1) + + +@unittest.skipUnless(SEMSPEC.is_file(), "built semspec required") +class StartupIntegration(unittest.TestCase): + def test_native_event_row_publishes_paired_metrics_for_eight_backends(self): + with tempfile.TemporaryDirectory(prefix="paired startup ") as temporary: + root = Path(temporary) + affinity = str(min(os.sched_getaffinity(0))) + batch = STARTUP.prepare_template( + ROOT / "startup/native_timing.json", root / ".data/batch", cpu_affinity=affinity + ) + result = subprocess.run( + [str(SEMSPEC), "bench", "--cwd", str(batch / "workspace"), str(batch / "in.json")], + capture_output=True, + text=True, + ) + self.assertEqual(result.returncode, 0, result.stderr) + source = root / "docs/src" + publish_startup(batch, source) + ready = json.loads( + (source / "en/benchmarks/startup-timing.ready.render.json").read_text() + ) + exited = json.loads( + (source / "en/benchmarks/startup-timing.exit.render.json").read_text() + ) + self.assertEqual(len(ready), 8) + self.assertEqual(ready["Native"]["N"], 60) + self.assertLessEqual( + float(ready["Native"]["Ready median ms"]), float(exited["Native"]["Exit median ms"]) + ) + self.assertTrue(all(v is None for v in ready["Firecracker"].values())) + self.assertEqual(ready["Native"]["N"], exited["Native"]["N"]) + self.assertEqual(len(list((batch / "trials").iterdir())), 64) + csv_path = source / "en/benchmarks/startup-timing.csv" + before = csv_path.read_bytes() + publish_startup(batch, source) + self.assertEqual(before, csv_path.read_bytes()) + provenance_path = ( + root / "docs/overrides/assets/benchmarks/startup-timing.provenance.json" + ) + provenance = json.loads(provenance_path.read_text()) + manifest = json.loads((batch / "manifest.json").read_text()) + self.assertEqual( + provenance["Native"]["evidence_sha256"], manifest["exports"]["in.bench.json"] + ) + self.assertNotIn(str(root), provenance_path.read_text()) + # Repeating a native row must preserve another cohort's configuration. + rows = list(csv.DictReader(io.StringIO(csv_path.read_text()))) + rows.append(rows[0] | {"condition": "pvisor-host", "batch": "previous-host"}) + buffer = io.StringIO(newline="") + writer = csv.DictWriter(buffer, fieldnames=list(rows[0])) + writer.writeheader() + writer.writerows(rows) + for locale in ("zh", "en"): + (source / locale / "benchmarks/startup-timing.csv").write_text(buffer.getvalue()) + host = dict( + batch="previous-host", + metric_scope=manifest["metric_scope"], + measurement_engine="python-perf_counter_ns", + evidence_sha256="a" * 64, + manifest_sha256="b" * 64, + configuration={"cpu_affinity": [3], "artifacts": {"pvisor": "c" * 64}}, + ) + # Match the retained cohort's identities to its existing CSV row. + host["evidence_sha256"] = rows[-1]["evidence_sha256"] + host["manifest_sha256"] = rows[-1]["manifest_sha256"] + provenance["pVisor host"] = host + provenance_path.write_text(json.dumps(provenance)) + raw = (batch / "manifest.json").read_bytes() + publish_startup(batch, source) + self.assertEqual(json.loads(provenance_path.read_text())["pVisor host"], host) + self.assertEqual((batch / "manifest.json").read_bytes(), raw) + for locale in ("zh", "en"): + base = source / locale / "benchmarks" + self.assertFalse((base / "startup-timing.provenance.json").exists()) + self.assertFalse((base / "startup-timing.render.json").exists()) + + def test_native_checks_all_trials_and_publishes_json_without_editing_source_md(self): + with tempfile.TemporaryDirectory(prefix="startup semspec ") as temporary: + root = Path(temporary) + affinity = {min(os.sched_getaffinity(0))} + batch = STARTUP.prepare_template( + ROOT / "startup/native_timing.json", + root / ".data" / "startup", + cpu_affinity=str(next(iter(affinity))), + ) + original = os.sched_getaffinity(0) + try: + os.sched_setaffinity(0, affinity) + result = subprocess.run( + [ + str(SEMSPEC), + "bench", + "--cwd", + str(batch / "workspace"), + str(batch / "in.json"), + ], + capture_output=True, + text=True, + ) + finally: + os.sched_setaffinity(0, original) + self.assertEqual(result.returncode, 0, result.stderr) + self.assertEqual(len(list((batch / "trials").iterdir())), 64) + evidence = json.loads((batch / "in.bench.json").read_text()) + self.assertEqual(len(evidence["commands"]), 1) + source = root / "docs/src" + for locale in ("zh", "en"): + page = source / locale / "benchmarks/startup.md" + page.parent.mkdir(parents=True) + page.write_text( + "# Startup\n\nhistorical Ready 100.91\n\n" + '{{ table("startup-timing.ready.render.json") }}\n\n' + '{{ table("startup-timing.exit.render.json") }}\n' + ) + original_md = { + locale: (source / locale / "benchmarks/startup.md").read_bytes() + for locale in ("zh", "en") + } + publish_startup(batch, source) + csv_path = source / "zh/benchmarks/startup-timing.csv" + published = csv_path.read_bytes() + self.assertEqual(published, (source / "en/benchmarks/startup-timing.csv").read_bytes()) + self.assertEqual(len(list(csv.DictReader(io.StringIO(csv_path.read_text())))), 1) + for locale in ("zh", "en"): + page = source / locale / "benchmarks/startup.md" + self.assertEqual(page.read_bytes(), original_md[locale]) + render = subprocess.run( + [str(SEMSPEC), "render", str(page)], capture_output=True, text=True + ) + self.assertEqual(render.returncode, 0, render.stderr) + self.assertIn("| Native | 60 |", render.stdout) + expected = evidence["commands"][0]["raw"]["Ready median ms"] + self.assertIn(f"{expected:.2f}", render.stdout) + (next((batch / "trials").iterdir()) / "condition.json").unlink() + with self.assertRaisesRegex(ValueError, "missing preflight"): + publish_startup(batch, source) + self.assertEqual(published, csv_path.read_bytes()) + + +if __name__ == "__main__": + unittest.main() diff --git a/benchmark.new/update.py b/benchmark.new/update.py new file mode 100644 index 000000000..bf7e6e3f5 --- /dev/null +++ b/benchmark.new/update.py @@ -0,0 +1,302 @@ +#!/usr/bin/env python3 +"""Validate semspec/hyperfine evidence, then append CSV and publish JSON tables.""" + +import argparse +import csv +import io +import json +import math +import os +import shlex +import subprocess +import sys +import tempfile +from pathlib import Path + +from common import FILESYSTEM_SCOPE, METRIC_SCOPE, READY_EXIT_SCOPE, ROOT, digest, save + +FIELDS = { + "N": "count", + "Median ms": "median", + "Mean ms": "mean", + "SD ms": "stddev", + "Min ms": "min", + "Max ms": "max", +} + + +def replace(path, content): + path = Path(path) + path.parent.mkdir(parents=True, exist_ok=True) + with tempfile.NamedTemporaryFile( + mode="w", encoding="utf-8", newline="", dir=path.parent, delete=False + ) as stream: + temporary = Path(stream.name) + stream.write(content) + temporary.replace(path) + + +def validate_batch(directory): + directory = Path(directory).resolve() + manifest_path = directory / "manifest.json" + manifest = json.loads(manifest_path.read_text()) + if manifest["status"] not in ("prepared", "passed") or manifest["metric_scope"] not in ( + METRIC_SCOPE, + READY_EXIT_SCOPE, + FILESYSTEM_SCOPE, + ): + raise ValueError("expected a prepared or successful direct-command semspec batch") + if manifest["benchmark_id"] == "B-STARTUP" and manifest["metric_scope"] != READY_EXIT_SCOPE: + raise ValueError("startup validation requires paired Ready / Exit data") + if ( + manifest["entry"] == "filesystem/tools.json" + and manifest["metric_scope"] != FILESYSTEM_SCOPE + ): + raise ValueError("filesystem validation requires the current Python command timer") + if digest(directory / "in.json") != manifest["input_sha256"]: + raise ValueError("benchmark input changed") + if manifest["status"] == "prepared": + for path, expected in manifest["artifacts"].items(): + if digest(path) != expected: + raise ValueError("measured artifact changed: " + path) + for name, expected in manifest["harness"].items(): + if digest(directory / "harness" / name) != expected: + raise ValueError("frozen harness changed: " + name) + exports = {name: digest(directory / name) for name in ("in.bench.json", "in.render.json")} + if manifest["metric_scope"] == READY_EXIT_SCOPE: + exports["timing.samples.json"] = digest(directory / "timing.samples.json") + if manifest["metric_scope"] == FILESYSTEM_SCOPE: + for condition in manifest["conditions"]: + if condition not in ("native-metadata", "native-read"): + raise ValueError("unknown filesystem condition") + name = condition.removeprefix("native-") + ".samples.json" + exports[name] = digest(directory / name) + if manifest["status"] == "passed" and manifest["exports"] != exports: + raise ValueError("export checksum mismatch") + evidence = json.loads((directory / "in.bench.json").read_text()) + resolved = json.loads((directory / "in.render.json").read_text()) + template = json.loads((directory / "in.json").read_text()) + if ( + evidence["format"] != 1 + or evidence["cwd"] != manifest["cwd"] + or evidence["template"] != str(directory / "in.json") + ): + raise ValueError("semspec evidence does not match the prepared input/cwd") + if manifest["metric_scope"] == READY_EXIT_SCOPE: + sys.path.insert(0, str(ROOT / "startup")) + from audit import audit_timing + + results = audit_timing(directory, manifest, evidence, resolved, template) + manifest["exports"], manifest["status"] = exports, "passed" + save(manifest_path, manifest) + return manifest, resolved, results + if manifest["metric_scope"] == FILESYSTEM_SCOPE: + import importlib.util + + sys.path.insert(0, str(ROOT / "filesystem")) + spec = importlib.util.spec_from_file_location( + "filesystem_audit", ROOT / "filesystem/audit.py" + ) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + results = module.audit_filesystem(directory, manifest, evidence, resolved, template) + manifest["exports"], manifest["status"] = exports, "passed" + save(manifest_path, manifest) + return manifest, resolved, results + records = {item["command"]: item for item in evidence["commands"]} + if len(records) != len(evidence["commands"]): + raise ValueError("duplicate command evidence") + if evidence["cells"] or set(resolved) != set(template): + raise ValueError("expected complete row-command evidence without cell commands") + results = {} + expected_rows = [] + for row, expression in template.items(): + if not isinstance(expression, str) or not expression.startswith("$ "): + raise ValueError("invalid row command") + command = expression[2:] + matches = [ + item for key, item in records.items() if shlex.split(key) == shlex.split(command) + ] + record = matches[0] if len(matches) == 1 else None + if record is None or record.get("kind") != "hyperfine-row": + raise ValueError("missing raw hyperfine row evidence") + result = audit_hyperfine_row(record, manifest, row, resolved[row]) + condition = manifest["rows"][row] + if condition in results and results[condition] != result: + raise ValueError("one row refers to different experiments") + results[condition] = result + expected_rows.append( + dict(row=row, command=record["command"], expression=command, columns=list(FIELDS)) + ) + if evidence.get("rows", []) != expected_rows: + raise ValueError("incomplete row matrix") + if set(results) != set(manifest["conditions"]) or len(records) != len(results): + raise ValueError("incomplete condition set") + if manifest["benchmark_id"] == "B-REPLAY": + sys.path.insert(0, str(ROOT / "replay")) + from replay_hooks import audit_replay + + audit_replay(directory, manifest, results) + manifest["exports"] = exports + manifest["status"] = "passed" + save(manifest_path, manifest) + return manifest, resolved, results + + +def audit_hyperfine_row(record, manifest, row, resolved): + """Audit one full row against one direct hyperfine export, preserving its scope.""" + raw = record["raw"] + if raw.get("schema_version") != 2 or len(raw.get("results", [])) != 1: + raise ValueError("expected one raw hyperfine 2.x condition per command") + argv = shlex.split(record["command"]) + if argv[0] != "hyperfine" or "|" in argv: + raise ValueError("row command must invoke hyperfine directly") + if ( + int(argv[argv.index("--runs") + 1]) != manifest["runs"] + or int(argv[argv.index("--warmup") + 1]) != manifest["warmup"] + or argv[argv.index("--command-name") + 1] != manifest["rows"][row] + ): + raise ValueError("row command differs from prepared runs/warmup/condition") + if raw["metadata"]["hyperfine_version"] != manifest["hyperfine_version"].split()[-1]: + raise ValueError("hyperfine version differs from preparation") + result = raw["results"][0] + if result.get("name") != manifest["rows"][row] or result["command"] != argv[-1]: + raise ValueError("measured subject/name differs from template") + samples = result["measurements"] + summary = result["summary"]["time_wall_clock"] + if ( + len(samples) != manifest["runs"] + or any(s["exit_code"] != 0 for s in samples) + or summary["count"] != manifest["runs"] + or summary["unit"] != "second" + ): + raise ValueError("failed or incomplete hyperfine samples") + expected = { + column: summary[field] * (1 if column == "N" else 1000) for column, field in FIELDS.items() + } + if list(resolved) != list(FIELDS) or record["value"] != resolved: + raise ValueError("resolved row schema/value differs from recorded hyperfine row") + for column, value in expected.items(): + if type(resolved[column]) not in (int, float) or not math.isclose( + resolved[column], value, rel_tol=1e-12, abs_tol=0 + ): + raise ValueError("resolved cell differs from the recorded hyperfine selection") + return result + + +def display_table(table): + return { + name: { + column: f"{value:.2f}" if column.endswith(" ms") and value is not None else value + for column, value in row.items() + } + for name, row in table.items() + } + + +def publish_csv(directory, csv_path): + return _publish_csv(directory, csv_path, *validate_batch(directory)) + + +def _publish_csv(directory, csv_path, manifest, table, results, dry_run=False): + """Append already-validated evidence without repeating full artifact audits.""" + directory, csv_path = Path(directory).resolve(), Path(csv_path) + metadata = dict( + batch=manifest["batch"], + benchmark_id=manifest["benchmark_id"], + role=manifest["role"], + runs=manifest["runs"], + warmup=manifest["warmup"], + hyperfine_version=manifest["hyperfine_version"], + metric_scope=manifest["metric_scope"], + evidence_sha256=manifest["exports"]["in.bench.json"], + manifest_sha256=digest(directory / "manifest.json"), + ) + if manifest["metric_scope"] in (READY_EXIT_SCOPE, FILESYSTEM_SCOPE): + metadata.update( + measurement_engine=manifest["measurement_engine"], + python_version=manifest["python_version"], + ) + fields = [*metadata, "condition", "command", *next(iter(table.values()))] + rows = [ + metadata + | dict(condition=manifest["rows"][row], command=results[manifest["rows"][row]]["command"]) + | columns + for row, columns in table.items() + ] + existing = [] + if csv_path.exists(): + reader = csv.DictReader(io.StringIO(csv_path.read_text())) + if reader.fieldnames != fields: + raise ValueError("CSV schema differs; use a new result file") + existing = list(reader) + stream = io.StringIO(newline="") + writer = csv.DictWriter(stream, fieldnames=fields) + writer.writeheader() + writer.writerows(rows) + new = list(csv.DictReader(io.StringIO(stream.getvalue()))) + previous = [row for row in existing if row["batch"] == manifest["batch"]] + if previous and previous != new: + raise ValueError("batch name collision or incomplete published batch") + stream = io.StringIO(newline="") + writer = csv.DictWriter(stream, fieldnames=fields) + writer.writeheader() + writer.writerows(existing if previous else [*existing, *new]) + protected = {p.resolve() for p in directory.iterdir() if p.is_file()} + if csv_path.resolve() in protected: + raise ValueError("do not overwrite raw evidence") + if not dry_run: + replace(csv_path, stream.getvalue()) + return manifest, table + + +def publish(directory, csv_path=None, markdown_path=None): + manifest, table, results = validate_batch(directory) + entry = ROOT / manifest["entry"] + destination = ( + entry.parent / ".data/results" + if manifest["metric_scope"] in (READY_EXIT_SCOPE, FILESYSTEM_SCOPE) + else entry.parent / "results" + ) + csv_path = Path(csv_path or destination / (entry.stem + ".semspec.csv")) + custom_markdown = markdown_path is not None + markdown_path = Path(markdown_path or destination / (entry.stem + ".semspec.md")) + json_path = ( + markdown_path.with_suffix(".render.json") + if custom_markdown + else entry.with_suffix(".render.json") + ) + if len({csv_path.resolve(), markdown_path.resolve(), json_path.resolve()}) != 3 or any( + p.resolve().is_relative_to(Path(directory).resolve()) + for p in (csv_path, markdown_path, json_path) + ): + raise ValueError("result paths must be distinct and outside raw evidence") + if any(p.exists() and not p.is_file() for p in (csv_path, markdown_path, json_path)): + raise ValueError("result destinations must be regular files") + # CSV validation completes before any public table is changed. + _publish_csv(directory, csv_path, manifest, table, results) + save(json_path, display_table(table)) + replace( + markdown_path, + f'# {manifest["benchmark_id"]}: {"Ready / Exit" if manifest["metric_scope"] == READY_EXIT_SCOPE else "complete commands"}\n\n{manifest["role"]}; N={manifest["runs"]}, warmup={manifest["warmup"]}; separate conditions.\n\nBatch: `{manifest["batch"]}`.\n\n{{{{ table("{os.path.relpath(json_path, markdown_path.parent)}") }}}}\n', + ) + return json_path + + +if __name__ == "__main__": + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("directory", type=Path) + p.add_argument("--csv", type=Path) + p.add_argument("--markdown", type=Path) + p.add_argument("--startup-docs", type=Path) + args = p.parse_args() + try: + if args.startup_docs: + sys.path.insert(0, str(ROOT / "startup")) + from publish import publish_startup + + publish_startup(args.directory, args.startup_docs) + else: + print(publish(args.directory, args.csv, args.markdown)) + except (ValueError, KeyError, OSError, subprocess.SubprocessError) as error: + p.exit(1, f"{error}\n") diff --git a/benchmark.new/workflow/apply.json b/benchmark.new/workflow/apply.json new file mode 100644 index 000000000..95834f8b2 --- /dev/null +++ b/benchmark.new/workflow/apply.json @@ -0,0 +1,10 @@ +{ + "unprepared": { + "N": null, + "Median ms": null, + "Mean ms": null, + "SD ms": null, + "Min ms": null, + "Max ms": null + } +} diff --git a/benchmark.new/workflow/apply_ab.json b/benchmark.new/workflow/apply_ab.json new file mode 100644 index 000000000..95834f8b2 --- /dev/null +++ b/benchmark.new/workflow/apply_ab.json @@ -0,0 +1,10 @@ +{ + "unprepared": { + "N": null, + "Median ms": null, + "Mean ms": null, + "SD ms": null, + "Min ms": null, + "Max ms": null + } +} diff --git a/benchmark.new/workflow/apply_diagnostic.json b/benchmark.new/workflow/apply_diagnostic.json new file mode 100644 index 000000000..95834f8b2 --- /dev/null +++ b/benchmark.new/workflow/apply_diagnostic.json @@ -0,0 +1,10 @@ +{ + "unprepared": { + "N": null, + "Median ms": null, + "Mean ms": null, + "SD ms": null, + "Min ms": null, + "Max ms": null + } +} diff --git a/benchmark.new/workflow/review.json b/benchmark.new/workflow/review.json new file mode 100644 index 000000000..95834f8b2 --- /dev/null +++ b/benchmark.new/workflow/review.json @@ -0,0 +1,10 @@ +{ + "unprepared": { + "N": null, + "Median ms": null, + "Mean ms": null, + "SD ms": null, + "Min ms": null, + "Max ms": null + } +} diff --git a/benchmark.new/workflow/workflow.json b/benchmark.new/workflow/workflow.json new file mode 100644 index 000000000..95834f8b2 --- /dev/null +++ b/benchmark.new/workflow/workflow.json @@ -0,0 +1,10 @@ +{ + "unprepared": { + "N": null, + "Median ms": null, + "Mean ms": null, + "SD ms": null, + "Min ms": null, + "Max ms": null + } +} diff --git a/benchmark/README.md b/benchmark/README.md index aa3f0b8bf..32f149a2f 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -1,5 +1,11 @@ # PolicyVisor benchmarks +新的 hyperfine JSON 模板与 semspec 入口和逐批次 CSV / Markdown 更新方式见 +[`benchmark.new/README.md`](../benchmark.new/README.md)。本文件继续维护 benchmark ID 的需求与角色; +下述旧脚本及历史报告保留原始口径,不能作为 hyperfine 测量结果发布。 +`just benchmark` 的 B-PROCESS 已使用新入口;其余 ID 的新入口映射见 +[`benchmark.new/registry.csv`](../benchmark.new/registry.csv),仍需显式提供准备好的单次实验条件命令。 + **benchmark 的目的是给用户一个可以据此做决定的结论,再用实验支撑它。** 不回答用户问题的测量不写进用户文档;没有实验支撑的结论不写进任何文档。 本文件是所有 benchmark 工作的最高约束,优先于子目录 README、脚本注释和既有文档的写法。 @@ -87,7 +93,7 @@ - 原始报告、逐次样本、运行日志、profile、输入与二进制清单、冻结源码和 harness 保存到生成目录的 `.data/`。全仓库 `.data/` 被 Git 忽略;忽略规则不会自动移除已跟踪文件,迁移时保留原字节并核对摘要。 - Markdown 保存读者需要的加工表格与结论。需要支撑性下载时,同目录放整理后的 CSV,并在 Markdown 链接;CSV 必须保留批次、配置、样本数和统计口径。 - 不再把 typed TSV 完整报告、原始 samples CSV、压缩证据包或源码快照发布到 `docs/src/assets/benchmarks/`。保留本地数据与加工结果之间的摘要关联。 -- 发布脚本的输入和原始归档放 `.data/`,只有加工 CSV 与图表进入公开目录。站点构建和搜索也排除 `.data/`。 +- 发布脚本的输入和原始归档放 `.data/`,公开目录只放加工 CSV、图表与精简来源摘要。摘要保留批次、口径、配置和制品/证据哈希;完整 manifest、逐文件 harness 清单与软件包收据留在本地。双语来源摘要共用 `docs/overrides/assets/benchmarks/`。站点构建和搜索也排除 `.data/`。 - 退役脚本与模块没有新测量入口;活动 benchmark 的注册 ID、入口和复现手册必须同步。 ## 注册表 {#registry} @@ -129,7 +135,7 @@ Design: 负载、对照、控制变量与有效样本判据。 - 环境和镜像预先准备好,准备时间单独记录;热缓存与冷镜像分成两组。 - 统一 CPU 和内存预算,随机交替执行,每格至少 30 个样本。 - 完整 Ubuntu 只作为"完整 OS 启动成本"的对照,不与最小 VM 做 VMM 排名。 -- **入口脚本:** `reference_baselines.py --modes ready`(`firecracker_kernels.py` 为独立制品准备 helper)、`startup.py`、`linux_vm_ready.py`、`vm_ready.py`、`run_all.py`、`ubuntu_baselines.py`;启动工程实验与诊断见下列独立条目。 +- **入口脚本:** `benchmark.new/startup/timing.json` 是八个后端的 Ready / Exit 对比表,每行调用固定的 Python timing 脚本(3 次预热、60 次样本,同一次 Popen 记录精确 Ready 行与正常退出)。启动命令由 `subjects.py` 固定;解释器启动和输入准备不计时。`just benchmark startup/timing` 输出整表并生成 `timing.render.json`;`--row` 可选择单行。单后端 `*_timing.json` 可独立执行。旧 hyperfine completion 模板已删除,历史完成时间数据保留原口径。显式保留的成功批次可通过 `update.py --startup-docs` 发布,历史报告归档于 `benchmark.new/startup/.data/history/`。Firecracker 使用 stock 内核/MMIO,预检失败不报告有效 Exit。复现见 [`benchmark.new/startup/README.md`](../benchmark.new/startup/README.md)。历史结果仍由 `reference_baselines.py`、`startup.py`、`linux_vm_ready.py`、`vm_ready.py`、`run_all.py`、`ubuntu_baselines.py`;工程实验与诊断见下列独立条目。 ### B-LAZY-STARTUP:远程镜像按需读取能缩短多少启动等待 {#b-lazy-startup} @@ -166,7 +172,7 @@ Design: 负载、对照、控制变量与有效样本判据。 - 分别报告每项工具耗时和完整任务耗时(包括启动与收尾)。 - 每个样本都要校验工具输出;暂存模式还要校验宿主原文件未被写入、upper 内容完整。 - 不能回答的问题:大型仓库、冷磁盘、真实 registry 和并发吞吐。 -- **入口脚本:** `reference_baselines.py`(跨运行时对照)。 +- **入口脚本:** `reference_baselines.py`(跨运行时对照)。 新框架的 `benchmark.new/filesystem/tools.json` 使用两个 Python 行命令,测量原生 metadata/read 的完整 Python 命令(32 目录 / 2,048 文件 / 64 MiB、3 预热 / 30 样本)。`filesystem/prepare.py` 创建一份共享输入并预检,`fs_timing.py` 用 perf_counter_ns 采样,`audit.py` 复算逐次证据;`just benchmark filesystem/tools` 输出表格并生成 `tools.render.json`,支持 `--row`。解释器、管道和负载校验包含在完整命令时间内;输入准备与观察者启动不计时。它不能替代七工具内部阶段、跨后端任务或配对区间;新 Python 时钟数据独立于历史 `filesystem-semspec.csv`,旧结果归档于 `.data/history/`,复现见 [`filesystem/README.md`](../benchmark.new/filesystem/README.md)。 ### B-FS-ENG:文件系统改动的工程 A/B {#b-fs-eng} @@ -186,6 +192,14 @@ Design: 负载、对照、控制变量与有效样本判据。 - **读队列与 journal 锁对照:** `host_read_journal_ab.py` / `host_read_journal_driver.rs`;冻结同一源码,仅撤销/保留读调度与指纹锁改动。macOS FSKit 的实际部分 copy-up 窗口中并发测只读 OPEN、已打开描述符 READ、OPENDIR,关闭日志、legacy 与 compact strict 分条件;另以共享 Core 的真实 1 GiB 原生文件指纹计算测 unrelated journal 观测,legacy/compact 独立成批。原生文件访问时间变化证明首次读已发生,开始探测时哈希线程必须仍在运行,不人工延迟。每格 3 次预热、30 个 seeded 随机配对样本,空闲对照、完整内容/观察/卸载和干扰门禁、配对 bootstrap 95% CI;不合并复制与哈希条件,不宣称冷磁盘、Linux、VM、吞吐或端到端工具收益。报告/加工 CSV 在 `benchmark/pvisor/`,原始来源和失败在 `.data/`。 +- **Linux staged 空闲调度对照:** 使用 `reference_baselines.run_trial` 的原 task timer,以冻结的 coordinator/namespace helper 随机配对同源码基线与仅修改 host dispatch 的候选,API/README 和私有测试同步更新。CPU 0,1、整父组两核 quota / 16 GiB / swap 0,Btrfs 热宿主缓存、相同空工具缓存与 Python bytecode 策略;每样本独立 user/mount/PID namespace、owned pivot_root 和 Host listener,namespace/输入准备不计时。3 warmups、30 配对轮次、完整七工具输出/Run Bundle/宿主隔离/upper 字节校验、前后输入清单与外部 0.5 s 构建/测试干扰门禁。插桩关闭、保留有效慢样本,5,000 次配对 bootstrap,分离簇不作整体排名。raw helper、冻结源码、制品和失败/中间尝试保留 `.data/`;整理 CSV 与结论仅进入 `design/filesystem-performance-analysis.md#host-idle-dispatch`。不作为 hyperfine 数据,也不与跨运行时用户批次或 macOS/VM 结果合并。 + +- **Linux VM worker 调度对照:** 同一冻结源码的三版本配对:撤销 `65e65c93`/`09bd1d59` 的优化前版本、保留当前优化与 host 修复的基线、仅修改 VM worker 调度/私有测试及 README 的候选。每版本 3 warmups、30 个随机同轮样本;独立 namespace、owned pivot_root、fresh VM/Host listener,整父组 CPU 0,1 / quota 两核 / 16 GiB / swap 0,同 B-FS-TOOLS 七工具及输入、缓存、逐字节 upper、宿主隔离与干扰门禁。使用原 reference timer,profiling 关闭;与当前基线的差异用于候选归因,优化前版本只用于同批恢复程度对照。5,000 次配对 bootstrap,分离簇不报整体排名;各候选批次独立保留,只发布通过门禁的最终制品,不合并样本。原始 helper、冻结源码、构建和诊断在 `.data/`,加工 CSV 与结果仅进入 `design/filesystem-performance-analysis.md#vm-completion-dispatch`,不重算跨运行时用户表,不外推 macOS、冷磁盘或并发吞吐。 + +- **不可变 share 的 DAX 开关对照:** `benchmark.new/filesystem/dax_ab.py` 准备同一优化制品、固件和物理不可变输入,生成 `in.json` 后通过 `semspec bench` 执行 Python 配对行;不使用 hyperfine。两组均通过公开 VM API 建立只读 DAX-capable share 和 128 MiB 窗口,仅 guest 挂载选项 `dax=never` / `dax=inode` 不同;statx 必须验证实际 DAX 属性。宿主热缓存、新 guest、独占 rootfs upper/work;完整 VM 组 CPU 0,1、两核 quota、2 GiB、swap 0,guest 2 vCPU/512 MiB。静态 C 探针测独立 64 MiB 文件的顺序读取、双线程读取和 mmap 页遍历,以及 128×64 KiB 小文件 open/read/close、2,048 路径 metadata;首次数据访问与 guest 内重复访问分列,全量内容验证在阶段计时之外。小文件组有超过窗口容量的映射,保留其正常回收成本。各组 3 次预热、30 个预先随机平衡配对,共 60 个正式 VM;5,000 次配对 bootstrap,先检查分布,保留有效慢样本,不报告 P99。校验错误、输入/制品变化、非零退出、预算不符、OOM 或外部构建/测试干扰使批次失败;报告失败和剔除数。冻结 harness、源码、制品、全部日志与样本在 `.data/`,加工表和 CSV 为工程报告;不将只读 share 的结果外推到 CLI 可写 workspace、启动退化或磁盘冷缓存。 + +- **guest 写合并开关对照:** 同一冻结的直接队列 GNU release/gateway 制品,仅切换宿主 runner 的 `PVISOR_VM_FS_WRITEBACK=1` / `0`,由专用 worker 恢复并校验原环境。Linux VM 的 rootfs/workspace 均无 DAX 窗口;与 B-FS-TOOLS 相同的七项操作、完整任务和校验,整父组 CPU 0,1、两核 quota、16 GiB、swap 0,guest 2 vCPU/16 GiB。热宿主缓存、每次私有 namespace/pivot_root、独立工作区与工具缓存;每臂 3 warmups、30 个 seeded 随机配对,共 60 个正式任务。输入与制品摘要、完整 upper 字节、Run Bundle、正常退出、预算和外部构建/测试门禁通过后才能发布;保留慢有效样本,分离簇不给整体排名,5,000 次配对 bootstrap,不报告 P95/P99。原始及冻结执行入口保留 `.data/writeback-ab-*` 与 `.data/writeback-quick-*`;工程表和 CSV 进入 `design/filesystem-performance-analysis.md#writeback-ab`,生产默认值和跨运行时用户表不由该开关实验改写。 + ### B-FS-DIAG:文件系统请求成本分解 {#b-fs-diag} - **角色:** diagnostic @@ -203,6 +217,16 @@ Design: 负载、对照、控制变量与有效样本判据。 - **实验设计:** 相同 runner、rootfs 与 payload,随机交错实现,不作为跨产品用户对照。 - **入口脚本:** `guest_init.py`。 +### B-STARTUP-REVISION-ENG:提交版本的启动与收尾对照 {#b-startup-revision-eng} + +- **角色:** engineering A/B;结果独立保存在 `benchmark.new/startup/.data/`,不更新用户启动水位。 +- **Motivation:** 定位指定提交与父提交或已归档历史版本是否改变首条有效输出和正常退出等待。 +- **想要的结论:** host、staged、VM 的 Ready / Exit 统计及同轮中位数差的 95% 配对 bootstrap 区间;不能用跨批次数字归因。 +- **实验设计:** 精确源码归档、相同 release/musl/gateway 构建参数和冻结内核;相同 rootfs、探针、CPU 0,1、2 vCPU / 128 MiB、热缓存。各条件 3 次预热、60 对正式样本,固定 seed 随机交替两个版本;Ready / Exit 同次采样。版本在独立 user/mount/PID namespace 中运行,私有 Host 状态目录,退出 namespace 时销毁自有服务;不重启宿主已有 Host。复用当前启动 observer 与完整正确性门禁,保留全部有效慢样本,无事后剔除。 +- **入口:** 每次生成并冻结的 `.data/revision-/bench.py`,由同目录 `in.json` 经 `semspec bench` 执行;构建收据、轮次顺序、逐次输出与统计一起归档。 +- **历史差距复核:** 先以同一 observer、探针和私有环境随机配对历史原始二进制与指定提交,明确 GNU 动态 / musl 静态等构建差异;此对照只能判断制品差距。确认差距后,以同一固件和构建目标重建源码版本,再判断源码贡献。原始固件提取出的内核摘要必须与冻结内核逐字节核对,不能仅根据链接方式断言固件不同。 +- **构建控制:** 历史 GNU、指定提交 GNU、指定提交 musl 可在同轮三条件随机顺序中采样,各 3 次预热 / 60 次正式试次,固定同一历史内核;分别报告 GNU 源码对照及指定提交 GNU/musl 路径对照的配对区间。内核差异另用同源码、同 musl 参数的两条件对照,不能从多个批次差值相减估算贡献。 + ### B-KERNEL-ENG:裁剪固件是否缩短当前产品的启动 {#b-kernel-eng} - **角色:** engineering A/B;放入 `docs/src/*/design/vm-startup-performance-analysis.md`,不作为跨产品用户排名。 @@ -214,10 +238,11 @@ Design: 负载、对照、控制变量与有效样本判据。 ### B-STARTUP-DIAG:固件启动阶段诊断 {#b-startup-diag} - **角色:** diagnostic -- **Motivation:** 定位内核和初始化阶段对就绪时间的贡献。 +- **Motivation:** 定位内核、初始化和宿主启动/收尾阶段对就绪时间及正常退出的贡献。 - **想要的结论:** 各阶段计时及计时边界,不作为用户启动水位。 - **实验设计:** 固定 guest payload 与 runner;早期内核时钟和收尾时间分别解释。 - **入口脚本:** `firmware_boot.py`。 +- **版本退化诊断:** 冻结历史与指定版本制品,在启动工程对照的私有环境下启用 `PVISOR_STARTUP_TIMING=1` / `PVISOR_STARTUP_PROFILE=1`,各做少量独立试次,保留 stderr 阶段记录。诊断行命令与输出独立归档;插桩时间不发布为性能水位,不将重叠 span 相加,缺失阶段标记视为未知。 ### B-AGENT-TASK:一次完整的 Agent 修复任务要多久,主流 CLI 能否正常运行 {#b-agent-task} @@ -321,7 +346,7 @@ Design: 负载、对照、控制变量与有效样本判据。 - **Motivation:** 训练和复现需要把历史轨迹准确还原到同一个起点。用户需要知道支持哪些轨迹格式、准备是否有副作用、成本是多少。 - **想要的结论:** "支持的格式全部通过一致性校验,准备阶段不执行工具也不修改工作区,每条轨迹的准备耗时为 X ms"。 - **实验设计:** 对每个适配器使用固定的原生格式样本,校验前缀结构、工具参数和观测内容,同时校验工作区没有被修改。 -- **入口脚本:** `v1/replay.py`。 +- **入口脚本:** `benchmark.new/replay/replay.json`(`prepare.py` 冻结和预检,semspec bench 直接测量,hooks 逐次验证,`docs.py publish` 更新 JSON 表格)。每个适配器一条固定轨迹的 hyperfine 数据为独立批次;历史二十条轨迹、三次重复的完整一致性证据保留 `v1/replay.py` 的原口径。 ### B-VM-MEMORY:VM 闲置时能省多少内存,恢复要付出什么 {#b-vm-memory} @@ -430,7 +455,7 @@ Design: 负载、对照、控制变量与有效样本判据。 - **Motivation:** 防止 Run 的启动和 Bundle 读取在日常开发中出现回归。 - **想要的结论:** "候选版本相对主干,最小 Run 的耗时和 status 读取耗时没有超过阈值的回归"。 - **实验设计:** 同机同套件,smoke 使用 2 次预热、10 个样本,nightly 使用 10 次预热、50 个样本;默认 15% 阈值。 -- **入口脚本:** `bench.py`(`just benchmark`)。 +- **入口脚本:** `benchmark.new/process/smoke.json`、`nightly.json`(`prepare.py` 准备,semspec bench 测量,`just benchmark`);旧 `bench.py` 仅保留历史复现与统计辅助函数。 ## 检查清单 {#checklist} diff --git a/crates/pvisor-cli/README.md b/crates/pvisor-cli/README.md index 1f585c74f..7851ec717 100644 --- a/crates/pvisor-cli/README.md +++ b/crates/pvisor-cli/README.md @@ -56,6 +56,29 @@ Help, version and feature queries emit no startup logs. For execution commands, `process.entry` and `cli.parsed` are emitted only after command parsing; these markers do not measure the complete process-entry or argument-parsing overhead. +Host execution verifies the loaded executable once per process and pins that +inode. Request validation and the check immediately before worker spawn reuse +its digest only when the opened file's device, inode, size, nanosecond mtime and +ctime, mode and ownership match the verified image. Changes trigger a full hash; +metadata must remain stable across verification. Ancestor permissions, path +identity, peer credentials and listener compatibility remain checked. A warm +listener therefore normally needs no new content hash, while each new frontend +and worker still independently hash their loaded images; no caller-supplied hash +or persistent cross-process cache substitutes for that attestation. +The frontend's signal-only Tokio runtime uses one worker thread; execution +retains its separate worker runtime. + +Successful final process-tree cleanup is idempotent; failed cleanup remains +retryable and descendant-only cleanup does not finalize ownership. Linux +observes the stop state of every discovered live process and thread before +rescanning descendants, instead of assuming a fixed delay quiesces forkers. +Pidfds wake descendant/root exit waits, and worker response events wake the +service immediately. The frontend still owns Child reaping and adopted orphan +cleanup; the service's Finish handshake and bounded escalation remain in place. +macOS retains birth-identity checks and polling during tree quiescence. These +changes remove redundant work in the source path; runtime latency improvements +require a new paired measurement. + ## Source layout - `src/cli/`: Job commands, typed internal Host requests/listener/workers, diff --git a/crates/pvisor-cli/src/cli/host_process.rs b/crates/pvisor-cli/src/cli/host_process.rs index 54e58866f..9af52b6e6 100644 --- a/crates/pvisor-cli/src/cli/host_process.rs +++ b/crates/pvisor-cli/src/cli/host_process.rs @@ -70,6 +70,71 @@ fn exited(fd: &OwnedFd) -> bool { p.revents & (libc::POLLIN | libc::POLLHUP) != 0 } #[cfg(target_os = "linux")] +fn wait_pidfds(mut fds: Vec, deadline: Instant) -> anyhow::Result<()> { + if fds.is_empty() { + return Ok(()); + } + let remaining = deadline.saturating_duration_since(Instant::now()); + let millis = remaining + .as_millis() + .saturating_add(1) + .min(i32::MAX as u128) as i32; + let result = unsafe { libc::poll(fds.as_mut_ptr(), fds.len() as libc::nfds_t, millis) }; + if result < 0 { + let error = std::io::Error::last_os_error(); + if error.kind() != std::io::ErrorKind::Interrupted { + return Err(error.into()); + } + } + Ok(()) +} +#[cfg(target_os = "linux")] +fn process_stopped(pid: i32, fd: &OwnedFd) -> anyhow::Result { + if exited(fd) { + return Ok(true); + } + let state = std::fs::read_to_string(format!("/proc/{pid}/stat")); + // An exit between the pidfd check and /proc read is also quiescent. + if exited(fd) { + return Ok(true); + } + let state = state?; + let state = state + .rsplit_once(')') + .context("invalid process stat")? + .1 + .split_whitespace() + .next() + .context("missing process state")?; + if !matches!(state, "T" | "t") { + return Ok(false); + } + // A stopped leader alone is insufficient while another thread is still + // entering a group stop: that thread may still fork a new descendant. + let tasks = std::fs::read_dir(format!("/proc/{pid}/task")); + if exited(fd) { + return Ok(true); + } + for task in tasks? { + let state = match std::fs::read_to_string(task?.path().join("stat")) { + Ok(state) => state, + Err(error) if error.kind() == std::io::ErrorKind::NotFound => continue, + Err(error) => return Err(error.into()), + }; + let state = state + .rsplit_once(')') + .context("invalid task stat")? + .1 + .split_whitespace() + .next() + .context("missing task state")?; + if !matches!(state, "T" | "t" | "Z" | "X") { + return Ok(false); + } + } + Ok(true) +} +#[cfg(target_os = "linux")] fn stat(pid: i32) -> anyhow::Result<(i32, i32, u64)> { let value = std::fs::read_to_string(format!("/proc/{pid}/stat"))?; let fields: Vec<_> = value @@ -260,6 +325,8 @@ impl StableProcess { pub(super) struct OwnedTree { root: i32, + /// Set only after successful final cleanup. Partial cleanup remains retryable. + cleaned: bool, /// Only the originating frontend adopts request orphans; its independently /// started service must never become part of the request cleanup. #[cfg(target_os = "linux")] @@ -270,8 +337,6 @@ pub(super) struct OwnedTree { #[cfg(target_os = "macos")] identity: (u64, u64), #[cfg(target_os = "macos")] - cleaned: bool, - #[cfg(target_os = "macos")] parent: i32, #[cfg(target_os = "macos")] processes: BTreeMap, @@ -292,6 +357,7 @@ impl OwnedTree { ); Ok(Self { root, + cleaned: false, adopt: _adopt.then_some(parent), excluded, processes: BTreeMap::from([(root, fd)]), @@ -423,70 +489,128 @@ impl OwnedTree { #[cfg(not(any(target_os = "linux", target_os = "macos")))] true } + /// Wait on the pinned Linux process, without adding a polling interval to + /// an already completed exit. The final cleanup still discovers orphans. + pub(super) fn wait_root(&self, timeout: Duration) -> anyhow::Result { + let deadline = Instant::now() + timeout; + while !self.root_exited() && Instant::now() < deadline { + #[cfg(target_os = "linux")] + wait_pidfds( + vec![libc::pollfd { + fd: self.processes[&self.root].as_raw_fd(), + events: libc::POLLIN, + revents: 0, + }], + deadline, + )?; + #[cfg(not(target_os = "linux"))] + std::thread::sleep(Duration::from_millis(1)); + } + Ok(self.root_exited()) + } /// Stop every forker before killing: repeated discovery closes the snapshot /// race for children in different groups/sessions and double-fork orphans. pub(super) fn cleanup(&mut self, kill_root: bool) -> anyhow::Result<()> { + if self.cleaned { + return Ok(()); + } #[cfg(target_os = "linux")] { let deadline = Instant::now() + Duration::from_secs(2); - let mut stopped = BTreeSet::new(); - loop { - self.refresh()?; + let result = (|| -> anyhow::Result<()> { + loop { + self.refresh()?; + for fd in self.processes.values() { + if !exited(fd) { + signal(fd, libc::SIGSTOP)?; + } + } + // SIGSTOP delivery is asynchronous. Observe every live + // forker stopped before discovering children again. + loop { + let mut quiescent = true; + for (&pid, fd) in &self.processes { + quiescent &= process_stopped(pid, fd)?; + } + if quiescent { + break; + } + ensure!( + Instant::now() < deadline, + "request tree would not stop for cleanup" + ); + std::thread::sleep(Duration::from_millis(1)); + } + self.refresh()?; + let mut quiescent = true; + for (&pid, fd) in &self.processes { + quiescent &= process_stopped(pid, fd)?; + } + if quiescent { + break; + } + ensure!( + Instant::now() < deadline, + "request tree would not quiesce for cleanup" + ); + } for (&pid, fd) in &self.processes { - if stopped.insert(pid) { - signal(fd, libc::SIGSTOP)?; + if pid != self.root { + signal(fd, libc::SIGKILL)?; } } - std::thread::sleep(Duration::from_millis(10)); - let before = self.processes.len(); - self.refresh()?; - if self.processes.len() == before { - break; + if kill_root { + self.signal_root(libc::SIGKILL)?; + } else { + self.signal_root(libc::SIGCONT)?; } - ensure!( - Instant::now() < deadline, - "request tree would not quiesce for cleanup" - ); - } - for (&pid, fd) in &self.processes { - if pid != self.root { - signal(fd, libc::SIGKILL)?; + while self + .processes + .iter() + .any(|(&pid, fd)| (pid != self.root || kill_root) && !exited(fd)) + { + ensure!( + Instant::now() < deadline, + "request descendant cleanup timed out" + ); + let fds = self + .processes + .iter() + .filter(|(pid, fd)| (**pid != self.root || kill_root) && !exited(fd)) + .map(|(_, fd)| libc::pollfd { + fd: fd.as_raw_fd(), + events: libc::POLLIN, + revents: 0, + }) + .collect(); + wait_pidfds(fds, deadline)?; } - } - if kill_root { - self.signal_root(libc::SIGKILL)?; - } else { - self.signal_root(libc::SIGCONT)?; - } - while self - .processes - .iter() - .any(|(&pid, fd)| (pid != self.root || kill_root) && !exited(fd)) - { - ensure!( - Instant::now() < deadline, - "request descendant cleanup timed out" - ); - std::thread::sleep(Duration::from_millis(10)); - } - // Only reap adopted descendants of this frontend, never another - // request or the service. The root Child is reaped by its owner. - if self.adopt.is_some() { - for &pid in self.processes.keys() { - if pid != self.root { - unsafe { - libc::waitpid(pid, std::ptr::null_mut(), libc::WNOHANG); + // Only reap adopted descendants of this frontend, never + // another request or the service. Child owns the root wait. + if self.adopt.is_some() { + for &pid in self.processes.keys() { + if pid != self.root { + unsafe { + libc::waitpid(pid, std::ptr::null_mut(), libc::WNOHANG); + } } } } + Ok(()) + })(); + if result.is_err() { + // Do not leave a still-owned process suspended on a failed + // stop/discovery/kill path; Drop can retry final cleanup. + for fd in self.processes.values() { + let _ = signal(fd, libc::SIGCONT); + } + } else if kill_root { + self.cleaned = true; } - Ok(()) + result } #[cfg(target_os = "macos")] { - if self.cleaned { - return Ok(()); - } let deadline = Instant::now() + Duration::from_secs(2); let mut stopped = BTreeSet::new(); // Stop the root BEFORE enumerating/killing children. This prevents @@ -688,6 +812,49 @@ while True: time.sleep(1) assert!(started.elapsed() < Duration::from_secs(3)); assert!(exited(&descendant), "escaped session survived cleanup"); assert!(!child.wait().unwrap().success()); + assert!(tree.cleaned); + // Repeated finish/Drop must not adopt and kill a later independent + // child merely because it now has the same frontend parent. + let mut unrelated = Command::new("sleep") + .arg("60") + .process_group(0) + .spawn() + .unwrap(); + tree.cleanup(false).unwrap(); + tree.cleanup(true).unwrap(); + let survived = unrelated.try_wait().unwrap().is_none(); + let _ = unrelated.kill(); + let _ = unrelated.wait(); + assert!(survived, "completed tree adopted a later independent child"); + } + + #[test] + fn descendant_cleanup_does_not_finalize_live_root_ownership() { + let mut child = Command::new("sleep") + .arg("60") + .process_group(0) + .spawn() + .unwrap(); + let mut tree = + OwnedTree::new(child.id() as i32, std::process::id() as i32, false, 0).unwrap(); + tree.cleanup(false).unwrap(); + assert!(!tree.cleaned); + assert!(!tree.root_exited()); + tree.cleanup(true).unwrap(); + assert!(tree.cleaned); + assert!(!child.wait().unwrap().success()); + } + + #[test] + fn root_exit_wait_preserves_child_reaping_ownership() { + let mut child = Command::new("/bin/true").process_group(0).spawn().unwrap(); + let mut tree = + OwnedTree::new(child.id() as i32, std::process::id() as i32, false, 0).unwrap(); + assert!(tree.wait_root(Duration::from_secs(2)).unwrap()); + tree.cleanup(true).unwrap(); + assert!(child.wait().unwrap().success()); + tree.cleanup(true).unwrap(); + assert!(tree.wait_root(Duration::ZERO).unwrap()); } } diff --git a/crates/pvisor-cli/src/cli/host_service.rs b/crates/pvisor-cli/src/cli/host_service.rs index db9bd143d..4ccfaa5e4 100644 --- a/crates/pvisor-cli/src/cli/host_service.rs +++ b/crates/pvisor-cli/src/cli/host_service.rs @@ -29,7 +29,7 @@ use std::os::{ }; use std::{ fs::{self, File, OpenOptions}, - io::Read, + io::{Read, Seek}, path::{Path, PathBuf}, process::{Command, Stdio}, sync::{ @@ -161,7 +161,40 @@ struct Executable { inode: u64, digest: String, } -fn hash_file(mut file: File) -> anyhow::Result { +// Only metadata sampled around a successful content hash is reusable. In +// particular, dev/ino alone misses in-place builds and preserved mtimes. +#[derive(Debug, PartialEq, Eq)] +struct ImageIdentity { + device: u64, + inode: u64, + size: u64, + modified: (i64, i64), + changed: (i64, i64), + mode: u32, + uid: u32, + gid: u32, +} +impl ImageIdentity { + fn of(m: &fs::Metadata) -> Self { + Self { + device: m.dev(), + inode: m.ino(), + size: m.len(), + modified: (m.mtime(), m.mtime_nsec()), + changed: (m.ctime(), m.ctime_nsec()), + mode: m.mode(), + uid: m.uid(), + gid: m.gid(), + } + } +} +struct RunningImage { + digest: String, + identity: ImageIdentity, + // Pin the verified inode for the lifetime of the cached identity. + _file: File, +} +fn hash_file(mut file: impl Read) -> anyhow::Result { let mut hash = blake3::Hasher::new(); let mut buffer = [0; 64 * 1024]; loop { @@ -173,10 +206,10 @@ fn hash_file(mut file: File) -> anyhow::Result { } Ok(hash.finalize().to_hex().to_string()) } -fn running_digest() -> anyhow::Result { - static DIGEST: OnceLock = OnceLock::new(); - if let Some(digest) = DIGEST.get() { - return Ok(digest.clone()); +fn running_image() -> anyhow::Result<&'static RunningImage> { + static IMAGE: OnceLock = OnceLock::new(); + if let Some(image) = IMAGE.get() { + return Ok(image); } #[cfg(target_os = "linux")] let file = File::open("/proc/self/exe")?; @@ -188,8 +221,38 @@ fn running_digest() -> anyhow::Result { super::host_image::attest(&mut file)?; file }; - let digest = hash_file(file)?; - let _ = DIGEST.set(digest.clone()); + let mut file = file; + let identity = ImageIdentity::of(&file.metadata()?); + let digest = hash_file(&mut file)?; + ensure!( + identity == ImageIdentity::of(&file.metadata()?), + "running executable changed during content verification" + ); + let _ = IMAGE.set(RunningImage { + digest, + identity, + _file: file, + }); + Ok(IMAGE.get().expect("running image initialized")) +} +fn running_digest() -> anyhow::Result { + Ok(running_image()?.digest.clone()) +} +fn verified_digest( + file: &mut File, + image: &RunningImage, + before: ImageIdentity, +) -> anyhow::Result { + let digest = if before == image.identity { + image.digest.clone() + } else { + file.rewind()?; + hash_file(&mut *file)? + }; + ensure!( + before == ImageIdentity::of(&file.metadata()?), + "worker executable changed during content verification" + ); Ok(digest) } fn current_executable() -> anyhow::Result { @@ -217,7 +280,7 @@ fn validate_executable(executable: &Executable) -> anyhow::Result<()> { parent.display() ); } - let file = OpenOptions::new() + let mut file = OpenOptions::new() .read(true) .custom_flags(libc::O_NOFOLLOW | libc::O_CLOEXEC) .open(&executable.path)?; @@ -234,7 +297,7 @@ fn validate_executable(executable: &Executable) -> anyhow::Result<()> { "worker executable ownership/identity mismatch" ); ensure!( - hash_file(file)? == executable.digest, + verified_digest(&mut file, running_image()?, ImageIdentity::of(&m))? == executable.digest, "worker executable content changed, including an in-place build" ); Ok(()) @@ -1040,7 +1103,9 @@ fn serve(listener: UnixListener, dir: PathBuf, g: Generation) -> anyhow::Result< } let mut response = loop { tree.refresh()?; - if let Ok(result) = rx.try_recv() { + // A worker event wakes this wait immediately. Cancellation + // and descendant discovery retain a bounded check interval. + if let Ok(result) = rx.recv_timeout(Duration::from_millis(10)) { break match result { Ok(WorkerEvent::Complete { response }) => response, Ok(WorkerEvent::Finalizing) => { @@ -1100,15 +1165,10 @@ fn serve(listener: UnixListener, dir: PathBuf, g: Generation) -> anyhow::Result< "request teardown exceeded its cleanup deadline; effects may be ambiguous; not retried".into()) }; } - std::thread::sleep(Duration::from_millis(10)); }; tree.cleanup(false)?; let _ = write_frame(&mut authority, &WorkerInstruction::Finish); - let deadline = Instant::now() + GRACE; - while !tree.root_exited() && Instant::now() < deadline { - tree.refresh()?; - std::thread::sleep(Duration::from_millis(10)); - } + tree.wait_root(GRACE)?; tree.cleanup(true)?; if let Some(sig) = cancelled { response.result = Ok(Completion { @@ -1169,6 +1229,9 @@ impl FrontWorker { } impl Drop for FrontWorker { fn drop(&mut self) { + if self.child.is_none() { + return; // finish() already completed cleanup and reaped the Child. + } let cleanup = self.tree.cleanup(true); if let Some(mut child) = self.child.take() { if cleanup.is_ok() && self.tree.root_exited() { @@ -1183,7 +1246,11 @@ impl Drop for FrontWorker { } pub(crate) fn call(mut command: JobCommand) -> anyhow::Result { // Install and latch signals before autostart, any FD transfer, and admission. - let rt = tokio::runtime::Runtime::new()?; + // The frontend only latches signals; execution has its own worker runtime. + let rt = tokio::runtime::Builder::new_multi_thread() + .worker_threads(1) + .enable_all() + .build()?; let signal = signals(&rt)?; let target = command.pin_target()?; let interactive = command.inherits_terminal_input()?; @@ -1210,7 +1277,8 @@ pub(crate) fn call(mut command: JobCommand) -> anyhow::Result { } let request_id = uuid::Uuid::new_v4().to_string(); let executable = current_executable()?; - validate_executable(&executable)?; + // The service validates before issuing a ticket; this frontend validates + // again immediately before spawn, when pathname authority is needed. let request = Request { version: AGENTCTL_HOST_VERSION, request_id: request_id.clone(), @@ -1356,6 +1424,64 @@ pub(crate) fn call(mut command: JobCommand) -> anyhow::Result { mod tests { use super::*; use pvisor_core::host_protocol::AGENTCTL_HOST_MAX_FRAME_BYTES; + + #[test] + fn verified_image_reuses_hash_but_detects_in_place_and_inode_changes() { + use std::io::Seek; + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join("image"); + fs::write(&path, b"before").unwrap(); + let file = File::open(&path).unwrap(); + let metadata = file.metadata().unwrap(); + let image = RunningImage { + digest: hash_file(File::open(&path).unwrap()).unwrap(), + identity: ImageIdentity::of(&metadata), + _file: file, + }; + let mut unchanged = File::open(&path).unwrap(); + let before = ImageIdentity::of(&unchanged.metadata().unwrap()); + assert_eq!( + verified_digest(&mut unchanged, &image, before).unwrap(), + image.digest + ); + assert_eq!( + unchanged.stream_position().unwrap(), + 0, + "cache hit read file contents" + ); + + // Same inode, size and restored mtime: ctime must still invalidate the + // verified identity. Do not trust a timestamp/size-only cache. + fs::write(&path, b"after!").unwrap(); + File::options() + .write(true) + .open(&path) + .unwrap() + .set_times(fs::FileTimes::new().set_modified(metadata.modified().unwrap())) + .unwrap(); + let mut changed = File::open(&path).unwrap(); + assert_eq!(changed.metadata().unwrap().ino(), image.identity.inode); + let before = ImageIdentity::of(&changed.metadata().unwrap()); + assert_ne!( + verified_digest(&mut changed, &image, before).unwrap(), + image.digest + ); + + fs::remove_file(&path).unwrap(); + fs::write(&path, b"before").unwrap(); + let mut replaced = File::open(&path).unwrap(); + assert_ne!(replaced.metadata().unwrap().ino(), image.identity.inode); + let before = ImageIdentity::of(&replaced.metadata().unwrap()); + assert_eq!( + verified_digest(&mut replaced, &image, before).unwrap(), + image.digest + ); + assert_eq!( + replaced.stream_position().unwrap(), + 6, + "replacement was not hashed" + ); + } #[cfg(target_os = "linux")] #[test] fn terminal_signal_is_reported_before_hung_finalization() { diff --git a/crates/pvisor-cli/src/cli/runtime.rs b/crates/pvisor-cli/src/cli/runtime.rs index 27f5aa2f9..5c3a64485 100644 --- a/crates/pvisor-cli/src/cli/runtime.rs +++ b/crates/pvisor-cli/src/cli/runtime.rs @@ -477,9 +477,8 @@ pub fn inspect(args: InspectArgs) -> anyhow::Result { tree.cleanup(true)?; break child.wait()?; } - std::thread::sleep(std::time::Duration::from_millis(10)); + tree.wait_root(std::time::Duration::from_millis(10))?; }; - tree.cleanup(true)?; drop(foreground); if let Some(signal) = status.signal() && [libc::SIGINT, libc::SIGTERM, libc::SIGHUP].contains(&signal) diff --git a/crates/pvisor-overlay-core/README.md b/crates/pvisor-overlay-core/README.md index 92fd67faa..7be2e738b 100644 --- a/crates/pvisor-overlay-core/README.md +++ b/crates/pvisor-overlay-core/README.md @@ -51,10 +51,25 @@ mixed precedence, upper mutations, hardlink denials, capacity and disabled contr ## Copy-up and truncation +For a repeated live read, a compact-log owner may skip the pre-publication +path query only after refreshing and verifying the already published observation +under the log lock. It still resolves the current merged path and checks policy, +hardlink aliases and symlinks. Local cached membership is a hint, never authority; +external consumption recaptures the observation. First reads, legacy journals, +read-only stages and verification errors retain the original checked capture path. + Both legacy file journals and compact logs perform missing-entry checks under their journal lock, release it while computing full-file fingerprints, then -recheck and publish under the lock. Concurrent observations may compute duplicate -fingerprints; the first published observation wins, and durable promotion syncs +recheck and publish under the lock. Compact-log first observations of the same +path in one Core share a temporary capture admission; unrelated paths and +already published entries proceed. Waiting observers refresh verified journal +membership before reusing the actual publication, and external consumption still +recaptures. Pending paths are removed after success or failure; fingerprints are +never cached. Absence observations need no content capture admission. +The diagnostic `coalesced_fingerprints` counter reports captures skipped after +verified same-Core publication, not requests or bytes cached. +Legacy journals and different Core owners may still compute duplicate +fingerprints. The first published observation wins, and durable promotion syncs that actual winner. Capture failures publish nothing and do not poison the compact log; append/sync failures retain its existing reopen requirement. Unrelated observations and sync of already published entries can proceed during capture. @@ -140,6 +155,10 @@ attributes on demand for READDIRPLUS. Protected directory views validate children at snapshot creation to hide denied hardlink aliases. See the host adapter README for cookie and inode lifetime details. +Existing upper parents are checked with one no-follow metadata query during +create/copy-up preparation; +non-directories and I/O errors are not treated as absence. + ## Experimental parallel stage sealing `PVISOR_STAGE_SYNC_WORKERS=2|4|8` in the supervisor opts into a bounded pool diff --git a/crates/pvisor-overlay-core/src/core.rs b/crates/pvisor-overlay-core/src/core.rs index 32e0a8c35..ce5425775 100644 --- a/crates/pvisor-overlay-core/src/core.rs +++ b/crates/pvisor-overlay-core/src/core.rs @@ -10,7 +10,7 @@ use std::os::unix::ffi::{OsStrExt, OsStringExt}; use std::os::unix::fs::{FileTypeExt, MetadataExt, OpenOptionsExt, PermissionsExt}; use std::path::{Component, Path, PathBuf}; use std::sync::atomic::{AtomicU64, Ordering}; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Condvar, Mutex}; pub const WHITEOUT_PREFIX: &str = ".wh."; pub const OPAQUE_NAME: &str = ".wh..wh..opq"; @@ -245,11 +245,59 @@ pub struct OverlayCore { hard_link_sources: Mutex>, preimage_dir: Option, preimage_log: Option>, + fingerprint_flights: FingerprintFlights, // Read observations are published without fsync. Before the first upper // mutation, their file and directory are synced under this lock. preimage_lock: Mutex>, } +#[derive(Debug, Default)] +struct FingerprintFlights { + state: Mutex, + ready: Condvar, +} + +#[derive(Debug, Default)] +struct FingerprintFlightState { + paths: BTreeSet, + waiters: usize, +} + +// This tracks captures in progress, not fingerprints or journal membership. +// Its memory is bounded by simultaneous first observations and is released +// on both capture and publication errors. No journal lock is held while waiting. +struct FingerprintFlight<'a> { + flights: &'a FingerprintFlights, + path: &'a Path, +} + +impl FingerprintFlights { + fn enter<'a>(&'a self, path: &'a Path) -> io::Result> { + let poisoned = |_| io::Error::other("fingerprint admission lock poisoned"); + let mut state = self.state.lock().map_err(poisoned)?; + while state.paths.contains(path) { + state.waiters += 1; + state = self.ready.wait(state).map_err(poisoned)?; + state.waiters -= 1; + } + state.paths.insert(path.to_path_buf()); + Ok(FingerprintFlight { + flights: self, + path, + }) + } +} + +impl Drop for FingerprintFlight<'_> { + fn drop(&mut self) { + let mut state = self.flights.state.lock().unwrap_or_else(|p| p.into_inner()); + state.paths.remove(self.path); + if state.waiters != 0 { + self.flights.ready.notify_all(); + } + } +} + /// Private file contents prepared without publishing an upper namespace entry. /// Drop attempts to remove its private file and, for the last receipt, the /// exclusively created batch directory. Baseline observations survive failure. @@ -1034,6 +1082,7 @@ impl OverlayCore { access: crate::FileAccessPolicy::default(), preimage_dir, preimage_log, + fingerprint_flights: FingerprintFlights::default(), preimage_lock: Mutex::new(BTreeSet::new()), }; if initialize @@ -1306,6 +1355,24 @@ impl OverlayCore { return Ok(()); } after_missing()?; + let _flight = if observed_absent { + None + } else { + Some(self.fingerprint_flights.enter(rel)?) + }; + if _flight.is_some() { + let mut published = log + .lock() + .map_err(|_| io::Error::other("preimage log lock poisoned"))?; + // A same-Core predecessor may have published after our first + // check. The cheap hint avoids another flock on an uncontended + // first observation; a hit still refreshes/validates membership + // and promotes the actual winner, including external consume. + if published.may_contain(rel) && published.contains_or_promote(rel, durable)? { + self.profile.add("coalesced_fingerprints", 1); + return Ok(()); + } + } let state = if observed_absent { PathFingerprint::Absent } else { @@ -1947,7 +2014,10 @@ impl OverlayCore { reject_symlink: bool, ) -> io::Result { let _span = self.profile.span("observe_read"); - if !self.layout.frozen_baseline && self.preimage_dir.is_some() { + if !self.layout.frozen_baseline + && self.preimage_dir.is_some() + && !self.read_observation_is_recorded(rel) + { let before = self.metadata_resolved(rel)?; if reject_symlink && before.metadata.file_type().is_symlink() { return Err(error(libc::ELOOP)); @@ -1961,6 +2031,23 @@ impl OverlayCore { Ok(backing) } + // An already published observation needs no new capture or pre-publication + // resolution. Always resolve the live merged path afterward. The local map + // is only a miss hint; a hit must refresh consumption and verify the log's + // named binding under its file lock. Errors take the original checked path + // so policy and journal failure precedence are preserved. + fn read_observation_is_recorded(&self, rel: &Path) -> bool { + if !self.stage_writable || self.require_visible(rel).is_err() { + return false; + } + self.preimage_log + .as_ref() + .and_then(|log| log.lock().ok()) + .is_some_and(|mut log| { + log.may_contain(rel) && log.contains_or_promote(rel, false).unwrap_or(false) + }) + } + fn metadata_resolved_with_parents( &self, rel: &Path, @@ -2075,11 +2162,11 @@ impl OverlayCore { for component in parent.components() { current.push(component.as_os_str()); let upper = self.upper_path(¤t); - if exists(&upper) { - if !crate::backend::symlink_metadata(&upper)?.is_dir() { - return Err(error(libc::ENOTDIR)); - } - continue; + match crate::backend::symlink_metadata(&upper) { + Ok(metadata) if metadata.is_dir() => continue, + Ok(_) => return Err(error(libc::ENOTDIR)), + Err(error) if error.kind() == io::ErrorKind::NotFound => {} + Err(error) => return Err(error), } let resolved = self.resolve(¤t).ok_or_else(|| error(libc::ENOENT))?; let metadata = crate::backend::symlink_metadata(&resolved.path)?; @@ -2134,7 +2221,9 @@ impl OverlayCore { { continue; } - if let Some(copy) = self.prepare_copy_up(path, truncate, &mut directory)? { + if let Some(copy) = + self.prepare_copy_up(path, resolved, metadata, truncate, &mut directory)? + { copies.push(copy); } } @@ -2144,23 +2233,18 @@ impl OverlayCore { fn prepare_copy_up( &self, rel: &Path, + resolved: Resolved, + metadata: Metadata, truncate: bool, directory: &mut Option>, ) -> io::Result> { let _span = self.profile.span("copy_up_prepare"); - self.require_visible(rel)?; - Self::validate_rel(rel)?; - let Some(resolved) = self.resolve(rel) else { - return Ok(None); - }; - if resolved.is_upper { - return Ok(None); - } - let metadata = self.metadata(rel)?; - if !metadata.is_file() || self.copied_hard_link_metadata(rel)?.is_some() { + // The caller already validated/resolved this path and recorded its + // baseline. Reuse that request-local work; source identity is still + // checked after materialization, copying and before publication. + if self.copied_hard_link_metadata(rel)?.is_some() { return Ok(None); } - self.record_preimage(rel)?; let links = crate::backend::link_count(&resolved.path, metadata.nlink())?; let attributes = crate::backend::attributes(&resolved.path)?; let skip_contents = truncate && links == 1; @@ -3430,6 +3514,176 @@ mod tests { } } + #[test] + fn compact_concurrent_first_observations_hash_once_and_consumption_recaptures() { + use std::sync::Barrier; + let temp = tempfile::tempdir().unwrap(); + let lower = temp.path().join("lower"); + let journal = temp.path().join("journal"); + fs::create_dir(&lower).unwrap(); + let bytes = vec![0x5a; 16 << 20]; + fs::write(lower.join("file"), &bytes).unwrap(); + let expected = fingerprint_at(&lower, Path::new("file")).unwrap(); + let profile = crate::profile::Profile::enabled("first-observation-flight"); + let core = OverlayCore::new_with_exclusions_and_preimages( + vec![lower.clone()], + temp.path().join("upper"), + None, + vec![], + Some(journal.clone()), + ) + .unwrap() + .with_compact_preimages() + .unwrap() + .with_profile(profile.clone()); + let barrier = Barrier::new(8); + std::thread::scope(|scope| { + for _ in 0..8 { + scope.spawn(|| { + barrier.wait(); + core.observe_read(Path::new("file")).unwrap(); + }); + } + }); + assert_eq!(load_preimages(&journal).unwrap()[0].state, expected); + assert_eq!( + profile.report().unwrap().measurements["fingerprinted_paths"].units, + 1 + ); + assert_eq!( + profile.report().unwrap().measurements["fingerprint_bytes"].units, + bytes.len() as u64 + ); + assert!( + core.fingerprint_flights + .state + .lock() + .unwrap() + .paths + .is_empty() + ); + remove_preimages(&journal, &[PathBuf::from("file")]).unwrap(); + fs::write(lower.join("file"), b"new generation").unwrap(); + core.observe_read(Path::new("file")).unwrap(); + let expected = fingerprint_at(&lower, Path::new("file")).unwrap(); + assert_eq!(load_preimages(&journal).unwrap()[0].state, expected); + assert_eq!( + profile.report().unwrap().measurements["fingerprinted_paths"].units, + 2 + ); + assert!( + core.fingerprint_flights + .state + .lock() + .unwrap() + .paths + .is_empty() + ); + } + + #[test] + fn failed_compact_fingerprint_releases_its_path_for_retry() { + let temp = tempfile::tempdir().unwrap(); + let lower = temp.path().join("lower"); + let journal = temp.path().join("journal"); + fs::create_dir_all(lower.join("dir")).unwrap(); + fs::write(lower.join("dir/file"), b"original").unwrap(); + let core = OverlayCore::new_with_exclusions_and_preimages( + vec![lower.clone()], + temp.path().join("upper"), + None, + vec![], + Some(journal.clone()), + ) + .unwrap() + .with_compact_preimages() + .unwrap(); + // A malformed saved identity fails fingerprinting after admission, + // without relying on root-sensitive chmod permission failures. + let identity = OsStr::new("user.containers.override_stat"); + match sys::set_xattr(&lower.join("dir/file"), identity, b"invalid", 0) { + Ok(()) => {} + Err(error) if error.raw_os_error() == Some(libc::ENOTSUP) => return, + Err(error) => panic!("cannot install malformed identity: {error}"), + } + let error = core + .capture_preimage(Path::new("dir/file"), false) + .unwrap_err(); + assert_eq!(error.raw_os_error(), Some(libc::EIO)); + assert!( + core.fingerprint_flights + .state + .lock() + .unwrap() + .paths + .is_empty() + ); + assert!(load_preimages(&journal).unwrap().is_empty()); + sys::remove_xattr(&lower.join("dir/file"), identity).unwrap(); + core.observe_read(Path::new("dir/file")).unwrap(); + assert_eq!(load_preimages(&journal).unwrap().len(), 1); + } + + #[test] + fn compact_same_path_wait_does_not_block_an_unrelated_observation() { + use std::sync::mpsc; + use std::time::{Duration, Instant}; + let temp = tempfile::tempdir().unwrap(); + let lower = temp.path().join("lower"); + fs::create_dir(&lower).unwrap(); + fs::write(lower.join("file"), b"shared").unwrap(); + fs::write(lower.join("other"), b"unrelated").unwrap(); + let journal = temp.path().join("journal"); + let core = OverlayCore::new_with_exclusions_and_preimages( + vec![lower], + temp.path().join("upper"), + None, + vec![], + Some(journal.clone()), + ) + .unwrap() + .with_compact_preimages() + .unwrap(); + let capture = core.fingerprint_flights.enter(Path::new("file")).unwrap(); + std::thread::scope(|scope| { + let waiting = scope.spawn(|| core.observe_read(Path::new("file"))); + let deadline = Instant::now() + Duration::from_secs(2); + let registered = loop { + if core.fingerprint_flights.state.lock().unwrap().waiters != 0 { + break true; + } + if Instant::now() >= deadline { + break false; + } + std::thread::yield_now(); + }; + let (sent, received) = mpsc::channel(); + let shared = &core; + scope.spawn(move || { + sent.send(shared.observe_read(Path::new("other"))).unwrap(); + }); + let independent = received.recv_timeout(Duration::from_secs(2)); + drop(capture); + waiting.join().unwrap().unwrap(); + assert!( + registered, + "same-path observer did not wait for capture admission" + ); + independent + .expect("pending fingerprint blocked an unrelated path") + .unwrap(); + }); + assert_eq!(load_preimages(&journal).unwrap().len(), 2); + assert!( + core.fingerprint_flights + .state + .lock() + .unwrap() + .paths + .is_empty() + ); + } + #[test] fn one_core_can_publish_and_sync_other_observations_during_capture() { use super::*; @@ -4523,6 +4777,92 @@ mod backing_resolution_tests { ); } + #[test] + fn denied_repeated_read_does_not_repair_a_journal_tail() { + use std::io::Write; + let temp = tempfile::tempdir().unwrap(); + let lower = temp.path().join("lower"); + fs::create_dir(&lower).unwrap(); + fs::write(lower.join("file"), b"original").unwrap(); + let journal = temp.path().join("journal"); + let core = OverlayCore::new_with_exclusions_and_preimages( + vec![lower], + temp.path().join("upper"), + None, + vec![], + Some(journal.clone()), + ) + .unwrap() + .with_compact_preimages() + .unwrap(); + core.prepare_file_read(Path::new("file")).unwrap(); + let path = journal.join(PREIMAGE_LOG_NAME); + OpenOptions::new() + .append(true) + .open(&path) + .unwrap() + .write_all(b"PVR") + .unwrap(); + let before = fs::read(&path).unwrap(); + let core = core.with_access_policy( + &crate::FileAccessPolicy::new(vec!["file".into()], vec![]).unwrap(), + ); + assert_eq!( + core.prepare_file_read(Path::new("file")) + .unwrap_err() + .raw_os_error(), + Some(libc::EACCES) + ); + assert_eq!(fs::read(&path).unwrap(), before); + } + + #[test] + fn repeated_compact_read_resolves_once_but_refreshes_external_consumption() { + let temp = tempfile::tempdir().unwrap(); + let lower = temp.path().join("lower"); + fs::create_dir_all(lower.join("a/b")).unwrap(); + let rel = Path::new("a/b/file"); + fs::write(lower.join(rel), b"original").unwrap(); + let journal = temp.path().join("journal"); + let core = OverlayCore::new_with_exclusions_and_preimages( + vec![lower.clone()], + temp.path().join("upper"), + None, + vec![], + Some(journal.clone()), + ) + .unwrap() + .with_compact_preimages() + .unwrap() + .with_profile(crate::profile::Profile::enabled("repeated-read")); + core.prepare_file_read(rel).unwrap(); + let original = load_preimages(&journal).unwrap()[0].state.clone(); + let resolves = core.profile_report().unwrap().measurements["resolve"].calls; + fs::write(lower.join(rel), b"fresh live bytes").unwrap(); + assert_eq!(core.prepare_file_read(rel).unwrap().metadata.len(), 16); + assert_eq!( + core.profile_report().unwrap().measurements["resolve"].calls, + resolves + 1 + ); + assert!( + load_preimages(&journal).unwrap()[0] + .state + .matches(&original) + ); + // A different owner consumes the old generation. Local membership must + // not suppress a new observation, even though the hint still says hit. + remove_preimages(&journal, &[rel.to_path_buf()]).unwrap(); + core.prepare_file_read(rel).unwrap(); + let current = fingerprint_at(&lower, rel).unwrap(); + assert!(load_preimages(&journal).unwrap()[0].state.matches(¤t)); + fs::remove_file(lower.join(rel)).unwrap(); + std::os::unix::fs::symlink("missing", lower.join(rel)).unwrap(); + assert_eq!( + core.prepare_file_read(rel).unwrap_err().raw_os_error(), + Some(libc::ELOOP) + ); + } + #[test] fn observed_backing_preserves_symlinks_and_first_live_observations() { let temp = tempfile::tempdir().unwrap(); diff --git a/crates/pvisor-overlay-core/src/preimage_log.rs b/crates/pvisor-overlay-core/src/preimage_log.rs index 781477d9f..1c2be39ab 100644 --- a/crates/pvisor-overlay-core/src/preimage_log.rs +++ b/crates/pvisor-overlay-core/src/preimage_log.rs @@ -368,6 +368,14 @@ impl PreimageLog { // Split the transaction so OverlayCore can also release its handle mutex // while capturing bytes; both halves still refresh under flock. + // This is only a hint. A positive result must be checked by a refreshed, + // binding-verified transaction: another owner may have consumed the entry. + pub(crate) fn may_contain(&self, path: &Path) -> bool { + self.state + .observations + .contains_key(path.as_os_str().as_bytes()) + } + pub(crate) fn contains_or_promote(&mut self, path: &Path, durable: bool) -> io::Result { validate_path(path)?; let key = path.as_os_str().as_bytes(); diff --git a/crates/pvisor-overlayfs/README.md b/crates/pvisor-overlayfs/README.md index 4e729757e..90b677005 100644 --- a/crates/pvisor-overlayfs/README.md +++ b/crates/pvisor-overlayfs/README.md @@ -89,6 +89,17 @@ hardlink groups, open-handle rebinding and cache effects remain serialized by the existing operation handlers. macOS EXCHANGE uses the same held-handle and inode-remapping path as RENAME_EXCHANGE. +Callbacks requiring no copy preparation execute under the state lock on the +receive thread when the mutation lane is idle. Read callbacks do the same only +when both lanes are idle; READ requests of at least 64 KiB always retain worker +dispatch to preserve bulk-read pipelining. Any accepted mutation, including off-lock copy +preparation, keeps reads on their independent worker so metadata can continue +arriving during copying. An already accepted job prevents overtaking within its +lane. Admission bounds, inode pins and shutdown draining also cover inline +callbacks; copy preparation never runs on the receive thread. Normal reads and +short mutation handlers can still hold the state lock during backing I/O, as +they do on the worker path. + Preparation records durable baseline observations, copies each physical file once into one exclusively created private directory per batch, and preserves metadata. Receipts share its directory FD instead of retaining one FD per file. diff --git a/crates/pvisor-overlayfs/src/api.rs b/crates/pvisor-overlayfs/src/api.rs index bc5582247..e3c282823 100644 --- a/crates/pvisor-overlayfs/src/api.rs +++ b/crates/pvisor-overlayfs/src/api.rs @@ -211,6 +211,11 @@ pub trait OverlayMounting { /// with its own 128-request bound; a full mutation queue does not consume read admission. /// Directory release and read-only-handle flush/release/lseek share that /// queue. Fsync/fsyncdir and writable-handle lifecycle requests retain mutation ordering. + /// A mutation needing no copy preparation may run inline when its lane is + /// idle. A read may run inline only when both lanes are idle; READ requests + /// of at least 64 KiB always use the worker. An accepted + /// copy keeps reads on the worker so the receiver can serve metadata. + /// Inline callbacks retain admission/pins and are drained by shutdown too. /// The two workers serialize inode/handle access through the state lock; /// ordinary read/open/directory I/O can still hold that lock. There is no /// cross-queue arrival ordering for overlapping reads and mutations. diff --git a/crates/pvisor-overlayfs/src/dispatch.rs b/crates/pvisor-overlayfs/src/dispatch.rs index 0fddcea0e..2397d28c9 100644 --- a/crates/pvisor-overlayfs/src/dispatch.rs +++ b/crates/pvisor-overlayfs/src/dispatch.rs @@ -9,12 +9,13 @@ use fuser::{ ReplyEntry, ReplyLseek, ReplyOpen, ReplyStatfs, ReplyWrite, ReplyXattr, Request, TimeOrNow, }; use pvisor_overlay_core::{OverlayCore, PreparedCopyUp, service::FilesystemService}; +use std::borrow::Cow; use std::collections::BTreeSet; use std::ffi::{OsStr, OsString}; use std::io; use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::{Arc, Mutex, mpsc}; +use std::sync::{Arc, Condvar, Mutex, mpsc}; use std::thread::JoinHandle; use std::time::SystemTime; @@ -22,6 +23,237 @@ const REQUEST_LIMIT: usize = 128; const BYTE_LIMIT: usize = 16 * 1024 * 1024; type Callback = Box) + Send>; +#[derive(Default)] +struct IoActivity { + state: Mutex<(usize, bool)>, + ready: Condvar, +} + +impl IoActivity { + fn enter(&self) -> IoPermit<'_> { + let mut state = self.state.lock().unwrap(); + while state.1 { + state = self.ready.wait(state).unwrap(); + } + state.0 += 1; + IoPermit(self) + } + + fn namespace(&self) -> NamespacePermit<'_> { + let mut state = self.state.lock().unwrap(); + while state.1 { + state = self.ready.wait(state).unwrap(); + } + state.1 = true; + while state.0 != 0 { + state = self.ready.wait(state).unwrap(); + } + NamespacePermit(self) + } +} + +struct IoPermit<'a>(&'a IoActivity); + +impl Drop for IoPermit<'_> { + fn drop(&mut self) { + let mut state = self.0.state.lock().unwrap(); + state.0 -= 1; + if state.0 == 0 { + self.0.ready.notify_all(); + } + } +} + +struct NamespacePermit<'a>(&'a IoActivity); + +impl Drop for NamespacePermit<'_> { + fn drop(&mut self) { + self.0.state.lock().unwrap().1 = false; + self.0.ready.notify_all(); + } +} + +enum IoRequest<'a> { + Read { + ino: u64, + fh: u64, + offset: i64, + size: u32, + reply: ReplyData, + }, + Write { + ino: u64, + fh: u64, + offset: i64, + data: Cow<'a, [u8]>, + flags: i32, + reply: ReplyWrite, + }, + Sync { + fh: u64, + datasync: bool, + reply: ReplyEmpty, + }, +} + +impl IoRequest<'_> { + fn into_owned(self) -> IoRequest<'static> { + match self { + Self::Read { + ino, + fh, + offset, + size, + reply, + } => IoRequest::Read { + ino, + fh, + offset, + size, + reply, + }, + Self::Write { + ino, + fh, + offset, + data, + flags, + reply, + } => IoRequest::Write { + ino, + fh, + offset, + data: Cow::Owned(data.into_owned()), + flags, + reply, + }, + Self::Sync { + fh, + datasync, + reply, + } => IoRequest::Sync { + fh, + datasync, + reply, + }, + } + } + + fn fail(self, error: i32) { + match self { + Self::Read { reply, .. } => reply.error(error), + Self::Write { reply, .. } => reply.error(error), + Self::Sync { reply, .. } => reply.error(error), + } + } + + fn run(self, state: &Mutex, activity: &IoActivity) { + // This is an in-flight counter/fence, not a mutex held through I/O. + // Namespace publication waits here before acquiring filesystem state. + let _permit = activity.enter(); + match self { + Self::Read { + ino, + fh, + offset, + size, + reply, + } => { + let lease = state.lock().map_err(|_| libc::EIO).and_then(|fs| { + fs.begin_file_io(ino, fh, offset, false) + .map(|(lease, _)| lease) + .map_err(|error| error.raw_os_error().unwrap_or(libc::EIO)) + }); + let lease = match lease { + Ok(lease) => lease, + Err(error) => { + reply.error(error); + return; + } + }; + let result = lease.read(offset as u64, size); + match state.lock() { + Ok(fs) => fs.finish_read_io(&lease, &result), + Err(_) => { + reply.error(libc::EIO); + return; + } + } + match result { + Ok(bytes) => reply.data(&bytes), + Err(error) => reply.error(error.raw_os_error().unwrap_or(libc::EIO)), + } + } + Self::Write { + ino, + fh, + offset, + data, + flags, + reply, + } => { + let prepared = state.lock().map_err(|_| libc::EIO).and_then(|fs| { + fs.begin_file_io(ino, fh, offset, true) + .map_err(|error| error.raw_os_error().unwrap_or(libc::EIO)) + }); + let (lease, mutation) = match prepared { + Ok(prepared) => prepared, + Err(error) => { + reply.error(error); + return; + } + }; + let result = lease.write(offset as u64, &data, flags); + match state.lock() { + Ok(mut fs) => fs.finish_write_io(&lease, mutation, result, reply), + Err(poisoned) => { + // Complete the reserved cache effects even after a state + // failure; the backing write may already have happened. + poisoned.into_inner().finish_write_io( + &lease, + mutation, + Err(io::Error::from_raw_os_error(libc::EIO)), + reply, + ); + } + } + } + Self::Sync { + fh, + datasync, + reply, + } => { + let lease = state.lock().map_err(|_| libc::EIO).and_then(|fs| { + fs.file_io_lease(fh) + .map_err(|error| error.raw_os_error().unwrap_or(libc::EIO)) + }); + match lease.and_then(|lease| { + lease + .sync(datasync) + .map_err(|error| error.raw_os_error().unwrap_or(libc::EIO)) + }) { + Ok(()) => reply.ok(), + Err(error) => reply.error(error), + } + } + } + } +} + +enum Work { + Callback(Callback), + Io(IoRequest<'static>), +} + +impl Work { + fn fail(self, error: i32) { + match self { + Self::Callback(callback) => callback(Err(error)), + Self::Io(request) => request.fail(error), + } + } +} + pub(super) enum CopyRequest { None, Inode(u64), @@ -97,7 +329,7 @@ struct Job { preparation: CopyRequest, bytes: usize, inodes: Vec, - callback: Callback, + work: Work, } fn failed_preparation( @@ -189,18 +421,31 @@ impl DispatchControl { pub(crate) struct DispatchFs { state: Arc>, + io_activity: Arc, pub(crate) control: Arc, } impl DispatchFs { pub(crate) fn new(filesystem: OverlayFs) -> io::Result { let state = Arc::new(Mutex::new(filesystem)); + let io_activity = Arc::new(IoActivity::default()); let worker_state = state.clone(); + let mutation_activity = io_activity.clone(); let (sender, receiver) = mpsc::sync_channel::(REQUEST_LIMIT); let worker = std::thread::Builder::new() .name("overlay-mutations".into()) .spawn(move || { while let Ok(job) = receiver.recv() { + let callback = match job.work { + Work::Callback(callback) => callback, + Work::Io(request) => { + request.run(&worker_state, &mutation_activity); + if let Ok(mut fs) = worker_state.lock() { + fs.finish_request(job.bytes, &job.inodes); + } + continue; + } + }; let result = (|| { let (core, plan, mutation) = { let fs = worker_state.lock().map_err(|_| libc::EIO)?; @@ -218,37 +463,41 @@ impl DispatchFs { .map_err(|error| error.raw_os_error().unwrap_or(libc::EIO)); Ok((core, copies, mutation)) })(); + let _namespace = mutation_activity.namespace(); match worker_state.lock() { Ok(mut fs) => { match result { - Ok((core, copies, mutation)) => complete_preparation( - &mut fs, - &core, - copies, - mutation, - job.callback, - ), - Err(error) => (job.callback)(Err(error)), + Ok((core, copies, mutation)) => { + complete_preparation(&mut fs, &core, copies, mutation, callback) + } + Err(error) => callback(Err(error)), } fs.finish_request(job.bytes, &job.inodes); } - Err(_) => (job.callback)(Err(libc::EIO)), + Err(_) => callback(Err(libc::EIO)), } } })?; let read_state = state.clone(); + let read_activity = io_activity.clone(); let (read_sender, read_receiver) = mpsc::sync_channel::(REQUEST_LIMIT); let read_worker = match std::thread::Builder::new() .name("overlay-reads".into()) .spawn(move || { while let Ok(job) = read_receiver.recv() { - match read_state.lock() { - Ok(mut fs) => { - (job.callback)(Ok(&mut fs)); - fs.pending_read_requests -= 1; - fs.finish_request(job.bytes, &job.inodes); + match job.work { + Work::Io(request) => request.run(&read_state, &read_activity), + Work::Callback(callback) => { + let _permit = read_activity.enter(); + match read_state.lock() { + Ok(mut fs) => callback(Ok(&mut fs)), + Err(_) => callback(Err(libc::EIO)), + } } - Err(_) => (job.callback)(Err(libc::EIO)), + } + if let Ok(mut fs) = read_state.lock() { + fs.pending_read_requests -= 1; + fs.finish_request(job.bytes, &job.inodes); } } }) { @@ -261,6 +510,7 @@ impl DispatchFs { }; Ok(Self { state, + io_activity, control: Arc::new(DispatchControl { stopped: AtomicBool::new(false), senders: Mutex::new(vec![sender, read_sender]), @@ -300,11 +550,12 @@ impl DispatchFs { } fn enqueue(&self, preparation: CopyRequest, bytes: usize, inodes: &[u64], callback: Callback) { - self.enqueue_on(false, preparation, bytes, inodes, callback); + let short = matches!(preparation, CopyRequest::None); + self.enqueue_on(false, short, preparation, bytes, inodes, callback); } fn enqueue_read(&self, inodes: &[u64], callback: Callback) { - self.enqueue_on(true, CopyRequest::None, 0, inodes, callback); + self.enqueue_on(true, true, CopyRequest::None, 0, inodes, callback); } fn enqueue_open(&self, ino: u64, flags: i32, callback: Callback) { @@ -334,9 +585,81 @@ impl DispatchFs { } } + fn enqueue_io( + &self, + reading: bool, + short: bool, + bytes: usize, + ino: u64, + request: IoRequest<'_>, + ) { + let Ok(senders) = self.control.senders.lock() else { + request.fail(libc::EIO); + return; + }; + if self.control.stopped.load(Ordering::Acquire) { + request.fail(libc::EIO); + return; + } + let Some(sender) = senders.get(usize::from(reading)) else { + request.fail(libc::EIO); + return; + }; + let Ok(mut fs) = self.state.lock() else { + request.fail(libc::EIO); + return; + }; + let pending = if reading { + fs.pending_read_requests + } else { + fs.pending_requests - fs.pending_read_requests + }; + if pending >= REQUEST_LIMIT || bytes > BYTE_LIMIT - fs.pending_bytes { + drop(fs); + request.fail(libc::EAGAIN); + return; + } + *fs.pending_inodes.entry(ino).or_default() += 1; + fs.pending_requests += 1; + fs.pending_read_requests += usize::from(reading); + fs.pending_bytes += bytes; + if short && pending == 0 && (!reading || fs.pending_requests == 1) { + drop(fs); + // Keep admission locked through inline completion so shutdown drains + // it too. A borrowed WRITE payload needs no allocation or copy. + request.run(&self.state, &self.io_activity); + let mut fs = self + .state + .lock() + .unwrap_or_else(|poisoned| poisoned.into_inner()); + fs.pending_read_requests -= usize::from(reading); + fs.finish_request(bytes, &[ino]); + return; + } + // Copy only an admitted, actually queued WRITE. Rejections and idle + // inline writes never allocate a payload or a boxed callback. + let job = Job { + preparation: CopyRequest::None, + bytes, + inodes: vec![ino], + work: Work::Io(request.into_owned()), + }; + if let Err(error) = sender.try_send(job) { + let (job, errno) = match error { + mpsc::TrySendError::Full(job) => (job, libc::EAGAIN), + mpsc::TrySendError::Disconnected(job) => (job, libc::EIO), + }; + fs.pending_read_requests -= usize::from(reading); + fs.finish_request(bytes, &[ino]); + drop(fs); + job.work.fail(errno); + } + } + fn enqueue_on( &self, reading: bool, + short: bool, preparation: CopyRequest, bytes: usize, inodes: &[u64], @@ -346,22 +669,22 @@ impl DispatchFs { preparation, bytes, inodes: inodes.to_owned(), - callback, + work: Work::Callback(callback), }; let Ok(senders) = self.control.senders.lock() else { - (job.callback)(Err(libc::EIO)); + job.work.fail(libc::EIO); return; }; if self.control.stopped.load(Ordering::Acquire) { - (job.callback)(Err(libc::EIO)); + job.work.fail(libc::EIO); return; } let Some(sender) = senders.get(usize::from(reading)) else { - (job.callback)(Err(libc::EIO)); + job.work.fail(libc::EIO); return; }; let Ok(mut fs) = self.state.lock() else { - (job.callback)(Err(libc::EIO)); + job.work.fail(libc::EIO); return; }; let pending = if reading { @@ -371,7 +694,7 @@ impl DispatchFs { }; if pending >= REQUEST_LIMIT || bytes > BYTE_LIMIT - fs.pending_bytes { drop(fs); - (job.callback)(Err(libc::EAGAIN)); + job.work.fail(libc::EAGAIN); return; } for ino in &job.inodes { @@ -382,6 +705,39 @@ impl DispatchFs { fs.pending_read_requests += 1; } fs.pending_bytes += bytes; + // These handlers execute under the state lock. When + // their lane is idle, a worker handoff only adds scheduling latency. + // Keep admission, inode pins and the sender lock through completion: + // shutdown must also wait for an inline callback. Reads may be inline + // only when *both* lanes were idle: any accepted mutation, including + // off-lock copy preparation, keeps the receive thread free for metadata. + // Never overtake an accepted job in the same lane. + if short && pending == 0 && (!reading || fs.pending_requests == 1) { + drop(fs); + let (read_permit, namespace_permit) = if reading { + (Some(self.io_activity.enter()), None) + } else { + (None, Some(self.io_activity.namespace())) + }; + let mut fs = match self.state.lock() { + Ok(fs) => fs, + Err(_) => { + job.work.fail(libc::EIO); + return; + } + }; + if let Work::Callback(callback) = job.work { + callback(Ok(&mut fs)); + } + if reading { + fs.pending_read_requests -= 1; + } + fs.finish_request(bytes, &job.inodes); + drop(fs); + drop(read_permit); + drop(namespace_permit); + return; + } if let Err(error) = sender.try_send(job) { let (job, errno) = match error { mpsc::TrySendError::Full(job) => (job, libc::EAGAIN), @@ -392,7 +748,7 @@ impl DispatchFs { } fs.finish_request(bytes, &job.inodes); drop(fs); - (job.callback)(Err(errno)); + job.work.fail(errno); } } } @@ -620,12 +976,19 @@ impl Filesystem for DispatchFs { _lock_owner: Option, reply: ReplyData, ) { - self.enqueue_read( - &[ino], - Box::new(reply_callback!( + // Preserve worker pipelining for bulk reads, even on an idle mount. + self.enqueue_io( + true, + size < 64 * 1024, + 0, + ino, + IoRequest::Read { + ino, + fh, + offset, + size, reply, - read(ino, fh, offset, size, _flags, _lock_owner) - )), + }, ); } @@ -641,16 +1004,19 @@ impl Filesystem for DispatchFs { _lock_owner: Option, reply: ReplyWrite, ) { - let data = data.to_owned(); - let preparation = CopyRequest::None; - self.enqueue( - preparation, + self.enqueue_io( + false, + true, data.len(), - &[ino], - Box::new(reply_callback!( + ino, + IoRequest::Write { + ino, + fh, + offset, + data: Cow::Borrowed(data), + flags, reply, - write(ino, fh, offset, &data, _write_flags, flags, _lock_owner) - )), + }, ); } @@ -697,11 +1063,16 @@ impl Filesystem for DispatchFs { datasync: bool, reply: ReplyEmpty, ) { - self.enqueue( - CopyRequest::None, + self.enqueue_io( + false, + true, 0, - &[_ino], - Box::new(reply_callback!(reply, fsync(_ino, fh, datasync))), + _ino, + IoRequest::Sync { + fh, + datasync, + reply, + }, ); } @@ -970,6 +1341,37 @@ mod tests { use std::sync::mpsc::{Receiver, SyncSender}; use std::time::{Duration, UNIX_EPOCH}; + #[test] + fn namespace_publication_drains_io_without_holding_filesystem_state() { + let activity = Arc::new(IoActivity::default()); + let root = tempfile::tempdir().unwrap(); + let lower = root.path().join("lower"); + fs::create_dir(&lower).unwrap(); + let state = + Mutex::new(OverlayFs::new(vec![lower], root.path().join("upper"), None).unwrap()); + let io = activity.enter(); + let (sent, completed) = mpsc::channel(); + let other = activity.clone(); + let worker = std::thread::spawn(move || { + let _namespace = other.namespace(); + sent.send(()).unwrap(); + }); + let deadline = std::time::Instant::now() + Duration::from_secs(2); + loop { + if activity.state.lock().unwrap().1 { + break; + } + assert!(std::time::Instant::now() < deadline); + std::thread::yield_now(); + } + assert!(completed.try_recv().is_err()); + assert!(state.try_lock().is_ok()); + drop(io); + completed.recv_timeout(Duration::from_secs(2)).unwrap(); + worker.join().unwrap(); + let _next = activity.enter(); + } + struct SlowLower { root: PathBuf, first: AtomicBool, @@ -1078,6 +1480,102 @@ mod tests { fs.copy_plan(&CopyRequest::None).unwrap().0 } + #[test] + fn idle_short_callbacks_complete_without_a_worker_handoff() { + let f = fixture(false); + let caller = std::thread::current().id(); + for reading in [false, true] { + let callback: Callback = Box::new(move |state| { + let fs = state.unwrap(); + assert_eq!(std::thread::current().id(), caller); + assert_eq!(fs.pending_inodes.get(&FUSE_ROOT_ID), Some(&1)); + assert_eq!(fs.pending_requests, 1); + assert_eq!(fs.pending_read_requests, usize::from(reading)); + }); + if reading { + f.dispatch.enqueue_read(&[FUSE_ROOT_ID], callback); + } else { + f.dispatch + .enqueue(CopyRequest::None, 0, &[FUSE_ROOT_ID], callback); + } + let state = f.dispatch.state.lock().unwrap(); + assert_eq!(state.pending_requests, 0); + assert!(state.pending_inodes.is_empty()); + } + f.dispatch.control.shutdown().unwrap(); + } + + #[test] + fn idle_bulk_read_work_keeps_its_worker_and_inode_pin() { + let f = fixture(false); + let caller = std::thread::current().id(); + let (done, result) = mpsc::channel(); + f.dispatch.enqueue_on( + true, + false, + CopyRequest::None, + 0, + &[FUSE_ROOT_ID], + Box::new(move |state| { + let fs = state.unwrap(); + assert_ne!(std::thread::current().id(), caller); + assert_eq!(fs.pending_inodes.get(&FUSE_ROOT_ID), Some(&1)); + assert_eq!(fs.pending_read_requests, 1); + done.send(service(fs).metadata(Path::new("quick")).unwrap().len()) + .unwrap(); + }), + ); + assert_eq!(result.recv_timeout(Duration::from_secs(2)).unwrap(), 8); + f.dispatch.control.shutdown().unwrap(); + let state = f.dispatch.state.lock().unwrap(); + assert_eq!(state.pending_requests, 0); + assert!(state.pending_inodes.is_empty()); + } + + #[test] + fn shutdown_waits_for_an_inline_callback_and_rejects_new_admission() { + let f = fixture(false); + let (entered, active) = mpsc::channel(); + let (resume, gate) = mpsc::channel(); + let dispatch = f.dispatch.clone(); + let caller = std::thread::spawn(move || { + dispatch.enqueue( + CopyRequest::None, + 0, + &[FUSE_ROOT_ID], + Box::new(move |state| { + assert!(state.is_ok()); + entered.send(()).unwrap(); + gate.recv_timeout(Duration::from_secs(5)).unwrap(); + }), + ); + }); + active.recv_timeout(Duration::from_secs(2)).unwrap(); + let control = f.dispatch.control.clone(); + let (done, joined) = mpsc::channel(); + let shutdown = std::thread::spawn(move || { + control.shutdown().unwrap(); + done.send(()).unwrap(); + }); + while !f.dispatch.control.stopped.load(Ordering::Acquire) { + std::thread::yield_now(); + } + assert!(joined.try_recv().is_err()); + f.dispatch + .metadata(|state| assert_eq!(state.err(), Some(libc::EIO))); + resume.send(()).unwrap(); + caller.join().unwrap(); + joined.recv_timeout(Duration::from_secs(2)).unwrap(); + shutdown.join().unwrap(); + f.dispatch.enqueue_read( + &[FUSE_ROOT_ID], + Box::new(|state| { + assert_eq!(state.err(), Some(libc::EIO)); + }), + ); + assert_eq!(f.dispatch.state.lock().unwrap().pending_requests, 0); + } + #[test] fn metadata_completes_during_real_copy_preparation_and_mutations_stay_ordered() { let f = fixture(false); @@ -1268,6 +1766,17 @@ mod tests { #[test] fn shutdown_drains_the_read_worker_and_rejects_new_reads() { let f = fixture(false); + // A real pending copy keeps reads on the independent worker. Keep the + // existing drain test on that path, alongside the idle-inline drain test. + f.dispatch.enqueue( + copy_request(), + 0, + &[FUSE_ROOT_ID], + Box::new(|state| { + assert!(state.is_ok()); + }), + ); + f.entered.recv_timeout(Duration::from_secs(2)).unwrap(); let (entered, active) = mpsc::channel(); let (resume, gate) = mpsc::channel(); f.dispatch.enqueue_read( @@ -1307,6 +1816,7 @@ mod tests { Some(libc::EIO) ); assert!(joined.try_recv().is_err()); + f.resume.send(()).unwrap(); resume.send(()).unwrap(); joined .recv_timeout(Duration::from_secs(2)) diff --git a/crates/pvisor-overlayfs/src/fs.rs b/crates/pvisor-overlayfs/src/fs.rs index bc7dce75d..26e4240ed 100644 --- a/crates/pvisor-overlayfs/src/fs.rs +++ b/crates/pvisor-overlayfs/src/fs.rs @@ -105,12 +105,45 @@ struct Node { } struct OpenFile { - file: File, + file: Arc, ino: u64, path: PathBuf, flags: i32, backing: PathBuf, } + +/// Owns the opened descriptor independently of the inode/handle tables. +/// Dispatch fences namespace publication while the lease performs backing I/O. +pub(super) struct FileIoLease { + file: Arc, + core: Arc, + backing: PathBuf, + path: PathBuf, + profile: pvisor_overlay_core::profile::Profile, +} + +impl FileIoLease { + pub(super) fn read(&self, offset: u64, size: u32) -> io::Result> { + let _span = self.profile.span("read"); + let mut bytes = vec![0; size as usize]; + let count = self + .core + .read_at(&self.backing, &self.file, &mut bytes, offset)?; + bytes.truncate(count); + Ok(bytes) + } + + pub(super) fn write(&self, offset: u64, data: &[u8], flags: i32) -> io::Result { + let _span = self.profile.span("write"); + write_request(&self.file, data, offset, flags) + } + + pub(super) fn sync(&self, datasync: bool) -> io::Result<()> { + let _span = self.profile.span("fsync"); + self.core.sync_preimages()?; + sys::fsync(&self.file, datasync) + } +} impl std::ops::Deref for OpenFile { type Target = File; fn deref(&self) -> &File { @@ -214,6 +247,94 @@ fn time_value(value: TimeOrNow) -> SystemTime { } impl OverlayFs { + pub(super) fn file_io_lease(&self, fh: u64) -> io::Result { + let file = self + .open_files + .get(&fh) + .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?; + Ok(FileIoLease { + file: file.file.clone(), + core: self.core.clone(), + backing: file.backing.clone(), + path: file.path.clone(), + profile: self.profile.clone(), + }) + } + + pub(super) fn begin_file_io( + &self, + ino: u64, + fh: u64, + offset: i64, + writing: bool, + ) -> io::Result<(FileIoLease, Option)> { + let lease = if offset < 0 { + Err(io::Error::from_raw_os_error(libc::EINVAL)) + } else { + self.file_io_lease(fh) + }; + let lease = match lease { + Ok(lease) => lease, + Err(error) => { + let path = self + .open_files + .get(&fh) + .map(|file| file.path.clone()) + .or_else(|| self.node_path(ino).ok()); + self.observe_result( + path.as_deref(), + if writing { "write" } else { "read" }, + &Err::<(), _>(io::Error::from_raw_os_error(errno(&error))), + 0, + false, + ); + return Err(error); + } + }; + let mutation = if writing { + self.begin_mutation(&[Some(&lease.path)], &[ino], false)? + } else { + None + }; + Ok((lease, mutation)) + } + + pub(super) fn finish_read_io(&self, lease: &FileIoLease, result: &io::Result>) { + self.observe_result( + Some(&lease.path), + "read", + result, + result.as_ref().map_or(0, |bytes| bytes.len() as u64), + false, + ); + } + + pub(super) fn finish_write_io( + &mut self, + lease: &FileIoLease, + mutation: Option, + result: io::Result, + reply: ReplyWrite, + ) { + self.observe_result( + Some(&lease.path), + "write", + &result, + result.as_ref().copied().unwrap_or(0) as u64, + true, + ); + self.mutation_reply(mutation, move |valid| { + if !valid { + reply.error(libc::EIO); + } else { + match result { + Ok(count) => reply.written(count as u32), + Err(error) => reply.error(errno(&error)), + } + } + }); + } + pub(super) fn finish_request(&mut self, bytes: usize, inodes: &[u64]) { self.pending_requests -= 1; self.pending_bytes -= bytes; @@ -996,7 +1117,7 @@ impl OverlayFs { .open(&upper)?; let offset = sys::seek(file, 0, libc::SEEK_CUR)?; sys::seek(&replacement, offset, libc::SEEK_SET)?; - file.file = replacement; + file.file = Arc::new(replacement); } Ok(path) } @@ -1671,7 +1792,7 @@ impl OverlayFs { self.open_files.insert( handle, OpenFile { - file, + file: Arc::new(file), backing, ino, path: observed_path.unwrap_or_default(), @@ -1692,129 +1813,6 @@ impl OverlayFs { }); } - pub(super) fn read( - &mut self, - ino: u64, - fh: u64, - offset: i64, - size: u32, - _flags: i32, - _lock_owner: Option, - reply: ReplyData, - ) { - let profile = self.profile.clone(); - let _span = profile.span("read"); - let observed_path = self - .open_files - .get(&fh) - .map(|file| file.path.clone()) - .or_else(|| self.node_path(ino).ok()); - if offset < 0 { - self.observe_result( - observed_path.as_deref(), - "read", - &Err::<(), _>(io::Error::from_raw_os_error(libc::EINVAL)), - 0, - false, - ); - reply.error(libc::EINVAL); - return; - } - let Some(file) = self.open_files.get(&fh) else { - self.observe_result( - observed_path.as_deref(), - "read", - &Err::<(), _>(io::Error::from_raw_os_error(libc::EBADF)), - 0, - false, - ); - reply.error(libc::EBADF); - return; - }; - let mut data = vec![0; size as usize]; - let result = self - .core - .read_at(&file.backing, &file.file, &mut data, offset as u64); - self.observe_result( - observed_path.as_deref(), - "read", - &result, - result.as_ref().copied().unwrap_or(0) as u64, - false, - ); - match result { - Ok(read) => { - data.truncate(read); - reply.data(&data); - } - Err(error) => reply.error(errno(&error)), - } - } - - pub(super) fn write( - &mut self, - ino: u64, - fh: u64, - offset: i64, - data: &[u8], - _write_flags: u32, - flags: i32, - _lock_owner: Option, - reply: ReplyWrite, - ) { - let profile = self.profile.clone(); - let _span = profile.span("write"); - let observed_path = self - .open_files - .get(&fh) - .map(|file| file.path.clone()) - .or_else(|| self.node_path(ino).ok()); - if offset < 0 { - self.observe_result( - observed_path.as_deref(), - "write", - &Err::<(), _>(io::Error::from_raw_os_error(libc::EINVAL)), - 0, - false, - ); - reply.error(libc::EINVAL); - return; - } - let Some(file) = self.open_files.get(&fh) else { - self.observe_result( - observed_path.as_deref(), - "write", - &Err::<(), _>(io::Error::from_raw_os_error(libc::EBADF)), - 0, - false, - ); - reply.error(libc::EBADF); - return; - }; - let mutation = mutation_or_reply!( - self.begin_mutation(&[observed_path.as_deref()], &[ino], false), - reply - ); - let result = write_request(file, data, offset as u64, flags); - self.observe_result( - observed_path.as_deref(), - "write", - &result, - result.as_ref().copied().unwrap_or(0) as u64, - true, - ); - self.mutation_reply(mutation, move |valid| { - if !valid { - reply.error(libc::EIO); - return; - } - match result { - Ok(written) => reply.written(written as u32), - Err(error) => reply.error(errno(&error)), - } - }); - } - pub(super) fn flush(&mut self, _ino: u64, fh: u64, _lock_owner: u64, reply: ReplyEmpty) { let profile = self.profile.clone(); let _span = profile.span("flush"); @@ -1844,22 +1842,6 @@ impl OverlayFs { } } - pub(super) fn fsync(&mut self, _ino: u64, fh: u64, datasync: bool, reply: ReplyEmpty) { - let profile = self.profile.clone(); - let _span = profile.span("fsync"); - match self.open_files.get(&fh) { - Some(file) => match self - .core - .sync_preimages() - .and_then(|()| sys::fsync(file, datasync)) - { - Ok(()) => reply.ok(), - Err(error) => reply.error(errno(&error)), - }, - None => reply.error(libc::EBADF), - } - } - pub(super) fn opendir(&mut self, ino: u64, _flags: i32, reply: ReplyOpen) { let profile = self.profile.clone(); let _span = profile.span("opendir"); @@ -2108,7 +2090,7 @@ impl OverlayFs { let file = self.core.create_file(&path, mode & !umask, flags)?; let metadata = self.core.metadata(&path)?; let ino = self.allocate_inode(path.clone(), &metadata); - let attr = self.attr(ino, &path)?; + let attr = self.attr_from_metadata(ino, &metadata); Ok((file, attr)) })(); self.observe_result(observed_path.as_deref(), "create", &result, 0, true); @@ -2118,7 +2100,7 @@ impl OverlayFs { self.open_files.insert( handle, OpenFile { - file, + file: Arc::new(file), ino: attr.ino, backing: self .core @@ -2315,6 +2297,42 @@ impl OverlayFs { #[cfg(test)] mod tests { + #[test] + fn file_io_lease_keeps_the_descriptor_alive_after_handle_removal_and_unlink() { + let root = tempfile::tempdir().unwrap(); + let lower = root.path().join("lower"); + fs::create_dir(&lower).unwrap(); + fs::write(lower.join("file"), b"original").unwrap(); + let mut overlay = + OverlayFs::new(vec![lower.clone()], root.path().join("upper"), None).unwrap(); + let ino = overlay + .inode_for_path_with_metadata("file".into()) + .unwrap() + .0; + let (file, backing) = overlay + .open_inode_with_backing(ino, libc::O_RDONLY) + .unwrap(); + overlay.open_files.insert( + 1, + OpenFile { + file: Arc::new(file), + ino, + path: "file".into(), + flags: libc::O_RDONLY, + backing, + }, + ); + let (lease, mutation) = overlay.begin_file_io(ino, 1, 0, false).unwrap(); + assert!(mutation.is_none()); + overlay.open_files.remove(&1); + fs::remove_file(lower.join("file")).unwrap(); + assert_eq!(lease.read(0, 32).unwrap(), b"original"); + assert_eq!( + overlay.file_io_lease(1).err().unwrap().raw_os_error(), + Some(libc::EBADF) + ); + } + #[test] fn queued_requests_pin_only_their_referenced_inodes() { let root = tempfile::tempdir().unwrap(); @@ -2360,7 +2378,7 @@ mod tests { overlay.open_files.insert( 1, OpenFile { - file, + file: Arc::new(file), ino, backing, flags: libc::O_RDONLY, @@ -3311,7 +3329,7 @@ mod tests { overlay.open_files.insert( 1, OpenFile { - file, + file: Arc::new(file), backing, ino, path: path.clone(), diff --git a/crates/pvisor-vm/Cargo.toml b/crates/pvisor-vm/Cargo.toml index 92e240474..71afbc38e 100644 --- a/crates/pvisor-vm/Cargo.toml +++ b/crates/pvisor-vm/Cargo.toml @@ -95,3 +95,7 @@ path = "src/probes/hvf_ram_fault_case.rs" [[example]] name = "threaded_cold_restore_case" path = "src/probes/threaded_cold_restore_case.rs" + +[[example]] +name = "readonly_dax_case" +path = "src/probes/readonly_dax_case.rs" diff --git a/crates/pvisor-vm/README.md b/crates/pvisor-vm/README.md index b8340f899..c5091ce42 100644 --- a/crates/pvisor-vm/README.md +++ b/crates/pvisor-vm/README.md @@ -205,6 +205,73 @@ for caching. `PVISOR_FS_PROFILE=1` reports Core hit/miss/eviction counters; the shared Core README specifies bounds and exact measurement names. No real guest performance claim is made by these contract tests. +## 不可变只读文件的 DAX + +`VmConfiguration::immutable_filesystem(tag, path, window_size)` 为独立的、 +物理内容不可变的目录建立只读 virtio-fs share。当前支持 Linux x86_64/KVM, +不支持 TEE/Nitro;其他平台通过相同 API 返回 `Unsupported`。调用方须在 +VM 生命周期内持有目录的不可变所有权,包括 metadata、目录结构、祖先、 +挂载身份与 hardlink 别名;guest 的 `ro` 挂载本身不构成这个保证。 + +DAX 默认关闭。普通 `filesystem(..., 0)` 与 `overlay(..., 0)` 不创建 DAX +窗口;CLI 的 staged rootfs 和 workspace 使用此默认路径。保留 +`immutable_filesystem` 作为显式启用入口,只有调用它才为独立不可变 share +配置 DAX 窗口。 + +显式不可变 share 的目录项及 LinuxComplete 属性 TTL 为 60 秒,guest 使用 +`dax=never` 时同样生效。普通只读 share 和可写 Overlay 保留原 TTL; +LinuxSimplified 的属性仍不缓存,以保持请求 uid/gid 的语义。 +数据缓存策略保持原配置。 + +```rust +use pvisor_vm::api::{VmBuilder, VmConfiguration}; +use std::path::Path; + +fn attach_tools(vm: &mut VmBuilder, leased_directory: &Path) -> std::io::Result<()> { + vm.immutable_filesystem("tools", leased_directory, 128 << 20) +} +``` + +guest 内核需要 virtio-fs DAX 支持,挂载使用 +`mount -t virtiofs -o ro,dax=inode tools /opt/tools`。同一 share 可使用 +`ro,dax=never` 关闭 guest DAX,走普通 READ;此时仍保留已配置的窗口及 +该 attachment 的生命周期、快照限制。只对宿主 regular backing +file 返回 inode DAX hint;虚拟文件继续走普通 READ,不能对含虚拟文件的 +share 强制 `dax=always`。OPEN 和 metadata 仍经过 virtqueue;建立映射后, +文件的 read/mmap 数据经共享窗口访问。窗口由 guest 按需回收和重映射, +不把整个目录预读到 RAM。 + +映射复用 OPEN 的原生 FD。Linux 的 SETUPMAPPING 使用 `fh=-1`,因此从 +该 inode 的活跃只读 handle 取得 Arc lease;没有活跃 handle 时拒绝新映射, +不重新按路径打开文件。映射自身固定 backing,关闭 FD 不立即撤销已有映射。 +setup/remove 检查地址溢出、页对齐、窗口边界与重叠;批量 remove 先验证全部 +范围,移除后以匿名零页替换,不留下不可访问地址。尾部整页超出 EOF 时也 +使用零页,避免恶意物理地址读取引发宿主 SIGBUS。 + +KVM 将整个窗口登记为只读 memory slot,使正常读取保持宿主文件页共享, +guest 物理写入产生 MMIO exit。宿主映射保留 MAP_PRIVATE 的 COW 保护, +设备 descriptor 若引用窗口也不能改写宿主 backing。guest 的 MAP_PRIVATE +写入只改变自己的匿名页。窗口要求 2 MiB~1 GiB、以 2 MiB 为单位;它占用 +额外 guest 地址空间,resident 文件页和 COW 页仍计入宿主内存预算。 + +可写 merged OverlayFS、普通 writable passthrough 的非零 `shm_size` 明确 +返回 `Unsupported`。DAX 与 snapshot profile、restore、freeze/capture 互斥, +不通过省略映射状态生成不可恢复快照。该阶段只提供显式不可变 share 的 +数据路径,不声明现有 CLI 工作负载或启动时间已经加快。 + +公开 API 的真实 guest 正确性用例为 `readonly_dax_case`: + +```bash +cargo build --locked -p pvisor-vm --example readonly_dax_case +target/debug/examples/readonly_dax_case ROOTFS FIRMWARE IMMUTABLE_DIRECTORY +``` + +ROOTFS 须包含 `/usr/bin/python3`,FIRMWARE 是支持 DAX 的 libkrunfw 文件; +输入目录须含 `payload`,大小超过 6 MiB,第 i 个字节等于 `i % 251`。 +用例将 rootfs 写入隔离到临时 overlay;DAX 窗口仅 2 MiB,检查 inode hint、 +顺序/并行读取、mmap/私有 COW、关闭文件后的映射、虚拟文件普通读取和 +写入拒绝。PASS 是正确性结果,不是性能 benchmark。 + ## 验证入口 `just test pvisor-vm` 运行设备、快照和接口契约测试;macOS 自动使用仓库既有 Hypervisor entitlement 签署测试程序。真实 HVF/KVM 和本地 socket 测试需要宿主权限。Linux 测试中创建 VM 的用例需要可用 `/dev/kvm`。 @@ -231,53 +298,104 @@ without treating their changed names as a change to the captured workload. ## virtio-fs 并发与冻结契约 -每个文件系统设备仍提供一个普通 request queue 和一个 hiprio queue。 -普通队列对可重叠的大 READ(请求至少 64 KiB)和目录读取,自动启动有界 -blocking I/O 线程池;即使只有一个长请求也交给线程池,避免后续 metadata -和 hiprio 到达时队列 owner 仍在执行长 I/O。短元数据请求和需要串行的修改 -由队列 owner 内联处理。 -完成结果异步返回队列 owner,由 owner 独占 available/used ring 的更新。 -默认 worker 数为宿主可用 CPU 数,上限 4;在途请求(待执行、执行中、待发布) -总数最多为已协商普通 virtqueue 的大小,设备最大 1024。worker 数不随队列 -深度增加。接纳完整有界队列避免长请求突发用满旧 `2 × workers` 额度后, -停在下一条长请求上,挡住其后的短 metadata。短请求走内联路径,hiprio 的 -FORGET/INTERRUPT 也不占普通线程池容量。满额时仍停止接纳下一条长请求, -防止异常 driver 导致无界 backlog;已接纳长请求按 FIFO 交给 worker。 -这里的异步指请求完成与队列分发解耦;文件 I/O 仍使用既有 pread/pwrite。 - -线程间传递已校验的 descriptor 地址与长度,在执行线程内创建借用的 +设备提供一个 hiprio 和多个普通 request queues;默认普通队列数为宿主 +可用 CPU 数,上限 4,guest 可只初始化其中一部分。每个已初始化队列由 +专有线程直接取请求、执行和发布完成。hiprio 线程只处理 FORGET、 +BATCH_FORGET 和 INTERRUPT,不承担普通 I/O,也不参与 work stealing。 +INTERRUPT 当前不执行实际取消,独立队列不表示阻塞 syscall 可立即取消。 + +普通线程通过自己的 epoll/kqueue 直接等待和消费 guest kick,并直接 +执行、发布请求;普通请求没有协调线程到 owner 的唤醒交接。线程优先 +服务自己的队列,空闲时只认领重操作持续至少 2 ms 的 owner ring 中 +尚未执行的请求,轮换 victim。READ/WRITE、同步和可能 copy-up 的修改 +操作属于候选;LOOKUP/GETATTR 等短操作不会仅因 busy 被偷取。 +2 ms 是耗时启发式,不证明宿主线程处于内核阻塞状态。 + +控制线程只等待 stop,并每 2 ms 检查长操作与真实 ring backlog;有积压 +才唤醒一个已初始化的空闲 thief,不消费或转发普通 kick。队列线程空闲 +时无限期等待,不周期轮询;控制线程有上述周期检查成本。认领时在 +ring 锁内重新检查当前执行时长,拒绝过期救援提示。认领和 used-ring +发布使用队列短锁,I/O 在锁外执行;已执行请求不迁移,完成回写原队列。 +没有中间工作列表或完成 channel,每个执行线程至多持有一个在途请求。 +所有普通线程都阻塞时没有额外救援线程。 + +执行线程持有已校验的 descriptor 地址与长度,在执行线程内创建借用的 Reader/Writer,保留 RAM access lease 直到 used ring 发布完成。禁止通过 延长 VolatileSlice 的生命周期或新增 unsafe Send 绕过这个契约。 FUSE header 只解码一次;INIT/DESTROY 独占 session guard。 -OverlayFs 的 READ、LOOKUP、GETATTR、目录查询和只读 OPEN 可共享 operation guard。 -带 O_APPEND/O_TRUNC、非只读或需要 kill_priv 的 OPEN,以及改名、copy-up、 -写入、release 与快照恢复仍使用独占 guard。只读 OPEN 的首次观察继续由 -Core 的逐路径 journal 事务同步,快照必须等待所有 operation guard 释放; -读取通过 backing I/O 持有共享 guard,阻止原生句柄提前释放。文件句柄和 -不可变目录项只在查表时持有 handle map 锁,不把整张表锁带入 I/O。 +descriptor plan 在认领时校验全部输入、输出范围与累计长度;header +按捕获地址读入固定大小对象,不构造临时 Reader/Writer。执行线程仅为 +body 与 reply 建立一次借用 consumer,不重新读取 opcode。队列主动 +drain 时保持 guest kick 通知关闭,仅在空队列时 enable/recheck 后休眠; +控制线程独立检查长操作和 backlog,因此仍可救援正在执行的 owner。 + +OverlayFs 的普通 READ/WRITE/FLUSH/FSYNC 直接在 handle map 和 +I/O admission 短锁内取得原生 Arc lease,不取得 namespace operation guard。 +原生文件锁协调该 handle 的读写与同步。namespace mutation、copy-up、 +writable OPEN 仍串行,但只关闭受影响 handle 的 admission +并等待其在途 I/O:路径范围覆盖相关文件、目录子树和已知原生对象别名, +RELEASE/RELEASEDIR 使用 namespace 读锁,允许无关元数据查询继续;RELEASE +只等待被释放的 handle。FH GETATTR/LSEEK 的原生调用同样持有 admission +lease,关闭不会撤销它们正在使用的 mapping;目录枚举持有 Arc 快照。 +scope 屏障保持到修改完成,失败也会释放。 +没有受影响 handle 的路径屏障不取得 admission 锁,也不通知等待者。 +普通 I/O 完成只在全局排空或本 handle 的屏障已登记时通知;屏障关闭和 +排空检查仍在同一 admission 锁内,避免 close/freeze 丢失唤醒。 +等待时不持有 handle map 或 admission 锁,无关 handle 的新 I/O 仍可进入。 +LOOKUP/GETATTR 和目录元数据查询仍遵循 operation guard 的 namespace +串行约束。capture/restore/destroy 使用全局 admission 屏障并排空全部 +lease,普通 namespace 修改不使用 mount-wide I/O drain。 +普通 I/O lease 通过 Arc 共享不可变的 open 元数据,WRITE/FLUSH/FSYNC +不再逐请求分配并复制保存路径。原生 descriptor lease 与 admission 屏障 +继续保护 I/O、release 和 freeze;快照保留原有 File handle 的序列化格式。 +GETATTR 与 backing attributes 的重复查询只在同一请求内复用,不缓存 +可变 backing 的跨请求结果。 +Linux regular-file LOOKUP 和无 FH GETATTR 保留 Core 的 namespace、 +父目录、hardlink 与策略检查,再通过原生父目录的强引用 FD 对最终名称 +执行 no-follow fstatat。每次查询重新读取属性,省去仅用于 metadata 回复 +的叶子 O_PATH FD、native inode 插入与 forget。ROOT_ID 继续绑定初始化时 +的根 FD;目录、符号链接、其他类型、macOS、OPEN 和 FH GETATTR 保留 +原生路径,virtual metadata 的回复处理保持原有规则。 目录缓存用 `Arc` 持有原生 lookup 引用,借用者在缓存锁外执行 backing I/O; 淘汰、失效和 clear 仅移除缓存所有权,最后一个借用者离开后才 forget。 原生 inode 表在同一个 map 锁窗口内固定引用或仲裁插入,防止并发 lookup 替换已返回的 inode,以及 final forget 与引用固定之间的竞争。 -freeze/reset 停止接收新请求,排空已接收请求、回填所有完成结果并 join -全部 I/O worker 后才能返回。接纳窗口扩大后,冻结可能需等待更多已接纳 -I/O;不以丢弃这些请求缩短冻结。thaw/restore 主动扫描 available ring,不依赖 +freeze/reset 停止所有队列接收新请求,等待已认领请求发布 used ring,join +hiprio 和普通线程后才能返回。未认领请求保留在 guest ring;不以丢弃在途 +请求缩短冻结。thaw/restore 主动扫描 available ring,不依赖 guest 再发 kick。正在执行的请求不序列化进入快照;超时由现有 runner 失败契约处理。公开 `api` 的结构和方法不随平台或 worker 数改变。 -宿主诊断变量 `PVISOR_VM_FS_WORKERS=1..8` 可设置每个设备的 worker 上限; -`1` 禁用线程池分发。生产调用方通常不需要设置。`PVISOR_FS_PROFILE=1` -会额外记录 `virtio-fs-dispatch` 的 inline/pool 请求数及创建的 worker 数; +恢复机器在注册 MMIO/eventfd 前,从保存的队列清单恢复普通队列数, +不重新按宿主 CPU 数推导拓扑。原有单普通队列快照仍按其原拓扑恢复。 + +宿主诊断变量 `PVISOR_VM_FS_WORKERS=1..8` 现控制普通队列数量;`1` +仍有独立 hiprio 线程。`PVISOR_FS_PROFILE=1` 记录 `virtio-fs-dispatch` +的 direct、hiprio 和 stolen 请求数; 带 profiling 的运行只用于诊断,不作为性能验收。 -本次调度变化只覆盖 VM virtio-fs,不改变宿主 FUSE 回调循环或写入/copy-up -的独占锁约束。新增确定性回归检查长请求突发后的 GETATTR 可先完成、单条 -目录请求不占住 owner,保留满额回压、乱序完成及 RAM lease 检查;没有当前 -制品的真实 guest 性能验收,不能据单测宣称 benchmark 提速。 +staged OverlayFS 的新会话默认不协商 FUSE_WRITEBACK_CACHE;构造前设置 +`PVISOR_VM_FS_WRITEBACK=1` 可显式开启,`0` 可显式关闭。未显式覆盖的 +恢复会话保留快照中已协商的模式、guest 脏页与原生 handle 模式;首次 +或 DESTROY 后再次 INIT 按当前新会话策略协商。显式 `0` 仍拒绝恢复 +已开启 writeback 的快照,backing、handle 与 session 一致性校验不变。 +直接共享宿主目录的 passthrough 保持默认关闭。开启后工作区内容和元数据须由 guest +独占修改,不能依赖宿主并发修改的即时可见性。O_WRONLY 的原生 FD +转换为 O_RDWR 以支持部分页回读,O_APPEND 由 guest 计算 offset,原生 FD +清除 O_APPEND。close/fsync 和 guest 后台回写使脏数据到达宿主;文件写入 +错误可能延后报告。协商状态随 backing 和 FUSE session 保存并校验,完整 +机器快照保留 guest 脏页,不声称排空 virtqueue 已把 guest 脏页刷到 stage。 +恢复仍拒绝 exported descriptors/DAX 等既有不支持状态。 + +本次只修改 VM virtio-fs 执行路径,不改宿主 FUSE 的 dispatch。 +回归覆盖直接 kick/stop、短操作不偷取、长操作 backlog 原 ring 完成、 +同对象屏障、无关 namespace 修改及等待期间新 I/O admission,并保留 +hiprio、RAM lease、冻结与 writeback 恢复覆盖。性能结果使用独立冻结 +release 制品与正式配对批次,不由并发单测推导收益。 设计参考 [virtiofsd 的线程池和 EVENT_IDX 处理](https://gitlab.com/virtio-fs/virtiofsd/-/blob/main/src/vhost_user.rs) 及 [passthrough 的资源持有方式](https://gitlab.com/virtio-fs/virtiofsd/-/blob/main/src/passthrough/mod.rs)。 -并发调度使用 pVisor 内部实现,不复制上游代码,也不提供 vhost-user、DAX、 -writeback 或上游的工作目录切换;这些机制需要各自的权限、缓存和冻结契约。 +并发调度使用 pVisor 内部实现,不复制上游代码;不提供 vhost-user 或 +上游的工作目录切换。只读 DAX 使用上文的独立权限、缓存和冻结契约。 diff --git a/crates/pvisor-vm/src/api.rs b/crates/pvisor-vm/src/api.rs index 504243849..7c6008acd 100644 --- a/crates/pvisor-vm/src/api.rs +++ b/crates/pvisor-vm/src/api.rs @@ -17,7 +17,9 @@ //! # Filesystems and networking //! Disable implicit init before attaching a native-init `/dev/root`. Filesystem //! tags and virtual paths are unique. Host paths currently require UTF-8. DAX is -//! opt-in; pVisor overlays use zero DAX size. Synthetic files own their bytes. +//! disabled by default; only an explicit [`VmConfiguration::immutable_filesystem`] +//! attachment allocates a DAX window. Ordinary filesystems and pVisor overlays +//! use zero DAX size. Synthetic files own their bytes. //! Vsock never implicitly enables TSI, even when no network device is configured. //! //! # Snapshots and transitions @@ -130,7 +132,38 @@ pub trait VmConfiguration: Sized { /// Configure a custom init's command; requires implicit init to be disabled. /// Validate atomically before replacing the previous command. fn guest_command(&mut self, command: GuestCommand) -> io::Result<()>; + /// Attach an ordinary writable passthrough directory. `shm_size` must be + /// zero; nonzero windows return Unsupported before changing configuration. + /// Use `immutable_filesystem` for the supported read-only DAX contract. fn filesystem(&mut self, tag: &str, path: &Path, shm_size: usize) -> io::Result<()>; + /// Attach a guest-read-only, physically immutable directory with a DAX window. + /// Calling this method explicitly enables DAX capability for this share. + /// The caller must keep contents, metadata, namespace, ancestors and hardlink + /// aliases unchanged until VM teardown. This promise is not inferred from a + /// read-only mount. The guest mounts the tag with `ro,dax=inode` to use DAX, + /// or `ro,dax=never` for ordinary reads. Disabling DAX in the guest retains + /// the configured window and this attachment's lifecycle restrictions. + /// Only regular backing files support DAX; virtual files use ordinary reads. + /// OPEN authorizes the file; mappings require a live read-only handle for + /// that inode. Linux's `fh=-1` mapping requests reuse such an opened FD. + /// Guest writes cannot change the backing. Mapping requests may reserve zero + /// padding beyond EOF, but normal file reads retain their original size. + /// + /// Requires Linux x86_64 without TEE/Nitro, an absolute existing directory, + /// and a window of 2 MiB to 1 GiB in 2 MiB increments. Duplicate/empty tags + /// and invalid inputs fail before attachment. Other platforms return + /// Unsupported. DAX and snapshot profiles/restoration are mutually exclusive; + /// freeze/capture also reject DAX. Failure leaves previous attachments usable. + /// The window reserves additional guest address space, not ordinary guest RAM; + /// resident file pages and guest-induced private COW pages consume host memory. + fn immutable_filesystem( + &mut self, + tag: &str, + path: &Path, + dax_window_size: usize, + ) -> io::Result<()>; + /// Attach a writable merged overlay. `shm_size` must be zero; DAX for this + /// merged view is Unsupported. Failure preserves previous attachments. fn overlay(&mut self, tag: &str, overlay: OverlayConfig, shm_size: usize) -> io::Result<()>; fn virtual_file( &mut self, diff --git a/crates/pvisor-vm/src/builder.rs b/crates/pvisor-vm/src/builder.rs index ef03326fc..7af08e902 100644 --- a/crates/pvisor-vm/src/builder.rs +++ b/crates/pvisor-vm/src/builder.rs @@ -336,6 +336,12 @@ impl Builder { path: &std::path::Path, shm_size: usize, ) -> io::Result<()> { + if shm_size != 0 { + return Err(io::Error::new( + io::ErrorKind::Unsupported, + "writable DAX requires an explicit lifecycle contract; use immutable_filesystem", + )); + } self.attach_fs(FsDeviceConfig { fs_id: tag.into(), shared_dir: Some( @@ -352,12 +358,55 @@ impl Builder { virtual_entries: Vec::new(), }) } + #[cfg(all(target_os = "linux", target_arch = "x86_64"))] + pub(crate) fn immutable_filesystem( + &mut self, + tag: &str, + path: &std::path::Path, + window: usize, + ) -> io::Result<()> { + const UNIT: usize = 2 << 20; + if !(UNIT..=1 << 30).contains(&window) + || !window.is_multiple_of(UNIT) + || !path.is_absolute() + || !path.is_dir() + { + return Err(io::Error::new(io::ErrorKind::InvalidInput, "DAX requires an absolute directory and a 2 MiB-aligned window between 2 MiB and 1 GiB")); + } + if self.resources.snapshot_profile || self.resources.machine_restore.is_some() { + return Err(io::Error::new( + io::ErrorKind::Unsupported, + "DAX cannot be attached to a snapshot/restore profile", + )); + } + let shared_dir = path + .to_str() + .ok_or_else(|| { + io::Error::new(io::ErrorKind::InvalidInput, "filesystem path is not UTF-8") + })? + .to_owned(); + self.attach_fs(FsDeviceConfig { + fs_id: tag.into(), + shared_dir: Some(shared_dir), + semantics: PermissionSemantics::LinuxComplete, + shm_size: Some(window), + read_only: true, + overlay: None, + virtual_entries: Vec::new(), + }) + } pub fn overlay( &mut self, tag: &str, overlay: OverlayConfig, shm_size: usize, ) -> io::Result<()> { + if shm_size != 0 { + return Err(io::Error::new( + io::ErrorKind::Unsupported, + "DAX is not supported for writable merged overlays", + )); + } if overlay.lower_dirs.is_empty() { return Err(io::Error::new( io::ErrorKind::InvalidInput, @@ -442,6 +491,13 @@ impl Builder { ))] impl Builder { pub fn snapshot_profile(&mut self) -> io::Result<()> { + #[cfg(not(feature = "tee"))] + if self.resources.fs.iter().any(|fs| fs.shm_size.is_some()) { + return Err(io::Error::new( + io::ErrorKind::Unsupported, + "snapshot profiles do not support DAX windows", + )); + } if self.resources.nested_enabled { return Err(io::Error::new( io::ErrorKind::Unsupported, diff --git a/crates/pvisor-vm/src/contract_tests/vm_snapshot_fs.rs b/crates/pvisor-vm/src/contract_tests/vm_snapshot_fs.rs index 6cd280b46..ee4826e9b 100644 --- a/crates/pvisor-vm/src/contract_tests/vm_snapshot_fs.rs +++ b/crates/pvisor-vm/src/contract_tests/vm_snapshot_fs.rs @@ -53,6 +53,8 @@ impl GuestFs { Self::from_device(fs, restore) } fn from_device(mut fs: Fs, restore: Option<(GuestMemoryMmap, DeviceSnapshot, u16)>) -> Self { + // This protocol fixture models a one-vCPU guest with one request ring. + fs.configure_request_queues(1).unwrap(); let chip = interrupt_chip(); let interrupt = InterruptTransport::new(chip, "fs-check".into()).unwrap(); let events: Vec<_> = (0..2) diff --git a/crates/pvisor-vm/src/devices/virtio/descriptor_utils.rs b/crates/pvisor-vm/src/devices/virtio/descriptor_utils.rs index 910fb411b..f1359174d 100644 --- a/crates/pvisor-vm/src/devices/virtio/descriptor_utils.rs +++ b/crates/pvisor-vm/src/devices/virtio/descriptor_utils.rs @@ -6,7 +6,7 @@ use std::cmp; use std::collections::VecDeque; use std::fmt::{self, Display}; use std::io::{self, Read, Write}; -use std::mem::{size_of, MaybeUninit}; +use std::mem::{MaybeUninit, size_of}; use std::ops::Deref; use std::ptr::copy_nonoverlapping; use std::result; @@ -201,13 +201,24 @@ impl OwnedDescriptorChain { writable: Vec::new(), }; let mut writing = false; + let mut readable_len = 0usize; + let mut writable_len = 0usize; loop { + // Validate the complete captured plan before decoding a request. + // Header admission does not need to allocate Reader/Writer buffers. + descriptor_slice(chain.mem, chain.addr, chain.len)?; if chain.is_write_only() { writing = true; + writable_len = writable_len + .checked_add(chain.len as usize) + .ok_or(Error::DescriptorChainOverflow)?; plan.writable.push((chain.addr, chain.len)); } else if writing { return Err(Error::InvalidChain); } else { + readable_len = readable_len + .checked_add(chain.len as usize) + .ok_or(Error::DescriptorChainOverflow)?; plan.readable.push((chain.addr, chain.len)); } // has_next() masks the NEXT flag at the TTL limit. Reject cycles @@ -220,28 +231,64 @@ impl OwnedDescriptorChain { Ok(plan) } + /// Decode a fixed-size prefix without constructing descriptor consumers. + /// The captured plan and its RAM lease have already validated every range, + /// including writable buffers that this prefix itself does not touch. + pub(crate) fn read_prefix(&self, mem: &GuestMemoryMmap) -> Result { + let mut object = T::zeroed(); + let mut remaining = object.as_mut_slice(); + for &(address, length) in &self.readable { + if remaining.is_empty() { + break; + } + let count = remaining.len().min(length as usize); + mem.read_slice(&mut remaining[..count], address) + .map_err(Error::GuestMemoryError)?; + remaining = &mut remaining[count..]; + } + if !remaining.is_empty() { + return Err(Error::IoError(io::Error::new( + io::ErrorKind::UnexpectedEof, + "descriptor chain is shorter than the request prefix", + ))); + } + Ok(object) + } + + #[cfg(test)] pub(crate) fn reader_writer<'a>( &self, mem: &'a GuestMemoryMmap, ) -> Result<(Reader<'a>, Writer<'a>)> { - let consumer = |ranges: &[(GuestAddress, u32)]| { + self.reader_writer_after(mem, 0) + } + + /// Construct consumers after a previously captured readable prefix. This + /// avoids constructing and splitting a second Reader for the same header. + pub(crate) fn reader_writer_after<'a>( + &self, + mem: &'a GuestMemoryMmap, + prefix: usize, + ) -> Result<(Reader<'a>, Writer<'a>)> { + let consumer = |ranges: &[(GuestAddress, u32)], mut skip: usize| { + let requested_skip = skip; let mut total_len = 0usize; - let buffers = ranges - .iter() - .map(|&(addr, len)| { - total_len = total_len - .checked_add(len as usize) - .ok_or(Error::DescriptorChainOverflow)?; - let region = mem.find_region(addr).ok_or(Error::FindMemoryRegion)?; - let offset = addr - .checked_sub(region.start_addr().raw_value()) - .ok_or(Error::InvalidChain)?; - region - .deref() - .get_slice(offset.raw_value() as usize, len as usize) - .map_err(Error::VolatileMemoryError) - }) - .collect::>>>()?; + let mut buffers = VecDeque::with_capacity(ranges.len()); + for &(address, length) in ranges { + total_len = total_len + .checked_add(length as usize) + .ok_or(Error::DescriptorChainOverflow)?; + let slice = descriptor_slice(mem, address, length)?; + if skip >= length as usize { + skip -= length as usize; + continue; + } + buffers.push_back(slice.offset(skip).map_err(Error::VolatileMemoryError)?); + skip = 0; + } + if skip != 0 { + return Err(Error::SplitOutOfBounds(requested_skip)); + } Ok(DescriptorChainConsumer { memory_access: self.memory_access.clone(), buffers, @@ -250,15 +297,30 @@ impl OwnedDescriptorChain { }; Ok(( Reader { - buffer: consumer(&self.readable)?, + buffer: consumer(&self.readable, prefix)?, }, Writer { - buffer: consumer(&self.writable)?, + buffer: consumer(&self.writable, 0)?, }, )) } } +fn descriptor_slice( + mem: &GuestMemoryMmap, + address: GuestAddress, + length: u32, +) -> Result> { + let region = mem.find_region(address).ok_or(Error::FindMemoryRegion)?; + let offset = address + .checked_sub(region.start_addr().raw_value()) + .ok_or(Error::InvalidChain)?; + region + .deref() + .get_slice(offset.raw_value() as usize, length as usize) + .map_err(Error::VolatileMemoryError) +} + /// Provides high-level interface over the sequence of memory regions /// defined by readable descriptors in the descriptor chain. /// @@ -362,7 +424,7 @@ impl<'a> Reader<'a> { return Err(io::Error::new( io::ErrorKind::UnexpectedEof, "failed to fill whole buffer", - )) + )); } Ok(n) => count -= n, Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} @@ -511,7 +573,7 @@ impl<'a> Writer<'a> { return Err(io::Error::new( io::ErrorKind::WriteZero, "failed to write whole buffer", - )) + )); } Ok(n) => count -= n, Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} @@ -628,6 +690,89 @@ pub fn create_descriptor_chain( mod tests { use super::*; + #[test] + fn owned_prefix_spans_fragments_and_body_keeps_captured_addresses_and_ram_lease() { + let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); + let gate = super::super::memory_gate::register(&mem); + let chain = create_descriptor_chain( + &mem, + GuestAddress(0), + GuestAddress(0x100), + vec![ + (DescriptorType::Readable, 3), + (DescriptorType::Readable, 0), + (DescriptorType::Readable, 5), + (DescriptorType::Readable, 4), + (DescriptorType::Writable, 8), + ], + 8, + ) + .unwrap(); + let plan = OwnedDescriptorChain::new(chain).unwrap(); + let header = 0x0807060504030201u64; + mem.write_slice(&header.as_slice()[..3], plan.readable[0].0) + .unwrap(); + mem.write_slice(&header.as_slice()[3..], plan.readable[2].0) + .unwrap(); + mem.write_slice(b"body", plan.readable[3].0).unwrap(); + assert_eq!(plan.read_prefix::(&mem).unwrap(), header); + + // The decoded header is captured separately. Changing its bytes and + // the descriptor table cannot redirect or reread the body consumers. + mem.write_slice(&[0; 3], plan.readable[0].0).unwrap(); + mem.write_obj(0x800u64, GuestAddress(3 * 16)).unwrap(); + mem.write_slice(b"fake", GuestAddress(0x800)).unwrap(); + let (mut reader, mut writer) = plan.reader_writer_after(&mem, 8).unwrap(); + assert_eq!(reader.bytes_read(), 0); + assert_eq!(reader.available_bytes(), 4); + let mut body = [0u8; 4]; + reader.read_exact(&mut body).unwrap(); + assert_eq!(&body, b"body"); + writer.write_all(&body).unwrap(); + let mut output = [0u8; 4]; + mem.read_slice(&mut output, plan.writable[0].0).unwrap(); + assert_eq!(output, body); + assert!(matches!( + plan.reader_writer_after(&mem, 13), + Err(Error::SplitOutOfBounds(13)) + )); + drop((reader, writer)); + assert!(!gate.try_close().unwrap()); + drop(plan); + assert!(gate.try_close().unwrap()); + } + + #[test] + fn owned_prefix_rejects_short_input_and_validates_unread_output_ranges() { + let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); + let chain = create_descriptor_chain( + &mem, + GuestAddress(0), + GuestAddress(0x100), + vec![ + (DescriptorType::Readable, 4), + (DescriptorType::Writable, 16), + ], + 0, + ) + .unwrap(); + let plan = OwnedDescriptorChain::new(chain).unwrap(); + assert!(matches!( + plan.read_prefix::(&mem), + Err(Error::IoError(error)) if error.kind() == io::ErrorKind::UnexpectedEof + )); + drop(plan); + + // The prefix is readable, but executing it would have an invalid + // output buffer. Admission must reject the whole captured plan. + mem.write_obj(0xfff8u64, GuestAddress(16)).unwrap(); + let chain = DescriptorChain::checked_new(&mem, GuestAddress(0), 0x100, 0).unwrap(); + assert!(matches!( + OwnedDescriptorChain::new(chain), + Err(Error::VolatileMemoryError(_)) + )); + } + #[test] fn owned_plan_captures_addresses_and_keeps_ram_pinned_across_threads() { let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); diff --git a/crates/pvisor-vm/src/devices/virtio/device.rs b/crates/pvisor-vm/src/devices/virtio/device.rs index 300d92051..89567e322 100644 --- a/crates/pvisor-vm/src/devices/virtio/device.rs +++ b/crates/pvisor-vm/src/devices/virtio/device.rs @@ -221,6 +221,12 @@ pub trait VirtioDevice: AsAny + Send { fn shm_region(&self) -> Option<&VirtioShmRegion> { None } + + /// Device-specific ID of the single advertised shared-memory region. + /// virtio-fs uses zero; virtio-gpu's host-visible region uses one. + fn shm_region_id(&self) -> u32 { + 0 + } } pub trait VmmExitObserver: Send { diff --git a/crates/pvisor-vm/src/devices/virtio/fs/augment_fs.rs b/crates/pvisor-vm/src/devices/virtio/fs/augment_fs.rs index e4f6456e2..df5e527fd 100644 --- a/crates/pvisor-vm/src/devices/virtio/fs/augment_fs.rs +++ b/crates/pvisor-vm/src/devices/virtio/fs/augment_fs.rs @@ -245,6 +245,14 @@ impl> FileSystem for AugmentFs self.inner.init(capable) } + fn attribute_flags(&self, inode: Inode, attr: &bindings::stat64) -> u32 { + if self.is_virtual(inode) { + 0 + } else { + self.inner.attribute_flags(inode, attr) + } + } + fn destroy(&self) { self.inner.destroy() } @@ -730,57 +738,13 @@ impl> FileSystem for AugmentFs shm_size: u64, #[cfg(target_os = "macos")] map_sender: &Option>, ) -> io::Result<()> { - { - let inodes = self.inodes.read().unwrap(); - if let Some(vnode) = inodes.get(&inode) { - let data = vnode.data().ok_or_else(linux_errno::eisdir)?; - #[cfg(target_os = "linux")] - { - if (moffset + len) > shm_size { - return Err(linux_errno::einval()); - } - - let addr = host_shm_base + moffset; - let ret = unsafe { - libc::mmap( - addr as *mut libc::c_void, - len as usize, - libc::PROT_READ | libc::PROT_WRITE, - libc::MAP_PRIVATE | libc::MAP_ANONYMOUS | libc::MAP_FIXED, - -1, - 0, - ) - }; - if std::ptr::eq(ret, libc::MAP_FAILED) { - return Err(io::Error::last_os_error()); - } - - let foff = foffset as usize; - if foff < data.len() { - let available = data.len() - foff; - let to_copy = (len as usize).min(available); - unsafe { - libc::memcpy( - addr as *mut libc::c_void, - data.as_ptr().add(foff) as *const _, - to_copy, - ) - }; - } - - return Ok(()); - } - - // TODO: implement DAX for virtual files on macOS. - // Needs a shared memory region manager (see setupmapping - // in macos/passthrough.rs for the real-file DAX path). - #[cfg(target_os = "macos")] - { - let _ = data; - return Err(linux_errno::enosys()); - } - } + // Synthetic/one-shot files are not immutable backing files and never + // receive a DAX hint. Reject even forged mapping requests; ordinary + // reads retain their existing lifecycle and ownership semantics. + if self.is_virtual(inode) { + return Err(io::Error::from_raw_os_error(libc::ENOSYS)); } + self.inner.setupmapping( ctx, inode, diff --git a/crates/pvisor-vm/src/devices/virtio/fs/dax.rs b/crates/pvisor-vm/src/devices/virtio/fs/dax.rs new file mode 100644 index 000000000..2c7396a22 --- /dev/null +++ b/crates/pvisor-vm/src/devices/virtio/fs/dax.rs @@ -0,0 +1,340 @@ +//! Read-only DAX mappings into a caller-owned, permanently allocated window. +//! All replacements leave readable/writable memory at the same addresses. File +//! pages use private COW: even a hostile guest writing physical window addresses +//! cannot modify an immutable host file. Normal authorized reads share its pages. +use std::collections::BTreeMap; +use std::fs::File; +use std::io; +use std::os::fd::AsRawFd; +use std::sync::Mutex; + +use super::fuse::{RemovemappingOne, SetupmappingFlags}; +use crate::devices::virtio::VirtioShmRegion; + +pub(super) struct ReadOnlyDax { + region: VirtioShmRegion, + page: u64, + // Window offset -> exclusive end. Serialized across setup/remove; mappings + // outlive OPEN handles, as the mmap itself pins the native file object. + mappings: Mutex>, +} + +fn invalid() -> io::Error { + io::Error::from_raw_os_error(libc::EINVAL) +} + +impl ReadOnlyDax { + pub(super) fn new(region: VirtioShmRegion) -> io::Result { + let page = unsafe { libc::sysconf(libc::_SC_PAGESIZE) }; + if page <= 0 { + return Err(invalid()); + } + let page = page as u64; + if region.size == 0 + || !region.host_addr.is_multiple_of(page) + || !(region.size as u64).is_multiple_of(page) + || region.host_addr.checked_add(region.size as u64).is_none() + { + return Err(invalid()); + } + Ok(Self { + region, + page, + mappings: Mutex::new(BTreeMap::new()), + }) + } + + fn range(&self, offset: u64, len: u64, base: u64, size: u64) -> io::Result { + if base != self.region.host_addr + || size != self.region.size as u64 + || len == 0 + || !offset.is_multiple_of(self.page) + || !len.is_multiple_of(self.page) + { + return Err(invalid()); + } + offset + .checked_add(len) + .filter(|end| *end <= size) + .ok_or_else(invalid) + } + + fn replace(&self, offset: u64, len: u64, file: Option<(&File, u64)>) -> io::Result<()> { + let (fd, foffset, flags) = match file { + Some((file, off)) => ( + file.as_raw_fd(), + off as libc::off_t, + libc::MAP_PRIVATE | libc::MAP_FIXED, + ), + None => ( + -1, + 0, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS | libc::MAP_FIXED, + ), + }; + // range() has checked address arithmetic/alignment and the window is + // owned by GuestMemory for the entire worker lifetime. MAP_PRIVATE is + // essential: a guest's physical writes must never mutate backing bytes. + let result = unsafe { + libc::mmap( + (self.region.host_addr + offset) as *mut libc::c_void, + len as usize, + libc::PROT_READ | libc::PROT_WRITE, + flags, + fd, + foffset, + ) + }; + if result == libc::MAP_FAILED { + Err(io::Error::last_os_error()) + } else { + Ok(()) + } + } + + #[allow(clippy::too_many_arguments)] + pub(super) fn setup( + &self, + file: &File, + foffset: u64, + len: u64, + flags: u64, + moffset: u64, + base: u64, + size: u64, + ) -> io::Result<()> { + if flags & SetupmappingFlags::WRITE.bits() != 0 { + return Err(io::Error::from_raw_os_error(libc::EROFS)); + } + if flags != SetupmappingFlags::READ.bits() + || !foffset.is_multiple_of(self.page) + || foffset + .checked_add(len) + .filter(|end| *end <= i64::MAX as u64) + .is_none() + { + return Err(invalid()); + } + let end = self.range(moffset, len, base, size)?; + let metadata = file.metadata()?; + if !metadata.is_file() || foffset >= metadata.len() { + return Err(invalid()); + } + let available = (metadata.len() - foffset).min(len); + let file_len = + available.checked_add(self.page - 1).ok_or_else(invalid)? / self.page * self.page; + let mut mappings = self.mappings.lock().unwrap(); + if mappings + .range(..end) + .next_back() + .is_some_and(|(_, old_end)| *old_end > moffset) + { + return Err(invalid()); + } + // Never leave file-backed pages wholly beyond EOF: they would SIGBUS on + // access. The last partial file page is kernel-zero-filled; subsequent + // pages are anonymous zero padding, including malicious physical reads. + if file_len < len { + self.replace(moffset + file_len, len - file_len, None)?; + } + self.replace(moffset, file_len, Some((file, foffset)))?; + mappings.insert(moffset, end); + Ok(()) + } + + pub(super) fn remove( + &self, + requests: &[RemovemappingOne], + base: u64, + size: u64, + ) -> io::Result<()> { + // Validate the entire request before modifying a single mapping. On a + // later mmap syscall failure earlier removals remain removed, never + // leaving a stale file alias represented as free in the registry. + for req in requests { + self.range(req.moffset, req.len, base, size)?; + } + let mut mappings = self.mappings.lock().unwrap(); + for req in requests { + let end = req.moffset + req.len; + self.replace(req.moffset, req.len, None)?; + let affected: Vec<_> = mappings + .range(..end) + .filter(|(_, old_end)| **old_end > req.moffset) + .map(|(start, end)| (*start, *end)) + .collect(); + for (start, old_end) in affected { + mappings.remove(&start); + if start < req.moffset { + mappings.insert(start, req.moffset); + } + if old_end > end { + mappings.insert(end, old_end); + } + } + } + Ok(()) + } + + pub(super) fn clear(&self) -> io::Result<()> { + self.remove( + &[RemovemappingOne { + moffset: 0, + len: self.region.size as u64, + }], + self.region.host_addr, + self.region.size as u64, + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::io::Write; + use vm_memory::{Bytes, GuestAddress, GuestMemory, GuestMemoryMmap}; + + #[test] + fn private_mapping_zero_pads_eof_and_cannot_modify_backing() { + let memory = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 4 * 4096)]).unwrap(); + let region = VirtioShmRegion { + host_addr: memory.get_host_address(GuestAddress(0)).unwrap() as u64, + guest_addr: 0, + size: 4 * 4096, + }; + let dax = ReadOnlyDax::new(region.clone()).unwrap(); + let mut file = tempfile::tempfile().unwrap(); + file.write_all(&vec![0x5a; 4103]).unwrap(); + dax.setup( + &file, + 0, + region.size as u64, + 2, + 0, + region.host_addr, + region.size as u64, + ) + .unwrap(); + let mut bytes = vec![0; region.size]; + memory.read_slice(&mut bytes, GuestAddress(0)).unwrap(); + assert!(bytes[..4103].iter().all(|b| *b == 0x5a)); + assert!(bytes[4103..].iter().all(|b| *b == 0)); + memory.write_obj(0x11_u8, GuestAddress(0)).unwrap(); + use std::os::unix::fs::FileExt; + let mut byte = [0]; + file.read_exact_at(&mut byte, 0).unwrap(); + assert_eq!(byte, [0x5a]); + dax.remove( + &[RemovemappingOne { + moffset: 0, + len: region.size as u64, + }], + region.host_addr, + region.size as u64, + ) + .unwrap(); + memory.read_slice(&mut bytes, GuestAddress(0)).unwrap(); + assert!(bytes.iter().all(|b| *b == 0)); + // Removed slots remain valid writable guest memory and can be reused. + memory.write_obj(0x22_u8, GuestAddress(0)).unwrap(); + dax.setup(&file, 0, 4096, 2, 0, region.host_addr, region.size as u64) + .unwrap(); + assert_eq!(unsafe { *(region.host_addr as *const u8) }, 0x5a); + } + + #[test] + fn rejects_overflow_alignment_write_overlap_and_invalid_remove_atomically() { + let memory = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 4 * 4096)]).unwrap(); + let region = VirtioShmRegion { + host_addr: memory.get_host_address(GuestAddress(0)).unwrap() as u64, + guest_addr: 0, + size: 4 * 4096, + }; + let dax = ReadOnlyDax::new(region.clone()).unwrap(); + let mut file = tempfile::tempfile().unwrap(); + file.write_all(&vec![0x5a; 4 * 4096]).unwrap(); + for (foffset, len, flags, moffset) in [ + (0, 4096, 1, 0), + (0, 4096, 3, 0), + (0, 4096, 0, 0), + (1, 4096, 2, 0), + (0, 1, 2, 0), + (0, 0, 2, 0), + (0, 4096, 2, 1), + (0, 4096, 2, u64::MAX - 4095), + (u64::MAX - 4095, 4096, 2, 0), + ] { + assert!(dax + .setup( + &file, + foffset, + len, + flags, + moffset, + region.host_addr, + region.size as u64 + ) + .is_err()); + } + dax.setup(&file, 0, 12288, 2, 0, region.host_addr, region.size as u64) + .unwrap(); + assert!(dax + .setup( + &file, + 0, + 4096, + 2, + 4096, + region.host_addr, + region.size as u64 + ) + .is_err()); + assert!(dax + .remove( + &[ + RemovemappingOne { + moffset: 0, + len: 4096 + }, + RemovemappingOne { + moffset: region.size as u64, + len: 4096 + } + ], + region.host_addr, + region.size as u64 + ) + .is_err()); + assert_eq!(unsafe { *(region.host_addr as *const u8) }, 0x5a); + dax.remove( + &[RemovemappingOne { + moffset: 4096, + len: 4096, + }], + region.host_addr, + region.size as u64, + ) + .unwrap(); + dax.setup( + &file, + 0, + 4096, + 2, + 4096, + region.host_addr, + region.size as u64, + ) + .unwrap(); + assert!(dax + .setup( + &file, + 0, + 4096, + 2, + 8192, + region.host_addr, + region.size as u64 + ) + .is_err()); + } +} diff --git a/crates/pvisor-vm/src/devices/virtio/fs/device.rs b/crates/pvisor-vm/src/devices/virtio/fs/device.rs index 922d9c43a..b21d54868 100644 --- a/crates/pvisor-vm/src/devices/virtio/fs/device.rs +++ b/crates/pvisor-vm/src/devices/virtio/fs/device.rs @@ -47,6 +47,7 @@ pub struct Fs { acked_features: u64, device_state: DeviceState, config: VirtioFsConfig, + queue_config: Vec, allow_idmap: bool, shm_region: Option, passthrough_cfg: Option, @@ -78,7 +79,11 @@ impl Fs { let tag = fs_id.into_bytes(); let mut config = VirtioFsConfig::default(); config.tag[..tag.len()].copy_from_slice(tag.as_slice()); - config.num_request_queues = 1; + config.num_request_queues = super::worker::worker_count() as u32; + let queue_config = vec![ + QueueConfig::new(defs::QUEUE_SIZE); + config.num_request_queues as usize + 1 + ]; let attr_timeout = if matches!(semantics, PermissionSemantics::LinuxSimplified) { // As uid/gid are context-dependent, attributes can't be cached. @@ -102,6 +107,7 @@ impl Fs { acked_features: 0, device_state: DeviceState::Inactive, config, + queue_config, allow_idmap, shm_region: None, passthrough_cfg: fs_cfg, @@ -123,6 +129,15 @@ impl Fs { defs::FS_DEV_ID } + pub(crate) fn configure_request_queues(&mut self, count: usize) -> Result<(), String> { + if self.is_activated() || !(1..=8).contains(&count) { + return Err("invalid filesystem queue topology or lifecycle".into()); + } + self.config.num_request_queues = count as u32; + self.queue_config = vec![QueueConfig::new(defs::QUEUE_SIZE); count + 1]; + Ok(()) + } + pub fn set_shm_region(&mut self, shm_region: VirtioShmRegion) { self.shm_region = Some(shm_region); } @@ -245,7 +260,7 @@ impl VirtioDevice for Fs { } fn queue_config(&self) -> &[QueueConfig] { - &defs::QUEUE_CONFIG + &self.queue_config } fn read_config(&self, offset: u64, data: &mut [u8]) { @@ -266,6 +281,9 @@ impl VirtioDevice for Fs { interrupt: InterruptTransport, queues: Vec, ) -> ActivateResult { + if queues.len() != self.queue_config.len() { + return Err(ActivateError::BadActivate); + } if self.worker_thread.is_some() { panic!("virtio_fs: worker thread already exists"); } @@ -339,3 +357,24 @@ impl VirtioDevice for Fs { true } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn request_queue_configuration_preserves_hiprio_and_rejects_invalid_counts() { + let mut fs = Fs::new( + "queue-test".into(), PermissionSemantics::LinuxComplete, None, + Arc::new(AtomicI32::new(0)), false, vec![], None, + ).unwrap(); + fs.configure_request_queues(3).unwrap(); + let count = fs.config.num_request_queues; + assert_eq!(count, 3); + assert_eq!(fs.queue_config().len(), 4); + assert!(fs.queue_config().iter().all(|queue| queue.size == defs::QUEUE_SIZE)); + assert!(fs.configure_request_queues(0).is_err()); + assert!(fs.configure_request_queues(9).is_err()); + assert_eq!(fs.queue_config().len(), 4); + } +} diff --git a/crates/pvisor-vm/src/devices/virtio/fs/filesystem.rs b/crates/pvisor-vm/src/devices/virtio/fs/filesystem.rs index 912b93955..4c6a9fac5 100644 --- a/crates/pvisor-vm/src/devices/virtio/fs/filesystem.rs +++ b/crates/pvisor-vm/src/devices/virtio/fs/filesystem.rs @@ -1154,6 +1154,12 @@ pub trait FileSystem { Err(io::Error::from_raw_os_error(bindings::LINUX_ENOSYS)) } + /// Flags for a successfully obtained inode's attributes, without another + /// lookup. Virtual adapters must not inherit a backing inode's DAX hint. + fn attribute_flags(&self, _inode: Self::Inode, _attr: &bindings::stat64) -> u32 { + 0 + } + /// Setup a mapping so that guest can access files in DAX style. #[allow( clippy::too_many_arguments, diff --git a/crates/pvisor-vm/src/devices/virtio/fs/fuse.rs b/crates/pvisor-vm/src/devices/virtio/fs/fuse.rs index 442cdad98..89f45329a 100644 --- a/crates/pvisor-vm/src/devices/virtio/fs/fuse.rs +++ b/crates/pvisor-vm/src/devices/virtio/fs/fuse.rs @@ -364,6 +364,8 @@ bitflags! { /// /// This feature is enabled by default if supported by the kernel. const MAX_PAGES = MAX_PAGES; + /// Negotiate the host page alignment required by DAX window mappings. + const MAP_ALIGNMENT = MAP_ALIGNMENT; /// Indicates that the kernel supports caching READLINK responses. /// diff --git a/crates/pvisor-vm/src/devices/virtio/fs/linux/passthrough.rs b/crates/pvisor-vm/src/devices/virtio/fs/linux/passthrough.rs index bbfc10478..255cf7f21 100644 --- a/crates/pvisor-vm/src/devices/virtio/fs/linux/passthrough.rs +++ b/crates/pvisor-vm/src/devices/virtio/fs/linux/passthrough.rs @@ -2,20 +2,20 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -use std::collections::btree_map; use std::collections::BTreeMap; +use std::collections::btree_map; use std::convert::TryInto; use std::ffi::{CStr, CString}; use std::fs::File; use std::io; -use std::mem::{self, size_of, MaybeUninit}; +use std::mem::{self, MaybeUninit, size_of}; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::str::FromStr; use std::sync::atomic::{AtomicBool, AtomicI32, AtomicU64, Ordering}; use std::sync::{Arc, RwLock}; use std::time::Duration; -use caps::{has_cap, CapSet, Capability}; +use caps::{CapSet, Capability, has_cap}; use nix::request_code_read; use vm_memory::ByteValued; @@ -59,6 +59,9 @@ struct HandleData { exported: AtomicBool, } +/// Pins the actual native descriptor, rather than just its reusable handle ID. +pub(in crate::devices::virtio::fs) struct HandleLease(Arc); + #[repr(C, packed)] #[derive(Clone, Copy, Debug, Default)] struct LinuxDirent64 { @@ -450,6 +453,169 @@ enum FileOrLink { } impl PassthroughFs { + /// Read one regular-file child's attributes from its retained native parent. + /// This reply-only path does not allocate an inode or a leaf descriptor. + /// Non-regular children retain the ordinary lookup/submount path. + pub(in crate::devices::virtio::fs) fn regular_lookup_attributes( + &self, + parent: Inode, + name: &CStr, + ) -> io::Result> { + // Only a single validated name is allowed: fstatat must not follow an + // intermediate symlink which the ordinary component walk would reject. + if name.to_bytes().is_empty() + || name.to_bytes().contains(&b'/') + || matches!(name.to_bytes(), b"." | b"..") + { + return Err(einval()); + } + let parent = self + .inodes + .read() + .unwrap() + .get(&parent) + .cloned() + .ok_or_else(ebadf)?; + let mut attr = MaybeUninit::::zeroed(); + // SAFETY: the Arc owns the directory FD throughout the syscall, and + // both the NUL-terminated single name and output remain valid. The + // no-follow query preserves the inode bound by ROOT_ID/parent lookup. + let result = unsafe { + libc::fstatat64( + parent.file.as_raw_fd(), + name.as_ptr(), + attr.as_mut_ptr(), + libc::AT_SYMLINK_NOFOLLOW, + ) + }; + if result < 0 { + return Err(io::Error::last_os_error()); + } + // SAFETY: successful fstatat initialized the complete output. + let attr = unsafe { attr.assume_init() }; + Ok((attr.st_mode & libc::S_IFMT == libc::S_IFREG).then_some(( + attr, + self.cfg.attr_timeout, + self.cfg.entry_timeout, + ))) + } + + pub(in crate::devices::virtio::fs) fn with_dax_file( + &self, + inode: Inode, + handle: Handle, + map: impl FnOnce(&File) -> io::Result<()>, + ) -> io::Result<()> { + // Linux SETUPMAPPING supplies fh = -1: DAX ranges belong to an inode, + // rather than a particular open description. Borrow an already opened + // descriptor, so mapping never reopens a path or grants new authority. + // The Arc pins it if RELEASE races with this request. + let lease = if handle == u64::MAX { + self.handles + .read() + .unwrap() + .values() + .find(|data| data.inode == inode) + .cloned() + .map(HandleLease) + .ok_or_else(ebadf)? + } else { + self.lease_handle(inode, handle)? + }; + let file = lease.0.file.read().unwrap(); + map(&file) + } + pub(in crate::devices::virtio::fs) fn lease_handle( + &self, + inode: Inode, + handle: Handle, + ) -> io::Result { + self.handles + .read() + .unwrap() + .get(&handle) + .filter(|data| data.inode == inode) + .cloned() + .map(HandleLease) + .ok_or_else(ebadf) + } + + pub(in crate::devices::virtio::fs) fn read_leased( + &self, + lease: &HandleLease, + mut writer: W, + size: u32, + offset: u64, + ) -> io::Result { + let file = lease.0.file.read().unwrap(); + writer.write_from(&file, size as usize, offset) + } + + pub(in crate::devices::virtio::fs) fn flush_leased( + &self, + lease: &HandleLease, + ) -> io::Result<()> { + // Serialize descriptor effects against writes and sync; File's + // shared-reference methods do not imply shared operation semantics. + #[allow(clippy::readonly_write_lock)] + let file = lease.0.file.write().unwrap(); + // Match FUSE FLUSH's existing dup/close behavior while keeping this + // descriptor's lock and ownership through both syscalls. + let duplicate = unsafe { libc::dup(file.as_raw_fd()) }; + if duplicate < 0 { + return Err(io::Error::last_os_error()); + } + if unsafe { libc::close(duplicate) } < 0 { + return Err(io::Error::last_os_error()); + } + Ok(()) + } + + pub(in crate::devices::virtio::fs) fn sync_leased( + &self, + lease: &HandleLease, + datasync: bool, + ) -> io::Result<()> { + // Serialize descriptor effects against writes and sync; File's + // shared-reference methods do not imply shared operation semantics. + #[allow(clippy::readonly_write_lock)] + let file = lease.0.file.write().unwrap(); + let result = unsafe { + if datasync { + libc::fdatasync(file.as_raw_fd()) + } else { + libc::fsync(file.as_raw_fd()) + } + }; + if result < 0 { + Err(io::Error::last_os_error()) + } else { + Ok(()) + } + } + + pub(in crate::devices::virtio::fs) fn write_leased( + &self, + _ctx: Context, + lease: &HandleLease, + mut reader: R, + size: u32, + offset: u64, + kill_priv: bool, + ) -> io::Result { + let _killpriv_guard = if kill_priv { + drop_effective_cap(Capability::CAP_FSETID)? + } else { + None + }; + // Serialize writes and synchronization on this opened descriptor only. + // Serialize descriptor effects against writes and sync; File's + // shared-reference methods do not imply shared operation semantics. + #[allow(clippy::readonly_write_lock)] + let file = lease.0.file.write().unwrap(); + reader.read_to(&file, size as usize, offset) + } + pub fn new(cfg: Config, inode_alloc: Arc) -> io::Result { let fd = if let Some(fd) = cfg.proc_sfd_rawfd { fd @@ -955,9 +1121,10 @@ impl FileSystem for PassthroughFs { ); let mut opts = FsOptions::DO_READDIRPLUS | FsOptions::READDIRPLUS_AUTO; - if self.cfg.writeback && capable.contains(FsOptions::WRITEBACK_CACHE) { + let writeback = self.cfg.writeback && capable.contains(FsOptions::WRITEBACK_CACHE); + self.writeback.store(writeback, Ordering::Relaxed); + if writeback { opts |= FsOptions::WRITEBACK_CACHE; - self.writeback.store(true, Ordering::Relaxed); } if capable.contains(FsOptions::SUBMOUNTS) { @@ -1279,26 +1446,15 @@ impl FileSystem for PassthroughFs { _ctx: Context, inode: Inode, handle: Handle, - mut w: W, + w: W, size: u32, offset: u64, _lock_owner: Option, _flags: u32, ) -> io::Result { debug!("read: {inode:?}"); - let data = self - .handles - .read() - .unwrap() - .get(&handle) - .filter(|hd| hd.inode == inode) - .cloned() - .ok_or_else(ebadf)?; - - // This is safe because write_from uses preadv64, so the underlying file descriptor - // offset is not affected by this operation. - let f = data.file.read().unwrap(); - w.write_from(&f, size as usize, offset) + let lease = self.lease_handle(inode, handle)?; + self.read_leased(&lease, w, size, offset) } fn write( @@ -1306,7 +1462,7 @@ impl FileSystem for PassthroughFs { _ctx: Context, inode: Inode, handle: Handle, - mut r: R, + r: R, size: u32, offset: u64, _lock_owner: Option, @@ -1314,28 +1470,8 @@ impl FileSystem for PassthroughFs { kill_priv: bool, _flags: u32, ) -> io::Result { - let _killpriv_guard = if kill_priv { - // We need to drop FSETID during a write so that the kernel will remove setuid - // or setgid bits from the file if it was written to by someone other than the - // owner. - drop_effective_cap(Capability::CAP_FSETID)? - } else { - None - }; - - let data = self - .handles - .read() - .unwrap() - .get(&handle) - .filter(|hd| hd.inode == inode) - .cloned() - .ok_or_else(ebadf)?; - - // This is safe because read_to uses pwritev64, so the underlying file descriptor - // offset is not affected by this operation. - let f = data.file.read().unwrap(); - r.read_to(&f, size as usize, offset) + let lease = self.lease_handle(inode, handle)?; + self.write_leased(_ctx, &lease, r, size, offset, kill_priv) } fn getattr( @@ -1685,58 +1821,13 @@ impl FileSystem for PassthroughFs { handle: Handle, _lock_owner: u64, ) -> io::Result<()> { - let data = self - .handles - .read() - .unwrap() - .get(&handle) - .filter(|hd| hd.inode == inode) - .cloned() - .ok_or_else(ebadf)?; - - // Since this method is called whenever an fd is closed in the client, we can emulate that - // behavior by doing the same thing (dup-ing the fd and then immediately closing it). Safe - // because this doesn't modify any memory and we check the return values. - unsafe { - let newfd = libc::dup(data.file.write().unwrap().as_raw_fd()); - if newfd < 0 { - return Err(io::Error::last_os_error()); - } - - if libc::close(newfd) < 0 { - Err(io::Error::last_os_error()) - } else { - Ok(()) - } - } + let lease = self.lease_handle(inode, handle)?; + self.flush_leased(&lease) } fn fsync(&self, _ctx: Context, inode: Inode, datasync: bool, handle: Handle) -> io::Result<()> { - let data = self - .handles - .read() - .unwrap() - .get(&handle) - .filter(|hd| hd.inode == inode) - .cloned() - .ok_or_else(ebadf)?; - - let fd = data.file.write().unwrap().as_raw_fd(); - - // Safe because this doesn't modify any memory and we check the return value. - let res = unsafe { - if datasync { - libc::fdatasync(fd) - } else { - libc::fsync(fd) - } - }; - - if res == 0 { - Ok(()) - } else { - Err(io::Error::last_os_error()) - } + let lease = self.lease_handle(inode, handle)?; + self.sync_leased(&lease, datasync) } fn fsyncdir( @@ -2092,89 +2183,6 @@ impl FileSystem for PassthroughFs { } } - fn setupmapping( - &self, - _ctx: Context, - inode: Inode, - _handle: Handle, - foffset: u64, - len: u64, - flags: u64, - moffset: u64, - host_shm_base: u64, - shm_size: u64, - ) -> io::Result<()> { - let open_flags = if (flags & fuse::SetupmappingFlags::WRITE.bits()) != 0 { - libc::O_RDWR - } else { - libc::O_RDONLY - }; - - let prot_flags = if (flags & fuse::SetupmappingFlags::WRITE.bits()) != 0 { - libc::PROT_READ | libc::PROT_WRITE - } else { - libc::PROT_READ - }; - - if (moffset + len) > shm_size { - return Err(einval()); - } - - let addr = host_shm_base + moffset; - - debug!("setupmapping: ino {inode:?} addr={addr:x} len={len}"); - - let file = self.open_inode(inode, open_flags)?; - let fd = file.as_raw_fd(); - - let ret = unsafe { - libc::mmap( - addr as *mut libc::c_void, - len as usize, - prot_flags, - libc::MAP_SHARED | libc::MAP_FIXED, - fd, - foffset as libc::off_t, - ) - }; - if std::ptr::eq(ret, libc::MAP_FAILED) { - return Err(io::Error::last_os_error()); - } - - Ok(()) - } - - fn removemapping( - &self, - _ctx: Context, - requests: Vec, - host_shm_base: u64, - shm_size: u64, - ) -> io::Result<()> { - for req in requests { - let addr = host_shm_base + req.moffset; - if (req.moffset + req.len) > shm_size { - return Err(einval()); - } - debug!("removemapping: addr={:x} len={:?}", addr, req.len); - let ret = unsafe { - libc::mmap( - addr as *mut libc::c_void, - req.len as usize, - libc::PROT_NONE, - libc::MAP_ANONYMOUS | libc::MAP_PRIVATE | libc::MAP_FIXED, - -1, - 0_i64, - ) - }; - if std::ptr::eq(ret, libc::MAP_FAILED) { - return Err(io::Error::last_os_error()); - } - } - - Ok(()) - } - fn ioctl( &self, _ctx: Context, @@ -2247,6 +2255,153 @@ impl PassthroughFs { mod tests { use super::*; + #[test] + fn regular_attributes_match_native_lookup_without_reading_file_contents() { + use std::os::unix::fs::{MetadataExt, PermissionsExt}; + let root = tempfile::tempdir().unwrap(); + let path = root.path().join("file"); + std::fs::write(&path, b"attributes only").unwrap(); + std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o640)).unwrap(); + std::fs::hard_link(&path, root.path().join("alias")).unwrap(); + let time = std::time::UNIX_EPOCH + Duration::from_secs(1000); + File::open(&path) + .unwrap() + .set_times( + std::fs::FileTimes::new() + .set_accessed(time) + .set_modified(time), + ) + .unwrap(); + let fs = PassthroughFs::new( + Config { + root_dir: root.path().to_string_lossy().into_owned(), + attr_timeout: Duration::from_secs(3), + entry_timeout: Duration::from_secs(7), + ..Default::default() + }, + Arc::new(InodeAllocator::new()), + ) + .unwrap(); + fs.init(FsOptions::empty()).unwrap(); + let ctx = Context { + uid: 0, + gid: 0, + pid: 1, + }; + let before = std::fs::symlink_metadata(&path).unwrap(); + let (attr, attr_timeout, entry_timeout) = fs + .regular_lookup_attributes(fuse::ROOT_ID, c"file") + .unwrap() + .unwrap(); + let entry = fs.lookup(ctx, fuse::ROOT_ID, c"file").unwrap(); + macro_rules! same_fields { + ($($field:ident),+ $(,)?) => { $(assert_eq!(attr.$field, entry.attr.$field, stringify!($field));)+ }; + } + same_fields!( + st_dev, + st_ino, + st_mode, + st_nlink, + st_uid, + st_gid, + st_rdev, + st_size, + st_blksize, + st_blocks, + st_atime, + st_atime_nsec, + st_mtime, + st_mtime_nsec, + st_ctime, + st_ctime_nsec + ); + assert_eq!(attr.st_nlink, 2); + assert_eq!(attr_timeout, entry.attr_timeout); + assert_eq!(entry_timeout, entry.entry_timeout); + let after = std::fs::symlink_metadata(&path).unwrap(); + assert_eq!( + (after.atime(), after.atime_nsec()), + (before.atime(), before.atime_nsec()) + ); + fs.forget(ctx, entry.inode, 1); + std::fs::write(&path, b"fresh host size").unwrap(); + assert_eq!( + fs.regular_lookup_attributes(fuse::ROOT_ID, c"file") + .unwrap() + .unwrap() + .0 + .st_size, + 15 + ); + assert_eq!( + fs.regular_lookup_attributes(fuse::ROOT_ID, c"missing") + .err() + .unwrap() + .raw_os_error(), + Some(libc::ENOENT) + ); + } + + #[test] + fn regular_attributes_keep_single_name_nofollow_and_directory_fallback() { + let root = tempfile::tempdir().unwrap(); + std::fs::create_dir(root.path().join("directory")).unwrap(); + std::fs::write(root.path().join("file"), b"data").unwrap(); + std::os::unix::fs::symlink("file", root.path().join("symlink")).unwrap(); + let fs = PassthroughFs::new( + Config { + root_dir: root.path().to_string_lossy().into_owned(), + ..Default::default() + }, + Arc::new(InodeAllocator::new()), + ) + .unwrap(); + fs.init(FsOptions::SUBMOUNTS).unwrap(); + let ctx = Context { + uid: 0, + gid: 0, + pid: 1, + }; + for name in [c"directory", c"symlink"] { + assert!( + fs.regular_lookup_attributes(fuse::ROOT_ID, name) + .unwrap() + .is_none() + ); + let entry = fs.lookup(ctx, fuse::ROOT_ID, name).unwrap(); + assert_ne!(entry.attr.st_mode & libc::S_IFMT, libc::S_IFREG); + fs.forget(ctx, entry.inode, 1); + } + for name in [c"", c".", c"..", c"directory/../file"] { + assert_eq!( + fs.regular_lookup_attributes(fuse::ROOT_ID, name) + .err() + .unwrap() + .raw_os_error(), + Some(libc::EINVAL) + ); + } + // /proc is already a distinct Linux mount: this read-only check needs + // no test mount and exercises the original directory SUBMOUNTS reply. + let host = PassthroughFs::new( + Config { + root_dir: "/".into(), + ..Default::default() + }, + Arc::new(InodeAllocator::new()), + ) + .unwrap(); + host.init(FsOptions::SUBMOUNTS).unwrap(); + assert!( + host.regular_lookup_attributes(fuse::ROOT_ID, c"proc") + .unwrap() + .is_none() + ); + let proc = host.lookup(ctx, fuse::ROOT_ID, c"proc").unwrap(); + assert_ne!(proc.attr_flags & fuse::ATTR_SUBMOUNT, 0); + host.forget(ctx, proc.inode, 1); + } + // A non-zero uid the server can be made to run as, plus a guest // uid distinct from it and from root. Both must be mappable when // the test runs inside a user namespace (e.g. `buildah unshare`), diff --git a/crates/pvisor-vm/src/devices/virtio/fs/linux/passthrough_snapshot.rs b/crates/pvisor-vm/src/devices/virtio/fs/linux/passthrough_snapshot.rs index 64d0a17fc..23590bbeb 100644 --- a/crates/pvisor-vm/src/devices/virtio/fs/linux/passthrough_snapshot.rs +++ b/crates/pvisor-vm/src/devices/virtio/fs/linux/passthrough_snapshot.rs @@ -8,13 +8,8 @@ use std::os::unix::{ffi::OsStrExt, fs::OpenOptionsExt}; use std::path::Path; fn supported(fs: &PassthroughFs) -> io::Result<()> { - if fs.writeback.load(Ordering::Relaxed) - || fs.cfg.export_table.is_some() - || fs.cfg.proc_sfd_rawfd.is_some() - { - return Err(snapshot::unsupported( - "filesystem writeback or exported descriptors", - )); + if fs.cfg.export_table.is_some() || fs.cfg.proc_sfd_rawfd.is_some() { + return Err(snapshot::unsupported("filesystem exported descriptors")); } Ok(()) } @@ -166,6 +161,7 @@ pub(super) fn capture(fs: &PassthroughFs) -> io::Result { handles, next_handle: fs.next_handle.load(Ordering::Relaxed), submounts: fs.announce_submounts.load(Ordering::Relaxed), + writeback: fs.writeback.load(Ordering::Relaxed), })) } @@ -181,6 +177,7 @@ pub(super) fn restore(fs: &PassthroughFs, state: &FsSnapshot) -> io::Result<()> || fs.cfg.attr_timeout != state.attr_timeout || fs.cfg.cache_policy != state.cache_policy || fs.cfg.xattr != state.xattr + || (state.writeback && !fs.cfg.writeback) { return Err(snapshot::invalid("filesystem binding mismatch")); } @@ -262,6 +259,9 @@ pub(super) fn restore(fs: &PassthroughFs, state: &FsSnapshot) -> io::Result<()> || handles.contains_key(&saved.handle) || saved.flags & !allowed != 0 || saved.flags & libc::O_ACCMODE == libc::O_ACCMODE + || (state.writeback + && (saved.flags & libc::O_APPEND != 0 + || saved.flags & libc::O_ACCMODE == libc::O_WRONLY)) || saved.offset > i64::MAX as u64 { return Err(snapshot::invalid("invalid handle flags/id/offset")); @@ -324,5 +324,6 @@ pub(super) fn restore(fs: &PassthroughFs, state: &FsSnapshot) -> io::Result<()> fs.next_handle.store(state.next_handle, Ordering::Relaxed); fs.announce_submounts .store(state.submounts, Ordering::Relaxed); + fs.writeback.store(state.writeback, Ordering::Relaxed); Ok(()) } diff --git a/crates/pvisor-vm/src/devices/virtio/fs/macos/passthrough.rs b/crates/pvisor-vm/src/devices/virtio/fs/macos/passthrough.rs index dcf8cfe0d..b77821814 100644 --- a/crates/pvisor-vm/src/devices/virtio/fs/macos/passthrough.rs +++ b/crates/pvisor-vm/src/devices/virtio/fs/macos/passthrough.rs @@ -173,6 +173,9 @@ struct HandleData { dirstream: Mutex, } +/// Pins the actual native descriptor, rather than just its reusable handle ID. +pub(in crate::devices::virtio::fs) struct HandleLease(Arc); + fn descriptor_path(file: &File) -> io::Result { let mut path = [0u8; libc::PATH_MAX as usize]; if unsafe { libc::fcntl(file.as_raw_fd(), libc::F_GETPATH, path.as_mut_ptr()) } < 0 { @@ -666,6 +669,100 @@ struct NodeCreation { } impl PassthroughFs { + pub(in crate::devices::virtio::fs) fn lease_handle( + &self, + inode: Inode, + handle: Handle, + ) -> io::Result { + self.handles + .read() + .unwrap() + .get(&handle) + .filter(|data| data.inode == inode) + .cloned() + .map(HandleLease) + .ok_or_else(ebadf) + } + + pub(in crate::devices::virtio::fs) fn read_leased( + &self, + lease: &HandleLease, + mut writer: W, + size: u32, + offset: u64, + ) -> io::Result { + let file = lease.0.file.read().unwrap(); + writer.write_from(&file, size as usize, offset) + } + + pub(in crate::devices::virtio::fs) fn flush_leased(&self, lease: &HandleLease) -> io::Result<()> { + // Serialize descriptor effects against writes and sync; File's + // shared-reference methods do not imply shared operation semantics. + #[allow(clippy::readonly_write_lock)] + let file = lease.0.file.write().unwrap(); + let duplicate = unsafe { libc::dup(file.as_raw_fd()) }; + if duplicate < 0 { + return Err(linux_error(io::Error::last_os_error())); + } + if unsafe { libc::close(duplicate) } < 0 { + return Err(linux_error(io::Error::last_os_error())); + } + Ok(()) + } + + pub(in crate::devices::virtio::fs) fn sync_leased( + &self, + lease: &HandleLease, + _datasync: bool, + ) -> io::Result<()> { + // Serialize descriptor effects against writes and sync; File's + // shared-reference methods do not imply shared operation semantics. + #[allow(clippy::readonly_write_lock)] + let file = lease.0.file.write().unwrap(); + if unsafe { libc::fsync(file.as_raw_fd()) } < 0 { + Err(linux_error(io::Error::last_os_error())) + } else { + Ok(()) + } + } + + pub(in crate::devices::virtio::fs) fn write_leased( + &self, + ctx: Context, + lease: &HandleLease, + mut reader: R, + size: u32, + offset: u64, + kill_priv: bool, + ) -> io::Result { + // Serialize descriptor effects against writes and sync; File's + // shared-reference methods do not imply shared operation semantics. + #[allow(clippy::readonly_write_lock)] + let file = lease.0.file.write().unwrap(); + let result = reader.read_to(&file, size as usize, offset); + if result.is_ok() && kill_priv { + let fd = file.as_raw_fd(); + let inode_handle = InodeHandle::Fd(fd); + remove_security_capability(&inode_handle); + if let Ok(stat) = fstat(&ctx, self.cfg.semantics, fd, false) { + let mode = clear_suid_sgid(stat.st_mode as u32); + if mode != stat.st_mode as u32 { + if let Err(error) = set_stat( + &ctx, + self.cfg.semantics, + &inode_handle, + Some(stat), + None, + Some(mode), + ) { + error!("Couldn't clear suid/sgid for inode {}: {error}", lease.0.inode); + } + } + } + } + result + } + pub fn new(cfg: Config, inode_alloc: Arc) -> io::Result { let root = CString::new(cfg.root_dir.as_str()).expect("CString::new failed"); @@ -1292,13 +1389,12 @@ fn forget_one( impl FileSystem for PassthroughFs { fn capture_state(&self) -> io::Result { - if self.writeback.load(Ordering::Relaxed) - || !self.map_windows.lock().unwrap().is_empty() + if !self.map_windows.lock().unwrap().is_empty() || self.cfg.export_table.is_some() || self.cfg.proc_sfd_rawfd.is_some() { return Err(snapshot::unsupported( - "filesystem writeback, DAX or exported descriptors", + "filesystem DAX or exported descriptors", )); } let root = Path::new(&self.cfg.root_dir).canonicalize()?; @@ -1390,6 +1486,7 @@ impl FileSystem for PassthroughFs { handles: saved_handles, next_handle: self.next_handle.load(Ordering::Relaxed), submounts: self.announce_submounts.load(Ordering::Relaxed), + writeback: self.writeback.load(Ordering::Relaxed), })) } @@ -1397,13 +1494,12 @@ impl FileSystem for PassthroughFs { let FsSnapshot::Passthrough(state) = state else { return Err(snapshot::invalid("filesystem type mismatch")); }; - if self.writeback.load(Ordering::Relaxed) - || !self.map_windows.lock().unwrap().is_empty() + if !self.map_windows.lock().unwrap().is_empty() || self.cfg.export_table.is_some() || self.cfg.proc_sfd_rawfd.is_some() { return Err(snapshot::unsupported( - "filesystem writeback, DAX or exported descriptors", + "filesystem DAX or exported descriptors", )); } let root = Path::new(&self.cfg.root_dir).canonicalize()?; @@ -1413,6 +1509,7 @@ impl FileSystem for PassthroughFs { || self.cfg.attr_timeout != state.attr_timeout || self.cfg.cache_policy != state.cache_policy || self.cfg.xattr != state.xattr + || (state.writeback && !self.cfg.writeback) { return Err(snapshot::invalid( "filesystem root or permission semantics mismatch", @@ -1499,6 +1596,8 @@ impl FileSystem for PassthroughFs { || handles.contains_key(&saved.handle) || saved.flags & !allowed != 0 || saved.flags & libc::O_ACCMODE == libc::O_ACCMODE + || (state.writeback && (saved.flags & libc::O_APPEND != 0 + || saved.flags & libc::O_ACCMODE == libc::O_WRONLY)) || saved.offset > i64::MAX as u64 { return Err(snapshot::invalid(&format!( @@ -1566,6 +1665,7 @@ impl FileSystem for PassthroughFs { self.next_handle.store(state.next_handle, Ordering::Relaxed); self.announce_submounts .store(state.submounts, Ordering::Relaxed); + self.writeback.store(state.writeback, Ordering::Relaxed); Ok(()) } @@ -1630,9 +1730,10 @@ impl FileSystem for PassthroughFs { ); let mut opts = FsOptions::empty(); - if self.cfg.writeback && capable.contains(FsOptions::WRITEBACK_CACHE) { + let writeback = self.cfg.writeback && capable.contains(FsOptions::WRITEBACK_CACHE); + self.writeback.store(writeback, Ordering::Relaxed); + if writeback { opts |= FsOptions::WRITEBACK_CACHE; - self.writeback.store(true, Ordering::Relaxed); } if capable.contains(FsOptions::SUBMOUNTS) { @@ -2007,26 +2108,15 @@ impl FileSystem for PassthroughFs { _ctx: Context, inode: Inode, handle: Handle, - mut w: W, + w: W, size: u32, offset: u64, _lock_owner: Option, _flags: u32, ) -> io::Result { debug!("read: {inode:?}"); - let data = self - .handles - .read() - .unwrap() - .get(&handle) - .filter(|hd| hd.inode == inode) - .cloned() - .ok_or_else(ebadf)?; - - // This is safe because write_from uses preadv64, so the underlying file descriptor - // offset is not affected by this operation. - let f = data.file.read().unwrap(); - w.write_from(&f, size as usize, offset) + let lease = self.lease_handle(inode, handle)?; + self.read_leased(&lease, w, size, offset) } fn write( @@ -2034,7 +2124,7 @@ impl FileSystem for PassthroughFs { ctx: Context, inode: Inode, handle: Handle, - mut r: R, + r: R, size: u32, offset: u64, _lock_owner: Option, @@ -2042,46 +2132,8 @@ impl FileSystem for PassthroughFs { kill_priv: bool, _flags: u32, ) -> io::Result { - let data = self - .handles - .read() - .unwrap() - .get(&handle) - .filter(|hd| hd.inode == inode) - .cloned() - .ok_or_else(ebadf)?; - - // This is safe because read_to uses pwritev64, so the underlying file descriptor - // offset is not affected by this operation. - let f = data.file.read().unwrap(); - let result = r.read_to(&f, size as usize, offset); - - // If write succeeded and kill_priv is set, clear security.capability and suid/sgid - if result.is_ok() && kill_priv { - let fd = f.as_raw_fd(); - let ihandle = InodeHandle::Fd(fd); - - remove_security_capability(&ihandle); - - if let Ok(st) = fstat(&ctx, self.cfg.semantics, fd, false) { - let new_mode = clear_suid_sgid(st.st_mode as u32); - if new_mode != st.st_mode as u32 { - // Update mode in xattr - if let Err(err) = set_stat( - &ctx, - self.cfg.semantics, - &ihandle, - Some(st), - None, - Some(new_mode), - ) { - error!("Couldn't clear suid/sgid for inode {inode}: {err}"); - } - } - } - } - - result + let lease = self.lease_handle(inode, handle)?; + self.write_leased(ctx, &lease, r, size, offset, kill_priv) } fn getattr( @@ -2511,30 +2563,8 @@ impl FileSystem for PassthroughFs { handle: Handle, _lock_owner: u64, ) -> io::Result<()> { - let data = self - .handles - .read() - .unwrap() - .get(&handle) - .filter(|hd| hd.inode == inode) - .cloned() - .ok_or_else(ebadf)?; - - // Since this method is called whenever an fd is closed in the client, we can emulate that - // behavior by doing the same thing (dup-ing the fd and then immediately closing it). Safe - // because this doesn't modify any memory and we check the return values. - unsafe { - let newfd = libc::dup(data.file.write().unwrap().as_raw_fd()); - if newfd < 0 { - return Err(linux_error(io::Error::last_os_error())); - } - - if libc::close(newfd) < 0 { - Err(linux_error(io::Error::last_os_error())) - } else { - Ok(()) - } - } + let lease = self.lease_handle(inode, handle)?; + self.flush_leased(&lease) } fn fsync( @@ -2544,25 +2574,8 @@ impl FileSystem for PassthroughFs { _datasync: bool, handle: Handle, ) -> io::Result<()> { - let data = self - .handles - .read() - .unwrap() - .get(&handle) - .filter(|hd| hd.inode == inode) - .cloned() - .ok_or_else(ebadf)?; - - let fd = data.file.write().unwrap().as_raw_fd(); - - // Safe because this doesn't modify any memory and we check the return value. - let res = unsafe { libc::fsync(fd) }; - - if res == 0 { - Ok(()) - } else { - Err(linux_error(io::Error::last_os_error())) - } + let lease = self.lease_handle(inode, handle)?; + self.sync_leased(&lease, _datasync) } fn fsyncdir( diff --git a/crates/pvisor-vm/src/devices/virtio/fs/mod.rs b/crates/pvisor-vm/src/devices/virtio/fs/mod.rs index 883c3685b..a7d93a3ed 100644 --- a/crates/pvisor-vm/src/devices/virtio/fs/mod.rs +++ b/crates/pvisor-vm/src/devices/virtio/fs/mod.rs @@ -1,4 +1,6 @@ mod augment_fs; +#[cfg(target_os = "linux")] +mod dax; mod device; #[allow(dead_code)] mod filesystem; @@ -36,12 +38,8 @@ pub use self::filesystem::ExportTable; pub use self::overlay::Config as OverlayConfig; mod defs { - use super::super::QueueConfig; - pub const FS_DEV_ID: &str = "virtio_fs"; - pub const NUM_QUEUES: usize = 2; pub const QUEUE_SIZE: u16 = 1024; - pub static QUEUE_CONFIG: [QueueConfig; NUM_QUEUES] = [QueueConfig::new(QUEUE_SIZE); NUM_QUEUES]; // High priority queue. pub const HPQ_INDEX: usize = 0; // Request queue. diff --git a/crates/pvisor-vm/src/devices/virtio/fs/overlay.rs b/crates/pvisor-vm/src/devices/virtio/fs/overlay.rs index 46e26312b..8dade16c1 100644 --- a/crates/pvisor-vm/src/devices/virtio/fs/overlay.rs +++ b/crates/pvisor-vm/src/devices/virtio/fs/overlay.rs @@ -10,7 +10,7 @@ use std::os::unix::ffi::OsStrExt; use std::os::unix::fs::MetadataExt; use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicU64, Ordering}; -use std::sync::{Arc, Mutex, RwLock}; +use std::sync::{Arc, Condvar, Mutex, RwLock}; use std::time::Duration; use pvisor_overlay_core::{ @@ -86,6 +86,112 @@ struct FileHandle { relative_path: Vec, } +/// The handle map and admission state jointly protect descriptor acquisition. +/// Namespace remains serialized, but a fence closes only affected handle IDs. +#[derive(Default)] +struct IoState { + active: HashMap, + blocked: HashMap, + global: usize, +} + +#[derive(Default)] +struct IoActivity { + state: Mutex, + ready: Condvar, +} + +struct IoFence { + activity: Arc, + handles: Vec, + global: bool, +} + +impl IoActivity { + fn fence(self: &Arc, handles: Vec, global: bool) -> IoFence { + let fence = IoFence { + activity: self.clone(), + handles, + global, + }; + if !global && fence.handles.is_empty() { + return fence; + } + let mut state = self.state.lock().unwrap(); + if global { + state.global += 1; + } + for id in &fence.handles { + *state.blocked.entry(*id).or_default() += 1; + } + fence + } +} + +impl IoFence { + fn drain(&self) { + if !self.global && self.handles.is_empty() { + return; + } + let mut state = self.activity.state.lock().unwrap(); + while if self.global { + !state.active.is_empty() + } else { + self.handles.iter().any(|id| state.active.contains_key(id)) + } { + state = self.activity.ready.wait(state).unwrap(); + } + } +} + +impl Drop for IoFence { + fn drop(&mut self) { + if !self.global && self.handles.is_empty() { + return; + } + let mut state = self.activity.state.lock().unwrap(); + if self.global { + state.global -= 1; + } + for id in &self.handles { + let count = state.blocked.get_mut(id).expect("missing I/O fence"); + *count -= 1; + if *count == 0 { + state.blocked.remove(id); + } + } + self.activity.ready.notify_all(); + } +} + +struct FileIoLease { + id: u64, + // Native descriptor ownership outlives removal from the backing map. + native: passthrough::HandleLease, + handle: Arc, + activity: Arc, +} + +impl Drop for FileIoLease { + fn drop(&mut self) { + let mut state = self.activity.state.lock().unwrap(); + let count = state + .active + .get_mut(&self.id) + .expect("missing I/O admission"); + *count -= 1; + if *count == 0 { + state.active.remove(&self.id); + // Only a global drain or a fence on this handle can be waiting + // for its count to reach zero. Ordinary uncontended I/O neither + // opens admission nor needs to wake unrelated fenced handles. + if state.global != 0 || state.blocked.contains_key(&self.id) { + self.activity.ready.notify_all(); + } + } + } +} + #[derive(Clone, Debug)] #[cfg_attr( any(target_os = "macos", target_os = "linux"), @@ -111,7 +217,10 @@ struct DirectoryItem { serde(tag = "kind", content = "state", deny_unknown_fields) )] enum Handle { - File(FileHandle), + // Immutable open metadata is shared by descriptor leases. Cloning a lease + // must not allocate and copy the saved path for every WRITE/FLUSH/FSYNC. + // Arc serializes transparently, preserving the persisted handle format. + File(Arc), Directory(Arc>), // Keep the old Directory variant for snapshots with materialized entries. // New handles defer metadata/inode work until the caller consumes a page. @@ -148,6 +257,13 @@ pub struct OverlaySnapshot { } impl OverlaySnapshot { + pub(super) fn validate_writeback(&self, enabled: bool) -> io::Result<()> { + for layer in &self.layers { + layer.validate_writeback(enabled)?; + } + Ok(()) + } + pub(super) fn verify_frozen_backing(&self) -> io::Result<()> { for layer in &self.layers { layer.verify_frozen_backing()?; @@ -528,6 +644,16 @@ struct CachedDirectory { used: u64, } +enum BackingLookup { + Native(Entry), + #[cfg(target_os = "linux")] + RegularAttributes { + attr: bindings::stat64, + attr_timeout: Duration, + entry_timeout: Duration, + }, +} + #[derive(Default)] struct DirectoryCache { entries: HashMap<(usize, PathBuf), CachedDirectory>, @@ -536,11 +662,14 @@ struct DirectoryCache { pub struct OverlayFs { profile: pvisor_overlay_core::profile::Profile, - // Readers may overlap, but guest namespace changes, copy-up and handle - // release remain exclusive. Hold the guard through backing I/O: cloning - // numeric handle IDs alone would not pin the native handle against release. + // Namespace and metadata remain serialized. Descriptor I/O admission + // uses the handle map and scoped fences without acquiring this guard. operation_lock: RwLock<()>, + io_activity: Arc, core: FilesystemService, + // New INITs use the caller's current preference. Restoring an already + // negotiated session must not silently change its saved writeback mode. + new_session_writeback: bool, virtual_metadata: bool, snapshot_config: Config, roots: Vec, @@ -572,12 +701,71 @@ impl OverlayFs { fn write_operation(&self) -> io::Result> { let _wait = self.profile.span("operation_write_lock_wait"); - self.operation_lock + let operation = self + .operation_lock .write() - .map_err(|_| io::Error::from_raw_os_error(libc::EIO)) + .map_err(|_| io::Error::from_raw_os_error(libc::EIO))?; + Ok(operation) + } + + // Caller holds the exclusive operation guard, so admission and handle + // mappings cannot change while selecting and draining affected objects. + fn fence_paths(&self, paths: &[&Path]) -> IoFence { + let handles = self.handles.lock().unwrap(); + if handles.is_empty() { + return self.io_activity.fence(Vec::new(), false); + } + let nodes = self.nodes.lock().unwrap(); + let mut overlay_objects = BTreeSet::new(); + let mut native_objects = BTreeSet::new(); + for handle in handles.values() { + if let Handle::File(h) = handle { + // The namespace path can have moved while an opened backing + // descriptor continues to own its original physical generation. + if paths.iter().any(|path| { + Path::new(OsStr::from_bytes(&h.relative_path)).starts_with(path) + || nodes + .by_inode + .get(&h.overlay_inode) + .is_some_and(|p| p.starts_with(path)) + }) { + overlay_objects.insert(h.overlay_inode); + native_objects.insert((h.layer.0, h.inode)); + } + } + } + drop(nodes); + let ids: Vec<_> = handles + .iter() + .filter_map(|(id, handle)| match handle { + Handle::File(h) + if overlay_objects.contains(&h.overlay_inode) + || native_objects.contains(&(h.layer.0, h.inode)) => + { + Some(*id) + } + _ => None, + }) + .collect(); + // Mark blocked under the handle-map lock: an I/O admission cannot + // slip between selecting the IDs and closing those IDs to new leases. + let fence = self.io_activity.fence(ids, false); + drop(handles); + fence.drain(); + fence } fn build(cfg: Config, inode_alloc: Arc, restoring: bool) -> io::Result { + let writeback_setting = std::env::var("PVISOR_VM_FS_WRITEBACK").ok(); + Self::build_with_writeback(cfg, inode_alloc, restoring, writeback_setting.as_deref()) + } + + fn build_with_writeback( + cfg: Config, + inode_alloc: Arc, + restoring: bool, + writeback_setting: Option<&str>, + ) -> io::Result { if cfg.lower_dirs.is_empty() || (cfg.baseline_lower.is_some() && cfg.apply_target.is_none()) { return Err(io::Error::from_raw_os_error(libc::EINVAL)); @@ -614,6 +802,11 @@ impl OverlayFs { let mut roots = Vec::with_capacity(lowers.len() + 1); roots.push(upper); roots.extend(lowers); + let new_session_writeback = writeback_setting.is_some_and(|value| value != "0"); + // Unset allows old enabled snapshots to restore, while explicit "0" + // retains the existing refusal. Native restore still checks every + // saved mode, backing identity and handle before installing the state. + let restore_capability = writeback_setting.map_or(restoring, |value| value != "0"); let layers = roots .iter() .map(|root| { @@ -623,6 +816,9 @@ impl OverlayFs { semantics: cfg.semantics, attr_timeout: TTL, entry_timeout: TTL, + // The merged stage is guest-owned. Writeback READs on + // O_WRONLY and guest-managed append use native support. + writeback: restore_capability, ..Default::default() }, inode_alloc.clone(), @@ -649,7 +845,9 @@ impl OverlayFs { virtual_metadata, profile: pvisor_overlay_core::profile::Profile::from_env("virtio-fs-overlay"), operation_lock: RwLock::new(()), + io_activity: Arc::new(IoActivity::default()), core, + new_session_writeback, snapshot_config: cfg, roots, layers, @@ -826,6 +1024,21 @@ impl OverlayFs { ctx: Context, parents: &[BackingIdentity], ) -> io::Result { + match self.inner_lookup_with_parents(layer, path, ctx, parents, false)? { + BackingLookup::Native(entry) => Ok(entry), + #[cfg(target_os = "linux")] + BackingLookup::RegularAttributes { .. } => Err(io::Error::from_raw_os_error(libc::EIO)), + } + } + + fn inner_lookup_with_parents( + &self, + layer: Layer, + path: &Path, + ctx: Context, + parents: &[BackingIdentity], + reply_attributes: bool, + ) -> io::Result { let _span = self.profile.span("inner_inode"); let fs = &self.layers[layer.0]; let mut inode = fuse::ROOT_ID; @@ -833,6 +1046,10 @@ impl OverlayFs { let mut held_directory = None; let mut result = None; let mut prefix = PathBuf::new(); + #[cfg(not(target_os = "linux"))] + let _ = reply_attributes; + #[cfg(target_os = "linux")] + let components = path.components().count(); let cacheable = !parents.is_empty() && parents.len() + 1 == path.components().count() && parents @@ -878,6 +1095,33 @@ impl OverlayFs { self.profile.add("directory_cache_misses", 1); } let name = CString::new(component.as_os_str().as_bytes())?; + #[cfg(target_os = "linux")] + if reply_attributes && index + 1 == components { + let attributes = { + let _span = self.profile.span("backing_regular_attributes"); + fs.regular_lookup_attributes(inode, &name) + }; + match attributes { + Ok(Some((attr, attr_timeout, entry_timeout))) => { + if temporary_reference { + fs.forget(ctx, inode, 1); + } + drop(held_directory); + return Ok(BackingLookup::RegularAttributes { + attr, + attr_timeout, + entry_timeout, + }); + } + Ok(None) => {} // Directory/symlink/replacement needs native lookup. + Err(error) => { + if temporary_reference { + fs.forget(ctx, inode, 1); + } + return Err(error); + } + } + } let entry = { let _span = self.profile.span("backing_lookup"); fs.lookup(ctx, inode, &name) @@ -911,17 +1155,17 @@ impl OverlayFs { } drop(held_directory); if let Some(entry) = result { - return Ok(entry); + return Ok(BackingLookup::Native(entry)); } let (attr, timeout) = fs.getattr(ctx, fuse::ROOT_ID, None)?; - Ok(Entry { + Ok(BackingLookup::Native(Entry { inode: fuse::ROOT_ID, generation: 0, attr, attr_flags: 0, attr_timeout: timeout, entry_timeout: TTL, - }) + })) } fn inner_inode(&self, layer: Layer, path: &Path, ctx: Context) -> io::Result { @@ -929,6 +1173,7 @@ impl OverlayFs { } fn observed_inner(&self, ctx: Context, path: &Path) -> io::Result<(Layer, u64)> { + let _io = self.fence_paths(&[path]); let backing = self .core .observe_read_for_backing_lookup(path) @@ -947,10 +1192,36 @@ impl OverlayFs { ) -> io::Result { let _span = self.profile.span("entry"); let layer = Layer(backing.entry.layer); - let mut entry = self.inner_entry_with_parents(layer, path, ctx, &backing.parents)?; - if entry.inode != fuse::ROOT_ID { - self.layers[layer.0].forget(ctx, entry.inode, 1); - } + // Core still performs every namespace, alias and policy check. Its + // path metadata is only a type hint: attributes must come from the + // native parent's pinned FD, including after a host root replacement. + let mut entry = match self.inner_lookup_with_parents( + layer, + path, + ctx, + &backing.parents, + backing.entry.metadata.is_file(), + )? { + BackingLookup::Native(entry) => { + if entry.inode != fuse::ROOT_ID { + self.layers[layer.0].forget(ctx, entry.inode, 1); + } + entry + } + #[cfg(target_os = "linux")] + BackingLookup::RegularAttributes { + attr, + attr_timeout, + entry_timeout, + } => Entry { + inode, + generation: 0, + attr, + attr_flags: 0, + attr_timeout, + entry_timeout, + }, + }; self.backing_attributes(&backing.entry.resolved.path, &mut entry.attr)?; entry.inode = inode; entry.attr.st_ino = inode as _; @@ -966,7 +1237,8 @@ impl OverlayFs { } fn backing_attributes(&self, path: &Path, attr: &mut bindings::stat64) -> io::Result<()> { - if let Some(remote) = backend::attributes(path).map_err(linux_error)? { + let remote = backend::attributes(path).map_err(linux_error)?; + if let Some(remote) = &remote { attr.st_mode = (remote.kind.mode() | u32::from(remote.perm)) as _; attr.st_uid = remote.uid; attr.st_gid = remote.gid; @@ -985,7 +1257,7 @@ impl OverlayFs { attr.st_ctime_nsec = nsec as _; } #[cfg(target_os = "linux")] - if self.virtual_metadata && backend::attributes(path).map_err(linux_error)?.is_none() { + if self.virtual_metadata && remote.is_none() { // Copy-up retains the source's virtual ownership when an unprivileged // host cannot chown the native upper. Native mode/size/times stay live. if let Ok(bytes) = pvisor_overlay_core::sys::get_xattr( @@ -1013,6 +1285,7 @@ impl OverlayFs { } fn writable_inner(&self, ctx: Context, path: &Path) -> io::Result { + let _io = self.fence_paths(&[path]); // OverlayCore reports host errno; passthrough already reports Linux // errno. Convert only at the core boundary, never the whole request. self.core.copy_up(path).map_err(linux_error)?; @@ -1049,13 +1322,38 @@ impl OverlayFs { id: u64, f: impl FnOnce(&PassthroughFs, &FileHandle) -> io::Result, ) -> io::Result { - // Every caller holds an operation guard, so release cannot remove the - // backing handle while this small map lookup is followed by I/O. - let handle = match self.handles.lock().unwrap().get(&id) { - Some(Handle::File(handle)) => handle.clone(), - _ => return Err(io::Error::from_raw_os_error(libc::EBADF)), - }; - f(&self.layers[handle.layer.0], &handle) + // RELEASE shares the namespace read guard with metadata queries. + // Admit the whole callback so it cannot lose its native mapping while + // GETATTR or LSEEK is still using the opened object. + let lease = self.file_io_lease(id)?; + f(&self.layers[lease.handle.layer.0], &lease.handle) + } + + fn file_io_lease(&self, id: u64) -> io::Result { + loop { + let handles = self.handles.lock().unwrap(); + let handle = match handles.get(&id) { + Some(Handle::File(handle)) => handle.clone(), + _ => return Err(io::Error::from_raw_os_error(libc::EBADF)), + }; + let mut state = self.io_activity.state.lock().unwrap(); + if state.global != 0 || state.blocked.contains_key(&id) { + drop(handles); + // Wait without the operation or handle-map lock; after the + // fence opens, revalidate the handle and its native mapping. + state = self.io_activity.ready.wait(state).unwrap(); + drop(state); + continue; + } + let native = self.layers[handle.layer.0].lease_handle(handle.inode, handle.handle)?; + *state.active.entry(id).or_default() += 1; + return Ok(FileIoLease { + id, + native, + handle, + activity: self.io_activity.clone(), + }); + } } fn dtype(mode: u32) -> u32 { @@ -1067,6 +1365,8 @@ impl OverlayFs { impl FileSystem for OverlayFs { fn capture_state(&self) -> io::Result { let _operation = self.write_operation().unwrap(); + let _io = self.io_activity.fence(Vec::new(), true); + _io.drain(); self.core.sync_preimages()?; for root in self.roots.iter().skip(1) { backend::materialize_tree(root)?; @@ -1187,6 +1487,8 @@ impl FileSystem for OverlayFs { return Err(invalid("overlay filesystem type mismatch")); }; let _operation = self.write_operation().unwrap(); + let _io = self.io_activity.fence(Vec::new(), true); + _io.drain(); if state.config != self.snapshot_config || state.layers.len() != self.layers.len() || state.next_handle == 0 @@ -1291,6 +1593,10 @@ impl FileSystem for OverlayFs { fn init(&self, capable: FsOptions) -> io::Result { let _operation = self.write_operation()?; + let mut capable = capable; + if !self.new_session_writeback { + capable.remove(FsOptions::WRITEBACK_CACHE); + } let mut options = None; for layer in &self.layers { let layer_options = layer.init(capable)?; @@ -1309,6 +1615,8 @@ impl FileSystem for OverlayFs { fn destroy(&self) { let _operation = self.write_operation().unwrap(); + let _io = self.io_activity.fence(Vec::new(), true); + _io.drain(); self.clear_directory_cache(); self.handles.lock().unwrap().clear(); for layer in &self.layers { @@ -1338,15 +1646,22 @@ impl FileSystem for OverlayFs { let _operation = self.read_operation()?; // GETATTR may omit FH (e.g. stat through /proc/self/fd). An unlinked // inode still belongs to its open file, even after its path is gone. - let handle = handle.or_else(|| { - if self.path(inode).is_ok() { - return None; - } - // Only detached inodes require a linear scan of open handles. - self.handles.lock().unwrap().iter().find_map(|(id, h)| { - matches!(h, Handle::File(h) if h.overlay_inode == inode).then_some(*id) - }) - }); + let (handle, path) = match handle { + Some(handle) => (Some(handle), None), + None => match self.path(inode) { + Ok(path) => (None, Some(path)), + Err(error) => { + // Only detached inodes require a linear scan of open handles. + let handle = self.handles.lock().unwrap().iter().find_map(|(id, h)| { + matches!(h, Handle::File(h) if h.overlay_inode == inode).then_some(*id) + }); + match handle { + Some(handle) => (Some(handle), None), + None => return Err(error), + } + } + }, + }; if let Some(handle) = handle { return self.with_file_handle(handle, |fs, h| { if h.overlay_inode != inode { @@ -1367,7 +1682,7 @@ impl FileSystem for OverlayFs { Ok((attr, timeout)) }); } - let entry = self.entry(ctx, &self.path(inode)?, inode)?; + let entry = self.entry(ctx, &path.expect("GETATTR resolved the inode path"), inode)?; Ok((entry.attr, entry.attr_timeout)) } @@ -1381,6 +1696,7 @@ impl FileSystem for OverlayFs { ) -> io::Result<(bindings::stat64, Duration)> { let _operation = self.write_operation()?; let path = self.path(inode)?; + let _io = self.fence_paths(&[&path]); self.core .prepare_metadata_change(&path) .map_err(linux_error)?; @@ -1514,6 +1830,7 @@ impl FileSystem for OverlayFs { fn unlink(&self, _ctx: Context, parent: u64, name: &CStr) -> io::Result<()> { let _operation = self.write_operation()?; let path = self.child(parent, name)?; + let _io = self.fence_paths(&[&path]); self.core.remove(&path, false).map_err(linux_error)?; self.remove_path(&path); Ok(()) @@ -1522,6 +1839,7 @@ impl FileSystem for OverlayFs { fn rmdir(&self, _ctx: Context, parent: u64, name: &CStr) -> io::Result<()> { let _operation = self.write_operation()?; let path = self.child(parent, name)?; + let _io = self.fence_paths(&[&path]); self.core.remove(&path, true).map_err(linux_error)?; self.remove_path(&path); Ok(()) @@ -1539,6 +1857,7 @@ impl FileSystem for OverlayFs { let _operation = self.write_operation()?; let old = self.child(olddir, oldname)?; let new = self.child(newdir, newname)?; + let _io = self.fence_paths(&[&old, &new]); match flags { 0 => self.core.rename(&old, &new, false).map_err(linux_error)?, RENAME_NOREPLACE => self.core.rename(&old, &new, true).map_err(linux_error)?, @@ -1561,6 +1880,7 @@ impl FileSystem for OverlayFs { let _operation = self.write_operation()?; let source = self.path(inode)?; let destination = self.child(newparent, newname)?; + let _io = self.fence_paths(&[&source, &destination]); self.core .hard_link(&source, &destination) .map_err(linux_error)?; @@ -1592,6 +1912,7 @@ impl FileSystem for OverlayFs { Some(self.write_operation()?) }; let path = self.path(inode)?; + let _io = (!readonly).then(|| self.fence_paths(&[&path])); let (layer, inner) = match self .core .prepare_open_for_backing_lookup(&path, flags as i32) @@ -1617,13 +1938,13 @@ impl FileSystem for OverlayFs { }; } }; - let id = self.allocate_handle(Handle::File(FileHandle { + let id = self.allocate_handle(Handle::File(Arc::new(FileHandle { overlay_inode: inode, layer, inode: inner, handle, relative_path: path.as_os_str().as_bytes().to_vec(), - })); + }))); Ok((Some(id), options)) } @@ -1640,6 +1961,7 @@ impl FileSystem for OverlayFs { ) -> io::Result<(Entry, Option, OpenOptions)> { let _operation = self.write_operation()?; let path = self.child(parent, name)?; + let _io = self.fence_paths(&[&path]); let inode = self.allocate_inode(path.clone()); let (upper_parent, upper_name) = self.upper_parent(ctx, &path)?; let (mut entry, handle, options) = self.layers[0].create( @@ -1659,42 +1981,41 @@ impl FileSystem for OverlayFs { entry.inode = inode; entry.attr.st_ino = inode as _; let handle = handle.ok_or_else(|| io::Error::from_raw_os_error(libc::EIO))?; - let id = self.allocate_handle(Handle::File(FileHandle { + let id = self.allocate_handle(Handle::File(Arc::new(FileHandle { overlay_inode: inode, layer: Layer(0), inode: inner_inode, handle, relative_path: path.as_os_str().as_bytes().to_vec(), - })); + }))); Ok((entry, Some(id), options)) } fn read( &self, - ctx: Context, + _ctx: Context, _inode: u64, handle: u64, mut w: W, size: u32, offset: u64, - lock_owner: Option, - flags: u32, + _lock_owner: Option, + _flags: u32, ) -> io::Result { let _span = self.profile.span("read"); - let _operation = self.read_operation()?; - self.with_file_handle(handle, |fs, h| { - let backing = self.roots[h.layer.0].join(OsStr::from_bytes(&h.relative_path)); - if let Some(bytes) = self - .core - .read_remote(&backing, offset, size) - .map_err(linux_error)? - { - w.write_all(&bytes)?; - Ok(bytes.len()) - } else { - fs.read(ctx, h.inode, h.handle, w, size, offset, lock_owner, flags) - } - }) + let lease = self.file_io_lease(handle)?; + let h = &lease.handle; + let backing = self.roots[h.layer.0].join(OsStr::from_bytes(&h.relative_path)); + if let Some(bytes) = self + .core + .read_remote(&backing, offset, size) + .map_err(linux_error)? + { + w.write_all(&bytes)?; + Ok(bytes.len()) + } else { + self.layers[h.layer.0].read_leased(&lease.native, w, size, offset) + } } fn write( @@ -1705,38 +2026,32 @@ impl FileSystem for OverlayFs { r: R, size: u32, offset: u64, - lock_owner: Option, - delayed_write: bool, + _lock_owner: Option, + _delayed_write: bool, kill_priv: bool, - flags: u32, + _flags: u32, ) -> io::Result { let _span = self.profile.span("write"); - let _operation = self.write_operation()?; - self.with_file_handle(handle, |fs, h| { - fs.write( - ctx, - h.inode, - h.handle, - r, - size, - offset, - lock_owner, - delayed_write, - kill_priv, - flags, - ) - }) + let lease = self.file_io_lease(handle)?; + self.layers[lease.handle.layer.0].write_leased( + ctx, + &lease.native, + r, + size, + offset, + kill_priv, + ) } - fn flush(&self, ctx: Context, _inode: u64, handle: u64, lock_owner: u64) -> io::Result<()> { - let _operation = self.write_operation()?; - self.with_file_handle(handle, |fs, h| fs.flush(ctx, h.inode, h.handle, lock_owner)) + fn flush(&self, _ctx: Context, _inode: u64, handle: u64, _lock_owner: u64) -> io::Result<()> { + let lease = self.file_io_lease(handle)?; + self.layers[lease.handle.layer.0].flush_leased(&lease.native) } - fn fsync(&self, ctx: Context, _inode: u64, datasync: bool, handle: u64) -> io::Result<()> { - let _operation = self.write_operation()?; + fn fsync(&self, _ctx: Context, _inode: u64, datasync: bool, handle: u64) -> io::Result<()> { + let lease = self.file_io_lease(handle)?; self.core.sync_preimages()?; - self.with_file_handle(handle, |fs, h| fs.fsync(ctx, h.inode, datasync, h.handle)) + self.layers[lease.handle.layer.0].sync_leased(&lease.native, datasync) } fn fsyncdir(&self, _ctx: Context, inode: u64, _datasync: bool, _handle: u64) -> io::Result<()> { @@ -1764,7 +2079,12 @@ impl FileSystem for OverlayFs { flock_release: bool, lock_owner: Option, ) -> io::Result<()> { - let _operation = self.write_operation()?; + // Closing an open description does not mutate the namespace. A shared + // guard still orders it against capture/destroy and namespace changes; + // only this handle's admitted operations need to finish first. + let _operation = self.read_operation()?; + let _io = self.io_activity.fence(vec![handle], false); + _io.drain(); let handle = self.handles.lock().unwrap().remove(&handle); match handle { Some(Handle::File(h)) => { @@ -1808,6 +2128,7 @@ impl FileSystem for OverlayFs { pvisor_overlay_core::validate_guest_xattr(OsStr::from_bytes(name.to_bytes())) .map_err(linux_error)?; let path = self.path(inode)?; + let _io = self.fence_paths(&[&path]); self.core .prepare_metadata_change(&path) .map_err(linux_error)?; @@ -1846,6 +2167,7 @@ impl FileSystem for OverlayFs { pvisor_overlay_core::validate_guest_xattr(OsStr::from_bytes(name.to_bytes())) .map_err(linux_error)?; let path = self.path(inode)?; + let _io = self.fence_paths(&[&path]); self.core .prepare_metadata_change(&path) .map_err(linux_error)?; @@ -2015,7 +2337,9 @@ impl FileSystem for OverlayFs { } fn releasedir(&self, _ctx: Context, _inode: u64, _flags: u32, handle: u64) -> io::Result<()> { - let _operation = self.write_operation()?; + // An enumeration already owns an Arc snapshot. Removing its registry + // entry cannot invalidate that snapshot or change namespace contents. + let _operation = self.read_operation()?; match self.handles.lock().unwrap().remove(&handle) { Some(Handle::Directory(_) | Handle::DirectoryNames { .. }) => Ok(()), _ => Err(io::Error::from_raw_os_error(libc::EBADF)), @@ -2113,48 +2437,748 @@ mod tests { Ok(len) } } + + struct WriteReader(io::Cursor>); + + #[cfg(target_os = "linux")] + struct WritebackTestFs(Arc); + + #[cfg(target_os = "linux")] + impl FileSystem for WritebackTestFs { + type Inode = u64; + type Handle = u64; + + fn capture_state(&self) -> io::Result { + self.0.capture_state() + } + + fn restore_state(&self, state: &super::super::snapshot::FsSnapshot) -> io::Result<()> { + self.0.restore_state(state) + } + + fn init(&self, capable: FsOptions) -> io::Result { + self.0.init(capable) + } + + fn destroy(&self) { + self.0.destroy(); + } + } + + #[cfg(target_os = "linux")] + fn writeback_test_overlay( + root: &Path, + restoring: bool, + setting: Option<&str>, + ) -> Arc { + Arc::new( + OverlayFs::build_with_writeback( + Config { + lower_mutability: Vec::new(), + lower_dirs: vec![root.join("lower").to_string_lossy().into_owned()], + apply_target: None, + baseline_lower: None, + baseline_content_index: None, + upper_dir: root.join("upper").to_string_lossy().into_owned(), + work_dir: None, + preimage_dir: None, + excluded_paths: Vec::new(), + access_policy: Default::default(), + semantics: passthrough::PermissionSemantics::LinuxComplete, + }, + Arc::new(InodeAllocator::new()), + restoring, + setting, + ) + .unwrap(), + ) + } + + #[cfg(target_os = "linux")] + fn writeback_test_control_request( + server: &super::super::server::Server, + opcode: fuse::Opcode, + body: &[u8], + ) -> (vm_memory::GuestMemoryMmap, vm_memory::GuestAddress, usize) { + use super::super::descriptor_utils::{ + create_descriptor_chain, DescriptorType, OwnedDescriptorChain, + }; + use vm_memory::{Bytes, GuestAddress, GuestMemoryMmap}; + let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); + let chain = create_descriptor_chain( + &mem, + GuestAddress(0), + GuestAddress(0x100), + vec![ + (DescriptorType::Readable, body.len() as u32), + (DescriptorType::Writable, 256), + ], + 0, + ) + .unwrap(); + mem.write_slice(body, GuestAddress(0x100)).unwrap(); + let plan = OwnedDescriptorChain::new(chain).unwrap(); + let (reader, writer) = plan.reader_writer(&mem).unwrap(); + let len = server + .handle_message( + ( + fuse::InHeader { + len: (std::mem::size_of::() + body.len()) as u32, + opcode: opcode as u32, + unique: 1, + ..Default::default() + }, + reader, + ), + writer, + false, + &None, + &Arc::new(std::sync::atomic::AtomicI32::new(0)), + ) + .unwrap(); + (mem, GuestAddress(0x100 + body.len() as u64), len) + } + + #[cfg(target_os = "linux")] + fn negotiate_writeback_test_session( + server: &super::super::server::Server, + capable: FsOptions, + ) -> FsOptions { + use vm_memory::{Address, ByteValued, Bytes}; + let body = fuse::InitInCompat { + major: fuse::KERNEL_VERSION, + minor: fuse::KERNEL_MINOR_VERSION, + flags: capable.bits() as u32, + ..Default::default() + }; + let (mem, address, len) = + writeback_test_control_request(server, fuse::Opcode::Init, body.as_slice()); + let reply: fuse::OutHeader = mem.read_obj(address).unwrap(); + assert_eq!(reply.error, 0); + assert!( + len >= std::mem::size_of::() + std::mem::size_of::() + ); + let out: fuse::InitOut = mem + .read_obj( + address + .checked_add(std::mem::size_of::() as u64) + .unwrap(), + ) + .unwrap(); + FsOptions::from_bits((u64::from(out.flags2) << 32) | u64::from(out.flags)).unwrap() + } + + #[cfg(target_os = "linux")] + #[test] + fn new_overlay_sessions_require_explicit_writeback_and_guest_capability() { + for (setting, capable, expected) in [ + (None, FsOptions::WRITEBACK_CACHE, false), + (Some("0"), FsOptions::WRITEBACK_CACHE, false), + (Some("1"), FsOptions::WRITEBACK_CACHE, true), + (Some("1"), FsOptions::empty(), false), + ] { + let temp = tempfile::tempdir().unwrap(); + std::fs::create_dir(temp.path().join("lower")).unwrap(); + let fs = writeback_test_overlay(temp.path(), false, setting); + let server = super::super::server::Server::new(WritebackTestFs(fs.clone())); + let options = negotiate_writeback_test_session(&server, capable); + assert_eq!(options.contains(FsOptions::WRITEBACK_CACHE), expected); + let state = server + .capture_state(fs.inode_alloc.snapshot_next()) + .unwrap(); + assert_eq!(state.options, options.bits()); + state.fs.validate_writeback(expected).unwrap(); + } + } + + #[cfg(target_os = "linux")] #[test] - fn cached_directory_borrow_survives_eviction_and_releases_the_final_reference() { + fn default_restore_preserves_enabled_session_and_write_only_append_handles() { + use super::super::snapshot::{FsSnapshot, ServerSnapshot}; let temp = tempfile::tempdir().unwrap(); - std::fs::create_dir_all(temp.path().join("lower/parent")).unwrap(); - std::fs::write(temp.path().join("lower/parent/file"), b"content").unwrap(); - let fs = parent_cache_fixture(temp.path()); + std::fs::create_dir(temp.path().join("lower")).unwrap(); + std::fs::write(temp.path().join("lower/file"), b"abcd").unwrap(); + let fs = writeback_test_overlay(temp.path(), false, Some("1")); + let server = super::super::server::Server::new(WritebackTestFs(fs.clone())); + let options = negotiate_writeback_test_session(&server, FsOptions::WRITEBACK_CACHE); let ctx = Context { uid: 0, gid: 0, pid: 1, }; - let backing = fs - .core - .metadata_for_backing_lookup(Path::new("parent/file")) + let inode = fs.lookup(ctx, fuse::ROOT_ID, c"file").unwrap().inode; + let flags = (libc::O_WRONLY | libc::O_APPEND) as u32; + let handle = fs.open(ctx, inode, false, flags).unwrap().0.unwrap(); + let saved = server + .capture_state(fs.inode_alloc.snapshot_next()) .unwrap(); - let entry = fs - .inner_entry_with_parents(Layer(1), Path::new("parent/file"), ctx, &backing.parents) + let saved: ServerSnapshot = + serde_json::from_slice(&serde_json::to_vec(&saved).unwrap()).unwrap(); + drop((server, fs)); + + let fs = writeback_test_overlay(temp.path(), true, None); + let server = super::super::server::Server::new(WritebackTestFs(fs.clone())); + server.restore_state(&saved).unwrap(); + fs.inode_alloc.restore_next(saved.next_inode).unwrap(); + let recaptured = server + .capture_state(fs.inode_alloc.snapshot_next()) .unwrap(); - fs.layers[1].forget(ctx, entry.inode, 1); - let reference = fs - .directory_cache - .lock() - .unwrap() - .entries - .values() - .next() - .unwrap() - .reference - .clone(); - let inode = reference.inode; - fs.clear_directory_cache(); - assert!(fs.directory_cache.lock().unwrap().entries.is_empty()); - assert!(fs.layers[1].getattr(ctx, inode, None).is_ok()); - let child = fs.layers[1].lookup(ctx, inode, c"file").unwrap(); - fs.layers[1].forget(ctx, child.inode, 1); - drop(reference); - assert!(fs.layers[1].getattr(ctx, inode, None).is_err()); + assert_eq!(recaptured.options, options.bits()); + let (FsSnapshot::Overlay(old), FsSnapshot::Overlay(new)) = (&saved.fs, &recaptured.fs) + else { + panic!("overlay snapshots expected") + }; + assert_eq!( + serde_json::to_value(&new.handles).unwrap(), + serde_json::to_value(&old.handles).unwrap() + ); + for (old, new) in old.layers.iter().zip(&new.layers) { + let (FsSnapshot::Passthrough(old), FsSnapshot::Passthrough(new)) = (old, new) else { + panic!("native snapshots expected") + }; + assert!(new.writeback); + // Existing restore reopens the verified path with O_NOFOLLOW, + // while normal OPEN follows the retained /proc/self/fd symlink. + // All other flags and the saved handle state must be preserved. + let mut expected_handles = old.handles.clone(); + for handle in &mut expected_handles { + handle.flags |= libc::O_NOFOLLOW; + } + assert_eq!( + serde_json::to_value(&new.handles).unwrap(), + serde_json::to_value(&expected_handles).unwrap() + ); + } + let mut output = ReadWriter { + gate: None, + bytes: Vec::new(), + }; + assert_eq!( + fs.read(ctx, inode, handle, &mut output, 4, 0, None, 0) + .unwrap(), + 4 + ); + assert_eq!(output.bytes, b"abcd"); + // WB allows partial-page READ on O_WRONLY; guest append offsets must + // still reach the restored native FD without host O_APPEND rewriting. + fs.write( + ctx, + inode, + handle, + WriteReader(io::Cursor::new(b"X".to_vec())), + 1, + 0, + None, + true, + false, + flags, + ) + .unwrap(); + fs.fsync(ctx, inode, false, handle).unwrap(); + assert_eq!( + std::fs::read(temp.path().join("upper/file")).unwrap(), + b"Xbcd" + ); + fs.release(ctx, inode, 0, handle, false, false, None) + .unwrap(); + assert_eq!( + fs.release(ctx, inode, 0, handle, false, false, None) + .err() + .unwrap() + .raw_os_error(), + Some(libc::EBADF) + ); } + #[cfg(target_os = "linux")] #[test] - fn concurrent_native_lookup_has_one_identity_and_keeps_each_returned_inode_alive() { - let temp = tempfile::tempdir().unwrap(); + fn restored_pre_init_snapshot_uses_the_new_session_preference() { + for (setting, expected) in [(None, false), (Some("1"), true)] { + let temp = tempfile::tempdir().unwrap(); + std::fs::create_dir(temp.path().join("lower")).unwrap(); + let fs = writeback_test_overlay(temp.path(), false, None); + let server = super::super::server::Server::new(WritebackTestFs(fs.clone())); + let saved = server + .capture_state(fs.inode_alloc.snapshot_next()) + .unwrap(); + assert_eq!(saved.options, 0); + saved.fs.validate_writeback(false).unwrap(); + drop((server, fs)); + + let fs = writeback_test_overlay(temp.path(), true, setting); + let server = super::super::server::Server::new(WritebackTestFs(fs.clone())); + server.restore_state(&saved).unwrap(); + fs.inode_alloc.restore_next(saved.next_inode).unwrap(); + assert_eq!( + server + .capture_state(fs.inode_alloc.snapshot_next()) + .unwrap() + .options, + 0 + ); + let options = negotiate_writeback_test_session(&server, FsOptions::WRITEBACK_CACHE); + assert_eq!(options.contains(FsOptions::WRITEBACK_CACHE), expected); + let recaptured = server + .capture_state(fs.inode_alloc.snapshot_next()) + .unwrap(); + assert_eq!(recaptured.options, options.bits()); + recaptured.fs.validate_writeback(expected).unwrap(); + } + } + + #[cfg(target_os = "linux")] + #[test] + fn restored_enabled_session_reinitializes_using_the_new_session_preference() { + for (setting, expected) in [(None, false), (Some("1"), true)] { + let temp = tempfile::tempdir().unwrap(); + std::fs::create_dir(temp.path().join("lower")).unwrap(); + let fs = writeback_test_overlay(temp.path(), false, Some("1")); + let server = super::super::server::Server::new(WritebackTestFs(fs.clone())); + negotiate_writeback_test_session(&server, FsOptions::WRITEBACK_CACHE); + let saved = server + .capture_state(fs.inode_alloc.snapshot_next()) + .unwrap(); + drop((server, fs)); + + let fs = writeback_test_overlay(temp.path(), true, setting); + let server = super::super::server::Server::new(WritebackTestFs(fs.clone())); + server.restore_state(&saved).unwrap(); + fs.inode_alloc.restore_next(saved.next_inode).unwrap(); + server + .capture_state(fs.inode_alloc.snapshot_next()) + .unwrap() + .fs + .validate_writeback(true) + .unwrap(); + assert_eq!( + writeback_test_control_request(&server, fuse::Opcode::Destroy, &[]).2, + 0 + ); + let options = negotiate_writeback_test_session(&server, FsOptions::WRITEBACK_CACHE); + assert_eq!(options.contains(FsOptions::WRITEBACK_CACHE), expected); + let recaptured = server + .capture_state(fs.inode_alloc.snapshot_next()) + .unwrap(); + assert_eq!(recaptured.options, options.bits()); + recaptured.fs.validate_writeback(expected).unwrap(); + } + } + + #[cfg(target_os = "linux")] + #[test] + fn enabled_snapshot_restore_keeps_explicit_off_and_session_mismatch_rejections() { + let temp = tempfile::tempdir().unwrap(); + std::fs::create_dir(temp.path().join("lower")).unwrap(); + let fs = writeback_test_overlay(temp.path(), false, Some("1")); + let server = super::super::server::Server::new(WritebackTestFs(fs.clone())); + negotiate_writeback_test_session(&server, FsOptions::WRITEBACK_CACHE); + let saved = server + .capture_state(fs.inode_alloc.snapshot_next()) + .unwrap(); + drop((server, fs)); + + let fs = writeback_test_overlay(temp.path(), true, Some("0")); + let server = super::super::server::Server::new(WritebackTestFs(fs.clone())); + assert!(server.restore_state(&saved).is_err()); + assert_eq!( + server + .capture_state(fs.inode_alloc.snapshot_next()) + .unwrap() + .options, + 0 + ); + drop((server, fs)); + + let fs = writeback_test_overlay(temp.path(), true, None); + let server = super::super::server::Server::new(WritebackTestFs(fs.clone())); + let mut mismatched = saved.clone(); + mismatched.options &= !FsOptions::WRITEBACK_CACHE.bits(); + assert!(server.restore_state(&mismatched).is_err()); + assert_eq!( + server + .capture_state(fs.inode_alloc.snapshot_next()) + .unwrap() + .options, + 0 + ); + } + + #[test] + fn writeback_mode_and_write_only_append_handles_survive_snapshot_restore() { + let temp = tempfile::tempdir().unwrap(); + std::fs::write(temp.path().join("file"), b"abcd").unwrap(); + let config = passthrough::Config { + root_dir: temp.path().to_string_lossy().into_owned(), + writeback: true, + ..Default::default() + }; + let fs = PassthroughFs::new(config.clone(), Arc::new(InodeAllocator::new())).unwrap(); + assert!(fs + .init(FsOptions::WRITEBACK_CACHE) + .unwrap() + .contains(FsOptions::WRITEBACK_CACHE)); + let ctx = Context { + uid: 0, + gid: 0, + pid: 1, + }; + let inode = fs.lookup(ctx, fuse::ROOT_ID, c"file").unwrap().inode; + let flags = (libc::O_WRONLY | libc::O_APPEND) as u32; + let handle = fs.open(ctx, inode, false, flags).unwrap().0.unwrap(); + let state = fs.capture_state().unwrap(); + state.validate_writeback(true).unwrap(); + assert!(state.validate_writeback(false).is_err()); + let state = serde_json::from_slice(&serde_json::to_vec(&state).unwrap()).unwrap(); + drop(fs); + let restored = PassthroughFs::new(config, Arc::new(InodeAllocator::new())).unwrap(); + restored.restore_state(&state).unwrap(); + let mut output = ReadWriter { + gate: None, + bytes: Vec::new(), + }; + assert_eq!( + restored + .read(ctx, inode, handle, &mut output, 4, 0, None, 0) + .unwrap(), + 4 + ); + assert_eq!(output.bytes, b"abcd"); + // Guest supplies append offsets after merging; the native fd must not + // retain O_APPEND, which would override this explicit offset. + restored + .write( + ctx, + inode, + handle, + WriteReader(io::Cursor::new(b"X".to_vec())), + 1, + 0, + None, + true, + false, + flags, + ) + .unwrap(); + restored.fsync(ctx, inode, false, handle).unwrap(); + assert_eq!(std::fs::read(temp.path().join("file")).unwrap(), b"Xbcd"); + } + + impl io::Read for WriteReader { + fn read(&mut self, bytes: &mut [u8]) -> io::Result { + io::Read::read(&mut self.0, bytes) + } + } + + impl ZeroCopyReader for WriteReader { + fn read_to( + &mut self, + file: &std::fs::File, + count: usize, + offset: u64, + ) -> io::Result { + use std::os::unix::fs::FileExt; + let mut bytes = vec![0; count]; + let count = io::Read::read(&mut self.0, &mut bytes)?; + file.write_at(&bytes[..count], offset) + } + } + + #[test] + fn restored_open_file_handles_support_leased_io_and_release() { + let temp = tempfile::tempdir().unwrap(); + std::fs::create_dir(temp.path().join("lower")).unwrap(); + std::fs::write(temp.path().join("lower/file"), b"before").unwrap(); + let fs = parent_cache_fixture(temp.path()); + let ctx = Context { + uid: 0, + gid: 0, + pid: 1, + }; + let inode = fs.lookup(ctx, fuse::ROOT_ID, c"file").unwrap().inode; + let handle = fs + .open(ctx, inode, false, libc::O_RDWR as u32) + .unwrap() + .0 + .unwrap(); + let config = fs.snapshot_config.clone(); + let wire = serde_json::to_vec(&fs.capture_state().unwrap()).unwrap(); + drop(fs); + + let restored = OverlayFs::open_existing(config, Arc::new(InodeAllocator::new())).unwrap(); + restored.init(FsOptions::empty()).unwrap(); + restored + .restore_state(&serde_json::from_slice(&wire).unwrap()) + .unwrap(); + let mut output = ReadWriter { + gate: None, + bytes: Vec::new(), + }; + assert_eq!( + restored + .read(ctx, inode, handle, &mut output, 6, 0, None, 0) + .unwrap(), + 6 + ); + assert_eq!(output.bytes, b"before"); + assert_eq!( + restored + .write( + ctx, + inode, + handle, + WriteReader(io::Cursor::new(b"after!".to_vec())), + 6, + 0, + None, + false, + false, + 0, + ) + .unwrap(), + 6 + ); + restored.fsync(ctx, inode, false, handle).unwrap(); + restored + .release(ctx, inode, 0, handle, false, false, None) + .unwrap(); + assert_eq!( + restored.file_io_lease(handle).err().unwrap().raw_os_error(), + Some(libc::EBADF) + ); + assert_eq!( + std::fs::read(temp.path().join("upper/file")).unwrap(), + b"after!" + ); + assert_eq!( + std::fs::read(temp.path().join("lower/file")).unwrap(), + b"before" + ); + } + + #[test] + fn backing_read_releases_the_operation_lock_and_allows_independent_write_and_fsync() { + use std::sync::mpsc::channel; + let temp = tempfile::tempdir().unwrap(); + std::fs::create_dir(temp.path().join("lower")).unwrap(); + std::fs::write(temp.path().join("lower/a"), b"first").unwrap(); + std::fs::write(temp.path().join("lower/b"), b"second").unwrap(); + let fs = parent_cache_fixture(temp.path()); + fs.init(FsOptions::empty()).unwrap(); + let ctx = Context { + uid: 0, + gid: 0, + pid: 1, + }; + let a = fs.lookup(ctx, fuse::ROOT_ID, c"a").unwrap().inode; + let b = fs.lookup(ctx, fuse::ROOT_ID, c"b").unwrap().inode; + let ah = fs + .open(ctx, a, false, libc::O_RDONLY as u32) + .unwrap() + .0 + .unwrap(); + let bh = fs + .open(ctx, b, false, libc::O_RDWR as u32) + .unwrap() + .0 + .unwrap(); + let (started, entered) = channel(); + let (resume, waiting) = channel(); + let (sent, completed) = channel(); + std::thread::scope(|scope| { + let first = scope.spawn(|| { + let mut writer = ReadWriter { + gate: Some((started, waiting)), + bytes: Vec::new(), + }; + let result = fs.read(ctx, a, ah, &mut writer, 16, 0, None, 0); + (result, writer.bytes) + }); + if let Err(error) = entered.recv_timeout(Duration::from_secs(2)) { + let _ = resume.send(()); + panic!("READ did not reach backing I/O: {error}"); + } + let lock_is_free = fs.operation_lock.try_write().is_ok(); + scope.spawn(|| { + let result = fs + .write( + ctx, + b, + bh, + WriteReader(io::Cursor::new(b"updated".to_vec())), + 7, + 0, + None, + false, + false, + 0, + ) + .and_then(|count| fs.fsync(ctx, b, false, bh).map(|()| count)); + sent.send(result).unwrap(); + }); + let result = completed.recv_timeout(Duration::from_secs(2)); + resume.send(()).unwrap(); + let (read, bytes) = first.join().unwrap(); + assert!(lock_is_free, "READ retained the global operation lock"); + assert_eq!( + result + .expect("unrelated WRITE/FSYNC blocked on READ") + .unwrap(), + 7 + ); + assert_eq!(read.unwrap(), 5); + assert_eq!(bytes, b"first"); + }); + fs.release(ctx, a, 0, ah, false, false, None).unwrap(); + fs.release(ctx, b, 0, bh, false, false, None).unwrap(); + assert_eq!( + std::fs::read(temp.path().join("upper/b")).unwrap(), + b"updated" + ); + assert_eq!( + std::fs::read(temp.path().join("lower/b")).unwrap(), + b"second" + ); + } + + #[test] + fn snapshot_global_drain_is_woken_by_the_last_file_io_lease() { + use std::sync::mpsc::channel; + use std::time::{Duration, Instant}; + let temp = tempfile::tempdir().unwrap(); + std::fs::create_dir(temp.path().join("lower")).unwrap(); + std::fs::write(temp.path().join("lower/a"), b"first").unwrap(); + let fs = parent_cache_fixture(temp.path()); + fs.init(FsOptions::empty()).unwrap(); + let ctx = Context { + uid: 0, + gid: 0, + pid: 1, + }; + let inode = fs.lookup(ctx, fuse::ROOT_ID, c"a").unwrap().inode; + let handle = fs + .open(ctx, inode, false, libc::O_RDONLY as u32) + .unwrap() + .0 + .unwrap(); + let lease = fs.file_io_lease(handle).unwrap(); + let (sent, received) = channel(); + std::thread::scope(|scope| { + scope.spawn(|| { + sent.send(fs.capture_state()).unwrap(); + }); + let deadline = Instant::now() + Duration::from_secs(2); + let installed = loop { + if fs.io_activity.state.lock().unwrap().global != 0 { + break true; + } + if Instant::now() >= deadline { + break false; + } + std::thread::yield_now(); + }; + let premature = received.try_recv(); + drop(lease); + let completed = received.recv_timeout(Duration::from_secs(2)); + assert!(installed, "snapshot did not install its global drain"); + assert!(premature.is_err(), "snapshot captured admitted I/O"); + completed + .expect("last lease did not wake the global drain") + .unwrap(); + }); + fs.release(ctx, inode, 0, handle, false, false, None) + .unwrap(); + } + + #[test] + fn native_descriptor_lease_survives_native_handle_removal() { + let temp = tempfile::tempdir().unwrap(); + std::fs::create_dir(temp.path().join("lower")).unwrap(); + std::fs::write(temp.path().join("lower/a"), b"first").unwrap(); + let fs = parent_cache_fixture(temp.path()); + fs.init(FsOptions::empty()).unwrap(); + let ctx = Context { + uid: 0, + gid: 0, + pid: 1, + }; + let inode = fs.lookup(ctx, fuse::ROOT_ID, c"a").unwrap().inode; + let handle = fs + .open(ctx, inode, false, libc::O_RDONLY as u32) + .unwrap() + .0 + .unwrap(); + let lease = fs.file_io_lease(handle).unwrap(); + let native = &fs.layers[lease.handle.layer.0]; + native + .release( + ctx, + lease.handle.inode, + 0, + lease.handle.handle, + false, + false, + None, + ) + .unwrap(); + assert!(native + .lease_handle(lease.handle.inode, lease.handle.handle) + .is_err()); + let mut writer = ReadWriter { + gate: None, + bytes: Vec::new(), + }; + assert_eq!( + native + .read_leased(&lease.native, &mut writer, 16, 0) + .unwrap(), + 5 + ); + assert_eq!(writer.bytes, b"first"); + drop(lease); + assert!(fs.io_activity.state.lock().unwrap().active.is_empty()); + } + #[test] + fn cached_directory_borrow_survives_eviction_and_releases_the_final_reference() { + let temp = tempfile::tempdir().unwrap(); + std::fs::create_dir_all(temp.path().join("lower/parent")).unwrap(); + std::fs::write(temp.path().join("lower/parent/file"), b"content").unwrap(); + let fs = parent_cache_fixture(temp.path()); + let ctx = Context { + uid: 0, + gid: 0, + pid: 1, + }; + let backing = fs + .core + .metadata_for_backing_lookup(Path::new("parent/file")) + .unwrap(); + let entry = fs + .inner_entry_with_parents(Layer(1), Path::new("parent/file"), ctx, &backing.parents) + .unwrap(); + fs.layers[1].forget(ctx, entry.inode, 1); + let reference = fs + .directory_cache + .lock() + .unwrap() + .entries + .values() + .next() + .unwrap() + .reference + .clone(); + let inode = reference.inode; + fs.clear_directory_cache(); + assert!(fs.directory_cache.lock().unwrap().entries.is_empty()); + assert!(fs.layers[1].getattr(ctx, inode, None).is_ok()); + let child = fs.layers[1].lookup(ctx, inode, c"file").unwrap(); + fs.layers[1].forget(ctx, child.inode, 1); + drop(reference); + assert!(fs.layers[1].getattr(ctx, inode, None).is_err()); + } + + #[test] + fn concurrent_native_lookup_has_one_identity_and_keeps_each_returned_inode_alive() { + let temp = tempfile::tempdir().unwrap(); std::fs::create_dir(temp.path().join("lower")).unwrap(); for index in 0..32 { std::fs::create_dir(temp.path().join(format!("lower/d{index}"))).unwrap(); @@ -2268,10 +3292,27 @@ mod tests { released.send(result).unwrap(); }); let premature = done.recv_timeout(Duration::from_millis(20)); + let (queried, query_done) = channel(); + scope.spawn(move || { + queried + .send( + fs.lookup(ctx, fuse::ROOT_ID, c"b") + .map(|entry| entry.attr.st_size), + ) + .unwrap(); + }); + // A blocked close must not exclude metadata on another object. + let unrelated_metadata = query_done.recv_timeout(Duration::from_secs(2)); resume.send(()).unwrap(); assert_eq!(result.unwrap(), 6); assert_eq!(bytes, b"second"); assert!(premature.is_err(), "release raced retained backing handle"); + assert_eq!( + unrelated_metadata + .expect("RELEASE blocked unrelated LOOKUP") + .unwrap(), + 6 + ); done.recv_timeout(Duration::from_secs(2)).unwrap().unwrap(); let (result, bytes) = first.join().unwrap(); assert_eq!(result.unwrap(), 5); @@ -2289,6 +3330,7 @@ mod tests { std::fs::create_dir(temp.path().join("lower")).unwrap(); std::fs::write(temp.path().join("lower/a"), b"first").unwrap(); std::fs::write(temp.path().join("lower/b"), b"second").unwrap(); + std::fs::write(temp.path().join("lower/c"), b"third").unwrap(); let fs = parent_cache_fixture(temp.path()); fs.init(FsOptions::empty()).unwrap(); let ctx = Context { @@ -2303,6 +3345,12 @@ mod tests { .unwrap() .0 .unwrap(); + let c = fs.lookup(ctx, fuse::ROOT_ID, c"c").unwrap().inode; + let ch = fs + .open(ctx, c, false, libc::O_RDONLY as u32) + .unwrap() + .0 + .unwrap(); let (started, entered) = channel(); let (resume, waiting) = channel(); let (opened, ready) = channel(); @@ -2339,16 +3387,67 @@ mod tests { panic!("read-only OPEN failed: {result:?}"); } }; + let (changed, change_ready) = channel(); + scope.spawn(move || { + let result = (|| { + let wh = fs.open(ctx, b, false, libc::O_RDWR as u32)?.0.unwrap(); + fs.release(ctx, b, libc::O_RDWR as u32, wh, false, false, None)?; + fs.rename(ctx, fuse::ROOT_ID, c"b", fuse::ROOT_ID, c"renamed", 0)?; + fs.unlink(ctx, fuse::ROOT_ID, c"renamed")?; + fs.release(ctx, b, libc::O_RDONLY as u32, bh, false, false, None) + })(); + changed.send(result).unwrap(); + }); + let independent = change_ready.recv_timeout(Duration::from_secs(2)); + if independent.is_err() { + resume.send(()).unwrap(); + panic!("independent namespace mutation waited for unrelated READ"); + } + independent.unwrap().unwrap(); scope.spawn(move || { write_started.send(()).unwrap(); write_opened - .send(fs.open(ctx, b, false, libc::O_RDWR as u32)) + .send(fs.open(ctx, a, false, libc::O_RDWR as u32)) .unwrap(); }); if let Err(error) = write_entered.recv_timeout(Duration::from_secs(2)) { resume.send(()).unwrap(); panic!("writable OPEN did not start: {error}"); } + let deadline = std::time::Instant::now() + Duration::from_secs(2); + while !fs + .io_activity + .state + .lock() + .unwrap() + .blocked + .contains_key(&ah) + { + if std::time::Instant::now() >= deadline { + resume.send(()).unwrap(); + panic!("same-object writer did not install its fence"); + } + std::thread::yield_now(); + } + // The namespace writer is now waiting for a, holding the metadata + // write guard. New I/O admission on c must still proceed. + let (read_done, read_ready) = channel(); + scope.spawn(move || { + let mut writer = ReadWriter { + gate: None, + bytes: Vec::new(), + }; + let result = fs.read(ctx, c, ch, &mut writer, 16, 0, None, 0); + read_done.send((result, writer.bytes)).unwrap(); + }); + let other = read_ready.recv_timeout(Duration::from_secs(2)); + if other.is_err() { + resume.send(()).unwrap(); + panic!("new unrelated I/O admission waited for the namespace writer"); + } + let (result, bytes) = other.unwrap(); + assert_eq!(result.unwrap(), 5); + assert_eq!(bytes, b"third"); let premature = write_ready.recv_timeout(Duration::from_millis(20)); resume.send(()).unwrap(); assert!( @@ -2362,13 +3461,13 @@ mod tests { .0 .unwrap(); first.join().unwrap(); - fs.release(ctx, b, libc::O_RDONLY as u32, bh, false, false, None) - .unwrap(); - fs.release(ctx, b, libc::O_RDWR as u32, wh, false, false, None) + fs.release(ctx, a, libc::O_RDWR as u32, wh, false, false, None) .unwrap(); }); fs.release(ctx, a, libc::O_RDONLY as u32, ah, false, false, None) .unwrap(); + fs.release(ctx, c, libc::O_RDONLY as u32, ch, false, false, None) + .unwrap(); assert_eq!( std::fs::read(temp.path().join("lower/b")).unwrap(), b"second" @@ -2453,6 +3552,185 @@ mod tests { fs } + #[cfg(target_os = "linux")] + #[test] + fn regular_metadata_reply_keeps_the_pinned_root_after_host_replacement() { + let temp = tempfile::tempdir().unwrap(); + let lower = temp.path().join("lower"); + let original = temp.path().join("original-lower"); + std::fs::create_dir(&lower).unwrap(); + std::fs::write(lower.join("file"), b"old root").unwrap(); + let fs = parent_cache_fixture(temp.path()); + let ctx = Context { + uid: 0, + gid: 0, + pid: 1, + }; + std::fs::rename(&lower, &original).unwrap(); + std::fs::create_dir(&lower).unwrap(); + std::fs::write(lower.join("file"), b"different root contents").unwrap(); + assert_eq!(fs.core.metadata(Path::new("file")).unwrap().len(), 23); + let before = std::fs::symlink_metadata(original.join("file")).unwrap(); + let entry = fs.lookup(ctx, fuse::ROOT_ID, c"file").unwrap(); + assert_eq!(entry.attr.st_size, 8); + assert_eq!(fs.getattr(ctx, entry.inode, None).unwrap().0.st_size, 8); + let after = std::fs::symlink_metadata(original.join("file")).unwrap(); + assert_eq!( + (after.atime(), after.atime_nsec()), + (before.atime(), before.atime_nsec()) + ); + let handle = fs + .open(ctx, entry.inode, false, libc::O_RDONLY as u32) + .unwrap() + .0 + .unwrap(); + let mut output = ReadWriter { + gate: None, + bytes: Vec::new(), + }; + assert_eq!( + fs.read(ctx, entry.inode, handle, &mut output, 64, 0, None, 0) + .unwrap(), + 8 + ); + assert_eq!(output.bytes, b"old root"); + fs.release(ctx, entry.inode, 0, handle, false, false, None) + .unwrap(); + } + + #[cfg(target_os = "linux")] + #[test] + fn regular_metadata_reply_revalidates_parents_and_rejects_symlink_retargeting() { + let temp = tempfile::tempdir().unwrap(); + let lower = temp.path().join("lower"); + std::fs::create_dir_all(lower.join("directory")).unwrap(); + std::fs::write(lower.join("directory/file"), b"old").unwrap(); + let fs = parent_cache_fixture(temp.path()); + let ctx = Context { + uid: 0, + gid: 0, + pid: 1, + }; + let directory = fs.lookup(ctx, fuse::ROOT_ID, c"directory").unwrap().inode; + let original = fs.lookup(ctx, directory, c"file").unwrap(); + assert_eq!(original.attr.st_size, 3); + assert_eq!(fs.getattr(ctx, original.inode, None).unwrap().0.st_size, 3); + std::fs::rename(lower.join("directory"), lower.join("old-directory")).unwrap(); + std::fs::create_dir(lower.join("directory")).unwrap(); + std::fs::write(lower.join("directory/file"), b"updated").unwrap(); + let replaced = fs.lookup(ctx, directory, c"file").unwrap(); + assert_eq!(replaced.attr.st_size, 7); + let handle = fs + .open(ctx, replaced.inode, false, libc::O_RDONLY as u32) + .unwrap() + .0 + .unwrap(); + let mut output = ReadWriter { + gate: None, + bytes: Vec::new(), + }; + fs.read(ctx, replaced.inode, handle, &mut output, 64, 0, None, 0) + .unwrap(); + assert_eq!(output.bytes, b"updated"); + fs.release(ctx, replaced.inode, 0, handle, false, false, None) + .unwrap(); + std::fs::rename(lower.join("directory"), lower.join("replaced-directory")).unwrap(); + let outside = temp.path().join("outside"); + std::fs::create_dir(&outside).unwrap(); + std::fs::write(outside.join("file"), b"must not follow the ancestor link").unwrap(); + std::os::unix::fs::symlink(&outside, lower.join("directory")).unwrap(); + assert_eq!( + fs.lookup(ctx, directory, c"file") + .err() + .unwrap() + .raw_os_error(), + Some(libc::ENOTDIR) + ); + let symlink = fs.lookup(ctx, fuse::ROOT_ID, c"directory").unwrap(); + assert_eq!(symlink.attr.st_mode & libc::S_IFMT, libc::S_IFLNK); + assert_eq!( + fs.getattr(ctx, symlink.inode, None).unwrap().0.st_mode & libc::S_IFMT, + libc::S_IFLNK + ); + } + + #[cfg(target_os = "linux")] + #[test] + fn regular_metadata_hint_preserves_leaf_replacement_type_and_absence() { + let temp = tempfile::tempdir().unwrap(); + let lower = temp.path().join("lower"); + std::fs::create_dir(&lower).unwrap(); + std::fs::write(lower.join("file"), b"original").unwrap(); + let fs = parent_cache_fixture(temp.path()); + let ctx = Context { + uid: 0, + gid: 0, + pid: 1, + }; + let path = Path::new("file"); + let inode = fs.allocate_inode(path.to_path_buf()); + let backing = fs.core.metadata_for_backing_lookup(path).unwrap(); + std::fs::remove_file(lower.join("file")).unwrap(); + std::os::unix::fs::symlink("outside", lower.join("file")).unwrap(); + let entry = fs.entry_on_backing(ctx, path, inode, &backing).unwrap(); + assert_eq!(entry.attr.st_mode & libc::S_IFMT, libc::S_IFLNK); + assert_eq!(entry.attr.st_size, 7); + std::fs::remove_file(lower.join("file")).unwrap(); + std::fs::create_dir(lower.join("file")).unwrap(); + let entry = fs.entry_on_backing(ctx, path, inode, &backing).unwrap(); + assert_eq!(entry.attr.st_mode & libc::S_IFMT, libc::S_IFDIR); + std::fs::remove_dir(lower.join("file")).unwrap(); + assert_eq!( + fs.entry_on_backing(ctx, path, inode, &backing) + .err() + .unwrap() + .raw_os_error(), + Some(libc::ENOENT) + ); + } + + #[cfg(target_os = "linux")] + #[test] + fn regular_metadata_reply_keeps_upper_virtual_owners_and_hardlink_denials() { + let temp = tempfile::tempdir().unwrap(); + let lower = temp.path().join("lower"); + let upper = temp.path().join("upper"); + std::fs::create_dir(&lower).unwrap(); + std::fs::create_dir(&upper).unwrap(); + let marker = OsStr::new("user.containers.override_stat"); + pvisor_overlay_core::sys::set_xattr(&upper, marker, b"1234:2345:700", 0).unwrap(); + std::fs::write(upper.join("file"), b"upper bytes").unwrap(); + pvisor_overlay_core::sys::set_xattr(&upper.join("file"), marker, b"1234:2345:751", 0) + .unwrap(); + let fs = parent_cache_fixture(temp.path()); + let ctx = Context { + uid: 0, + gid: 0, + pid: 1, + }; + let entry = fs.lookup(ctx, fuse::ROOT_ID, c"file").unwrap(); + assert_eq!((entry.attr.st_uid, entry.attr.st_gid), (1234, 2345)); + assert_eq!(entry.attr.st_mode & libc::S_IFMT, libc::S_IFREG); + assert_eq!(entry.attr.st_mode & 0o7777, 0o751); + assert_eq!(entry.attr.st_size, 11); + std::fs::write(upper.join("file"), b"fresh upper metadata").unwrap(); + let (attr, _) = fs.getattr(ctx, entry.inode, None).unwrap(); + assert_eq!((attr.st_uid, attr.st_gid), (1234, 2345)); + assert_eq!(attr.st_mode & 0o7777, 0o751); + assert_eq!(attr.st_size, 20); + std::fs::write(lower.join("public"), b"protected alias").unwrap(); + std::fs::create_dir(lower.join("private")).unwrap(); + std::fs::hard_link(lower.join("public"), lower.join("private/alias")).unwrap(); + assert_eq!( + fs.lookup(ctx, fuse::ROOT_ID, c"public") + .err() + .unwrap() + .raw_os_error(), + Some(libc::EACCES) + ); + assert!(fs.lookup(ctx, fuse::ROOT_ID, c"private").is_err()); + } + #[test] fn restored_exclusions_only_add_unobserved_private_paths() { let temp = tempfile::tempdir().unwrap(); @@ -2972,7 +4250,16 @@ mod tests { hard_link_origins: vec![], layers: vec![], nodes: vec![(1, b"/".to_vec())], - handles: vec![], + handles: vec![( + 1, + Handle::File(Arc::new(FileHandle { + overlay_inode: 1, + layer: Layer(0), + inode: 1, + handle: 7, + relative_path: b"file".to_vec(), + })), + )], next_handle: 2, }; let legacy = serde_json::to_value(&state.config).unwrap(); @@ -2984,6 +4271,18 @@ mod tests { let roundtrip: Config = serde_json::from_value(serde_json::to_value(&declared).unwrap()).unwrap(); assert_eq!(roundtrip.lower_mutability, declared.lower_mutability); + // Shared in-memory ownership adds no fields or wrapper to old handles. + let legacy_handle = serde_json::json!({ + "kind": "File", + "state": { "overlay_inode": 1, "layer": 0, "inode": 1, + "handle": 7, "relative_path": [102, 105, 108, 101] } + }); + assert_eq!( + serde_json::to_value(&state.handles[0].1).unwrap(), + legacy_handle + ); + let decoded_handle: Handle = serde_json::from_value(legacy_handle.clone()).unwrap(); + assert_eq!(serde_json::to_value(decoded_handle).unwrap(), legacy_handle); // Box must add no wrapper to the tagged enum's persisted state format, // including when nested in layers. let old_wire = serde_json::json!({ diff --git a/crates/pvisor-vm/src/devices/virtio/fs/read_only.rs b/crates/pvisor-vm/src/devices/virtio/fs/read_only.rs index 7a56435ed..c89d20c76 100644 --- a/crates/pvisor-vm/src/devices/virtio/fs/read_only.rs +++ b/crates/pvisor-vm/src/devices/virtio/fs/read_only.rs @@ -54,18 +54,45 @@ fn read_only_open_flags(flags: u32) -> io::Result { pub struct PassthroughFsRo { inner: PassthroughFs, + #[cfg(target_os = "linux")] + dax: Option, } impl PassthroughFsRo { pub fn new(cfg: passthrough::Config, inode_alloc: Arc) -> io::Result { Ok(Self { inner: PassthroughFs::new(cfg, inode_alloc)?, + #[cfg(target_os = "linux")] + dax: None, }) } + #[cfg(target_os = "linux")] + pub(super) fn enable_dax( + &mut self, + region: crate::devices::virtio::VirtioShmRegion, + ) -> io::Result<()> { + self.dax = Some(super::dax::ReadOnlyDax::new(region)?); + Ok(()) + } + fn dax_attr_flags(&self, attr: &bindings::stat64) -> u32 { + #[cfg(target_os = "linux")] + if self.dax.is_some() && attr.st_mode & libc::S_IFMT == libc::S_IFREG { + return 2; + } // FUSE_ATTR_DAX + let _ = attr; + 0 + } } impl FileSystem for PassthroughFsRo { fn capture_state(&self) -> io::Result { + #[cfg(target_os = "linux")] + if self.dax.is_some() { + return Err(io::Error::new( + io::ErrorKind::Unsupported, + "DAX mappings cannot be captured", + )); + } Ok(super::snapshot::FsSnapshot::ReadOnly(Box::new( self.inner.capture_state()?, ))) @@ -86,16 +113,34 @@ impl FileSystem for PassthroughFsRo { fn init(&self, capable: FsOptions) -> io::Result { let opts = self.inner.init(capable)?; + #[cfg(target_os = "linux")] + let opts = if self.dax.is_some() { + opts | (capable & (FsOptions::HAS_INODE_DAX | FsOptions::MAP_ALIGNMENT)) + } else { + opts + }; // Strip WRITEBACK_CACHE to prevent the guest kernel from buffering writes. Ok(opts & !FsOptions::WRITEBACK_CACHE) } fn destroy(&self) { + #[cfg(target_os = "linux")] + if let Some(dax) = &self.dax { + if let Err(error) = dax.clear() { + log::error!("clear DAX window: {error}"); + } + } self.inner.destroy() } fn lookup(&self, ctx: Context, parent: Inode, name: &CStr) -> io::Result { - self.inner.lookup(ctx, parent, name) + let mut entry = self.inner.lookup(ctx, parent, name)?; + entry.attr_flags |= self.dax_attr_flags(&entry.attr); + Ok(entry) + } + + fn attribute_flags(&self, _inode: Inode, attr: &bindings::stat64) -> u32 { + self.dax_attr_flags(attr) } fn forget(&self, ctx: Context, inode: Inode, count: u64) { @@ -226,13 +271,16 @@ impl FileSystem for PassthroughFsRo { handle: Handle, size: u32, offset: u64, - add_entry: F, + mut add_entry: F, ) -> io::Result<()> where F: FnMut(DirEntry, Entry) -> io::Result, { self.inner - .readdirplus(ctx, inode, handle, size, offset, add_entry) + .readdirplus(ctx, inode, handle, size, offset, |dir, mut entry| { + entry.attr_flags |= self.dax_attr_flags(&entry.attr); + add_entry(dir, entry) + }) } fn fsyncdir( @@ -284,19 +332,36 @@ impl FileSystem for PassthroughFsRo { if (flags & fuse::SetupmappingFlags::WRITE.bits()) != 0 { return Err(erofs()); } - self.inner.setupmapping( - ctx, - inode, - handle, - foffset, - len, - flags, - moffset, - host_shm_base, - shm_size, - #[cfg(target_os = "macos")] - map_sender, - ) + #[cfg(target_os = "linux")] + { + let _ = ctx; + let dax = self + .dax + .as_ref() + .ok_or_else(|| io::Error::from_raw_os_error(libc::ENOSYS))?; + self.inner.with_dax_file(inode, handle, |file| { + dax.setup(file, foffset, len, flags, moffset, host_shm_base, shm_size) + }) + } + #[cfg(target_os = "macos")] + { + let _ = ( + ctx, + inode, + handle, + foffset, + len, + flags, + moffset, + host_shm_base, + shm_size, + map_sender, + ); + Err(io::Error::new( + io::ErrorKind::Unsupported, + "read-only DAX is supported only on Linux", + )) + } } fn removemapping( @@ -307,14 +372,22 @@ impl FileSystem for PassthroughFsRo { shm_size: u64, #[cfg(target_os = "macos")] map_sender: &Option>, ) -> io::Result<()> { - self.inner.removemapping( - ctx, - requests, - host_shm_base, - shm_size, - #[cfg(target_os = "macos")] - map_sender, - ) + #[cfg(target_os = "linux")] + { + let _ = ctx; + self.dax + .as_ref() + .ok_or_else(|| io::Error::from_raw_os_error(libc::ENOSYS))? + .remove(&requests, host_shm_base, shm_size) + } + #[cfg(target_os = "macos")] + { + let _ = (ctx, requests, host_shm_base, shm_size, map_sender); + Err(io::Error::new( + io::ErrorKind::Unsupported, + "read-only DAX is supported only on Linux", + )) + } } fn ioctl( @@ -512,4 +585,155 @@ mod tests { assert_eq!(err.raw_os_error(), Some(libc::EROFS)); } + + #[cfg(target_os = "linux")] + #[test] + fn dax_requires_opened_regular_file_and_preserves_readonly_lifecycle() { + use super::*; + use vm_memory::{Bytes, GuestAddress, GuestMemory, GuestMemoryMmap}; + let directory = tempfile::tempdir().unwrap(); + std::fs::write(directory.path().join("payload"), vec![0x5a; 4096]).unwrap(); + std::fs::create_dir(directory.path().join("dir")).unwrap(); + let memory = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 4096)]).unwrap(); + let region = crate::devices::virtio::VirtioShmRegion { + host_addr: memory.get_host_address(GuestAddress(0)).unwrap() as u64, + guest_addr: 0, + size: 4096, + }; + let mut fs = PassthroughFsRo::new( + passthrough::Config { + root_dir: directory.path().to_string_lossy().into_owned(), + ..Default::default() + }, + Arc::new(InodeAllocator::new()), + ) + .unwrap(); + fs.enable_dax(region.clone()).unwrap(); + let options = fs + .init(FsOptions::HAS_INODE_DAX | FsOptions::MAP_ALIGNMENT | FsOptions::WRITEBACK_CACHE) + .unwrap(); + assert!(options.contains(FsOptions::HAS_INODE_DAX | FsOptions::MAP_ALIGNMENT)); + assert!(!options.contains(FsOptions::WRITEBACK_CACHE)); + let ctx = Context { + uid: unsafe { libc::geteuid() }, + gid: unsafe { libc::getegid() }, + pid: 0, + }; + let entry = fs.lookup(ctx, fuse::ROOT_ID, c"payload").unwrap(); + assert_eq!(entry.attr_flags, 2); + assert_eq!(fs.attribute_flags(entry.inode, &entry.attr), 2); + assert_eq!(fs.lookup(ctx, fuse::ROOT_ID, c"dir").unwrap().attr_flags, 0); + assert_eq!( + fs.setupmapping(ctx, entry.inode, 999, 0, 4096, 2, 0, region.host_addr, 4096) + .unwrap_err() + .raw_os_error(), + Some(libc::EBADF) + ); + assert_eq!( + fs.setupmapping( + ctx, + entry.inode, + u64::MAX, + 0, + 4096, + 2, + 0, + region.host_addr, + 4096 + ) + .unwrap_err() + .raw_os_error(), + Some(libc::EBADF) + ); + let handle = fs + .open(ctx, entry.inode, false, libc::O_RDONLY as u32) + .unwrap() + .0 + .unwrap(); + assert_eq!( + fs.setupmapping( + ctx, + entry.inode + 100, + handle, + 0, + 4096, + 2, + 0, + region.host_addr, + 4096 + ) + .unwrap_err() + .raw_os_error(), + Some(libc::EBADF) + ); + assert_eq!( + fs.setupmapping( + ctx, + entry.inode, + handle, + 0, + 4096, + 3, + 0, + region.host_addr, + 4096 + ) + .unwrap_err() + .raw_os_error(), + Some(libc::EROFS) + ); + fs.setupmapping( + ctx, + entry.inode, + u64::MAX, + 0, + 4096, + 2, + 0, + region.host_addr, + 4096, + ) + .unwrap(); + fs.release(ctx, entry.inode, 0, handle, false, false, None) + .unwrap(); + assert_eq!(memory.read_obj::(GuestAddress(0)).unwrap(), 0x5a); + assert_eq!( + fs.setupmapping( + ctx, + entry.inode, + handle, + 0, + 4096, + 2, + 0, + region.host_addr, + 4096 + ) + .unwrap_err() + .raw_os_error(), + Some(libc::EBADF) + ); + assert_eq!( + fs.setupmapping( + ctx, + entry.inode, + u64::MAX, + 0, + 4096, + 2, + 0, + region.host_addr, + 4096 + ) + .unwrap_err() + .raw_os_error(), + Some(libc::EBADF) + ); + assert_eq!( + fs.capture_state().unwrap_err().kind(), + io::ErrorKind::Unsupported + ); + fs.destroy(); + assert_eq!(memory.read_obj::(GuestAddress(0)).unwrap(), 0); + } } diff --git a/crates/pvisor-vm/src/devices/virtio/fs/server.rs b/crates/pvisor-vm/src/devices/virtio/fs/server.rs index b81e303b7..f46899cbd 100644 --- a/crates/pvisor-vm/src/devices/virtio/fs/server.rs +++ b/crates/pvisor-vm/src/devices/virtio/fs/server.rs @@ -82,16 +82,24 @@ impl Server { pub fn capture_state(&self, next_inode: u64) -> io::Result { self.profile.emit_checkpoint(); + let options = self.options.load(Ordering::Relaxed); + let fs = self.fs.capture_state()?; + fs.validate_writeback( + FsOptions::from_bits_truncate(options).contains(FsOptions::WRITEBACK_CACHE), + )?; Ok(super::snapshot::ServerSnapshot { - options: self.options.load(Ordering::Relaxed), + options, next_inode, - fs: self.fs.capture_state()?, + fs, }) } pub fn restore_state(&self, state: &super::snapshot::ServerSnapshot) -> io::Result<()> { if FsOptions::from_bits(state.options).is_none() { return Err(super::snapshot::invalid("invalid FUSE options")); } + state.fs.validate_writeback( + FsOptions::from_bits_truncate(state.options).contains(FsOptions::WRITEBACK_CACHE), + )?; self.fs.restore_state(&state.fs)?; self.options.store(state.options, Ordering::Relaxed); Ok(()) @@ -298,7 +306,10 @@ impl Server { attr_valid: timeout.as_secs(), attr_valid_nsec: timeout.subsec_nanos(), dummy: 0, - attr: st.into(), + attr: Attr::with_flags( + st, + self.fs.attribute_flags(in_header.nodeid.into(), &st), + ), }; reply_ok(Some(out), None, in_header.unique, w) } @@ -995,7 +1006,11 @@ impl Server { max_write: MAX_BUFFER_SIZE, time_gran: 1, // nanoseconds max_pages: max_pages.try_into().unwrap(), - map_alignment: 0, + map_alignment: if enabled & FsOptions::MAP_ALIGNMENT.bits() != 0 { + page_size.trailing_zeros() as u16 + } else { + 0 + }, flags2: (enabled >> 32) as u32, ..Default::default() }; diff --git a/crates/pvisor-vm/src/devices/virtio/fs/snapshot.rs b/crates/pvisor-vm/src/devices/virtio/fs/snapshot.rs index 7978d091f..3d1060628 100644 --- a/crates/pvisor-vm/src/devices/virtio/fs/snapshot.rs +++ b/crates/pvisor-vm/src/devices/virtio/fs/snapshot.rs @@ -45,6 +45,9 @@ pub struct PassthroughSnapshot { pub(crate) handles: Vec, pub(crate) next_handle: u64, pub(crate) submounts: bool, + /// Negotiated mode; dirty guest pages are preserved by the RAM snapshot. + #[serde(default)] + pub(crate) writeback: bool, } #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(deny_unknown_fields)] @@ -326,6 +329,16 @@ impl ServerSnapshot { } impl FsSnapshot { + pub(super) fn validate_writeback(&self, enabled: bool) -> io::Result<()> { + match self { + Self::ReadOnly(inner) | Self::Augment { inner, .. } => inner.validate_writeback(enabled), + Self::Overlay(state) => state.validate_writeback(enabled), + Self::Passthrough(state) if state.writeback == enabled => Ok(()), + Self::Null if !enabled => Ok(()), + _ => Err(invalid("FUSE and backing writeback modes disagree")), + } + } + pub(super) fn verify_frozen_backing(&self) -> io::Result<()> { match self { Self::ReadOnly(inner) | Self::Augment { inner, .. } => inner.verify_frozen_backing(), @@ -613,6 +626,7 @@ mod owned_copy_tests { handles: Vec::new(), next_handle: 1, submounts: false, + writeback: false, }); (state, source, destination) } diff --git a/crates/pvisor-vm/src/devices/virtio/fs/worker.rs b/crates/pvisor-vm/src/devices/virtio/fs/worker.rs index 06839ffb3..ba8961c8d 100644 --- a/crates/pvisor-vm/src/devices/virtio/fs/worker.rs +++ b/crates/pvisor-vm/src/devices/virtio/fs/worker.rs @@ -5,9 +5,10 @@ use crossbeam_channel::Sender; use std::io; use std::os::fd::AsRawFd; -use std::sync::atomic::AtomicI32; +use std::sync::atomic::{AtomicBool, AtomicI32, Ordering}; use std::sync::{Arc, RwLock}; use std::thread; +#[cfg(test)] use std::time::Instant; use crate::utils::epoll::{ControlOperation, Epoll, EpollEvent, EventSet}; @@ -18,7 +19,9 @@ use super::super::Queue; use super::augment_fs::AugmentFs; use super::defs::{HPQ_INDEX, REQ_INDEX}; use super::descriptor_utils::{OwnedDescriptorChain, Reader, Writer}; -use super::fuse::{InHeader, Opcode, ReadIn}; +#[cfg(test)] +use super::fuse::ReadIn; +use super::fuse::{InHeader, Opcode}; use super::inode_alloc::InodeAllocator; use super::null_fs::NullFs; use super::overlay::{Config as OverlayConfig, OverlayFs}; @@ -28,6 +31,8 @@ use super::server::Server; use super::virtual_entry::VirtualDirEntry; use crate::devices::virtio::{InterruptTransport, VirtioShmRegion}; +mod scheduler; + enum FsServer { ReadWrite(Server>), ReadOnly(Server>), @@ -113,14 +118,9 @@ impl Execution { fn handle(&self, request: &Request) -> usize { let decoded = request .buffers - .reader_writer(&self.mem) - .and_then(|(mut reader, writer)| { - // The owner captured the header exactly once. Skip its guest bytes - // without trusting a second read of the driver-controlled opcode. - reader - .split_at(std::mem::size_of::()) - .map(|body| (body, writer)) - }); + // The owner captured the header exactly once. Skip its guest bytes + // without constructing or decoding another header reader. + .reader_writer_after(&self.mem, std::mem::size_of::()); match decoded { Ok((reader, writer)) => self.handle_decoded(request.header, reader, writer), Err(error) => { @@ -160,9 +160,10 @@ impl Execution { } } -/// Keep short metadata operations inline to avoid a thread handoff. Directory -/// enumeration and large reads use the pool even when they arrive alone, so -/// the queue owner stays available for metadata and high-priority requests. +// The old pool is retained only as a test fixture for the existing descriptor, +// memory-lease and wakeup regressions during this scheduling migration. The +// production worker exclusively uses scheduler::serve below. +#[cfg(test)] fn parallel_candidate(header: &InHeader, body: &Reader) -> bool { match header.opcode { x if x == Opcode::Read as u32 => body @@ -184,11 +185,12 @@ struct Request { index: u16, header: InHeader, buffers: OwnedDescriptorChain, - // Submit timestamp, replaced by completion-ready timestamp after service. - // None when diagnostics are disabled; normal requests read no clock. + // Legacy pool test diagnostics; queue-local execution reads no clock here. + #[cfg(test)] profile_timestamp: Option, } +#[cfg(test)] struct Completion { // Keep both captured descriptors and the RAM lease until add_used finishes. request: Request, @@ -198,16 +200,20 @@ struct Completion { /// A per-device bounded blocking-I/O pool. Completion is asynchronous to the /// queue owner; the existing pread/pwrite implementation remains zero-copy. /// No borrowed VolatileSlice or unsafe Send implementation crosses threads. +#[cfg(test)] struct RequestPool { jobs: Option>, + idle_jobs: Option>, completed: crossbeam_channel::Receiver, wake: Arc, + awaiting_wake: Arc, threads: Vec>, in_flight: usize, limit: usize, profile: pvisor_overlay_core::profile::Profile, } +#[cfg(test)] impl RequestPool { fn new( handle: impl Fn(&Request) -> usize + Send + Sync + 'static, @@ -223,14 +229,19 @@ impl RequestPool { } let handle = Arc::new(handle); let (jobs, receive) = crossbeam_channel::bounded::(limit); + // Keep isolated requests on one warm worker. Bursts still use the + // shared FIFO; this slot counts against the same in-flight bound. + let (idle_jobs, idle_receive) = crossbeam_channel::bounded::(1); // Although this channel is unbounded, at most `limit` accepted requests // can exist, including queued, executing and unpublished completions. let (completed, completions) = crossbeam_channel::unbounded(); let wake = Arc::new(EventFd::new(EFD_NONBLOCK)?); let mut pool = Self { jobs: Some(jobs), + idle_jobs: Some(idle_jobs), completed: completions, wake, + awaiting_wake: Arc::new(AtomicBool::new(false)), threads: Vec::new(), in_flight: 0, limit, @@ -238,8 +249,10 @@ impl RequestPool { }; for id in 0..workers { let receive = receive.clone(); + let idle_receive = idle_receive.clone(); let completed = completed.clone(); let wake = pool.wake.clone(); + let awaiting_wake = pool.awaiting_wake.clone(); let handle = handle.clone(); let profile = pool.profile.clone(); pool.threads @@ -247,7 +260,16 @@ impl RequestPool { thread::Builder::new() .name(format!("fs io {id}")) .spawn(move || { - for mut request in receive { + loop { + let received = if id == 0 { + crossbeam_channel::select_biased! { + recv(idle_receive) -> result => result.or_else(|_| receive.recv()), + recv(receive) -> result => result, + } + } else { + receive.recv() + }; + let Ok(mut request) = received else { break }; profile.record_since("pool_queue_wait", request.profile_timestamp); let service = profile.span("pool_service"); // A lost worker would make freeze wait forever. Fail the @@ -265,9 +287,11 @@ impl RequestPool { if completed.send(Completion { request, len }).is_err() { break; } - if let Err(error) = wake.write(1) { - error!("filesystem completion wake failed: {error}"); - std::process::abort(); + if awaiting_wake.swap(false, Ordering::AcqRel) { + if let Err(error) = wake.write(1) { + error!("filesystem completion wake failed: {error}"); + std::process::abort(); + } } } })?, @@ -276,12 +300,30 @@ impl RequestPool { Ok(pool) } + fn wait_timeout(&self) -> i32 { + // Pair the owner's arm with the worker's disarm after channel send. + // If completion wins, acquire makes it visible before the empty check; + // if arm wins, that worker signals the sleeping owner. Never park with + // an already queued completion, but still poll stop/kick events first. + self.awaiting_wake.swap(true, Ordering::AcqRel); + if self.completed.is_empty() { + -1 + } else { + 0 + } + } + fn submit(&mut self, mut request: Request) { request.profile_timestamp = self.profile.timestamp(); assert!(self.in_flight < self.limit); // Capacity covers every in-flight request, so the owner never blocks // behind I/O and can always service the dedicated high-priority queue. - self.jobs + let sender = if self.in_flight == 0 { + &self.idle_jobs + } else { + &self.jobs + }; + sender .as_ref() .expect("filesystem pool closed") .try_send(request) @@ -290,9 +332,11 @@ impl RequestPool { } } +#[cfg(test)] impl Drop for RequestPool { fn drop(&mut self) { self.jobs.take(); + self.idle_jobs.take(); for worker in self.threads.drain(..) { if worker.join().is_err() { std::process::abort(); @@ -301,9 +345,9 @@ impl Drop for RequestPool { } } -fn worker_count() -> usize { - // Host-side diagnostic override. The public VM API and guest queue topology - // stay platform-independent. One restores entirely synchronous dispatch. +pub(super) fn worker_count() -> usize { + // Controls normal queue count; hiprio always gets its own lane. The guest + // may initialize fewer queues, for example on a single-vCPU VM. std::env::var("PVISOR_VM_FS_WORKERS") .ok() .and_then(|value| value.parse::().ok()) @@ -362,8 +406,28 @@ impl FsWorker { virtual_entries, )))) } - (None, Some(cfg)) if read_only => { + (None, Some(mut cfg)) if read_only => { + // A shared-memory window on a read-only attachment is created + // only by immutable_filesystem, whose caller owns physical + // contents and namespace for this VM's lifetime. Ordinary ro + // mounts do not imply that guarantee. Extend only metadata and + // entry TTLs; keep the existing data-cache policy with dax=never. + if shm_region.is_some() { + cfg.entry_timeout = std::time::Duration::from_secs(60); + // LinuxSimplified attributes depend on the requesting uid. + if !cfg.attr_timeout.is_zero() { + cfg.attr_timeout = std::time::Duration::from_secs(60); + } + } let inner = PassthroughFsRo::new(cfg, inode_alloc.clone())?; + #[cfg(target_os = "linux")] + let inner = { + let mut inner = inner; + if let Some(region) = shm_region.as_ref() { + inner.enable_dax(region.clone())?; + } + inner + }; FsServer::ReadOnly(Server::new(AugmentFs::new( inner, &inode_alloc, @@ -448,80 +512,19 @@ impl FsWorker { } fn work(mut self) -> Self { - let workers = worker_count(); - let mut pool = None; - let epoll = Epoll::new().unwrap(); - for fd in self - .queue_evts - .iter() - .map(|event| event.as_raw_fd()) - .chain(std::iter::once(self.stop_fd.as_raw_fd())) - { - epoll - .ctl( - ControlOperation::Add, - fd, - &EpollEvent::new(EventSet::IN, fd as u64), - ) - .expect("filesystem epoll registration"); - } - // A restored/thawed queue may already contain requests without a new - // kick. Scan before waiting, and after every completion frees capacity. - self.service_queues(&mut pool, workers, &epoll); - let mut events = vec![EpollEvent::new(EventSet::empty(), 0); 32]; - loop { - let count = match epoll.wait(events.len(), -1, &mut events) { - Ok(count) => count, - Err(error) => { - debug!("filesystem epoll wait: {error}"); - continue; - } - }; - // Stop admission before servicing any other event in this batch. - if events[..count] - .iter() - .any(|e| e.fd() == self.stop_fd.as_raw_fd()) - { - let _ = self.stop_fd.read(); - if let Some(pool) = &mut pool { - let mut completed = [false; 2]; - while pool.in_flight != 0 { - let completion = pool.completed.recv().expect("filesystem completion lost"); - completed[completion.request.queue] = true; - self.publish_completion(completion); - pool.in_flight -= 1; - } - self.notify_completed(completed); - } - // Dropping the pool joins every I/O worker before returning the - // parked device to capture_state, reset or offload. - drop(pool); - self.dispatch_profile.emit_checkpoint(); - return self; - } - for event in &events[..count] { - if let Some(queue) = self.queue_evts.iter().find(|q| q.as_raw_fd() == event.fd()) { - let _ = queue.read(); - } else if let Some(pool) = &mut pool { - if event.fd() == pool.wake.as_raw_fd() { - // macOS uses a pipe, so consume all pending wake tokens. - while pool.wake.read().is_ok() {} - } - } - } - if let Some(pool) = &mut pool { - let mut completed = [false; 2]; - while let Ok(completion) = pool.completed.try_recv() { - completed[completion.request.queue] = true; - self.publish_completion(completion); - pool.in_flight -= 1; - } - self.notify_completed(completed); - } - self.service_queues(&mut pool, workers, &epoll); - } + self.queues = scheduler::serve( + std::mem::take(&mut self.queues), + &self.queue_evts, + &self.stop_fd, + self.interrupt.clone(), + self.execution.clone(), + self.dispatch_profile.clone(), + ); + self.dispatch_profile.emit_checkpoint(); + self } + #[cfg(test)] fn complete(&mut self, completion: Completion) { let index = completion.request.queue; self.publish_completion(completion); @@ -530,6 +533,7 @@ impl FsWorker { self.notify_completed(completed); } + #[cfg(test)] fn publish_completion(&mut self, completion: Completion) { self.dispatch_profile .record_since("pool_completion_wait", completion.request.profile_timestamp); @@ -546,6 +550,7 @@ impl FsWorker { self.dispatch_profile.add("published_completions", 1); } + #[cfg(test)] fn notify_completed(&mut self, completed: [bool; 2]) { let mut notify = false; for (index, completed) in completed.into_iter().enumerate() { @@ -562,6 +567,7 @@ impl FsWorker { } } + #[cfg(test)] fn service_queues(&mut self, pool: &mut Option, workers: usize, epoll: &Epoll) { let profile = self.dispatch_profile.clone(); let _span = profile.span("admission"); @@ -696,11 +702,12 @@ mod tests { use std::time::Duration; use vm_memory::GuestAddress; - fn queue_worker(mem: GuestMemoryMmap, queues: Vec) -> FsWorker { + pub(super) fn queue_worker(mem: GuestMemoryMmap, queues: Vec) -> FsWorker { use crate::devices::legacy::DummyIrqChip; + let queue_count = queues.len(); let mut worker = FsWorker::new(FsWorkerConfig { queues, - queue_evts: (0..2) + queue_evts: (0..queue_count) .map(|_| Arc::new(EventFd::new(EFD_NONBLOCK).unwrap())) .collect(), interrupt: InterruptTransport::new(DummyIrqChip::new().into(), "test".into()).unwrap(), @@ -1061,6 +1068,91 @@ mod tests { assert_eq!(reply.unique, 99); } + #[test] + fn completion_wakes_only_an_armed_owner_and_ready_completion_never_parks() { + for armed in [false, true] { + let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); + let chain = create_descriptor_chain( + &mem, + GuestAddress(0), + GuestAddress(0x2000), + vec![(DescriptorType::Readable, 8), (DescriptorType::Writable, 8)], + 0, + ) + .unwrap(); + let mut pool = RequestPool::new(|_| 8, 2, 8, Default::default()).unwrap(); + if armed { + assert_eq!(pool.wait_timeout(), -1); + } + pool.submit(Request { + queue: REQ_INDEX, + index: 0, + header: InHeader::default(), + buffers: OwnedDescriptorChain::new(chain).unwrap(), + profile_timestamp: None, + }); + let deadline = Instant::now() + std::time::Duration::from_secs(2); + while pool.completed.is_empty() { + assert!(Instant::now() < deadline); + thread::yield_now(); + } + let wake = pool.wake.clone(); + // Joining ensures the worker finished the send/disarm/wake sequence. + for worker in pool.threads.drain(..) { + // Disconnect both job inputs before joining. + pool.jobs.take(); + pool.idle_jobs.take(); + worker.join().unwrap(); + } + assert_eq!(wake.read().is_ok(), armed); + assert_eq!(pool.wait_timeout(), 0); + assert_eq!(pool.completed.try_recv().unwrap().len, 8); + pool.in_flight -= 1; + } + } + + #[test] + fn isolated_requests_reuse_the_primary_worker_without_losing_completion() { + let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); + let caller = thread::current().id(); + let mut pool = RequestPool::new( + move |_| { + assert_ne!(thread::current().id(), caller); + assert_eq!(thread::current().name(), Some("fs io 0")); + 8 + }, + 4, + 8, + Default::default(), + ) + .unwrap(); + for index in 0..16 { + let chain = create_descriptor_chain( + &mem, + GuestAddress(index as u64 * 0x100), + GuestAddress(0x2000 + index as u64 * 0x100), + vec![(DescriptorType::Readable, 8), (DescriptorType::Writable, 8)], + 0, + ) + .unwrap(); + pool.submit(Request { + queue: REQ_INDEX, + index, + header: InHeader::default(), + buffers: OwnedDescriptorChain::new(chain).unwrap(), + profile_timestamp: None, + }); + let completed = pool + .completed + .recv_timeout(std::time::Duration::from_secs(2)) + .unwrap(); + assert_eq!(completed.request.index, index); + assert_eq!(completed.len, 8); + pool.in_flight -= 1; + } + drop(pool); + } + #[test] fn pool_completes_out_of_order_and_retains_unpublished_memory_leases() { let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); diff --git a/crates/pvisor-vm/src/devices/virtio/fs/worker/scheduler.rs b/crates/pvisor-vm/src/devices/virtio/fs/worker/scheduler.rs new file mode 100644 index 000000000..9429638df --- /dev/null +++ b/crates/pvisor-vm/src/devices/virtio/fs/worker/scheduler.rs @@ -0,0 +1,767 @@ +//! Queue-local execution with stealing directly from a blocked owner's ring. +//! Only descriptor admission and used-ring publication hold the ring mutex. +use super::*; +use std::sync::atomic::AtomicU64; +use std::sync::Mutex; +use std::time::{Duration, Instant}; + +const STEAL_DELAY: Duration = Duration::from_millis(2); + +struct Wake(EventFd); + +impl Default for Wake { + fn default() -> Self { + Self(EventFd::new(EFD_NONBLOCK).unwrap()) + } +} + +impl Wake { + fn signal(&self) { + let _ = self.0.write(1); + } +} + +fn drain_event(event: &EventFd) { + // Bounded for pipe-backed eventfds too; a continuous kick stream cannot + // prevent observing stop or scanning descriptors. + for _ in 0..64 { + if event.read().is_err() { + break; + } + } +} + +fn heavy(opcode: u32) -> bool { + matches!(opcode, x if x == Opcode::Read as u32 || x == Opcode::Write as u32 + || x == Opcode::Fsync as u32 || x == Opcode::Fsyncdir as u32 + || x == Opcode::Open as u32 || x == Opcode::Create as u32 + || x == Opcode::Setattr as u32 || x == Opcode::Rename as u32 + || x == Opcode::Rename2 as u32 || x == Opcode::Unlink as u32 + || x == Opcode::Rmdir as u32 || x == Opcode::Link as u32) +} + +struct Lane { + ring: Mutex, + busy: AtomicBool, + started: AtomicU64, + stealable: AtomicBool, + wake: Wake, +} + +struct Runtime { + lanes: Vec, + epoch: Instant, + stopped: AtomicBool, + execution: Arc, + interrupt: InterruptTransport, + profile: pvisor_overlay_core::profile::Profile, +} + +impl Runtime { + fn wake_thieves(&self, victim: usize) { + for (index, lane) in self.lanes.iter().enumerate().skip(REQ_INDEX) { + if index != victim + && lane.ring.try_lock().is_ok_and(|ring| ring.ready) + && !lane.busy.load(Ordering::Acquire) + { + lane.wake.signal(); + // The thief propagates a wake if backlog remains after its + // claim, avoiding a herd of contenders for a single request. + break; + } + } + } + + fn long_running(&self, lane: &Lane) -> bool { + let started = lane.started.load(Ordering::Acquire); + started != 0 + && lane.busy.load(Ordering::Acquire) + && (self.epoch.elapsed().as_micros() as u64 + 1).saturating_sub(started) + >= STEAL_DELAY.as_micros() as u64 + } + + fn take(&self, owner: usize, victim: usize) -> Option { + if self.stopped.load(Ordering::Acquire) { + return None; + } + let lane = &self.lanes[victim]; + let mut ring = if owner == victim { + // The owner must not sleep just because a thief briefly publishes + // a completion: there may be no later kick for the remaining work. + lane.ring.lock().unwrap() + } else { + lane.ring.try_lock().ok()? + }; + // A guest may initialize fewer request queues than advertised. + if !ring.ready || self.stopped.load(Ordering::Acquire) { + return None; + } + if owner != victim && !self.long_running(lane) { + return None; + } + let mem = &self.execution.mem; + loop { + if self.stopped.load(Ordering::Acquire) { + return None; + } + ring.disable_notification(mem).unwrap(); + // The guest can publish between an empty pop and a subsequent + // avail read. Classify malformed heads from the pre-pop state; + // a later publication belongs to the notification recheck. + let available = !ring.is_empty(mem); + let head = if available { ring.pop(mem) } else { None }; + let Some(head) = head else { + if available { + // Invalid descriptor heads must not spin forever and + // prevent device freeze from stopping admission. + error!("invalid filesystem available descriptor"); + std::process::abort(); + } + if ring.enable_notification(mem).unwrap() { + continue; + } + return None; + }; + let index = head.index; + let buffers = match OwnedDescriptorChain::new(head) { + Ok(buffers) => buffers, + Err(error) => { + error!("invalid filesystem descriptor chain: {error}"); + ring.add_used(mem, index, 0).unwrap(); + if ring.needs_notification(mem).unwrap() { + self.interrupt.signal_used_queue(); + } + continue; + } + }; + let header = buffers + .read_prefix::(mem) + .map_err(super::super::FsError::QueueReader); + let header = match header { + Ok(header) => header, + Err(error) => { + error!("invalid filesystem header: {error:?}"); + ring.add_used(mem, index, 0).unwrap(); + if ring.needs_notification(mem).unwrap() { + self.interrupt.signal_used_queue(); + } + continue; + } + }; + if victim == HPQ_INDEX + && !matches!(header.opcode, + x if x == Opcode::Forget as u32 + || x == Opcode::BatchForget as u32 + || x == Opcode::Interrupt as u32) + { + error!("normal request submitted to filesystem hiprio queue"); + ring.add_used(mem, index, 0).unwrap(); + if ring.needs_notification(mem).unwrap() { + self.interrupt.signal_used_queue(); + } + continue; + } + // Keep guest notifications suppressed while this queue is being + // drained. Empty-ring admission above arms and rechecks before + // an owner may sleep. Delayed rescue scans backlog independently + // of guest kicks, including when the owner remains in a syscall. + self.lanes[owner].busy.store(true, Ordering::Release); + self.lanes[owner].stealable.store(false, Ordering::Release); + self.lanes[owner].started.store( + if owner != HPQ_INDEX && heavy(header.opcode) { + self.epoch.elapsed().as_micros() as u64 + 1 + } else { + 0 + }, + Ordering::Release, + ); + drop(ring); + return Some(Request { + queue: victim, + index, + header, + buffers, + #[cfg(test)] + profile_timestamp: None, + }); + } + } + + fn next(&self, owner: usize, cursor: &mut usize) -> Option { + if let Some(request) = self.take(owner, owner) { + return Some(request); + } + if owner == HPQ_INDEX { + return None; + } + // Rotate victims so an overloaded queue cannot monopolize idle lanes. + let count = self.lanes.len() - REQ_INDEX; + for _ in 0..count { + let victim = REQ_INDEX + *cursor % count; + *cursor = (*cursor + 1) % count; + if victim != owner + && self.lanes[victim].stealable.load(Ordering::Acquire) + && self.lanes[victim].busy.load(Ordering::Acquire) + { + if let Some(request) = self.take(owner, victim) { + self.profile.add("stolen_requests", 1); + return Some(request); + } + } + } + None + } + + fn run(&self, owner: usize, kick: &EventFd) { + // Queue ownership includes waiting for the guest kick. There is no + // coordinator-to-owner handoff in the ordinary request path. + let epoll = Epoll::new().unwrap(); + for event in [kick, &self.lanes[owner].wake.0] { + epoll + .ctl( + ControlOperation::Add, + event.as_raw_fd(), + &EpollEvent::new(EventSet::IN, event.as_raw_fd() as u64), + ) + .unwrap(); + } + let mut events: [EpollEvent; 2] = + std::array::from_fn(|_| EpollEvent::new(EventSet::empty(), 0)); + let mut cursor = owner; + while !self.stopped.load(Ordering::Acquire) { + if let Some(request) = self.next(owner, &mut cursor) { + self.profile.add( + if owner == HPQ_INDEX { + "hiprio_requests" + } else { + "direct_requests" + }, + 1, + ); + let len = self.execution.handle(&request); + self.lanes[owner].started.store(0, Ordering::Release); + self.publish(request, len); + self.lanes[owner].stealable.store(false, Ordering::Release); + self.lanes[owner].busy.store(false, Ordering::Release); + continue; + } + if self.stopped.load(Ordering::Acquire) { + break; + } + match epoll.wait(events.len(), -1, &mut events) { + Ok(count) => { + for event in &events[..count] { + if event.fd() == kick.as_raw_fd() { + drain_event(kick); + } else { + drain_event(&self.lanes[owner].wake.0); + } + } + } + Err(error) => debug!("filesystem lane epoll wait: {error}"), + } + } + } + + fn publish(&self, request: Request, len: usize) { + let mut ring = self.lanes[request.queue].ring.lock().unwrap(); + ring.add_used(&self.execution.mem, request.index, len as u32) + .unwrap(); + if ring.needs_notification(&self.execution.mem).unwrap() { + self.interrupt.signal_used_queue(); + } + // The request owns its RAM lease through used-ring publication. + drop(ring); + drop(request); + } +} + +pub(super) fn serve( + queues: Vec, + events: &[Arc], + stop: &EventFd, + interrupt: InterruptTransport, + execution: Arc, + profile: pvisor_overlay_core::profile::Profile, +) -> Vec { + assert!(queues.len() > REQ_INDEX && queues.len() == events.len()); + let runtime = Arc::new(Runtime { + epoch: Instant::now(), + lanes: queues + .into_iter() + .map(|ring| Lane { + ring: Mutex::new(ring), + busy: AtomicBool::new(false), + started: AtomicU64::new(0), + stealable: AtomicBool::new(false), + wake: Wake::default(), + }) + .collect(), + stopped: AtomicBool::new(false), + execution, + interrupt, + profile, + }); + let epoll = Epoll::new().unwrap(); + epoll + .ctl( + ControlOperation::Add, + stop.as_raw_fd(), + &EpollEvent::new(EventSet::IN, stop.as_raw_fd() as u64), + ) + .unwrap(); + let mut threads = Vec::new(); + for (owner, kick) in events.iter().enumerate() { + if !runtime.lanes[owner].ring.lock().unwrap().ready { + continue; + } + let runtime = runtime.clone(); + let kick = kick.clone(); + threads.push( + thread::Builder::new() + .name(if owner == HPQ_INDEX { + "fs hiprio".into() + } else { + format!("fs queue {owner}") + }) + .spawn(move || { + // A lost lane would leave requests unpublished. Never capture + // a snapshot after an execution or ring-publication panic. + if std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + runtime.run(owner, &kick) + })) + .is_err() + { + std::process::abort(); + } + }) + .unwrap_or_else(|_| std::process::abort()), + ); + } + // Control path only: stop and delayed rescue. Elapsed time is a bounded + // heuristic for a long-running operation, not proof of kernel sleep. No + // ordinary kick is consumed or forwarded here, and short requests never + // trigger a thief wake. The 2 ms control timer is independent of I/O. + let mut ready = [EpollEvent::new(EventSet::empty(), 0); 1]; + loop { + let count = match epoll.wait(ready.len(), 2, &mut ready) { + Ok(count) => count, + Err(error) => { + debug!("filesystem control epoll wait: {error}"); + continue; + } + }; + if count != 0 { + drain_event(stop); + runtime.stopped.store(true, Ordering::Release); + for lane in &runtime.lanes { + lane.wake.signal(); + } + break; + } + let now = runtime.epoch.elapsed().as_micros() as u64 + 1; + for (index, lane) in runtime.lanes.iter().enumerate().skip(REQ_INDEX) { + let started = lane.started.load(Ordering::Acquire); + if started != 0 + && now.saturating_sub(started) >= STEAL_DELAY.as_micros() as u64 + && lane.busy.load(Ordering::Acquire) + { + lane.stealable.store(true, Ordering::Release); + if lane + .ring + .try_lock() + .is_ok_and(|ring| ring.ready && !ring.is_empty(&runtime.execution.mem)) + { + runtime.wake_thieves(index); + } + } + } + } + for thread in threads { + thread.join().unwrap_or_else(|_| std::process::abort()); + } + // Every accepted request has executed and published before queue ownership + // returns to the frozen device. Pending guest descriptors stay in the ring. + let runtime = Arc::try_unwrap(runtime) + .ok() + .expect("filesystem lane still running"); + runtime + .lanes + .into_iter() + .map(|lane| lane.ring.into_inner().unwrap()) + .collect() +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::devices::virtio::queue::tests::VirtQueue; + use vm_memory::{Bytes, GuestAddress}; + + fn fill(queue: &VirtQueue<'_>, mem: &GuestMemoryMmap, address: u64, opcodes: &[Opcode]) { + for (slot, opcode) in opcodes.iter().enumerate() { + let input = address + slot as u64 * 0x200; + let descriptor = slot * 2; + queue.dtable[descriptor].addr.set(input); + queue.dtable[descriptor] + .len + .set(std::mem::size_of::() as u32); + queue.dtable[descriptor] + .flags + .set(crate::devices::virtio::queue::VIRTQ_DESC_F_NEXT); + queue.dtable[descriptor].next.set((descriptor + 1) as u16); + queue.dtable[descriptor + 1].addr.set(input + 0x100); + queue.dtable[descriptor + 1].len.set(128); + queue.dtable[descriptor + 1] + .flags + .set(crate::devices::virtio::queue::VIRTQ_DESC_F_WRITE); + mem.write_obj( + InHeader { + len: std::mem::size_of::() as u32, + opcode: *opcode as u32, + ..Default::default() + }, + GuestAddress(input), + ) + .unwrap(); + queue.avail.ring[slot].set(descriptor as u16); + } + queue.avail.idx.set(opcodes.len() as u16); + } + + fn runtime(mem: GuestMemoryMmap, queues: Vec) -> Runtime { + let worker = super::super::tests::queue_worker(mem, queues); + Runtime { + epoch: Instant::now(), + lanes: worker + .queues + .into_iter() + .map(|ring| Lane { + ring: Mutex::new(ring), + busy: AtomicBool::new(false), + started: AtomicU64::new(0), + stealable: AtomicBool::new(false), + wake: Wake::default(), + }) + .collect(), + stopped: AtomicBool::new(false), + execution: worker.execution, + interrupt: worker.interrupt, + profile: worker.dispatch_profile, + } + } + + #[test] + fn draining_suppresses_kicks_until_empty_and_rearms_both_notification_modes() { + for event_idx in [false, true] { + let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); + let high = VirtQueue::new(GuestAddress(0), &mem, 8); + let first = VirtQueue::new(GuestAddress(0x400), &mem, 8); + fill( + &first, + &mem, + 0x4000, + &[Opcode::Read, Opcode::Getattr, Opcode::Getattr], + ); + first.avail.idx.set(1); + let mut ordinary = first.create_queue(); + ordinary.set_event_idx(event_idx); + let runtime = runtime(mem.clone(), vec![high.create_queue(), ordinary]); + + let request = runtime.next(1, &mut 0).unwrap(); + if event_idx { + assert_eq!(first.used.event.get(), 0); + } else { + assert_eq!(first.used.flags.get(), 1); // VRING_USED_F_NO_NOTIFY + } + // A driver may add a descriptor while kicks are suppressed. The + // owner must consume it directly after its current completion. + first.avail.idx.set(2); + runtime.publish(request, 16); + runtime.lanes[1].busy.store(false, Ordering::Release); + let next = runtime.next(1, &mut 0).unwrap(); + assert_eq!(next.index, 2); + runtime.publish(next, 16); + runtime.lanes[1].busy.store(false, Ordering::Release); + assert!(runtime.next(1, &mut 0).is_none()); + if event_idx { + assert_eq!(first.used.event.get(), 2); + } else { + assert_eq!(first.used.flags.get(), 0); + } + // Empty-ring rearming makes the next driver submission eligible + // for a kick, and the next admission must find that request. + first.avail.idx.set(3); + let final_request = runtime.next(1, &mut 0).unwrap(); + assert_eq!(final_request.index, 4); + runtime.publish(final_request, 16); + assert_eq!(first.used.idx.get(), 3); + } + } + + #[test] + fn concurrent_guest_publication_after_empty_checks_is_consumed() { + let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); + let high = VirtQueue::new(GuestAddress(0), &mem, 8); + let first = VirtQueue::new(GuestAddress(0x400), &mem, 8); + fill(&first, &mem, 0x4000, &[Opcode::Getattr]); + first.avail.idx.set(0); + for slot in 0..8 { + first.avail.ring[slot].set(0); + } + let runtime = runtime(mem.clone(), vec![high.create_queue(), first.create_queue()]); + let deadline = Instant::now() + Duration::from_secs(5); + thread::scope(|scope| { + let producer = scope.spawn(|| { + for count in 0..5000u16 { + while mem + .load::(first.used.idx.location, Ordering::Acquire) + .unwrap() + != count + { + assert!(Instant::now() < deadline, "guest completion wait timed out"); + thread::yield_now(); + } + mem.store(count + 1, first.avail.idx.location, Ordering::Release) + .unwrap(); + } + }); + for _ in 0..5000 { + let request = loop { + if let Some(request) = runtime.next(1, &mut 0) { + break request; + } + assert!(Instant::now() < deadline, "guest publication was lost"); + thread::yield_now(); + }; + runtime.publish(request, 16); + runtime.lanes[1].busy.store(false, Ordering::Release); + } + producer.join().unwrap(); + }); + assert_eq!(first.used.idx.get(), 5000); + } + + #[test] + fn blocked_owner_backlog_is_stolen_and_completed_on_its_original_ring() { + let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); + let high = VirtQueue::new(GuestAddress(0), &mem, 8); + let first = VirtQueue::new(GuestAddress(0x400), &mem, 8); + let second = VirtQueue::new(GuestAddress(0x800), &mem, 8); + fill(&first, &mem, 0x4000, &[Opcode::Read, Opcode::Getattr]); + fill(&high, &mem, 0x6000, &[Opcode::Forget]); + let gate = crate::devices::virtio::memory_gate::register(&mem); + let runtime = runtime( + mem.clone(), + vec![ + high.create_queue(), + first.create_queue(), + second.create_queue(), + ], + ); + // Keep the first request outstanding, as if its owner were in a syscall. + let blocked = runtime.next(1, &mut 0).unwrap(); + assert!(!gate.try_close().unwrap()); + assert!(runtime.lanes[1].ring.try_lock().is_ok()); + // A busy short request is not enough to admit stealing. + assert!(runtime.next(2, &mut 0).is_none()); + std::thread::sleep(STEAL_DELAY); + runtime.lanes[1].stealable.store(true, Ordering::Release); + let stolen = runtime.next(2, &mut 0).unwrap(); + assert_eq!(stolen.queue, 1); + assert_eq!(stolen.header.opcode, Opcode::Getattr as u32); + runtime.publish(stolen, 16); + assert!(!gate.try_close().unwrap()); + assert_eq!(first.used.idx.get(), 1); + assert_eq!(second.used.idx.get(), 0); + assert_eq!(first.used.ring[0].get().id, 2); + assert!(runtime.next(2, &mut 0).is_none()); + // Neither busy ordinary execution nor stealing consumes hiprio. + let control = runtime.next(0, &mut 0).unwrap(); + assert_eq!(control.header.opcode, Opcode::Forget as u32); + runtime.publish(control, 0); + runtime.publish(blocked, 32); + assert_eq!(high.used.idx.get(), 1); + assert_eq!(first.used.idx.get(), 2); + assert!(gate.try_close().unwrap()); + } + + #[test] + fn idle_owners_are_not_stolen_and_stop_preserves_unaccepted_descriptors() { + let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); + let high = VirtQueue::new(GuestAddress(0), &mem, 8); + let first = VirtQueue::new(GuestAddress(0x400), &mem, 8); + let second = VirtQueue::new(GuestAddress(0x800), &mem, 8); + fill(&first, &mem, 0x4000, &[Opcode::Getattr]); + let runtime = runtime( + mem.clone(), + vec![ + high.create_queue(), + first.create_queue(), + second.create_queue(), + ], + ); + assert!(runtime.next(2, &mut 0).is_none()); + runtime.stopped.store(true, Ordering::Release); + assert!(runtime.next(1, &mut 0).is_none()); + assert_eq!( + runtime.lanes[1] + .ring + .lock() + .unwrap() + .len(&runtime.execution.mem), + 1 + ); + assert_eq!(first.used.idx.get(), 0); + } + + #[test] + fn concurrent_thieves_claim_distinct_descriptors() { + let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); + let high = VirtQueue::new(GuestAddress(0), &mem, 8); + let first = VirtQueue::new(GuestAddress(0x400), &mem, 8); + let second = VirtQueue::new(GuestAddress(0x800), &mem, 8); + let third = VirtQueue::new(GuestAddress(0xc00), &mem, 8); + fill( + &first, + &mem, + 0x4000, + &[ + Opcode::Read, + Opcode::Getattr, + Opcode::Getattr, + Opcode::Getattr, + ], + ); + let runtime = runtime( + mem.clone(), + vec![ + high.create_queue(), + first.create_queue(), + second.create_queue(), + third.create_queue(), + ], + ); + let blocked = runtime.next(1, &mut 0).unwrap(); + std::thread::sleep(STEAL_DELAY); + runtime.lanes[1].stealable.store(true, Ordering::Release); + let steal = |owner| { + let deadline = Instant::now() + Duration::from_secs(2); + let mut cursor = owner; + loop { + if let Some(request) = runtime.next(owner, &mut cursor) { + let index = request.index; + runtime.publish(request, 16); + return index; + } + assert!(Instant::now() < deadline, "thief failed to claim backlog"); + thread::yield_now(); + } + }; + let mut claimed = thread::scope(|scope| { + let a = scope.spawn(|| steal(2)); + let b = scope.spawn(|| steal(3)); + vec![a.join().unwrap(), b.join().unwrap()] + }); + claimed.sort_unstable(); + assert_eq!(claimed, [2, 4]); + runtime.publish(blocked, 32); + assert_eq!(first.used.idx.get(), 3); + assert_eq!( + runtime.lanes[1] + .ring + .lock() + .unwrap() + .len(&runtime.execution.mem), + 1 + ); + } + + #[test] + fn hiprio_rejects_normal_operations_and_never_steals_them() { + let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); + let high = VirtQueue::new(GuestAddress(0), &mem, 8); + let first = VirtQueue::new(GuestAddress(0x400), &mem, 8); + fill(&high, &mem, 0x4000, &[Opcode::Read]); + fill(&first, &mem, 0x6000, &[Opcode::Getattr]); + let runtime = runtime(mem.clone(), vec![high.create_queue(), first.create_queue()]); + runtime.lanes[1].busy.store(true, Ordering::Release); + assert!(runtime.next(0, &mut 0).is_none()); + assert_eq!(high.used.idx.get(), 1); + assert_eq!(first.used.idx.get(), 0); + } + + #[test] + fn lane_consumes_its_kick_and_stops_without_a_forwarder() { + let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); + let high = VirtQueue::new(GuestAddress(0), &mem, 8); + let first = VirtQueue::new(GuestAddress(0x400), &mem, 8); + fill(&first, &mem, 0x4000, &[Opcode::Getattr]); + first.avail.idx.set(0); + let runtime = runtime(mem.clone(), vec![high.create_queue(), first.create_queue()]); + let kick = EventFd::new(EFD_NONBLOCK).unwrap(); + thread::scope(|scope| { + let lane = scope.spawn(|| runtime.run(1, &kick)); + mem.store(1u16, first.avail.idx.location, Ordering::Release) + .unwrap(); + kick.write(1).unwrap(); + let deadline = Instant::now() + Duration::from_secs(2); + while mem + .load::(first.used.idx.location, Ordering::Acquire) + .unwrap() + != 1 + { + if Instant::now() >= deadline { + runtime.stopped.store(true, Ordering::Release); + runtime.lanes[1].wake.signal(); + panic!("direct queue kick was not serviced"); + } + thread::yield_now(); + } + runtime.stopped.store(true, Ordering::Release); + runtime.lanes[1].wake.signal(); + lane.join().unwrap(); + }); + } + + #[test] + fn short_operations_are_not_stealable_even_after_the_delay() { + let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); + let high = VirtQueue::new(GuestAddress(0), &mem, 8); + let first = VirtQueue::new(GuestAddress(0x400), &mem, 8); + let second = VirtQueue::new(GuestAddress(0x800), &mem, 8); + fill(&first, &mem, 0x4000, &[Opcode::Getattr, Opcode::Getattr]); + let runtime = runtime( + mem.clone(), + vec![ + high.create_queue(), + first.create_queue(), + second.create_queue(), + ], + ); + let short = runtime.next(1, &mut 0).unwrap(); + thread::sleep(STEAL_DELAY); + runtime.lanes[1].stealable.store(true, Ordering::Release); // stale rescue hint + assert!(runtime.next(2, &mut 0).is_none()); + runtime.publish(short, 16); + assert_eq!(runtime.lanes[1].ring.lock().unwrap().len(&mem), 1); + } + + #[test] + fn wake_before_wait_is_retained() { + let wake = Wake::default(); + wake.signal(); + let epoll = Epoll::new().unwrap(); + epoll + .ctl( + ControlOperation::Add, + wake.0.as_raw_fd(), + &EpollEvent::new(EventSet::IN, wake.0.as_raw_fd() as u64), + ) + .unwrap(); + let mut events = [EpollEvent::new(EventSet::empty(), 0)]; + assert_eq!(epoll.wait(1, 100, &mut events).unwrap(), 1); + drain_event(&wake.0); + assert_eq!(epoll.wait(1, 0, &mut events).unwrap(), 0); + } +} diff --git a/crates/pvisor-vm/src/devices/virtio/gpu/device.rs b/crates/pvisor-vm/src/devices/virtio/gpu/device.rs index a006020d1..603cc5e62 100644 --- a/crates/pvisor-vm/src/devices/virtio/gpu/device.rs +++ b/crates/pvisor-vm/src/devices/virtio/gpu/device.rs @@ -234,4 +234,8 @@ impl VirtioDevice for Gpu { debug!("virtio_gpu: GET_shm_region"); self.shm_region.as_ref() } + + fn shm_region_id(&self) -> u32 { + u32::from(super::protocol::VIRTIO_GPU_SHM_ID_HOST_VISIBLE) + } } diff --git a/crates/pvisor-vm/src/devices/virtio/mmio.rs b/crates/pvisor-vm/src/devices/virtio/mmio.rs index ffac2d215..2d4a2990f 100644 --- a/crates/pvisor-vm/src/devices/virtio/mmio.rs +++ b/crates/pvisor-vm/src/devices/virtio/mmio.rs @@ -78,6 +78,31 @@ pub struct MmioSnapshot { } impl MmioSnapshot { + /// Restore filesystem topology before registering queue kick eventfds. + #[cfg(not(any(feature = "tee", feature = "aws-nitro")))] + pub(crate) fn filesystem_request_queues( + &self, + expected_tag: &[u8], + ) -> Result, String> { + let DeviceSnapshotState::Fs { tag, .. } = &self.device.state else { + return Ok(None); + }; + let end = tag.iter().position(|byte| *byte == 0).unwrap_or(tag.len()); + if &tag[..end] != expected_tag { + return Ok(None); + } + let queues = self + .device + .queues + .as_ref() + .or(self.queues.as_ref()) + .ok_or("filesystem snapshot has no queues")?; + if !(2..=9).contains(&queues.len()) { + return Err("invalid filesystem snapshot queue count".into()); + } + Ok(Some(queues.len() - 1)) + } + #[cfg(not(any(feature = "tee", feature = "aws-nitro")))] pub fn rebind_filesystem_exclusions( &mut self, @@ -749,14 +774,16 @@ impl BusDevice for MmioTransport { 0xfc => self.config_generation, 0xb0..=0xbc => { // For no SHM region or invalid region the kernel looks for length of -1 - let (shm_base, shm_len) = if self.shm_region_select > 1 { - (0, !0) - } else { - match self.locked_device().shm_region() { - Some(region) => (region.guest_addr, region.size as u64), - None => (0, !0), - } - }; + let device = self.locked_device(); + let (shm_base, shm_len) = + if self.shm_region_select != device.shm_region_id() { + (0, !0) + } else { + match device.shm_region() { + Some(region) => (region.guest_addr, region.size as u64), + None => (0, !0), + } + }; match offset { 0xb0 => shm_len as u32, 0xb4 => (shm_len >> 32) as u32, @@ -890,6 +917,8 @@ pub(crate) mod tests { avail_features: u64, device_activated: bool, config_bytes: [u8; 0xeff], + shm: Option, + shm_id: u32, } impl DummyDevice { @@ -899,6 +928,8 @@ pub(crate) mod tests { avail_features: 0, device_activated: false, config_bytes: [0; 0xeff], + shm: None, + shm_id: 0, } } @@ -955,6 +986,14 @@ pub(crate) mod tests { fn is_activated(&self) -> bool { self.device_activated } + + fn shm_region(&self) -> Option<&VirtioShmRegion> { + self.shm.as_ref() + } + + fn shm_region_id(&self) -> u32 { + self.shm_id + } } fn set_device_status(d: &mut MmioTransport, status: u32) { @@ -988,6 +1027,50 @@ pub(crate) mod tests { assert!(!d.with_queue_mut(|q| q.size = 16)); } + #[test] + fn shared_memory_selection_exposes_only_the_device_region_id() { + let memory = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 4096)]).unwrap(); + let dummy = Arc::new(Mutex::new(DummyDevice::new())); + dummy.lock().unwrap().shm = Some(VirtioShmRegion { + host_addr: 0, + guest_addr: 0x1234_5678_9000, + size: 2 << 20, + }); + let mut device = + MmioTransport::new(memory, DummyIrqChip::new().into(), dummy.clone()).unwrap(); + let mut bytes = [0; 4]; + // Test both virtio-fs (0) and GPU host-visible (1) ID contracts. + for id in [0, 1] { + dummy.lock().unwrap().shm_id = id; + for selected in [0, 1, 2, u32::MAX] { + write_le_u32(&mut bytes, selected); + device.write(0, 0xac, &bytes); + device.read(0, 0xb0, &mut bytes); + assert_eq!( + read_le_u32(&bytes), + if selected == id { 2 << 20 } else { u32::MAX } + ); + device.read(0, 0xb4, &mut bytes); + assert_eq!( + read_le_u32(&bytes), + if selected == id { 0 } else { u32::MAX } + ); + device.read(0, 0xb8, &mut bytes); + assert_eq!( + read_le_u32(&bytes), + if selected == id { 0x5678_9000 } else { 0 } + ); + device.read(0, 0xbc, &mut bytes); + assert_eq!(read_le_u32(&bytes), if selected == id { 0x1234 } else { 0 }); + } + } + dummy.lock().unwrap().shm = None; + write_le_u32(&mut bytes, 1); + device.write(0, 0xac, &bytes); + device.read(0, 0xb0, &mut bytes); + assert_eq!(read_le_u32(&bytes), u32::MAX); + } + #[test] fn test_bus_device_read() { let m = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(); diff --git a/crates/pvisor-vm/src/portable.rs b/crates/pvisor-vm/src/portable.rs index 5495bf903..6c8b78a7b 100644 --- a/crates/pvisor-vm/src/portable.rs +++ b/crates/pvisor-vm/src/portable.rs @@ -303,6 +303,30 @@ impl VmConfiguration for VmBuilder { Err(unsupported("virtio-fs")) } } + fn immutable_filesystem( + &mut self, + tag: &str, + path: &Path, + dax_window_size: usize, + ) -> io::Result<()> { + #[cfg(all( + target_os = "linux", + target_arch = "x86_64", + not(any(feature = "tee", feature = "aws-nitro")) + ))] + { + self.inner.immutable_filesystem(tag, path, dax_window_size) + } + #[cfg(not(all( + target_os = "linux", + target_arch = "x86_64", + not(any(feature = "tee", feature = "aws-nitro")) + )))] + { + let _ = (tag, path, dax_window_size); + Err(unsupported("immutable virtio-fs DAX")) + } + } fn overlay(&mut self, tag: &str, overlay: OverlayConfig, shm_size: usize) -> io::Result<()> { #[cfg(not(any(feature = "tee", feature = "aws-nitro")))] { diff --git a/crates/pvisor-vm/src/probes/readonly_dax_case.rs b/crates/pvisor-vm/src/probes/readonly_dax_case.rs new file mode 100644 index 000000000..bc54ebbbb --- /dev/null +++ b/crates/pvisor-vm/src/probes/readonly_dax_case.rs @@ -0,0 +1,100 @@ +//! Real-guest DAX contract probe through the public API. Supply a prepared +//! Python rootfs, firmware file and an immutable directory containing payload. +use pvisor_vm::api::{OverlayConfig, PermissionSemantics, VmBuilder, VmConfiguration, VmRuntime}; +use std::path::PathBuf; + +fn main() -> Result<(), Box> { + let args: Vec<_> = std::env::args_os().skip(1).collect(); + if args.len() != 3 { + return Err("expected ROOTFS FIRMWARE IMMUTABLE_DIRECTORY".into()); + } + // Keep the prepared rootfs unchanged while the supervisor initializes /dev + // and /proc. This writable root is separate from the immutable DAX share. + let stage = tempfile::tempdir()?; + let upper = stage.path().join("upper"); + let work = stage.path().join("work"); + std::fs::create_dir(&upper)?; + std::fs::create_dir(&work)?; + let mut vm = VmBuilder::new(2, 512)?; + vm.set_firmware_path(PathBuf::from(&args[1]))?; + vm.overlay( + "/dev/root", + OverlayConfig { + lower_dirs: vec![PathBuf::from(&args[0])], + lower_mutability: vec![], + upper_dir: upper, + work_dir: Some(work), + preimage_dir: None, + apply_target: None, + baseline_lower: None, + baseline_content_index: None, + excluded_paths: vec![], + access_policy: Default::default(), + semantics: PermissionSemantics::LinuxComplete, + }, + 0, + )?; + // Deliberately smaller than payload: exercise window eviction/reuse too. + vm.immutable_filesystem("pvisor-dax", &PathBuf::from(&args[2]), 2 << 20)?; + vm.virtual_file( + "pvisor-dax", + "/virtual", + b"virtual bytes".to_vec(), + 0o400, + false, + )?; + let script = r#" +import concurrent.futures,ctypes,errno,mmap,os +c=ctypes.CDLL(None,use_errno=True) +def mount(src,dst,kind,flags,data): + if c.mount(src,dst,kind,ctypes.c_ulong(flags),data)!=0: raise OSError(ctypes.get_errno(),dst) +mount(b'tmpfs',b'/tmp',b'tmpfs',0,None) +os.mkdir('/tmp/dax') +mount(b'pvisor-dax',b'/tmp/dax',b'virtiofs',1,b'dax=inode') +p=b'/tmp/dax/payload' +s=ctypes.create_string_buffer(256) +assert c.statx(-100,p,0,0x7ff,s)==0 +assert int.from_bytes(s.raw[8:16],'little') & 0x200000, 'STATX_ATTR_DAX absent' +assert c.statx(-100,b'/tmp/dax/virtual',0,0x7ff,s)==0 +assert not int.from_bytes(s.raw[8:16],'little') & 0x200000, 'virtual file marked DAX' +with open('/tmp/dax/virtual','rb') as v: assert v.read()==b'virtual bytes' +f=os.open(p,os.O_RDONLY) +size=os.fstat(f).st_size +assert size>3*(2<<20), 'payload must exceed the DAX window' +for off in range(0,size,65536): + b=os.pread(f,min(65536,size-off),off) + assert b==bytes((i%251 for i in range(off,off+len(b)))) +def read_range(off): + b=os.pread(f,min(65536,size-off),off) + assert b==bytes((i%251 for i in range(off,off+len(b)))) +with concurrent.futures.ThreadPoolExecutor(max_workers=4) as workers: + list(workers.map(read_range,range(0,size,65536))) +with mmap.mmap(f,0,access=mmap.ACCESS_COPY) as m: + m[0]=0xff + assert m[0]==0xff and os.pread(f,1,0)==b'\0' +with mmap.mmap(f,0,access=mmap.ACCESS_READ) as m: + os.close(f) + for off in range(0,size,4096): assert m[off]==off%251 + assert m[-1]==(size-1)%251 +try: + os.open(p,os.O_WRONLY) + raise AssertionError('write accepted') +except OSError as e: assert e.errno==errno.EROFS +assert c.umount(b'/tmp/dax')==0 +print('DAX_CASE_PASS bytes='+str(size),flush=True) +"#; + let config = serde_json::json!({ + "argv": ["/usr/bin/python3", "-B", "-u", "-c", script], + "env": {"PYTHONDONTWRITEBYTECODE": "1"}, + "cwd": "/" + }); + vm.virtual_file( + "/dev/root", + "/.pvisor-guest.json", + serde_json::to_vec(&config)?, + 0o400, + true, + )?; + vm.run(|_| Ok(()))?; + Ok(()) +} diff --git a/crates/pvisor-vm/src/vmm/builder.rs b/crates/pvisor-vm/src/vmm/builder.rs index f138ccbc2..b838a2ac2 100644 --- a/crates/pvisor-vm/src/vmm/builder.rs +++ b/crates/pvisor-vm/src/vmm/builder.rs @@ -668,6 +668,25 @@ pub fn build_microvm_for_arch( #[allow(unused_mut)] let mut vm = setup_vm(&guest_memory, vm_resources.nested_enabled)?; + #[cfg(all( + target_os = "linux", + target_arch = "x86_64", + not(any(feature = "tee", feature = "aws-nitro")) + ))] + for (index, config) in vm_resources.fs.iter().enumerate() { + if config.read_only { + if let Some(window) = _shm_manager.fs_region(index) { + vm.protect_readonly_window( + &guest_memory, + window.guest_addr.raw_value(), + window.size, + ) + .map_err(Error::Vm) + .map_err(StartMicrovmError::Internal)?; + } + } + } + #[cfg(feature = "tee")] let (_kvm, vm) = { let kvm = KvmContext::new() @@ -1160,6 +1179,10 @@ pub fn build_microvm_for_arch( attach_fs_devices( &mut vmm, &vm_resources.fs, + vm_resources + .machine_restore + .as_ref() + .map(|restore| restore.state.devices.as_slice()), &mut _shm_manager, #[cfg(not(feature = "tee"))] export_table, @@ -2188,9 +2211,11 @@ fn attach_mmio_device( } #[cfg(not(any(feature = "tee", feature = "aws-nitro")))] +#[cfg_attr(target_os = "macos", allow(clippy::too_many_arguments))] fn attach_fs_devices( vmm: &mut Vmm, fs_devs: &[FsDeviceConfig], + restored_devices: Option<&[crate::devices::snapshot::BusMappingSnapshot]>, shm_manager: &mut ShmManager, #[cfg(not(feature = "tee"))] export_table: Option, intc: IrqChip, @@ -2213,6 +2238,31 @@ fn attach_fs_devices( .unwrap(), )); + if let Some(devices) = restored_devices { + let mut saved_count = None; + for mapping in devices { + if let crate::devices::snapshot::BusDeviceSnapshot::Virtio(state) = &mapping.device + { + if let Some(count) = state + .filesystem_request_queues(config.fs_id.as_bytes()) + .map_err(StartMicrovmError::GuestMemoryMmap)? + { + if saved_count.replace(count).is_some() { + return Err(StartMicrovmError::GuestMemoryMmap( + "duplicate filesystem snapshot tag".into(), + )); + } + } + } + } + if let Some(count) = saved_count { + fs.lock() + .unwrap() + .configure_request_queues(count) + .map_err(StartMicrovmError::GuestMemoryMmap)?; + } + } + let id = format!("{}{}", String::from(fs.lock().unwrap().id()), i); if let Some(shm_region) = shm_manager.fs_region(i) { diff --git a/crates/pvisor-vm/src/vmm/linux/vstate.rs b/crates/pvisor-vm/src/vmm/linux/vstate.rs index 4e62c4b8f..9e9915eb6 100644 --- a/crates/pvisor-vm/src/vmm/linux/vstate.rs +++ b/crates/pvisor-vm/src/vmm/linux/vstate.rs @@ -659,6 +659,47 @@ impl Vm { &self.supported_msrs } + /// Protects a complete DAX window before any vCPU starts running. + #[cfg(not(feature = "tee"))] + pub(crate) fn protect_readonly_window( + &self, + guest_mem: &GuestMemoryMmap, + start: u64, + size: usize, + ) -> Result<()> { + // Only called immediately after initial memory registration, before + // vCPUs or additional slots exist. DAX windows each own a whole region. + let (slot, region) = guest_mem + .iter() + .enumerate() + .find(|(_, region)| { + region.start_addr().raw_value() == start && region.len() == size as u64 + }) + .ok_or(Error::NotEnoughMemorySlots)?; + if self.next_mem_slot as usize != guest_mem.num_regions() { + return Err(Error::NotEnoughMemorySlots); + } + let mapping = kvm_userspace_memory_region { + slot: slot as u32, + flags: kvm_bindings::KVM_MEM_READONLY, + guest_phys_addr: start, + memory_size: region.len(), + userspace_addr: guest_mem.get_host_address(region.start_addr()).unwrap() as u64, + }; + // READONLY avoids KVM requesting writable pins (and causing COW) on + // normal reads. Physical guest stores become MMIO exits, not file writes. + // KVM rejects changing READONLY on an existing slot. No vCPU exists + // yet, so unregister and reinstall this same window atomically with + // respect to guest execution. On failure the fresh VM is discarded. + let empty = kvm_userspace_memory_region { + memory_size: 0, + flags: 0, + ..mapping + }; + unsafe { self.fd.set_user_memory_region(empty) }.map_err(Error::SetUserMemoryRegion)?; + unsafe { self.fd.set_user_memory_region(mapping) }.map_err(Error::SetUserMemoryRegion) + } + /// Initializes the guest memory. pub fn memory_init( &mut self, @@ -2197,6 +2238,90 @@ mod tests { assert!(vm.memory_init(&gm, kvm_context.max_memslots()).is_err()); } + /// Guest reads must share file pages; guest physical stores must exit to + /// userspace instead of silently creating private copies in the DAX window. + #[cfg(all(target_arch = "x86_64", not(feature = "tee")))] + #[test] + fn readonly_dax_slot_shares_reads_and_traps_physical_writes() { + use std::io::Write; + use std::os::fd::AsRawFd; + use std::os::unix::fs::FileExt; + use vm_memory::Bytes; + + let kvm = KvmContext::new().unwrap(); + let mut vm = Vm::new(kvm.fd()).unwrap(); + let memory = + GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 4096), (GuestAddress(0x2000), 4096)]) + .unwrap(); + vm.memory_init(&memory, kvm.max_memslots()).unwrap(); + // Partial/unknown windows cannot change RAM slot protection. + assert!(vm.protect_readonly_window(&memory, 0x2000, 2048).is_err()); + vm.protect_readonly_window(&memory, 0x2000, 4096).unwrap(); + let mut file = tempfile::tempfile().unwrap(); + file.write_all(&vec![0x5a; 4096]).unwrap(); + let address = memory.get_host_address(GuestAddress(0x2000)).unwrap(); + // Mirrors the host mapping protection, including the private COW + // fallback for host descriptor access to guest physical memory. + assert_ne!( + unsafe { + libc::mmap( + address.cast(), + 4096, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_FIXED, + file.as_raw_fd(), + 0, + ) + }, + libc::MAP_FAILED + ); + // Real mode: MOV AL,[0x2000]; OUT 0x80,AL; + // MOV BYTE [0x2000],0x11; HLT. + memory + .write_slice( + &[ + 0xa0, 0x00, 0x20, 0xe6, 0x80, 0xc6, 0x06, 0x00, 0x20, 0x11, 0xf4, + ], + GuestAddress(0), + ) + .unwrap(); + let mut cpu = vm.fd().create_vcpu(0).unwrap(); + let mut sregs = cpu.get_sregs().unwrap(); + sregs.cs.base = 0; + sregs.cs.selector = 0; + sregs.ds.base = 0; + sregs.ds.selector = 0; + cpu.set_sregs(&sregs).unwrap(); + cpu.set_regs(&kvm_regs { + rip: 0, + rflags: 2, + ..Default::default() + }) + .unwrap(); + match cpu.run().unwrap() { + VcpuExit::IoOut(0x80, data) => assert_eq!(data, &[0x5a]), + other => panic!("unexpected DAX read exit: {other:?}"), + } + // pagemap's file bit is readable without disclosing physical addresses. + let pagemap = std::fs::File::open("/proc/self/pagemap").unwrap(); + let mut entry = [0; 8]; + pagemap + .read_exact_at(&mut entry, (address as u64 / 4096) * 8) + .unwrap(); + let entry = u64::from_le_bytes(entry); + assert_ne!(entry & (1 << 63), 0, "DAX page is not resident"); + assert_ne!(entry & (1 << 61), 0, "KVM read caused a private COW page"); + match cpu.run().unwrap() { + VcpuExit::MmioWrite(0x2000, data) => assert_eq!(data, &[0x11]), + other => panic!("unexpected DAX physical write exit: {other:?}"), + } + assert!(matches!(cpu.run().unwrap(), VcpuExit::Hlt)); + assert_eq!(memory.read_obj::(GuestAddress(0x2000)).unwrap(), 0x5a); + let mut byte = [0]; + file.read_exact_at(&mut byte, 0).unwrap(); + assert_eq!(byte, [0x5a]); + } + #[cfg(target_arch = "x86_64")] #[test] fn test_configure_vcpu() { diff --git a/crates/pvisor-vm/tests/api_contract.rs b/crates/pvisor-vm/tests/api_contract.rs index d895880f4..078d49f3d 100644 --- a/crates/pvisor-vm/tests/api_contract.rs +++ b/crates/pvisor-vm/tests/api_contract.rs @@ -5,6 +5,68 @@ use pvisor_vm::api::{ }; use syn::visit::{self, Visit}; +#[test] +fn immutable_dax_configuration_validates_before_attachment_and_rejects_snapshots() { + let root = tempfile::tempdir().unwrap(); + let mut builder = VmBuilder::new(1, 128).unwrap(); + #[cfg(all( + target_os = "linux", + target_arch = "x86_64", + not(any(feature = "tee", feature = "aws-nitro")) + ))] + { + for size in [0, 4096, (2 << 20) + 4096, (1 << 30) + (2 << 20)] { + assert_eq!( + builder + .immutable_filesystem("data", root.path(), size) + .unwrap_err() + .kind(), + std::io::ErrorKind::InvalidInput + ); + } + assert_eq!( + builder + .immutable_filesystem("data", std::path::Path::new("relative"), 2 << 20) + .unwrap_err() + .kind(), + std::io::ErrorKind::InvalidInput + ); + builder + .immutable_filesystem("data", root.path(), 2 << 20) + .unwrap(); + assert!(builder + .immutable_filesystem("data", root.path(), 2 << 20) + .is_err()); + assert_eq!( + builder.snapshot_profile().unwrap_err().kind(), + std::io::ErrorKind::Unsupported + ); + builder.filesystem("ordinary", root.path(), 0).unwrap(); + let mut snapshots = VmBuilder::new(1, 128).unwrap(); + snapshots.snapshot_profile().unwrap(); + assert_eq!( + snapshots + .immutable_filesystem("data", root.path(), 2 << 20) + .unwrap_err() + .kind(), + std::io::ErrorKind::Unsupported + ); + snapshots.filesystem("data", root.path(), 0).unwrap(); + } + #[cfg(not(all( + target_os = "linux", + target_arch = "x86_64", + not(any(feature = "tee", feature = "aws-nitro")) + )))] + assert_eq!( + builder + .immutable_filesystem("data", root.path(), 2 << 20) + .unwrap_err() + .kind(), + std::io::ErrorKind::Unsupported + ); +} + #[test] fn api_is_unconditional_and_contains_no_implementation() { struct Guard; diff --git a/crates/pvisor/src/image/cache/client.rs b/crates/pvisor/src/image/cache/client.rs index 5fc1c772b..24f0897b2 100644 --- a/crates/pvisor/src/image/cache/client.rs +++ b/crates/pvisor/src/image/cache/client.rs @@ -1531,9 +1531,12 @@ pub(super) mod tests { }, ) .unwrap(); - socket - .write_all(if failure == 3 { b"a" } else { b"bad" }) - .unwrap(); + // An oversized declaration is rejected before the body. + if failure != 4 { + socket + .write_all(if failure == 3 { b"a" } else { b"bad" }) + .unwrap(); + } } } drop(socket); @@ -1559,7 +1562,10 @@ pub(super) mod tests { length: 3, } }; - assert!(client.request(request).is_err()); + let error = client.request(request).unwrap_err(); + if failure == 4 { + assert!(error.to_string().contains("invalid cache data length")); + } let CacheTransport::Server { pool, .. } = &client.transport else { panic!() }; diff --git a/docs/README.md b/docs/README.md index 753dfa08e..a4c8fba6d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -37,6 +37,40 @@ tables for labels and nested arrays for sections and groups, without repeated Python build wrapper, source copy, temporary configuration, HTML rewriting, custom search index or custom HTTP server is involved. +`docs.semspec_tables` is a Python-Markdown preprocessor registered in both native +configurations. A standalone `{{ table("file.render.json") }}` line loads a JSON +table relative to its article and becomes ordinary Markdown before HTML rendering; +the source file retains the placeholder. `just docs-build` and `just docs-serve` +build the standalone semspec CLI and set `SEMSPEC_BIN` and `PYTHONPATH` for the +extension. The documentation recipes therefore also require the Rust toolchain. +Rendering reads completed results only. Run `just semspec bench path/to/in.json` +explicitly to execute a template and create `in.render.json` plus raw evidence in +`in.bench.json`. See [semspec's table format](../tools/semspec/README.md#executable-benchmark-tables). +Fixed historical benchmark tables are written directly in Markdown, retaining +their measured values, statistical scope and source links. Tables updated by +benchmark publication and the generated measurement-coverage table use JSON; +their bindings are registered in +[`benchmark.new/docs.json`](../benchmark.new/docs.json). Run +`python3 benchmark.new/docs.py check` before building. Historical tables retain +their original statistics and claims; only explicit live bindings are updated +by `python3 benchmark.new/docs.py publish BATCH`. Links/code in dynamic JSON +tables opt into `markdown: true`, with `row_names: false` when the first column +is already present. Semspec validates optional alignments and displayed headers. + +Native `watch` entries track benchmark data files for preview rebuilds. Add table +data stored in other directories to the locale configuration's `watch` list. +`docs-build` also uses Zensical's `--clean` to discard cached table expansions. + +`docs-serve` sets `PVISOR_DOCS_LIVE=1` and renders tables with semspec's +`--best-effort`: missing, malformed and unresolved tables display warnings while +valid tables and page text remain available. Errors also appear in the terminal. +After creating a previously missing table, save its referring Markdown page to +trigger another render if Zensical has not yet registered the new file for watching. +Renderer launch errors or +timeouts leave the page text available with a warning. Preview preflight and +semspec build failures warn and continue with an existing renderer when available. +`docs-build` explicitly disables live mode and retains strict checks. + Zensical has one theme language and search index per project. Keep exactly two native configurations: `zensical.zh.toml` and `zensical.en.toml`, building each locale directly into `site/zh` and `site/en`. `docs/index.html` is a small static @@ -98,6 +132,25 @@ The canonical Chinese documentation uses these sections: - `design/`: implementation ownership, mechanisms, ADRs and research directions. - `community/`: contributing, development, testing, release, roadmap and policies. +Experimental feature documents live under `design/experimental/` in both locale +trees and appear in the Design and research tab. Keep the feature registry, +designs, implementation plans and validation records there. Each feature has +one `index.md` combining its status, design, implementation plan and validation +records; do not split a feature into separate articles. The experimental registry +retains its own `index.md`. Do not create a separate repository-root `experimental/` +documentation tree. Feature code stays in its owning crate or `fw/`, and raw +performance evidence stays with its registered benchmark. Preserve experimental +IDs, platform/profile status and the distinction between proposals, implemented +prototypes and accepted capabilities when moving or updating these articles. + +Organize each experimental feature's main design into three top-level sections: +Problem statement, Core design, and Alternatives and comparison +(问题提出、核心设计、方案讨论和对比). Establish the problem and current gaps +before proposing mechanisms; compare routes, tradeoffs and decision evidence +after explaining the core design. Keep protocol, authority and lifecycle details +under these sections. Nest implementation plans and validation records under +Core design while retaining their milestone and evidence structures. + Readers and entry points: | Reader | Entry | Main path | @@ -120,7 +173,16 @@ network policy guide owns the executor boundary matrix (summarized in observations. Other articles link to these definitions. Implementation pages explain mechanisms and code ownership. Benchmark and comparison pages state method, environment and date, and keep dated raw samples in local `.data/` directories. -Only derived Markdown tables and same-directory CSV downloads are versioned. +Derived Markdown tables, resolved JSON tables and same-directory CSV downloads +are versioned. Public benchmark provenance summaries live once under +`docs/overrides/assets/benchmarks/`; both languages link to their local +`assets/benchmarks/` copies published by Zensical. Keep the batch, measurement +scope, configuration and input/binary/evidence hashes in these summaries. +Full manifests, per-file harness records, host paths and complete package receipts +remain in the benchmark's ignored `.data/`. Publishers update the shared summary +and only the JSON projections referenced by articles. +Keep executable benchmark templates and raw `*.bench.json` +evidence outside the published article tree; articles reference resolved JSON. The native `exclude` plugin excludes `.data/` at every depth, and the generated site checker rejects leaked evidence. Zensical publishes the shared theme assets in each locale, including images and JSON downloads, so native previews need no diff --git a/docs/overrides/assets/benchmarks/filesystem-publication-audit.json b/docs/overrides/assets/benchmarks/filesystem-publication-audit.json new file mode 100644 index 000000000..ac1ec552f --- /dev/null +++ b/docs/overrides/assets/benchmarks/filesystem-publication-audit.json @@ -0,0 +1,32 @@ +{ + "format": 1, + "state": "passed", + "benchmark_id": "B-FS-TOOLS", + "role": "user-facing", + "batch": "fo5a4e7d2b-f", + "date": "2026-10-12", + "date_timezone": "Asia/Shanghai", + "formal_rows": 240, + "samples_per_backend": 30, + "warmups_per_backend": 3, + "failed": 0, + "summary_records": 80, + "raw_sha256": "0127dbdd8a04317be06a0235e5b1cec75dc298b3c6e312f22f1382b8d19f7262", + "acceptance_sha256": "45f479ab7709723e781bfd6b78d8ace3dfa2a8914a687173babfd9ee23daf24e", + "csv_sha256": { + "filesystem.csv": "27dc60670d758f4d0f0852e7c3535b08a8ec290e6892a72295901e590b6389aa", + "filesystem-comparisons.csv": "d7063001b2c0c2121f603e5111db50b240e91285209cc10742fd642c5e47670b", + "filesystem-provenance.csv": "e88d4292d73e52a4d243d6a3b7a07404ff1594d027abdbdcca4bac43155925cd" + }, + "defaults": { + "reviewed_from_frozen_source": true, + "new_vm_writeback": false, + "ordinary_dax": false, + "stage_sync_workers": 1 + }, + "timing_scope": "one full reference cohort; complete tasks and seven tool phases; no whole-command probe replacement", + "confidence_method": "5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI", + "separated_clusters": "retain medians/counts; no overall P50/ranking", + "documentation_checks": "accepted input distributions/exports rechecked during preparation; Markdown/HTML consistency requires the separate audit command", + "engineering_cohorts": [] +} diff --git a/docs/overrides/assets/benchmarks/replay-semspec.provenance.json b/docs/overrides/assets/benchmarks/replay-semspec.provenance.json new file mode 100644 index 000000000..021e01bfa --- /dev/null +++ b/docs/overrides/assets/benchmarks/replay-semspec.provenance.json @@ -0,0 +1,37 @@ +{ + "batch": "replay-20261010T194213Z-9zpco2sv", + "benchmark_id": "B-REPLAY", + "role": "user-facing", + "entry": "replay/replay.json", + "recorded_at": "2026-10-10T19:42:13.896344+00:00", + "runs": 60, + "warmup": 0, + "metric_scope": "direct subject command; hyperfine hooks and semspec excluded", + "platform": "Linux-7.2.8-200.fc44.x86_64-x86_64-with-glibc2.43", + "kernel": "7.2.8-200.fc44.x86_64", + "cpu_affinity": [ + 0, + 1 + ], + "source_commit": "b3e2f03509396b8ebe840a9ccdefa63424bc1afe", + "source_commit_scope": "harness worktree; binary digest does not prove source-build correspondence", + "fixture_scope": "one fixed synthetic task 0 per adapter; distinct from historical twenty-task cohort", + "hyperfine_version": "hyperfine 2.0.0", + "exports": { + "in.bench.json": "34f0c4a59ad1612860c382951158167a4fb3a2f45e47dbf635d0dca793105eca", + "in.render.json": "22244571709617b5be20c3d769fc2f6e4c6d9482917ad69ab34ded49ca21b12c" + }, + "manifest_sha256": "202c53d97959bbdf76bb10fd5fb071d22eb2c435f194f48e3372800b02dd1c8e", + "artifacts": { + "pvisor-replay": "675ce7d3ce53bacbf10d6ed7e7df2fbac6d592507435200babb0782822e55275", + "fixtures/claude-code/trajectory.json": "639c918bffe7a61a695b44dc313fd677233333faae5fafe3638a5bcca92f50de", + "fixtures/codex/trajectory.json": "f81e8d7f42b477cbe55a63b89415bc23455a12ad3b000ef69ae564ec6de535ef", + "fixtures/opencode/trajectory.json": "e9e29d0129f973c5073f7964799df27cdc3049a29d42181d309214190227b8b5", + "fixtures/mini-swe-agent/trajectory.json": "29b0b9cb3897efb7e2cc0feec69d11c2a8ffb82ab1c0583cec7f9213ef5dd7a9", + "fixtures/openhands/trajectory.json": "d6daee3a05aaf026786a804a4f07c4aa232a469d7a1339b02cd5b00d8ec5d119", + "fixtures/pi-agent/trajectory.json": "beebfaa9e7264e9e0c576c6040986831fe98f4ee8774a54db41988fdd43c679e", + "fixtures/swe-agent/trajectory.json": "bab27618703c9d3b9718ef9eddcd46faf55e9dfc25ea98c3e69ca9b92b6ac96d", + "protocol.json": "2bd5b02729a6861e57e79b560a4169b2aecbf403d9991632d77da78138e29a2f" + }, + "publisher_sha256": "0aa41588227e9bf2a86f1a358cc7c91056d8b2d605a1bcf3ad9ad3782dbdfa7a" +} diff --git a/docs/overrides/assets/benchmarks/startup-ready.provenance.json b/docs/overrides/assets/benchmarks/startup-ready.provenance.json new file mode 100644 index 000000000..834f43297 --- /dev/null +++ b/docs/overrides/assets/benchmarks/startup-ready.provenance.json @@ -0,0 +1,9 @@ +{ + "pVisor VM": { + "batch": "pvisor_vm-20261010T190923Z-a6pqcxmk", + "metric_scope": "Popen launch to receipt of exact REFERENCE_READY line; perf_counter_ns; preparation and teardown excluded", + "measurement_engine": "python-perf_counter_ns", + "evidence_sha256": "316cd941068ddf87b6d690ba4fdb124a7b7feb0c8b3c3b50179efdf70a78f7c9", + "manifest_sha256": "be31c9369cb2024ec98c04ed10771972c31970ce830e980cbc9e91bc846a1992" + } +} diff --git a/docs/overrides/assets/benchmarks/startup-semspec.provenance.json b/docs/overrides/assets/benchmarks/startup-semspec.provenance.json new file mode 100644 index 000000000..177c93612 --- /dev/null +++ b/docs/overrides/assets/benchmarks/startup-semspec.provenance.json @@ -0,0 +1,37 @@ +{ + "Native": { + "batch": "native-20261010T184227Z-qg9cqk2u", + "evidence_sha256": "1504d0bfb38f01b3e1481d4c58854d4c986ecf5e7fb5b0e4ec1237434b497c4f", + "manifest_sha256": "d312adb83d9dd7a36a163987bc4b2126351170d545e5fa0f1583d94b603e8462" + }, + "pVisor host": { + "batch": "pvisor_host-20261010T184736Z-3xq4m_wt", + "evidence_sha256": "01e92e3c7134054451ab05fff2115608a5538654c6a8ff3f08c1fcf09984c161", + "manifest_sha256": "c14a02b20a9f9794c4158249ff7eb46f8ebda683df147bf055dc2f4d518b9188" + }, + "pVisor staged": { + "batch": "pvisor_staged-20261010T170343Z-kthbiapw", + "evidence_sha256": "a9a90bc0bb92f45906bc95c26693d867aa8a08e7a3d05fbab38672c8fc298b4a", + "manifest_sha256": "985bc6975f3eabbf2673f3bc58f1a38089aa2848f8e44ade7f91897d91990d2e" + }, + "pVisor VM": { + "batch": "pvisor_vm-20261010T184321Z-6f0yo0wi", + "evidence_sha256": "7b9eb8a585dd63280e902e8aefbf2a4a917a74c528341b7440ee9fb839687e91", + "manifest_sha256": "ab2327fd977f75438a97e84cbc7899fcf5be0e27deda3445e55f9107e81d74a9" + }, + "Docker": { + "batch": "docker-20261010T170722Z-t1_bih1h", + "evidence_sha256": "830cff142a2e0fc5b2e3048f21be9baab881202d9d14fee6db52d7171bb6529f", + "manifest_sha256": "dfd4d2226e5135c2b74e528f7ae6bcf2636cf80a244dbe4262ebd497acdc5234" + }, + "QEMU": { + "batch": "qemu-20261010T171009Z-blrhft4p", + "evidence_sha256": "d14ab629a2e5c3bab8c91bf922b8ffb5a4d5d764ce2c6146c9f9897334132ffb", + "manifest_sha256": "f3929e404d8239b906596b2b86144dbe31a3fe10cb7e1c9f4f2b3bac65065ffa" + }, + "QEMU microvm": { + "batch": "qemu_microvm-20261010T171456Z-btvcjqlb", + "evidence_sha256": "318fc8345a5ad21155e992ccd1f51242845a002df72aee3cefdd6ab2bfc17918", + "manifest_sha256": "fae8d591de1055660a02087934500efff85366ef6ad87e5ec96922f74f39bc68" + } +} diff --git a/docs/overrides/assets/benchmarks/startup-timing.provenance.json b/docs/overrides/assets/benchmarks/startup-timing.provenance.json new file mode 100644 index 000000000..8f641b982 --- /dev/null +++ b/docs/overrides/assets/benchmarks/startup-timing.provenance.json @@ -0,0 +1,269 @@ +{ + "Native": { + "batch": "native_timing-20261011T004342Z-8wjyuy21", + "metric_scope": "same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded", + "measurement_engine": "python-perf_counter_ns", + "evidence_sha256": "355c05aa8af734b85978052114fc98485d339ec681f1e1cebc5bd5b317ac4948", + "manifest_sha256": "571e859d1e439e764b42696e5280aa02bdeb4a3cb49797164d54116286d797e0", + "configuration": { + "cpu_affinity": [ + 0, + 1 + ], + "cache": "warm; no host cache eviction", + "source_commit_scope": "harness worktree; binary digest does not prove source-build correspondence", + "artifacts": { + "protocol.json": "3ef56beced1a1cfe490a97bee7abaa4acaec30b92015bdc8d82ece4b8bd7fefb" + }, + "publisher_sha256": "46d249fe87c2879229867500d93f9d568e5aabf861d6ec0d44039893a61c220d" + } + }, + "pVisor host": { + "batch": "pvisor_host_timing-20261011T004343Z-gdenfkzx", + "metric_scope": "same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded", + "measurement_engine": "python-perf_counter_ns", + "evidence_sha256": "b5ec9fd9f67ad90cfa3b5aaa77041a2aad2a3426f82a7afd2c63efeccd954adb", + "manifest_sha256": "768f808263dc663e926b58944b46d1e966d0b700f42a4cdacf91a7e0bd7c27fa", + "configuration": { + "cpu_affinity": [ + 0, + 1 + ], + "cache": "warm; no host cache eviction", + "source_commit_scope": "harness worktree; binary digest does not prove source-build correspondence", + "artifacts": { + "assets.json": "c678eaba542f4e30feb9d033ee8a4af84e955cc6a67bc08e234c0ec22512198d", + "input-manifest.json": "358ca833b32ed7ef60b6333cc873a77a531a9f8076e5419cb57b4e629185013f", + "pvisor": "d3f09687cca60b617629c215fa1359c441ab6ac19f8c63b68f1dcdf7614e6b30", + "protocol.json": "1fac9b4a3f827244454f5d8e44d269339d4de49efc235fd60abb590607553cf7" + }, + "publisher_sha256": "46d249fe87c2879229867500d93f9d568e5aabf861d6ec0d44039893a61c220d" + } + }, + "pVisor staged": { + "batch": "pvisor_staged_timing-20261011T004437Z-0_8_myox", + "metric_scope": "same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded", + "measurement_engine": "python-perf_counter_ns", + "evidence_sha256": "ecad00dd273bd159cd74112059f1a1a09a0bf0a58cf999f596d5a5d7a2027049", + "manifest_sha256": "8a4d6d031779febc3b156bfe29163e68ba7ccaa3f3294ff810cdd913b094e22f", + "configuration": { + "cpu_affinity": [ + 0, + 1 + ], + "cache": "warm; no host cache eviction", + "source_commit_scope": "harness worktree; binary digest does not prove source-build correspondence", + "artifacts": { + "assets.json": "c678eaba542f4e30feb9d033ee8a4af84e955cc6a67bc08e234c0ec22512198d", + "input-manifest.json": "358ca833b32ed7ef60b6333cc873a77a531a9f8076e5419cb57b4e629185013f", + "pvisor": "d3f09687cca60b617629c215fa1359c441ab6ac19f8c63b68f1dcdf7614e6b30", + "protocol.json": "283300340310a364630a2cb623c27e646b49b1400aa44b1b9808c5bdcedfb191" + }, + "publisher_sha256": "46d249fe87c2879229867500d93f9d568e5aabf861d6ec0d44039893a61c220d" + } + }, + "pVisor VM": { + "batch": "pvisor_vm-20261011T004527Z-4o2qaxr9", + "metric_scope": "same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded", + "measurement_engine": "python-perf_counter_ns", + "evidence_sha256": "6f1b2e5eb12b8012bb1f221d94944017c3792625ae27038e489d29808ae43de3", + "manifest_sha256": "a63cb47729c4b6cf1f37e259241a0b5ee1105157e58a1eb5b79f5e8da82db252", + "configuration": { + "cpu_affinity": [ + 0, + 1 + ], + "guest_cpus": 2, + "guest_memory_mib": 128, + "cache": "warm; no host cache eviction", + "source_commit_scope": "harness worktree; binary digest does not prove source-build correspondence", + "artifacts": { + "assets.json": "c678eaba542f4e30feb9d033ee8a4af84e955cc6a67bc08e234c0ec22512198d", + "input-manifest.json": "358ca833b32ed7ef60b6333cc873a77a531a9f8076e5419cb57b4e629185013f", + "pvisor": "d3f09687cca60b617629c215fa1359c441ab6ac19f8c63b68f1dcdf7614e6b30", + "protocol.json": "4ad73228a7567a5099d6ec8712e4c2e9e8fe17be21017f03dda6b7122b0943cd" + }, + "publisher_sha256": "46d249fe87c2879229867500d93f9d568e5aabf861d6ec0d44039893a61c220d" + } + }, + "Docker": { + "batch": "docker_timing-20261011T004624Z-8aq0yusi", + "metric_scope": "same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded", + "measurement_engine": "python-perf_counter_ns", + "evidence_sha256": "9df027d8154142787594d86cd7572465957be3513333dd83c734debd50558c10", + "manifest_sha256": "aea46ba225d18a1fd202e2460933d809a691636e33b0346319fcd4cc01eeb8d5", + "configuration": { + "cpu_affinity": [ + 0, + 1 + ], + "cache": "warm; no host cache eviction", + "source_commit_scope": "harness worktree; binary digest does not prove source-build correspondence", + "artifacts": { + "assets.json": "c678eaba542f4e30feb9d033ee8a4af84e955cc6a67bc08e234c0ec22512198d", + "input-manifest.json": "358ca833b32ed7ef60b6333cc873a77a531a9f8076e5419cb57b4e629185013f", + "docker-import.json": "f79716a9a03dca9c30854458052c6652b6d98c42f219125a22f50677d769802f", + "docker": "1e4ba89993b43d6ad35c0c7fad3192a1ec6eb268037768023c570ae6fadc8d38", + "protocol.json": "1ebc89f9512f3783fbf1ee2b13d53c3173a8286017efdf10e1c9e80977c792ba" + }, + "publisher_sha256": "46d249fe87c2879229867500d93f9d568e5aabf861d6ec0d44039893a61c220d" + } + }, + "QEMU": { + "batch": "qemu_timing-20261011T004840Z-vh5l4h7y", + "metric_scope": "same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded", + "measurement_engine": "python-perf_counter_ns", + "evidence_sha256": "7d8b028d8df05ff7b3cc881e9d30b998fb55de23b51c29329969ed1b86ce1ae0", + "manifest_sha256": "1431f57af3f73fac2331982e186a0d61ba8de88d7f07e1213af23751a1c1fad9", + "configuration": { + "cpu_affinity": [ + 0, + 1 + ], + "guest_cpus": 2, + "guest_memory_mib": 128, + "kernel": { + "receipt_sha256": "b9f102941262a6671f52e5cb0f361df5b8a45192b8e54e71ad00d6fd559ed350", + "variant": "fc-system", + "provenance": { + "distribution": "Fedora Linux 44", + "package": "kernel-core", + "package_version": "7.2.8-200.fc44.x86_64", + "source_url": "https://src.fedoraproject.org/rpms/kernel", + "origin": "official-distro-stock", + "pvisor_firmware": false, + "initrd": "minimal stock-module loader; not host systemd initramfs" + }, + "artifacts": { + "config": "de2947fbecd9addbf0612ba44a963e082bb34a35a552184b10a00e62690756f8", + "extractor": "f81eb1706b6be6fe452d6dfa019b8b3b06030e27316a64c5e16c267a40584126", + "initrd": "9da85569fdccde17f235407a1cd87a6fcaae25c89f90c47de30239c4f78c9069", + "kernel": "50a85e7c4c13307eb8a9ef79873e550ee21ef53a91c45f9b69cabc5c91ad2703", + "provenance": "0dd26dab29b058294df69a271a5737ce48f2a17a69e7cf6472be969a1a7d039a", + "vmlinuz": "2e8f2993295da228471a6e6849469b40be4513303f413029f62e565d44d4fc47" + } + }, + "cache": "warm; no host cache eviction", + "source_commit_scope": "harness worktree; binary digest does not prove source-build correspondence", + "artifacts": { + "assets.json": "c678eaba542f4e30feb9d033ee8a4af84e955cc6a67bc08e234c0ec22512198d", + "input-manifest.json": "358ca833b32ed7ef60b6333cc873a77a531a9f8076e5419cb57b4e629185013f", + "kernel-receipt.json": "b9f102941262a6671f52e5cb0f361df5b8a45192b8e54e71ad00d6fd559ed350", + "config": "de2947fbecd9addbf0612ba44a963e082bb34a35a552184b10a00e62690756f8", + "extractor": "f81eb1706b6be6fe452d6dfa019b8b3b06030e27316a64c5e16c267a40584126", + "initrd": "9da85569fdccde17f235407a1cd87a6fcaae25c89f90c47de30239c4f78c9069", + "kernel": "50a85e7c4c13307eb8a9ef79873e550ee21ef53a91c45f9b69cabc5c91ad2703", + "provenance": "0dd26dab29b058294df69a271a5737ce48f2a17a69e7cf6472be969a1a7d039a", + "vmlinuz": "2e8f2993295da228471a6e6849469b40be4513303f413029f62e565d44d4fc47", + "qemu-system-x86_64": "27cd395848940fc6482256d85096fc64bc4fe3f3e909824d51c202f8314cd9e9", + "protocol.json": "ca51aabc0036cc5451493541d26def13cea178132d599429d855a9223ae8b163" + }, + "publisher_sha256": "46d249fe87c2879229867500d93f9d568e5aabf861d6ec0d44039893a61c220d" + } + }, + "QEMU microvm": { + "batch": "qemu_microvm_timing-20261011T005005Z-hra4_tyy", + "metric_scope": "same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded", + "measurement_engine": "python-perf_counter_ns", + "evidence_sha256": "eb2c5c6f18620ac4f0f30e1d67aa47aa1fcaa223f6b050ad70ec9982f6a3b457", + "manifest_sha256": "d5c74b3517c2b8ee545c4a274a8cea36e93d9f3c6b4b9153d0ea7ab4ce1d974a", + "configuration": { + "cpu_affinity": [ + 0, + 1 + ], + "guest_cpus": 2, + "guest_memory_mib": 128, + "kernel": { + "receipt_sha256": "b9f102941262a6671f52e5cb0f361df5b8a45192b8e54e71ad00d6fd559ed350", + "variant": "fc-system", + "provenance": { + "distribution": "Fedora Linux 44", + "package": "kernel-core", + "package_version": "7.2.8-200.fc44.x86_64", + "source_url": "https://src.fedoraproject.org/rpms/kernel", + "origin": "official-distro-stock", + "pvisor_firmware": false, + "initrd": "minimal stock-module loader; not host systemd initramfs" + }, + "artifacts": { + "config": "de2947fbecd9addbf0612ba44a963e082bb34a35a552184b10a00e62690756f8", + "extractor": "f81eb1706b6be6fe452d6dfa019b8b3b06030e27316a64c5e16c267a40584126", + "initrd": "9da85569fdccde17f235407a1cd87a6fcaae25c89f90c47de30239c4f78c9069", + "kernel": "50a85e7c4c13307eb8a9ef79873e550ee21ef53a91c45f9b69cabc5c91ad2703", + "provenance": "0dd26dab29b058294df69a271a5737ce48f2a17a69e7cf6472be969a1a7d039a", + "vmlinuz": "2e8f2993295da228471a6e6849469b40be4513303f413029f62e565d44d4fc47" + } + }, + "cache": "warm; no host cache eviction", + "source_commit_scope": "harness worktree; binary digest does not prove source-build correspondence", + "artifacts": { + "assets.json": "c678eaba542f4e30feb9d033ee8a4af84e955cc6a67bc08e234c0ec22512198d", + "input-manifest.json": "358ca833b32ed7ef60b6333cc873a77a531a9f8076e5419cb57b4e629185013f", + "kernel-receipt.json": "b9f102941262a6671f52e5cb0f361df5b8a45192b8e54e71ad00d6fd559ed350", + "config": "de2947fbecd9addbf0612ba44a963e082bb34a35a552184b10a00e62690756f8", + "extractor": "f81eb1706b6be6fe452d6dfa019b8b3b06030e27316a64c5e16c267a40584126", + "initrd": "9da85569fdccde17f235407a1cd87a6fcaae25c89f90c47de30239c4f78c9069", + "kernel": "50a85e7c4c13307eb8a9ef79873e550ee21ef53a91c45f9b69cabc5c91ad2703", + "provenance": "0dd26dab29b058294df69a271a5737ce48f2a17a69e7cf6472be969a1a7d039a", + "vmlinuz": "2e8f2993295da228471a6e6849469b40be4513303f413029f62e565d44d4fc47", + "qemu-system-x86_64": "27cd395848940fc6482256d85096fc64bc4fe3f3e909824d51c202f8314cd9e9", + "protocol.json": "ddc637ed77e0f68a2a993fe629d8d14ff18649546b0ade1ac61095054eb3b7e7" + }, + "publisher_sha256": "46d249fe87c2879229867500d93f9d568e5aabf861d6ec0d44039893a61c220d" + } + }, + "Firecracker": { + "batch": "fc_system_timing-20261011T004712Z-97n8gne7", + "metric_scope": "same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded", + "measurement_engine": "python-perf_counter_ns", + "evidence_sha256": "b0343f5973991391c32b67b4a8431a5e992c80cb6c403338d3dc4965e7ce2251", + "manifest_sha256": "107c830711205462c88ae27f0de0e1088ffee1176a5f9842d483dacc1e8413f3", + "configuration": { + "cpu_affinity": [ + 0, + 1 + ], + "guest_cpus": 2, + "guest_memory_mib": 128, + "device_transport": "virtio-mmio", + "kernel": { + "receipt_sha256": "b9f102941262a6671f52e5cb0f361df5b8a45192b8e54e71ad00d6fd559ed350", + "variant": "fc-system", + "provenance": { + "distribution": "Fedora Linux 44", + "package": "kernel-core", + "package_version": "7.2.8-200.fc44.x86_64", + "source_url": "https://src.fedoraproject.org/rpms/kernel", + "origin": "official-distro-stock", + "pvisor_firmware": false, + "initrd": "minimal stock-module loader; not host systemd initramfs" + }, + "artifacts": { + "config": "de2947fbecd9addbf0612ba44a963e082bb34a35a552184b10a00e62690756f8", + "extractor": "f81eb1706b6be6fe452d6dfa019b8b3b06030e27316a64c5e16c267a40584126", + "initrd": "9da85569fdccde17f235407a1cd87a6fcaae25c89f90c47de30239c4f78c9069", + "kernel": "50a85e7c4c13307eb8a9ef79873e550ee21ef53a91c45f9b69cabc5c91ad2703", + "provenance": "0dd26dab29b058294df69a271a5737ce48f2a17a69e7cf6472be969a1a7d039a", + "vmlinuz": "2e8f2993295da228471a6e6849469b40be4513303f413029f62e565d44d4fc47" + } + }, + "cache": "warm; no host cache eviction", + "source_commit_scope": "harness worktree; binary digest does not prove source-build correspondence", + "artifacts": { + "assets.json": "c678eaba542f4e30feb9d033ee8a4af84e955cc6a67bc08e234c0ec22512198d", + "input-manifest.json": "358ca833b32ed7ef60b6333cc873a77a531a9f8076e5419cb57b4e629185013f", + "kernel-receipt.json": "b9f102941262a6671f52e5cb0f361df5b8a45192b8e54e71ad00d6fd559ed350", + "config": "de2947fbecd9addbf0612ba44a963e082bb34a35a552184b10a00e62690756f8", + "extractor": "f81eb1706b6be6fe452d6dfa019b8b3b06030e27316a64c5e16c267a40584126", + "initrd": "9da85569fdccde17f235407a1cd87a6fcaae25c89f90c47de30239c4f78c9069", + "kernel": "50a85e7c4c13307eb8a9ef79873e550ee21ef53a91c45f9b69cabc5c91ad2703", + "provenance": "0dd26dab29b058294df69a271a5737ce48f2a17a69e7cf6472be969a1a7d039a", + "vmlinuz": "2e8f2993295da228471a6e6849469b40be4513303f413029f62e565d44d4fc47", + "firecracker": "c41309a2452e05149c5183e867c4d34b9b8d07e2885673d465714e238ad57d98", + "protocol.json": "0a0b44b39041776d5b1bc198f964b8f24a23a1c4c0487996588c68508087c85e" + }, + "publisher_sha256": "46d249fe87c2879229867500d93f9d568e5aabf861d6ec0d44039893a61c220d" + } + } +} diff --git a/docs/semspec_tables.py b/docs/semspec_tables.py new file mode 100644 index 000000000..73292607b --- /dev/null +++ b/docs/semspec_tables.py @@ -0,0 +1,73 @@ +"""Read-only semspec table preprocessing for the native Zensical renderer.""" + +import html +import logging +import os +import subprocess +from pathlib import Path + +from markdown import Extension +from markdown.preprocessors import Preprocessor +from zensical.extensions.context import ContextPreprocessor + +LOGGER = logging.getLogger(__name__) + + +class TablePreprocessor(Preprocessor): + """Pass Markdown to semspec before tables and fenced blocks are parsed.""" + + def run(self, lines): + text = "\n".join(lines) + if "{{ table(" not in text: + return lines + live = os.environ.get("PVISOR_DOCS_LIVE") == "1" + try: + return self.render(text, live) + except (RuntimeError, OSError, subprocess.TimeoutExpired) as error: + if not live: + raise + LOGGER.warning("Table rendering unavailable during live preview: %s", error) + return [ + '!!! warning "Table rendering unavailable during live preview"', + "", + " " + html.escape(str(error)).replace("\n", "
"), + "", + *lines, + ] + + def render(self, text, live): + context = ContextPreprocessor.from_markdown(self.md) + if context is None: + raise RuntimeError("semspec tables require Zensical page context") + root = Path(context.config.get("root_dir", ".")).resolve() + docs_dir = root / context.config["docs_dir"] + source = docs_dir / context.page.path + command = [os.environ.get("SEMSPEC_BIN", "semspec"), "render", str(source), "--stdin"] + if live: + command.append("--best-effort") + result = subprocess.run( + command, + input=text, + text=True, + encoding="utf-8", + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + check=False, + timeout=30, + ) + if result.returncode: + raise RuntimeError(f"semspec render failed for {source}: {result.stderr.strip()}") + if result.stderr.strip(): + LOGGER.warning("%s", result.stderr.strip()) + return result.stdout.split("\n") + + +class TableExtension(Extension): + """Register source substitution ahead of Python-Markdown parsing.""" + + def extendMarkdown(self, md): + md.preprocessors.register(TablePreprocessor(md), "semspec_tables", 35) + + +def makeExtension(**kwargs): + return TableExtension(**kwargs) diff --git a/docs/src/en/benchmarks/agent-tasks.md b/docs/src/en/benchmarks/agent-tasks.md index 81a6a6ae2..1c55a3ffb 100644 --- a/docs/src/en/benchmarks/agent-tasks.md +++ b/docs/src/en/benchmarks/agent-tasks.md @@ -11,6 +11,12 @@ | Independent guest kernel | Validate CLI compatibility and complete tool cost before choosing VM | | Remote API and managed elasticity | Evaluate clouds by repository synchronization, deployment and operations needs; matched performance and billing are unmeasured | + + +The numbers and conclusions on this page still belong to the original historical cohorts. The new framework has no matching rerun yet; see [measurement coverage](index.md#measurement-coverage). + + + ## Motivation {#motivation} A task includes environment preparation, model/tool interaction, verification, review/application and cleanup. Startup alone misses tool and result-handling costs; tool speed alone misses repeated workspace creation. A fixed tool plan removes inference and public-network variability, while a separate complete review workflow measures how the same edits reach the original workspace. diff --git a/docs/src/en/benchmarks/cluster-scalability.md b/docs/src/en/benchmarks/cluster-scalability.md index 6fe77cb27..290f2416c 100644 --- a/docs/src/en/benchmarks/cluster-scalability.md +++ b/docs/src/en/benchmarks/cluster-scalability.md @@ -10,6 +10,12 @@ | Size local execution or parked states | Use [capacity](density.md) and [VM memory](vm-memory/index.md), not Worker curves | | Size the daemon or compare schedulers | Unmeasured; do not transfer old throughput or history figures | + + +The numbers and conclusions on this page still belong to the original historical cohorts. The new framework has no matching rerun yet; see [measurement coverage](index.md#measurement-coverage). + + + ## Motivation {#motivation} Capacity decisions require evidence for the implementation actually deployed. Retiring Controller/Worker removes the subject of these experiments; renaming its measurements would conceal that boundary. Readiness, validated work and recoverable parked states answer different questions. diff --git a/docs/src/en/benchmarks/compare-rl-infra.md b/docs/src/en/benchmarks/compare-rl-infra.md index 5a4f259cf..9b4124e54 100644 --- a/docs/src/en/benchmarks/compare-rl-infra.md +++ b/docs/src/en/benchmarks/compare-rl-infra.md @@ -11,6 +11,12 @@ | Historical context or uncached images | Evaluate prefix preparation or lazy reads within their measured scope | | Existing RL stack | Keep task, model and training layers; dedicated integrations remain unvalidated | + + +The numbers and conclusions on this page still belong to the original historical cohorts. The new framework has no matching rerun yet; see [measurement coverage](index.md#measurement-coverage). + + + ## Motivation {#motivation} A rollout creates an environment, alternates model inference with tool calls, runs verification, assigns a reward and retains or rejects the sample. Failed attempts may need context preparation, environment restoration and another execution. Choosing an executor requires budgeting these stages separately and preserving the task's required boundary. diff --git a/docs/src/en/benchmarks/density.md b/docs/src/en/benchmarks/density.md index 1da33c2b3..f0c8b3153 100644 --- a/docs/src/en/benchmarks/density.md +++ b/docs/src/en/benchmarks/density.md @@ -10,6 +10,12 @@ | Many idle environments | Stage reaches higher tested concurrency, using a host-process boundary | | A VM boundary per task | Reserve more resources; do not size active tasks from idle counts | + + +The numbers and conclusions on this page still belong to the original historical cohorts. The new framework has no matching rerun yet; see [measurement coverage](index.md#measurement-coverage). + + + ## Motivation {#motivation} When multiple Agents execute together, completed work matters more than created environments. Capacity planning must include runtimes, helpers, page cache and VM backing; neither single-process RSS nor configured RAM can establish capacity. diff --git a/docs/src/en/benchmarks/evidence-sources.csv b/docs/src/en/benchmarks/evidence-sources.csv index 9e4d4aa10..4fee18183 100644 --- a/docs/src/en/benchmarks/evidence-sources.csv +++ b/docs/src/en/benchmarks/evidence-sources.csv @@ -1,7 +1,7 @@ source_documents,cohort,local_raw_record,raw_sha256,identity retired CLI comparison (historical record only),reference-env-20261004,docs/src/assets/benchmarks/.data/reference-env-20261004/report.tsv,090abcab9593a0c52f8e0b27186db51f6d684f4501221045098e239b80e92804,"{""driver_sha256"": ""404a61ee30ae4228b1c6f7d4493c5f1c3297bf953bcabc03ef6e06a3e765bc7c"", ""host_kernel"": ""7.2.8-200.fc44.x86_64"", ""kernel_sha256"": ""aa55fa2dbe2762a55020182560fabd16562d565392505b1986fc51326c741f3d"", ""pvisor_sha256"": ""1a2db5ad015c5ace40b3c96c7dd0dc94a08b8893de35150bd909554286e2cd3c"", ""recorded_at"": ""2026-10-04T06:07:14.020510+08:00"", ""scope"": ""pinned Claude/Codex; N=30 successful cells; no real inference"", ""source_commit"": ""944e32fcf30e71c37c67848ccf2ac4428cdfb053"", ""workload_sha256"": ""bb56a7727316cb566dfc7fca7223a5e0e1be591e6f90376d1b3c599a8fe4764e""}" startup.md;agent-tasks.md;compare-rl-infra.md;index.md,6r,benchmark/.data/6r/report.json,aa2ab1f5a4938365895cc7528263f1949e2178fc9f4e13f9c9a7332509b960c1,"{""binary_sha256"": ""04d73057a0042bd965a4fc49555e6d24c30126ae59fc349153d4466286cfb66b"", ""failures"": 0, ""samples"": 480, ""scope"": ""independent registered workload; no pooled samples"", ""source_manifest_sha256"": ""cad1cedb1c7b7745b53ad730a83bf5a3b69e35072f19969b0074dd66eace051f""}" -filesystem.md;agent-tasks.md;index.md,6f,benchmark/.data/6f/report.json,199a0a0a5ac4a4d0863c9ae4c02807bf57e6a560aa4edc176a20ae83ae969d7e,"{""binary_sha256"": ""04d73057a0042bd965a4fc49555e6d24c30126ae59fc349153d4466286cfb66b"", ""failures"": 0, ""samples"": 480, ""scope"": ""independent registered workload; no pooled samples"", ""source_manifest_sha256"": ""cad1cedb1c7b7745b53ad730a83bf5a3b69e35072f19969b0074dd66eace051f""}" +filesystem.md (historical 2026-10-06);agent-tasks.md;index.md,6f,benchmark/.data/6f/report.json,199a0a0a5ac4a4d0863c9ae4c02807bf57e6a560aa4edc176a20ae83ae969d7e,"{""binary_sha256"": ""04d73057a0042bd965a4fc49555e6d24c30126ae59fc349153d4466286cfb66b"", ""failures"": 0, ""samples"": 480, ""scope"": ""independent registered workload; no pooled samples"", ""source_manifest_sha256"": ""cad1cedb1c7b7745b53ad730a83bf5a3b69e35072f19969b0074dd66eace051f""}" agent-tasks.md;compare-rl-infra.md;index.md,6t,benchmark/.data/6t/report.json,176a8508dd2fc7bb358f95d54f1e25e8c0f1d18c521775240213bec8fdeaad77,"{""binary_sha256"": ""04d73057a0042bd965a4fc49555e6d24c30126ae59fc349153d4466286cfb66b"", ""failures"": 0, ""samples"": 480, ""scope"": ""independent registered workload; no pooled samples"", ""source_manifest_sha256"": ""cad1cedb1c7b7745b53ad730a83bf5a3b69e35072f19969b0074dd66eace051f""}" supervision-cost.md;agent-tasks.md;index.md,6w,benchmark/.data/6w/report.json,52521abbb0c6245dc8af1ab1b70cf23b750b09f605dbde4cad4a7f60850c5569,"{""binary_sha256"": ""04d73057a0042bd965a4fc49555e6d24c30126ae59fc349153d4466286cfb66b"", ""failures"": 0, ""samples"": 360, ""scope"": ""independent registered workload; no pooled samples"", ""source_manifest_sha256"": ""cad1cedb1c7b7745b53ad730a83bf5a3b69e35072f19969b0074dd66eace051f""}" network.md,6net,benchmark/.data/6net/report.json,a694f5930f7b49138d8e53b82e4e1a8c0ec0e494d489aee304220c8d50d6f9bc,"{""benchmark_id"": ""B-NETWORK"", ""binary_sha256"": ""f2cc7afd57efc7c029545744ad2d70c18c542bc29291e35f514cca75f364548a"", ""processed_provenance"": ""network-provenance.csv"", ""recorded_at"": ""2026-10-06T07:30:31.595026+08:00"", ""scope"": {""caches"": ""warm; no host cache eviction"", ""correctness_required"": true, ""git_configuration"": ""global/system config disabled; isolated fixture repository config; C locale"", ""percentile"": ""linear interpolation"", ""samples"": 30, ""startup_logging"": false, ""timing"": ""wall includes process launch and teardown; worker_ms is workload only"", ""vm_memory"": ""1GiB"", ""warmups"": 3}, ""source_manifest_sha256"": ""9ef6773efe0c4ec03a18c2da8824194fb5c2f517e787ad08b437d0594a7a3cd3""}" @@ -16,3 +16,11 @@ isolation-tests.md,6iso,benchmark/.data/6iso/report.json,6f2679977acc7e94d68c7fe isolation-tests.md,6iso,benchmark/.data/6iso/isolation-publication-audit.json,6ca02a14fbc2a6e4546292c89ea5eec99b81d2d9ac104e28dd9e58ecf3b0c1c5,"{""binary_sha256"": ""1a7144081fdbf55047ae57df6dcc13a9e57bb7107f568a17f2b5f209aa2d6f17"", ""recorded_at"": ""2026-10-06T16:01:52.975982+08:00"", ""scope"": ""21 retained command/Bundle/output/host-stage-byte checks; not an escape audit"", ""source_manifest_sha256"": ""7ef73174e02879122a410fe291867e3c8c08f4a1c944c69eb4aded7f521e1b7f""}" supervision-cost.md,6sup2,benchmark/.data/6sup2/report.json,62ddd9de8f70df8c10e8b1c0f5f6a316673094404f22460c6906331620a8b749,"{""binary_sha256"": ""1a7144081fdbf55047ae57df6dcc13a9e57bb7107f568a17f2b5f209aa2d6f17"", ""recorded_at"": ""2026-10-06T16:12:43.375659+08:00"", ""scope"": ""60 paired prepared-view review/select/apply/disposal samples; private-view creation and task execution excluded"", ""source_manifest_sha256"": ""7ef73174e02879122a410fe291867e3c8c08f4a1c944c69eb4aded7f521e1b7f""}" supervision-cost.md,6sup2,benchmark/.data/6sup2/supervision-publication-audit.json,1b4ba2d3c9661a6fcfd31cef360c12e4d0eb9c4577fba93f9163c8dde7b7cf17,"{""binary_sha256"": ""1a7144081fdbf55047ae57df6dcc13a9e57bb7107f568a17f2b5f209aa2d6f17"", ""recorded_at"": ""2026-10-06T16:12:43.375659+08:00"", ""scope"": ""60 formal and six warmup command/diff/final-byte/Run-boundary checks"", ""source_manifest_sha256"": ""7ef73174e02879122a410fe291867e3c8c08f4a1c944c69eb4aded7f521e1b7f""}" +filesystem.md (historical 2026-10-10),f10f2,benchmark/.data/f10f2/report.json,c2f544b79356db3fcda8f4ed34d944861a64f2190c18747c30441201271502ff,"{""binary_source_commit"": ""b3e2f03509396b8ebe840a9ccdefa63424bc1afe"", ""binary_source_manifest_sha256"": ""a52c826c8d6c70632f4f3d8e36fda672fe8029b7f87cb5f7138a72824b6b5578"", ""host_kernel"": ""7.2.8-200.fc44.x86_64"", ""input_manifest_sha256"": ""59d0389ac29bf0a6d216bfcb5fcf1ff48e143162b453e4e16f195f562f6f4670"", ""kernel_sha256"": ""aa55fa2dbe2762a55020182560fabd16562d565392505b1986fc51326c741f3d"", ""pvisor_sha256"": ""c6965986ac119544f0886e06806fcf41b814f54b54ad655978046a2449797c42"", ""recorded_at"": ""2026-10-10T21:33:50.026904+08:00""}" +filesystem.md;index.md,fs11f4,benchmark/.data/fs11f4/report.json,99eb2ee32bd9e595694c39434f33bc4a914b949e88d3c2b345733932e6884369,"{""binary_source_commit"": ""b3e2f03509396b8ebe840a9ccdefa63424bc1afe"", ""binary_source_manifest_sha256"": ""07b27144366d96dbac053bd18288a50c7468b231d0e8570b3f400b38c28d0f8f"", ""host_kernel"": ""7.2.8-200.fc44.x86_64"", ""input_manifest_sha256"": ""730df0172d73d3c260c741eebf7ac0926dea0add1f05a176c6a39176e9469518"", ""kernel_sha256"": ""aa55fa2dbe2762a55020182560fabd16562d565392505b1986fc51326c741f3d"", ""pvisor_sha256"": ""939f72289a72f73ba64d645c891842ed4a9408c7eaef4c54d5fa04aed0371e85"", ""recorded_at"": ""2026-10-11T13:39:23.065270+08:00""}" +filesystem.md;index.md;design/filesystem-performance-analysis.md,fo8e2d57ff-f,benchmark/.data/fo8e2d57ff-f/report.json,d37b961370f9d8ba3487fd1a6a7a85cf5e85e5de6b32ae91285eb69b9dbfdb92,"{""benchmark_id"": ""B-FS-TOOLS"", ""binary_source_commit"": ""b3e2f03509396b8ebe840a9ccdefa63424bc1afe"", ""binary_source_manifest_sha256"": ""5311e7c05ca1fc114c60416920c204866e5a6079f509343a91b95f6bf0d37b24"", ""failures"": 0, ""host_kernel"": ""7.2.8-200.fc44.x86_64"", ""input_manifest_sha256"": ""c3f1b6f957c19a403baf6c7299901fd92f41a2cf758cf7edbbab9a4a8108852b"", ""kernel_sha256"": ""aa55fa2dbe2762a55020182560fabd16562d565392505b1986fc51326c741f3d"", ""processed_provenance"": ""filesystem-provenance.csv"", ""pvisor_sha256"": ""8840b16cdc0610e381eec66b0f75f1e86efba76feb358c9eae5b3d923854b592"", ""record_scope"": ""accepted raw report"", ""recorded_at"": ""2026-10-11T14:57:50.329605+08:00"", ""role"": ""accepted full user-facing cohort"", ""samples"": 240, ""scope"": ""independent full cohort; no pooled samples; phase timings distinct from whole-command probes"", ""selected_build_defaults"": {""new_vm_writeback"": false, ""ordinary_dax"": false, ""reviewed_from_frozen_source"": true, ""stage_sync_workers"": 1}}" +filesystem.md;index.md;design/filesystem-performance-analysis.md,fo8e2d57ff-f,benchmark.new/filesystem/.data/fs-opt-report-20261011/publication-audit.json,ec38a6bc0311c07e45a756ed6ac0099270c680e432a3aa02252e78ea07f66528,"{""benchmark_id"": ""B-FS-TOOLS"", ""binary_source_commit"": ""b3e2f03509396b8ebe840a9ccdefa63424bc1afe"", ""binary_source_manifest_sha256"": ""5311e7c05ca1fc114c60416920c204866e5a6079f509343a91b95f6bf0d37b24"", ""failures"": 0, ""host_kernel"": ""7.2.8-200.fc44.x86_64"", ""input_manifest_sha256"": ""c3f1b6f957c19a403baf6c7299901fd92f41a2cf758cf7edbbab9a4a8108852b"", ""kernel_sha256"": ""aa55fa2dbe2762a55020182560fabd16562d565392505b1986fc51326c741f3d"", ""processed_provenance"": ""filesystem-provenance.csv"", ""pvisor_sha256"": ""8840b16cdc0610e381eec66b0f75f1e86efba76feb358c9eae5b3d923854b592"", ""record_scope"": ""accepted publication audit"", ""recorded_at"": ""2026-10-11T14:57:50.329605+08:00"", ""role"": ""accepted full user-facing cohort"", ""samples"": 240, ""scope"": ""independent full cohort; no pooled samples; phase timings distinct from whole-command probes"", ""selected_build_defaults"": {""new_vm_writeback"": false, ""ordinary_dax"": false, ""reviewed_from_frozen_source"": true, ""stage_sync_workers"": 1}}" +design/filesystem-performance-analysis.md,fof1,benchmark/.data/fof1/report.json,df8ddd01aa86ef0a61a04d4157dc8e02a6c935ebacecf3ccfb448ab624ded7b1,"{""benchmark_id"": ""B-FS-ENG"", ""processed_summary"": ""filesystem-optimization-n30-summary.csv"", ""processed_summary_sha256"": ""65b0287892724c266ebc39db20adcc138ac180aedb6b455cc0f713517d6b25dd"", ""record_scope"": ""raw engineering report"", ""role"": ""independent formal engineering A/B"", ""samples_per_arm"": 30, ""scope"": ""not pooled with user full cohort or screening/history"", ""warmups_per_arm"": 3}" +design/filesystem-performance-analysis.md,fof1,benchmark/.data/fof1/derived/audit.json,3a599448f7a7737f38e52b7d2943555d0a8cc4d74e030ebabbea5461d25fca29,"{""benchmark_id"": ""B-FS-ENG"", ""processed_summary"": ""filesystem-optimization-n30-summary.csv"", ""processed_summary_sha256"": ""65b0287892724c266ebc39db20adcc138ac180aedb6b455cc0f713517d6b25dd"", ""record_scope"": ""engineering acceptance"", ""role"": ""independent formal engineering A/B"", ""samples_per_arm"": 30, ""scope"": ""not pooled with user full cohort or screening/history"", ""warmups_per_arm"": 3}" +filesystem.md;index.md;design/filesystem-performance-analysis.md,fo5a4e7d2b-f,benchmark/.data/fo5a4e7d2b-f/report.json,0127dbdd8a04317be06a0235e5b1cec75dc298b3c6e312f22f1382b8d19f7262,"{""benchmark_id"": ""B-FS-TOOLS"", ""binary_source_commit"": ""3c5c647ee6890b7a310f838f5b80f6751c718575"", ""binary_source_manifest_sha256"": ""3a8356cfa2370ebfaa4716cbb407d50cf90b172ae1943749bb3efff8f2db4642"", ""failures"": 0, ""host_kernel"": ""7.2.8-200.fc44.x86_64"", ""input_manifest_sha256"": ""f33275693e1d765be1901e7ba856ef91a397d622abd29308d57699993ef4e49d"", ""kernel_sha256"": ""aa55fa2dbe2762a55020182560fabd16562d565392505b1986fc51326c741f3d"", ""processed_provenance"": ""filesystem-provenance.csv"", ""pvisor_sha256"": ""de4889d932ed8bb5ab376cb09ae43327084a2d017f66db5c54946b78b50446ac"", ""record_scope"": ""accepted raw report"", ""recorded_at"": ""2026-10-12T01:50:38.436534+08:00"", ""role"": ""accepted full user-facing cohort"", ""samples"": 240, ""scope"": ""independent full cohort; no pooled samples; phase timings distinct from whole-command probes"", ""selected_build_defaults"": {""new_vm_writeback"": false, ""ordinary_dax"": false, ""reviewed_from_frozen_source"": true, ""stage_sync_workers"": 1}}" +filesystem.md;index.md;design/filesystem-performance-analysis.md,fo5a4e7d2b-f,benchmark.new/filesystem/.data/fs-docs-20261012/publication-audit.json,45f479ab7709723e781bfd6b78d8ace3dfa2a8914a687173babfd9ee23daf24e,"{""benchmark_id"": ""B-FS-TOOLS"", ""binary_source_commit"": ""3c5c647ee6890b7a310f838f5b80f6751c718575"", ""binary_source_manifest_sha256"": ""3a8356cfa2370ebfaa4716cbb407d50cf90b172ae1943749bb3efff8f2db4642"", ""failures"": 0, ""host_kernel"": ""7.2.8-200.fc44.x86_64"", ""input_manifest_sha256"": ""f33275693e1d765be1901e7ba856ef91a397d622abd29308d57699993ef4e49d"", ""kernel_sha256"": ""aa55fa2dbe2762a55020182560fabd16562d565392505b1986fc51326c741f3d"", ""processed_provenance"": ""filesystem-provenance.csv"", ""pvisor_sha256"": ""de4889d932ed8bb5ab376cb09ae43327084a2d017f66db5c54946b78b50446ac"", ""record_scope"": ""accepted publication audit"", ""recorded_at"": ""2026-10-12T01:50:38.436534+08:00"", ""role"": ""accepted full user-facing cohort"", ""samples"": 240, ""scope"": ""independent full cohort; no pooled samples; phase timings distinct from whole-command probes"", ""selected_build_defaults"": {""new_vm_writeback"": false, ""ordinary_dax"": false, ""reviewed_from_frozen_source"": true, ""stage_sync_workers"": 1}}" diff --git a/docs/src/en/benchmarks/filesystem-cache-policy.csv b/docs/src/en/benchmarks/filesystem-cache-policy.csv new file mode 100644 index 000000000..b4dac69c2 --- /dev/null +++ b/docs/src/en/benchmarks/filesystem-cache-policy.csv @@ -0,0 +1,9 @@ +benchmark_id,role,batch,backend,validation_state,formal_valid,preflight_failed,cache_status,cache_filesystem,filesystem_magic,cache_directory,failure_reason,report_sha256,stdout_sha256 +B-AGENT-TASK,environment capability observation; no timing publication,f10e,native,available,1,0,ALREADY_ENABLED,tmpfs,0x1021994,/tmp/.data/pvisor-reference-cmlac1bu/node-compile-cache/v24.18.0-x64-cf738c9d-1000,,c94b5d3748122f49b9148530ce90f3922d196232d55f208dbd7499d5ea68733a,3c4e4e0ecf0ad75581624d294800e44b73b0c89a53545917c7f7b619ae72f6d9 +B-AGENT-TASK,environment capability observation; no timing publication,f10e,pvisor-host,available,1,0,ALREADY_ENABLED,tmpfs,0x1021994,/tmp/.data/pvisor-reference-9lzu3o5e/node-compile-cache/v24.18.0-x64-cf738c9d-1000,,c94b5d3748122f49b9148530ce90f3922d196232d55f208dbd7499d5ea68733a,b6a17b4ea92cc10d1490c1817a06d16a9d5121316c22c7fabfa8e0fea0313ac8 +B-AGENT-TASK,environment capability observation; no timing publication,f10e,pvisor-staged,available,1,0,ALREADY_ENABLED,tmpfs,0x1021994,/tmp/.data/pvisor-reference-ch2i5d0c/node-compile-cache/v24.18.0-x64-cf738c9d-1000,,c94b5d3748122f49b9148530ce90f3922d196232d55f208dbd7499d5ea68733a,527433a05c20885895cd39b3e15013bde21c85a98c3f1e6ac427a76794a89b78 +B-AGENT-TASK,environment capability observation; no timing publication,f10e,pvisor-vm,available,1,0,ALREADY_ENABLED,tmpfs,0x1021994,/.pvisor-tmp-run-c924a108-6c68-4e4f-81cd-14da4ae9ee84/.data/pvisor-reference-ywed6kwb/node-compile-cache/v24.18.0-x64-cf738c9d-0,,c94b5d3748122f49b9148530ce90f3922d196232d55f208dbd7499d5ea68733a,d62500b13045ada1b2fe7d90c02c017e93e94ba4ecb94d7f3cbe26e0e443eba5 +B-AGENT-TASK,environment capability observation; no timing publication,f10e,docker,available,1,0,ALREADY_ENABLED,overlayfs,0x794c7630,/tmp/.data/pvisor-reference-yd09ot2_/node-compile-cache/v24.18.0-x64-cf738c9d-0,,c94b5d3748122f49b9148530ce90f3922d196232d55f208dbd7499d5ea68733a,9b38b12fe39bbb929dfa6b8cc28d5d7bcdabc0584d0d01a94acae80d523f8a56 +B-AGENT-TASK,environment capability observation; no timing publication,f10e,firecracker,failed-preflight,0,1,ALREADY_ENABLED,ext4,0xef53,/tmp/.data/pvisor-reference-ymw5tlm5/node-compile-cache/v24.18.0-x64-cf738c9d-0,guest must emit exactly one Ready marker,c94b5d3748122f49b9148530ce90f3922d196232d55f208dbd7499d5ea68733a,a58fd162425fdb0dfd13eb1da9e3a310f1d19f64d025cea3099d70b6747c3e18 +B-AGENT-TASK,environment capability observation; no timing publication,f10e,qemu,failed-preflight,0,1,ALREADY_ENABLED,ext4,0xef53,/tmp/.data/pvisor-reference-lrewstqa/node-compile-cache/v24.18.0-x64-cf738c9d-0,guest must emit exactly one Ready marker,c94b5d3748122f49b9148530ce90f3922d196232d55f208dbd7499d5ea68733a,f6a45fc7aaa63c2d26080a4c50019ffc32d800b22c7f9f1b4e1bf6c36446c1f5 +B-AGENT-TASK,environment capability observation; no timing publication,f10e,qemu-microvm,failed-preflight,0,1,ALREADY_ENABLED,ext4,0xef53,/tmp/.data/pvisor-reference-92jbr460/node-compile-cache/v24.18.0-x64-cf738c9d-0,guest must emit exactly one Ready marker,c94b5d3748122f49b9148530ce90f3922d196232d55f208dbd7499d5ea68733a,d85e10eab369cb15a899a31034ef9bcce89e295eba24f14550e3c8c9a68756a3 diff --git a/docs/src/en/benchmarks/filesystem-comparisons.csv b/docs/src/en/benchmarks/filesystem-comparisons.csv new file mode 100644 index 000000000..7a745ee56 --- /dev/null +++ b/docs/src/en/benchmarks/filesystem-comparisons.csv @@ -0,0 +1,151 @@ +batch,benchmark_id,mode,candidate,control,metric,unit,n,difference_ms,ci95_low_ms,ci95_high_ms,conclusion,confidence_method +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,native,ready_ms,ms,30,21.354811499999983,20.717782675000013,23.427103624999987,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,native,result_ms,ms,30,52.800583000000074,48.103993025000044,62.20575908749991,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,native,completion_ms,ms,30,69.6681044999998,65.13375883750004,79.66904299999999,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,native,metadata_worker_ms,ms,30,-0.004884999999999806,-0.1306995000000004,0.16186400000000045,no detected difference,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,native,read_worker_ms,ms,30,0.6513659999999959,0.244121550000005,1.673229487499995,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,native,write_worker_ms,ms,30,0.07752499999999962,-0.13989657499999997,0.1782782874999999,no detected difference,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,native,git_worker_ms,ms,30,1.1633475000000004,0.08171393750000074,1.832967,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,native,rg_worker_ms,ms,30,0.018464999999999065,-0.2702340000000003,0.8803643124999997,no detected difference,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,native,cargo_worker_ms,ms,30,5.970192499999996,3.6305704000000003,8.054308924999997,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,native,npm_worker_ms,ms,30,13.438230500000031,11.142320762500013,15.550342387499997,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,docker,ready_ms,ms,30,-192.18906400000003,-238.00896250000005,-169.72389694999998,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,docker,result_ms,ms,30,-157.04292799999985,-200.36289886249986,-132.82160399999998,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,docker,completion_ms,ms,30,-200.42969400000015,-241.39521500000023,-167.35366419999994,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,docker,metadata_worker_ms,ms,30,-0.08691349999999964,-0.1813180000000001,0.04570827500000045,no detected difference,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,docker,read_worker_ms,ms,30,0.32456849999999804,0.07155249999999569,1.3483590624999908,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,docker,write_worker_ms,ms,30,-0.08147150000000014,-0.2650030000000001,0.05023117500000042,no detected difference,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,docker,git_worker_ms,ms,30,0.9607700000000001,-0.06168631250000115,1.675598024999998,no detected difference,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,docker,rg_worker_ms,ms,30,-0.47471250000000076,-0.7639528625000008,0.44008392499999843,no detected difference,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,docker,cargo_worker_ms,ms,30,10.127195,7.408730575,11.773046137500002,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,docker,npm_worker_ms,ms,30,9.155480000000011,7.278795000000002,11.67338657500002,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,firecracker,ready_ms,ms,30,-642.8175115,-648.5643,-640.7428865375,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,firecracker,result_ms,ms,30,-1584.6247370000003,-1593.5329044624998,-1565.5314213,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,firecracker,completion_ms,ms,30,-1607.1344020000001,-1617.5947384499998,-1595.17350005,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,firecracker,metadata_worker_ms,ms,30,-17.239526,-19.149071575,-17.003035050000005,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,firecracker,read_worker_ms,ms,30,-47.387362499999995,-47.7189269875,-46.22877482500001,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,firecracker,write_worker_ms,ms,30,-39.216832,-39.34645075,-39.122198000000004,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,firecracker,git_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,firecracker,rg_worker_ms,ms,30,-8.195251500000001,-8.6260351125,-7.213457,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,firecracker,cargo_worker_ms,ms,30,-327.178037,-334.33065750000003,-319.7967431375,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,firecracker,npm_worker_ms,ms,30,-330.77358649999996,-335.69834168750003,-321.427424,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu,ready_ms,ms,30,-280.10003400000005,-336.7426709999999,-275.017773,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu,result_ms,ms,30,-1109.1334920000002,-1240.0404424999997,-1026.4512989999996,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu,completion_ms,ms,30,-1125.8445930000003,-1278.7792020000002,-1045.7348415000001,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu,metadata_worker_ms,ms,30,-12.012132999999999,-14.212173950000002,-11.543457,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu,read_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu,write_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu,git_worker_ms,ms,30,-72.4847685,-75.97099,-68.3695655,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu,rg_worker_ms,ms,30,-6.818395500000001,-7.775931,-5.677642750000002,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu,cargo_worker_ms,ms,30,-314.82165399999997,-333.0714988625,-307.134967875,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu,npm_worker_ms,ms,30,-308.200423,-327.0849611,-240.9299808875,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu-microvm,ready_ms,ms,30,-211.32980099999997,-270.38579200000004,-164.125515,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu-microvm,result_ms,ms,30,-1247.1282525,-1305.98371,-1159.386878,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu-microvm,completion_ms,ms,30,-1289.3285475000002,-1326.194048425,-1186.7533285,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu-microvm,metadata_worker_ms,ms,30,-15.249888499999997,-16.5170625,-14.8280532375,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu-microvm,read_worker_ms,ms,30,-41.39920950000001,-55.87539199999999,-23.30983700000001,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu-microvm,write_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu-microvm,git_worker_ms,ms,30,-151.464851,-158.2403045,-145.42583,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu-microvm,rg_worker_ms,ms,30,-8.281779499999999,-9.007349587500002,-6.970837762500001,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu-microvm,cargo_worker_ms,ms,30,-371.4054985,-390.55829556250006,-339.63162848750017,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu-microvm,npm_worker_ms,ms,30,-355.344023,-370.229405,-318.6296337,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,native,ready_ms,ms,30,33.94435249999998,32.63888800000001,35.534462687500024,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,native,result_ms,ms,30,540.3684264999998,533.9711397000001,549.2702949875,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,native,completion_ms,ms,30,808.447336,793.5487008,816.4551753249999,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,native,metadata_worker_ms,ms,30,78.17458900000001,74.7027016375,79.0251345,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,native,read_worker_ms,ms,30,45.3585295,42.67462863749999,46.770893,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,native,write_worker_ms,ms,30,28.294047999999997,26.8714861,29.557854000000003,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,native,git_worker_ms,ms,30,147.95046100000002,138.37097541250003,156.8120554375,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,native,rg_worker_ms,ms,30,99.264704,97.42978045,102.072395,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,native,cargo_worker_ms,ms,30,37.0588795,34.918250574999995,38.4174845,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,native,npm_worker_ms,ms,30,32.37371999999999,29.259301550000014,35.9872963,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,docker,ready_ms,ms,30,-179.59952300000003,-225.62106475000004,-157.46620099999998,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,docker,result_ms,ms,30,330.5249154999999,287.3952519499999,355.58885750000013,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,docker,completion_ms,ms,30,538.3495375,497.98129399999993,570.6188934124999,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,docker,metadata_worker_ms,ms,30,78.09256050000002,74.62362001250001,78.962167,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,docker,read_worker_ms,ms,30,45.031732000000005,42.383445512499996,46.5158808625,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,docker,write_worker_ms,ms,30,28.1350515,26.697902137499998,29.387190500000003,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,docker,git_worker_ms,ms,30,147.7478835,138.2521645,156.5467665,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,docker,rg_worker_ms,ms,30,98.7715265,97.03198950000001,101.57483,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,docker,cargo_worker_ms,ms,30,41.21588200000001,38.604738999999995,42.3511745,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,docker,npm_worker_ms,ms,30,28.09096949999997,25.46852100000001,32.3563627625,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,firecracker,ready_ms,ms,30,-630.2279705000001,-635.6626974375,-628.522135,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,firecracker,result_ms,ms,30,-1097.0568935000006,-1108.9951634375,-1077.2782639999998,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,firecracker,completion_ms,ms,30,-868.3551705,-888.1790590999999,-855.6087235,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,firecracker,metadata_worker_ms,ms,30,60.939948000000015,57.203875999999994,61.75334599999999,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,firecracker,read_worker_ms,ms,30,-2.6801989999999876,-5.281012000000004,-1.245096999999987,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,firecracker,write_worker_ms,ms,30,-11.000309000000001,-12.401892500000002,-9.755308499999998,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,firecracker,git_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,firecracker,rg_worker_ms,ms,30,91.05098749999999,89.110830425,94.00118675,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,firecracker,cargo_worker_ms,ms,30,-296.08935,-303.490479075,-287.935798175,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,firecracker,npm_worker_ms,ms,30,-311.838097,-317.62806650000005,-301.456369,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu,ready_ms,ms,30,-267.51049300000005,-324.2623488499999,-262.8967688125,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu,result_ms,ms,30,-621.5656485000004,-754.2959272624998,-538.2588335124998,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu,completion_ms,ms,30,-387.0653615000001,-542.8254525000002,-306.6929869999999,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu,metadata_worker_ms,ms,30,66.16734100000002,61.9000293,67.237834,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu,read_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu,write_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu,git_worker_ms,ms,30,74.302345,65.117742,85.32994350000001,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu,rg_worker_ms,ms,30,92.4278435,90.606458,95.217265,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu,cargo_worker_ms,ms,30,-283.732967,-301.68573841250003,-275.3837045125,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu,npm_worker_ms,ms,30,-289.26493350000004,-309.15471318749996,-220.97989466250007,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu-microvm,ready_ms,ms,30,-198.74025999999998,-257.80681703749997,-151.31966550000004,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu-microvm,result_ms,ms,30,-759.5604090000002,-818.8753958375002,-670.6451519625,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu-microvm,completion_ms,ms,30,-550.5493160000001,-591.0010197375002,-450.1275515624999,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu-microvm,metadata_worker_ms,ms,30,62.929585500000016,59.17957199999999,63.9421089375,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu-microvm,read_worker_ms,ms,30,3.307953999999995,-12.026521499999987,21.577668774999996,no detected difference,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu-microvm,write_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu-microvm,git_worker_ms,ms,30,-4.677737500000006,-17.807794462499995,7.438843624999973,no detected difference,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu-microvm,rg_worker_ms,ms,30,90.9644595,89.125186,93.72936425,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu-microvm,cargo_worker_ms,ms,30,-340.31681150000003,-358.8761985,-307.98643649999997,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu-microvm,npm_worker_ms,ms,30,-336.40853350000003,-350.74429549999996,-298.74167303749994,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,native,ready_ms,ms,30,570.2632964999999,562.2344208125,579.9940357874998,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,native,result_ms,ms,30,4174.5818865,3980.3865591250005,4417.520103475,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,native,completion_ms,ms,30,4463.837227,4191.473749,4639.6729338000005,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,native,metadata_worker_ms,ms,30,172.54763100000002,158.8435415,182.67950011249997,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,native,read_worker_ms,ms,30,103.419885,101.56575838750001,107.19638047500001,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,native,write_worker_ms,ms,30,151.848376,148.17121107499997,158.20359850000003,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,native,git_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,native,rg_worker_ms,ms,30,419.492317,407.7423475,431.519171,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,native,cargo_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,native,npm_worker_ms,ms,30,903.2197145,875.8506089125002,957.7747454999999,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,docker,ready_ms,ms,30,356.7194209999999,311.06533828749997,380.0942947874999,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,docker,result_ms,ms,30,3964.7383755,3748.5483914875,4206.4075014,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,docker,completion_ms,ms,30,4193.7394285,3918.3813497124997,4376.5615969875,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,docker,metadata_worker_ms,ms,30,172.46560250000002,158.774123,182.6062945,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,docker,read_worker_ms,ms,30,103.0930875,101.273033975,106.80225150000001,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,docker,write_worker_ms,ms,30,151.6893795,147.95817799999998,158.08465427500002,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,docker,git_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,docker,rg_worker_ms,ms,30,418.9991395,407.4594651,431.08609261249995,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,docker,cargo_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,docker,npm_worker_ms,ms,30,898.936964,872.2733834750002,953.2581134999998,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,firecracker,ready_ms,ms,30,-93.9090265000001,-103.86495615,-85.38884900000005,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,firecracker,result_ms,ms,30,2537.1565665,2345.4487751124993,2784.6814974999998,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,firecracker,completion_ms,ms,30,2787.0347205000003,2516.8881205625003,2963.2176690000006,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,firecracker,metadata_worker_ms,ms,30,155.31299,141.31960164999998,165.32276119999997,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,firecracker,read_worker_ms,ms,30,55.3811565,53.44678000000002,59.151491500000006,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,firecracker,write_worker_ms,ms,30,112.55401899999998,108.80335499999998,118.93815100000002,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,firecracker,git_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,firecracker,rg_worker_ms,ms,30,411.2786005,399.5964279375,423.4593125,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,firecracker,cargo_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,firecracker,npm_worker_ms,ms,30,559.0078975,531.8477048374999,615.6954783874996,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu,ready_ms,ms,30,268.8084509999999,210.11025737499997,276.99498551249997,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu,result_ms,ms,30,3012.6478115,2829.1194989249993,3237.1212299999997,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu,completion_ms,ms,30,3268.3245295,3025.0442515,3409.2922661625,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu,metadata_worker_ms,ms,30,160.54038300000002,146.5920661875,170.57778726249998,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu,read_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu,write_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu,git_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu,rg_worker_ms,ms,30,412.6554565,400.9053585125,424.8616591625,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu,cargo_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu,npm_worker_ms,ms,30,581.581061,553.3811230000002,654.9121011625,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu-microvm,ready_ms,ms,30,337.57868399999995,275.98321250000004,384.5440325,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu-microvm,result_ms,ms,30,2874.6530510000002,2660.2120335,3136.7555492999995,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu-microvm,completion_ms,ms,30,3104.840575,2836.525256,3317.196449,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu-microvm,metadata_worker_ms,ms,30,157.30262750000003,143.2223885,167.27193947499998,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu-microvm,read_worker_ms,ms,30,61.369309499999986,46.62113199999999,80.64720150000001,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu-microvm,write_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu-microvm,git_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu-microvm,rg_worker_ms,ms,30,411.1920725,399.75469315,422.941422,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu-microvm,cargo_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu-microvm,npm_worker_ms,ms,30,534.437461,501.8885435000001,598.0208625,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" diff --git a/docs/src/en/benchmarks/filesystem-provenance.csv b/docs/src/en/benchmarks/filesystem-provenance.csv new file mode 100644 index 000000000..669ad62fb --- /dev/null +++ b/docs/src/en/benchmarks/filesystem-provenance.csv @@ -0,0 +1,43 @@ +batch,benchmark_id,field,value +fo5a4e7d2b-f,B-FS-TOOLS,recorded_at,2026-10-12T01:50:38.436534+08:00 +fo5a4e7d2b-f,B-FS-TOOLS,source_commit,b86d521403c1f04cb08cfbde04576365f7e40580 +fo5a4e7d2b-f,B-FS-TOOLS,pvisor_sha256,de4889d932ed8bb5ab376cb09ae43327084a2d017f66db5c54946b78b50446ac +fo5a4e7d2b-f,B-FS-TOOLS,kernel_sha256,aa55fa2dbe2762a55020182560fabd16562d565392505b1986fc51326c741f3d +fo5a4e7d2b-f,B-FS-TOOLS,bzimage_sha256,99de4727b274d92232bd67f9e62b9744e67e99ca89d1bdb09a55e2deb7c80619 +fo5a4e7d2b-f,B-FS-TOOLS,driver_sha256,ac76b9a0a848cd76cda22bec0ccad468297c31a0fb365ef4cccc9d2a8eb73a81 +fo5a4e7d2b-f,B-FS-TOOLS,workload_sha256,e7625cb3d73f09b5f42faa2e64999301a076d37fa66e6477163b8a9a50ec6b6a +fo5a4e7d2b-f,B-FS-TOOLS,host_kernel,7.2.8-200.fc44.x86_64 +fo5a4e7d2b-f,B-FS-TOOLS,host_cpu_model,AMD Ryzen 7 9700X 8-Core Processor +fo5a4e7d2b-f,B-FS-TOOLS,docker_storage_driver,overlay2 +fo5a4e7d2b-f,B-FS-TOOLS,docker_version,"Docker version 29.7.2, build 1.fc44" +fo5a4e7d2b-f,B-FS-TOOLS,firecracker_version,"Firecracker v1.13.1 + +2026-10-12T01:50:38.542183835 [anonymous-instance:main] Firecracker exiting successfully. exit_code=0" +fo5a4e7d2b-f,B-FS-TOOLS,qemu-system-x86_64_version,"QEMU emulator version 10.2.2 (qemu-10.2.2-1.fc44) +Copyright (c) 2003-2025 Fabrice Bellard and the QEMU Project developers" +fo5a4e7d2b-f,B-FS-TOOLS,report_sha256,0127dbdd8a04317be06a0235e5b1cec75dc298b3c6e312f22f1382b8d19f7262 +fo5a4e7d2b-f,B-FS-TOOLS,raw_location,benchmark/.data/fo5a4e7d2b-f/report.json +fo5a4e7d2b-f,B-FS-TOOLS,arguments,"{""backends"": ""native,pvisor-host,pvisor-staged,pvisor-vm,docker,firecracker,qemu,qemu-microvm"", ""cpu_affinity"": ""0,1"", ""host_isolation"": ""rootless_process"", ""memory_mib"": ""16384"", ""modes"": ""filesystem"", ""samples"": ""30"", ""seed"": ""20261012"", ""staged_isolation"": ""rootless_process"", ""warmups"": ""3""}" +fo5a4e7d2b-f,B-FS-TOOLS,statistics,batches separate; P95 descriptive at N>=30; no P99; separated clusters replace P50 +fo5a4e7d2b-f,B-FS-TOOLS,split_rule,"both clusters >=max(5,10% N); largest gap >=20% median and >3x median adjacent gap; cluster medians >=1.5x" +fo5a4e7d2b-f,B-FS-TOOLS,reference_kernels,"{""firecracker"": null, ""qemu"": null, ""qemu-microvm"": null}" +fo5a4e7d2b-f,B-FS-TOOLS,fc_ready_policy,normal +fo5a4e7d2b-f,B-FS-TOOLS,binary_source_commit,3c5c647ee6890b7a310f838f5b80f6751c718575 +fo5a4e7d2b-f,B-FS-TOOLS,binary_source_manifest_sha256,3a8356cfa2370ebfaa4716cbb407d50cf90b172ae1943749bb3efff8f2db4642 +fo5a4e7d2b-f,B-FS-TOOLS,input_manifest_sha256,f33275693e1d765be1901e7ba856ef91a397d622abd29308d57699993ef4e49d +fo5a4e7d2b-f,B-FS-TOOLS,source_commit_scope,runner worktree HEAD; measured binary source is recorded separately +fo5a4e7d2b-f,B-FS-TOOLS,host_guard_sha256,280559af658f371595ea9306a62db8cdc834a2a9e208dd227c83fc992ecfd039 +fo5a4e7d2b-f,B-FS-TOOLS,host_guard_scope,0.5 s scans of visible recognized same-UID builds/tests and foreign KVM FDs outside owned parent; 0 detections; inaccessible/short/unrecognized activity may be missed +fo5a4e7d2b-f,B-FS-TOOLS,namespace_helper_sha256,870d3e91d4ad8596b4b397443c2c2bbd356a3727dc6ba439ec9d77bbd18077e6 +fo5a4e7d2b-f,B-FS-TOOLS,namespace_path_mapping,Private /tmp/private-out aliases canonical raw root; Docker receives host canonical paths for the same physical workspaces +fo5a4e7d2b-f,B-FS-TOOLS,assets_equivalence_sha256,3f3e79bcd19fd000c9040a381598cd9ced030f2cd341194e3085224885cc278a +fo5a4e7d2b-f,B-FS-TOOLS,formal_source_before_sha256,c31e839f68c304b459eb35670d4ba13a3ea4208acb31e0181f6b3bda593f7048 +fo5a4e7d2b-f,B-FS-TOOLS,formal_source_after_sha256,c31e839f68c304b459eb35670d4ba13a3ea4208acb31e0181f6b3bda593f7048 +fo5a4e7d2b-f,B-FS-TOOLS,protocol_sha256,cec2792884d478d88c9c7ae0c7265163ae63c712507419aeab81501efb7b6175 +fo5a4e7d2b-f,B-FS-TOOLS,source_harness,/home/reiase/workspace/pvisor/benchmark.new/filesystem/.data/cross-runtime-20261011T052947Z/build/source/benchmark/pvisor +fo5a4e7d2b-f,B-FS-TOOLS,preflight_batch,/home/reiase/workspace/pvisor/benchmark/.data/fo5a4e7d2b-p (independent; not pooled) +fo5a4e7d2b-f,B-FS-TOOLS,cacheprobe_gate,independent diagnostic only; failures/unknown do not gate B-FS-TOOLS +fo5a4e7d2b-f,B-FS-TOOLS,per_trial_cache_validation,actual parent/child Python flags and absent bytecode prefix; fresh TMPDIR/HOME/Cargo/Node variable inheritance; Node API enable status and backing filesystem type not measured per filesystem trial +fo5a4e7d2b-f,B-FS-TOOLS,node_actual_probe,"{""backends"": {""docker"": {""node_api"": ""unknown"", ""node_filesystem_type"": ""unknown"", ""state"": ""unknown""}, ""firecracker"": {""node_api"": ""unknown"", ""node_filesystem_type"": ""unknown"", ""state"": ""unknown""}, ""native"": {""node_api"": ""unknown"", ""node_filesystem_type"": ""unknown"", ""state"": ""unknown""}, ""pvisor-host"": {""node_api"": ""unknown"", ""node_filesystem_type"": ""unknown"", ""state"": ""unknown""}, ""pvisor-staged"": {""node_api"": ""unknown"", ""node_filesystem_type"": ""unknown"", ""state"": ""unknown""}, ""pvisor-vm"": {""node_api"": ""unknown"", ""node_filesystem_type"": ""unknown"", ""state"": ""unknown""}, ""qemu"": {""node_api"": ""unknown"", ""node_filesystem_type"": ""unknown"", ""state"": ""unknown""}, ""qemu-microvm"": {""node_api"": ""unknown"", ""node_filesystem_type"": ""unknown"", ""state"": ""unknown""}}, ""reason"": ""independent env/cacheprobe not run"", ""scope"": ""independent env only; not a formal acceptance gate or per-filesystem-trial API verification"", ""state"": ""unknown""}" +fo5a4e7d2b-f,B-FS-TOOLS,resource_budget,"{""cpu_period"": 100000, ""cpu_placement"": ""affinity"", ""cpu_quota"": 200000, ""declared_cpu_set"": [0, 1], ""memory_max_bytes"": 17179869184, ""scope"": ""whole selected cgroup and descendants; precharged shared cache outside this parent is excluded"", ""swap_max_bytes"": 0}" +fo5a4e7d2b-f,B-FS-TOOLS,product_defaults,No PVISOR_* overrides; ordinary rootfs/workspace DAX off; selected build writeback/stage sync defaults diff --git a/docs/src/en/benchmarks/filesystem.csv b/docs/src/en/benchmarks/filesystem.csv index f91359c12..f36e9ff1a 100644 --- a/docs/src/en/benchmarks/filesystem.csv +++ b/docs/src/en/benchmarks/filesystem.csv @@ -1,81 +1,81 @@ benchmark_id,batch,mode,backend,metric,unit,planned,failed,capability,n,p50,p95_reference,minimum,maximum,distribution,low_n,low_p50,high_n,high_p50 -B-FS-TOOLS,6f,filesystem,native,ready_ms,ms,60,0,available,60,38.718969,40.673907,37.40886,46.106537,unsplit,,,, -B-FS-TOOLS,6f,filesystem,native,result_ms,ms,60,0,available,60,438.558489,447.14285685,422.181008,461.528705,unsplit,,,, -B-FS-TOOLS,6f,filesystem,native,completion_ms,ms,60,0,available,60,446.302805,455.78432404999995,429.385781,469.382669,unsplit,,,, -B-FS-TOOLS,6f,filesystem,native,metadata_worker_ms,ms,60,0,available,60,4.641109500000001,5.17389515,4.449222,5.362606,unsplit,,,, -B-FS-TOOLS,6f,filesystem,native,read_worker_ms,ms,60,0,available,60,32.6151435,35.157436749999995,31.506636,53.177359,unsplit,,,, -B-FS-TOOLS,6f,filesystem,native,write_worker_ms,ms,60,0,available,60,3.620475,4.1908692,3.472649,4.591477,unsplit,,,, -B-FS-TOOLS,6f,filesystem,native,git_worker_ms,ms,60,0,available,60,14.503667,15.87029235,13.365548,16.220334,unsplit,,,, -B-FS-TOOLS,6f,filesystem,native,rg_worker_ms,ms,60,0,available,60,7.35332,9.12002745,6.210087,10.598316,unsplit,,,, -B-FS-TOOLS,6f,filesystem,native,cargo_worker_ms,ms,60,0,available,60,51.001313999999994,53.7769879,46.616801,55.215895,unsplit,,,, -B-FS-TOOLS,6f,filesystem,native,npm_worker_ms,ms,60,0,available,60,170.5554055,174.53612955,160.418952,176.741996,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-host,ready_ms,ms,60,0,available,60,50.3116885,51.86005975,48.723523,53.31195,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-host,result_ms,ms,60,0,available,60,446.934048,451.42736494999997,431.481291,463.831691,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-host,completion_ms,ms,60,0,available,60,466.55926999999997,477.7210124,446.443268,489.674762,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-host,metadata_worker_ms,ms,60,0,available,60,4.629473,5.19579745,4.51267,5.306872,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-host,read_worker_ms,ms,60,0,available,60,31.726145,32.139979849999996,30.671117,32.341854,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-host,write_worker_ms,ms,60,0,available,60,3.6607655,4.23614365,3.434579,4.300063,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-host,git_worker_ms,ms,60,0,available,60,14.3027315,15.4673852,13.406535,15.856385,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-host,rg_worker_ms,ms,60,0,available,60,7.2227815,9.18533375,6.311987,9.701521,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-host,cargo_worker_ms,ms,60,0,available,60,50.814181000000005,53.590199600000005,45.446097,55.271089,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-host,npm_worker_ms,ms,60,0,available,60,170.220099,173.9290419,165.183504,176.402917,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-staged,ready_ms,ms,60,0,available,60,62.7551585,64.71813474999999,61.03481,78.609021,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-staged,result_ms,ms,60,0,available,60,855.4340265000001,879.9626576,823.212071,901.514323,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-staged,completion_ms,ms,60,0,available,60,1087.9090965,1133.47125295,1059.776843,1168.342319,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-staged,metadata_worker_ms,ms,60,0,available,60,73.5262865,75.310744,72.107391,76.874656,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-staged,read_worker_ms,ms,60,0,available,60,66.97873,73.16035419999997,64.76068,90.741488,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-staged,write_worker_ms,ms,60,0,available,60,26.2285335,28.0284303,25.085366,28.483614,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-staged,git_worker_ms,ms,60,0,available,60,120.013295,123.4570139,116.284012,125.390948,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-staged,rg_worker_ms,ms,60,0,available,60,84.812746,87.0158754,81.899975,88.733742,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-staged,cargo_worker_ms,ms,60,0,available,60,71.7562705,90.997613,66.586693,97.508324,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-staged,npm_worker_ms,ms,60,0,available,60,226.6667185,235.35751175000001,218.374569,236.520729,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-vm,ready_ms,ms,60,0,available,60,645.5001139999999,666.3695806,627.65156,673.798754,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-vm,result_ms,ms,60,0,available,60,4080.6987475,4389.8365378,3707.093396,4415.390039,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-vm,completion_ms,ms,60,0,available,60,4274.0440905,4597.3515632,3908.572492,4615.848642,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-vm,metadata_worker_ms,ms,60,0,available,60,152.966238,192.58089089999999,113.383179,207.592094,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-vm,read_worker_ms,ms,60,0,available,60,116.44990200000001,126.5072706,113.621026,136.267095,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-vm,write_worker_ms,ms,60,0,available,60,135.157989,160.57990535,95.251008,169.256275,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-vm,git_worker_ms,ms,60,0,available,60,,670.2830111,339.313361,687.730789,separated-clusters,43,352.112209,17,665.459033 -B-FS-TOOLS,6f,filesystem,pvisor-vm,rg_worker_ms,ms,60,0,available,60,434.070974,452.5059095,417.14269,453.968307,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-vm,cargo_worker_ms,ms,60,0,available,60,482.69275600000003,496.9210567,449.864108,498.039945,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-vm,npm_worker_ms,ms,60,0,available,60,1320.161248,1341.16265765,982.658604,1347.666952,unsplit,,,, -B-FS-TOOLS,6f,filesystem,docker,ready_ms,ms,60,0,available,60,304.26727200000005,318.89538605,289.097849,322.063816,unsplit,,,, -B-FS-TOOLS,6f,filesystem,docker,result_ms,ms,60,0,available,60,743.5170740000001,763.6446859499999,712.560459,773.706595,unsplit,,,, -B-FS-TOOLS,6f,filesystem,docker,completion_ms,ms,60,0,available,60,819.097779,848.1057026000001,785.880926,885.639052,unsplit,,,, -B-FS-TOOLS,6f,filesystem,docker,metadata_worker_ms,ms,60,0,available,60,4.6162475,5.2411318,4.503383,5.286664,unsplit,,,, -B-FS-TOOLS,6f,filesystem,docker,read_worker_ms,ms,60,0,available,60,32.639399,48.5405815,31.293198,58.949029,unsplit,,,, -B-FS-TOOLS,6f,filesystem,docker,write_worker_ms,ms,60,0,available,60,3.8166659999999997,4.389662749999999,3.61746,4.851903,unsplit,,,, -B-FS-TOOLS,6f,filesystem,docker,git_worker_ms,ms,60,0,available,60,14.42278,15.43605755,13.110323,15.679054,unsplit,,,, -B-FS-TOOLS,6f,filesystem,docker,rg_worker_ms,ms,60,0,available,60,6.864732999999999,8.571483899999999,5.467331,9.356478,unsplit,,,, -B-FS-TOOLS,6f,filesystem,docker,cargo_worker_ms,ms,60,0,available,60,48.141624,50.7564907,43.133882,51.876843,unsplit,,,, -B-FS-TOOLS,6f,filesystem,docker,npm_worker_ms,ms,60,0,available,60,215.771378,220.7936918,207.96636,227.922485,unsplit,,,, -B-FS-TOOLS,6f,filesystem,firecracker,ready_ms,ms,60,0,available,60,892.2389115000001,910.39147955,880.805193,971.501006,unsplit,,,, -B-FS-TOOLS,6f,filesystem,firecracker,result_ms,ms,60,0,available,60,2241.676295,2269.6311304,2168.287977,2322.53717,unsplit,,,, -B-FS-TOOLS,6f,filesystem,firecracker,completion_ms,ms,60,0,available,60,2294.594474,2330.73949115,2224.284116,2379.330947,unsplit,,,, -B-FS-TOOLS,6f,filesystem,firecracker,metadata_worker_ms,ms,60,0,available,60,23.4311765,25.91937505,22.497981,26.574197,unsplit,,,, -B-FS-TOOLS,6f,filesystem,firecracker,read_worker_ms,ms,60,0,available,60,79.7937295,80.76123965,78.436668,81.201274,unsplit,,,, -B-FS-TOOLS,6f,filesystem,firecracker,write_worker_ms,ms,60,0,available,60,43.1106805,43.4686728,42.745951,43.627144,unsplit,,,, -B-FS-TOOLS,6f,filesystem,firecracker,git_worker_ms,ms,60,0,available,60,129.974651,131.7618373,81.099614,133.869808,unsplit,,,, -B-FS-TOOLS,6f,filesystem,firecracker,rg_worker_ms,ms,60,0,available,60,15.8222085,17.270146,13.78636,18.510551,unsplit,,,, -B-FS-TOOLS,6f,filesystem,firecracker,cargo_worker_ms,ms,60,0,available,60,373.87753,391.22986235,357.174434,396.021742,unsplit,,,, -B-FS-TOOLS,6f,filesystem,firecracker,npm_worker_ms,ms,60,0,available,60,546.5018875000001,557.1935717499999,511.303333,559.989116,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu,ready_ms,ms,60,0,available,60,509.96446949999995,529.9265078499999,500.582695,573.519603,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu,result_ms,ms,60,0,available,60,1448.6987795,1464.8185786,1399.031148,1519.407691,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu,completion_ms,ms,60,0,available,60,1493.774382,1512.9859668000001,1445.566768,1557.982624,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu,metadata_worker_ms,ms,60,0,available,60,15.9239355,17.25538915,14.682698,18.692158,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu,read_worker_ms,ms,60,0,available,60,37.431223,39.95221694999999,35.397689,41.541492,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu,write_worker_ms,ms,60,0,available,60,5.0229405,5.4307787,4.78764,5.591216,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu,git_worker_ms,ms,60,0,available,60,80.27290400000001,88.06932905,70.208037,89.033935,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu,rg_worker_ms,ms,60,0,available,60,12.572064000000001,14.233104999999998,11.012685,16.505808,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu,cargo_worker_ms,ms,60,0,available,60,260.3383265,265.7006174,246.18703,267.048997,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu,npm_worker_ms,ms,60,0,available,60,399.416952,405.5594957,381.613346,408.54634,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu-microvm,ready_ms,ms,60,0,available,60,395.825773,402.77567065,382.520527,403.880963,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu-microvm,result_ms,ms,60,0,available,60,1450.5053165,1494.7084255,1370.234255,1511.26061,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu-microvm,completion_ms,ms,60,0,available,60,1496.0018344999999,1544.1113959999998,1410.115726,1557.370031,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu-microvm,metadata_worker_ms,ms,60,0,available,60,19.9123065,21.01612415,16.159798,21.152792,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu-microvm,read_worker_ms,ms,60,0,available,60,40.1853485,42.9384862,35.101444,43.804887,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu-microvm,write_worker_ms,ms,60,0,available,60,4.9870885000000005,5.52697975,4.741052,5.674583,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu-microvm,git_worker_ms,ms,60,0,available,60,155.025384,166.07573555,96.507041,174.913846,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu-microvm,rg_worker_ms,ms,60,0,available,60,13.029406999999999,14.6376077,11.522019,15.511221,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu-microvm,cargo_worker_ms,ms,60,0,available,60,296.9366325,307.6363157,275.839424,309.551662,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu-microvm,npm_worker_ms,ms,60,0,available,60,421.368042,438.87622930000003,399.675662,440.234561,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,native,ready_ms,ms,30,0,available,30,182.465548,184.66115815,180.50758,185.578294,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,native,result_ms,ms,30,0,available,30,987.9138695,1001.8607265,977.11348,1044.324118,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,native,completion_ms,ms,30,0,available,30,995.803697,1010.01738555,985.049088,1053.914362,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,native,metadata_worker_ms,ms,30,0,available,30,4.7363165,5.1789204,4.50331,5.361775,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,native,read_worker_ms,ms,30,0,available,30,32.178629,34.753323900000005,30.518614,71.186124,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,native,write_worker_ms,ms,30,0,available,30,3.710751,4.9750842,3.545145,12.282307,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,native,git_worker_ms,ms,30,0,available,30,15.5089435,18.981597100000002,14.618774,23.83834,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,native,rg_worker_ms,ms,30,0,available,30,7.832204000000001,9.151287649999999,6.211545,9.758558,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,native,cargo_worker_ms,ms,30,0,available,30,52.961877,59.60838480000001,49.294797,71.030684,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,native,npm_worker_ms,ms,30,0,available,30,212.5488825,219.6670915,207.729456,220.383747,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-host,ready_ms,ms,30,0,available,30,203.8203595,214.81943165,199.809798,223.144231,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-host,result_ms,ms,30,0,available,30,1040.7144525,1093.4281973500001,1027.069683,1112.758687,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-host,completion_ms,ms,30,0,available,30,1065.4718014999999,1128.24778055,1051.502181,1156.259654,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-host,metadata_worker_ms,ms,30,0,available,30,4.7314315,5.080784850000001,4.51399,5.209561,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-host,read_worker_ms,ms,30,0,available,30,32.829995,52.8599144,31.833638,61.459125,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-host,write_worker_ms,ms,30,0,available,30,3.7882759999999998,11.342525250000003,3.590529,41.182301,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-host,git_worker_ms,ms,30,0,available,30,16.672291,20.84634455,14.790798,38.526144,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-host,rg_worker_ms,ms,30,0,available,30,7.850669,9.7832336,6.093914,10.048358,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-host,cargo_worker_ms,ms,30,0,available,30,58.9320695,74.15180705,51.194873,76.845696,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-host,npm_worker_ms,ms,30,0,available,30,225.98711300000002,232.34957545,217.264237,236.371774,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-staged,ready_ms,ms,30,0,available,30,216.4099005,225.30323005,212.352451,226.313958,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-staged,result_ms,ms,30,0,available,30,1528.2822959999999,1584.4384019499998,1477.329154,1624.337424,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-staged,completion_ms,ms,30,0,available,30,1804.251033,1871.2055623,1744.139498,2003.345921,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-staged,metadata_worker_ms,ms,30,0,available,30,82.91090550000001,85.85953435,67.877186,87.453979,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-staged,read_worker_ms,ms,30,0,available,30,77.5371585,81.17731715000001,70.870722,101.522545,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-staged,write_worker_ms,ms,30,0,available,30,32.004799,41.0214825,27.877215,146.34535,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-staged,git_worker_ms,ms,30,0,available,30,163.4594045,189.6700826,147.495196,195.909548,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-staged,rg_worker_ms,ms,30,0,available,30,107.096908,116.3114228,98.449463,117.464964,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-staged,cargo_worker_ms,ms,30,0,available,30,90.0207565,94.58179555000001,81.224402,110.726564,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-staged,npm_worker_ms,ms,30,0,available,30,244.92260249999998,255.76343315,238.350971,265.638176,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-vm,ready_ms,ms,30,0,available,30,752.7288444999999,1090.86519745,713.56419,1467.695682,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-vm,result_ms,ms,30,0,available,30,5162.495756,6125.701362350001,4668.148745,7363.038623,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-vm,completion_ms,ms,30,0,available,30,5459.640924,6395.156288600001,4894.636097,7626.123884,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-vm,metadata_worker_ms,ms,30,0,available,30,177.2839475,235.70400335000002,130.330329,252.913481,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-vm,read_worker_ms,ms,30,0,available,30,135.598514,161.00462620000002,128.525866,250.076331,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-vm,write_worker_ms,ms,30,0,available,30,155.559127,183.81163030000002,140.975503,200.292732,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-vm,git_worker_ms,ms,30,0,available,30,,880.12217625,458.712227,917.710762,separated-clusters,24,503.527761,6,869.803677 +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-vm,rg_worker_ms,ms,30,0,available,30,427.324521,462.2170549,402.203797,480.548157,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-vm,cargo_worker_ms,ms,30,0,available,30,,918.0943852,548.518191,983.825618,separated-clusters,23,579.03501,7,897.466328 +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-vm,npm_worker_ms,ms,30,0,available,30,1115.768597,1595.5712444,936.477456,2778.370618,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,docker,ready_ms,ms,30,0,available,30,396.0094235,646.9344247000001,311.611065,1072.09258,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,docker,result_ms,ms,30,0,available,30,1197.7573805,1452.3970861000003,1114.786491,1894.671694,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,docker,completion_ms,ms,30,0,available,30,1265.9014955,1515.6430474000001,1168.358998,1949.623603,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,docker,metadata_worker_ms,ms,30,0,available,30,4.818345,5.32187935,4.663822,5.453125,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,docker,read_worker_ms,ms,30,0,available,30,32.5054265,34.59822705,31.221144,36.343896,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,docker,write_worker_ms,ms,30,0,available,30,3.8697475,4.45431805,3.624247,4.471353,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,docker,git_worker_ms,ms,30,0,available,30,15.711521000000001,16.7675555,14.958961,17.373886,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,docker,rg_worker_ms,ms,30,0,available,30,8.3253815,9.1626135,5.84598,9.857111,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,docker,cargo_worker_ms,ms,30,0,available,30,48.8048745,54.11412685,45.643443,61.830119,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,docker,npm_worker_ms,ms,30,0,available,30,216.831633,221.65962705,212.204345,224.303499,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,firecracker,ready_ms,ms,30,0,available,30,846.637871,865.51308065,838.879562,883.623234,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,firecracker,result_ms,ms,30,0,available,30,2625.3391895000004,2691.9045444999997,2574.05605,2698.165712,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,firecracker,completion_ms,ms,30,0,available,30,2672.6062035,2744.7993392,2616.419595,2750.43502,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,firecracker,metadata_worker_ms,ms,30,0,available,30,21.9709575,24.2486421,21.414358,24.43863,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,firecracker,read_worker_ms,ms,30,0,available,30,80.21735749999999,81.191374,78.859215,83.965081,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,firecracker,write_worker_ms,ms,30,0,available,30,43.005108,44.146844550000004,42.666974,44.957088,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,firecracker,git_worker_ms,ms,30,0,available,30,,138.15357565000002,80.67993,152.140491,separated-clusters,10,82.33407249999999,20,130.9950965 +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,firecracker,rg_worker_ms,ms,30,0,available,30,16.0459205,18.51288275,11.259585,21.360006,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,firecracker,cargo_worker_ms,ms,30,0,available,30,386.11010650000003,421.28405555,359.364871,450.699631,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,firecracker,npm_worker_ms,ms,30,0,available,30,556.7606995,586.09519605,527.486842,589.740707,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu,ready_ms,ms,30,0,available,30,483.92039350000005,730.3337502,472.093352,820.122658,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu,result_ms,ms,30,0,available,30,2149.8479445000003,2475.0982949,1820.90384,2624.344942,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu,completion_ms,ms,30,0,available,30,2191.3163945,2532.19803595,1870.616908,2687.187849,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu,metadata_worker_ms,ms,30,0,available,30,16.743564499999998,24.5816776,14.681197,34.06964,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu,read_worker_ms,ms,30,0,available,30,,96.6300496,36.733986,100.826339,separated-clusters,19,41.182007,11,92.980909 +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu,write_worker_ms,ms,30,0,available,30,,49.50765065,4.811522,55.025523,separated-clusters,22,8.0437125,8,43.339612 +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu,git_worker_ms,ms,30,0,available,30,89.1570595,108.8193965,75.078953,119.699847,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu,rg_worker_ms,ms,30,0,available,30,14.669064500000001,16.5042948,12.284764,16.946815,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu,cargo_worker_ms,ms,30,0,available,30,373.7537235,447.32145765,257.357305,453.279028,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu,npm_worker_ms,ms,30,0,available,30,534.187536,587.56610565,401.274419,610.731741,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu-microvm,ready_ms,ms,30,0,available,30,415.15016049999997,766.3505122,358.240682,784.439592,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu-microvm,result_ms,ms,30,0,available,30,2287.842705,2739.20379145,1824.908763,2851.011222,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu-microvm,completion_ms,ms,30,0,available,30,2354.800349,2790.853572,1873.416963,2896.791151,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu-microvm,metadata_worker_ms,ms,30,0,available,30,19.981319999999997,27.46570415,16.268874,29.255514,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu-microvm,read_worker_ms,ms,30,0,available,30,74.22920450000001,101.97725285,38.323857,108.963527,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu-microvm,write_worker_ms,ms,30,0,available,30,,45.767617200000004,4.85331,54.743675,separated-clusters,17,9.76511,13,43.537202 +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu-microvm,git_worker_ms,ms,30,0,available,30,168.137142,193.96028055000002,107.810104,202.634229,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu-microvm,rg_worker_ms,ms,30,0,available,30,16.1324485,23.8998906,12.319789,32.315682,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu-microvm,cargo_worker_ms,ms,30,0,available,30,430.33756800000003,502.6895008,288.163707,520.255369,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu-microvm,npm_worker_ms,ms,30,0,available,30,581.331136,668.5946378,442.379572,707.33894,unsplit,,,, diff --git a/docs/src/en/benchmarks/filesystem.md b/docs/src/en/benchmarks/filesystem.md index e7d0201ca..84c13734a 100644 --- a/docs/src/en/benchmarks/filesystem.md +++ b/docs/src/en/benchmarks/filesystem.md @@ -2,7 +2,7 @@ ## Conclusions {#conclusions} -**Seven tools through exit take 0.47 s in pVisor host, 1.09 s in staged and 4.27 s in VM; Docker takes 0.82 s, Firecracker 2.29 s and QEMU microvm 1.50 s. Staging adds file-access cost for review, and VM tool costs remain substantial.** +**With two CPUs, warm host caches and task-local tool caches, seven tools through exit take 1.07 s in pVisor host, 1.80 s staged and 5.46 s in VM; Docker takes 1.27 s, Firecracker 2.67 s and QEMU microvm 2.35 s. Budget complete waiting and individual operations separately; separated clusters retain their medians and counts.** | Need | Selection implication | | --- | --- | @@ -10,7 +10,13 @@ | Independent guest kernel | Budget both startup and VM tool waiting | | Concurrent or idle environments | Require fixed-budget throughput and physical-memory measurements | -These timings are the 2026-10-06 cross-runtime baseline and do not include subsequent cache optimizations. Current cache experiments do not establish new performance conclusions for default staged, VM or complete review tasks; use this baseline for selection. +This cross-runtime seven-tool cohort uses the selected frozen source/artifact and passed full acceptance on 2026-10-12. Ordinary rootfs/workspace DAX is disabled; new VM writeback is disabled by default and stage synchronization is serial. Historical cohorts stay separate. Isolation, caches, listeners and artifacts differ, so no cross-batch optimization speedup is calculated. + + + +Accepted cohort `fo5a4e7d2b-f`: thirty trials per backend across eight backends, with tool-internal phases and complete tasks reported separately. Historical data and engineering A/B remain separate; samples are not pooled. + + ## Motivation {#motivation} @@ -18,56 +24,74 @@ Agent tool loops traverse, read, write, search, compile and install dependencies ## Experiment design {#interpretation} -Linux x86_64, AMD Ryzen 7 9700X, Fedora 7.2.8-200.fc44.x86_64. Tool payloads and the private Docker daemon are pinned to CPUs 0,1; container initialization affinity over its complete lifetime is unverified. VMs use 2 vCPU, 128 MiB for the shell probe and 16 GiB for tools. Native/Docker memory is uncapped, so these results do not establish capacity under identical resource limits. Host/staged use rootless_process. +Linux x86_64, AMD Ryzen 7 9700X, Fedora 7.2.8-200.fc44.x86_64, Btrfs workspaces. Coordinator, payloads and the private rootless Docker daemon are pinned to CPUs 0,1 and alternate within one private cgroup parent: total CPU quota two, memory limit 16 GiB, zero swap. Each VM has 2 vCPU and 16 GiB guest RAM; guest configured capacity differs from the host-group limit. Shared prepared tool/image caches charged before this parent started remain outside its accounting. Process/thread checks observe visible moments and do not prove complete CPU placement of every short-lived process. Host/staged use rootless_process. Private user/mount namespaces and an owned pivot_root isolate existing host listeners. Namespace preparation is untimed; the private listener is reused after capability checks and warmups. Private /tmp is a 2 GiB tmpfs; workspace and stage remain on Btrfs, with Docker binds pointing to the same physical directories. The user cgroup lacks delegated cpuset, so inherited affinity and the two-core quota are used, retaining the limits of visible process/thread observations. -All backends share offline tools and fixed inputs, with a new workspace per trial. Warm caches, three warmups and 60 measured samples per cell; backends alternate in seeded randomized order. Preparation, builds, image import and fixture resets are outside timing; complete tasks include launch and exit. Docker Engine 29.7.2 uses a private rootless **overlay2** daemon, the classic image store and writable bind mounts. Firecracker 1.13.1 PCI runs without jailer; QEMU 10.2.2 uses q35/microvm and private ext4. pVisor VM uses virtio-fs and its own firmware. Kernels, storage and staging semantics differ: these are task costs for the stated configurations, not pure VMM or security rankings. +All backends share offline tools and fixed inputs, with a new workspace per trial. Warm host caches, three warmups and 30 measured samples per cell; seed 20261012 randomizes backends each round. Preparation, builds, image import and input resets are outside timing; complete tasks include launch and exit. Executor-provided TMPDIR is preserved, with fresh private HOME and Cargo caches per task and the `NODE_COMPILE_CACHE` environment variable configured; Python does not read/write bytecode. Seven-operation Cargo uses independent `_cargo-home`, `_tmp` and `_cargo-target` in the workspace. Temporary storage varies with executor configuration and contributes to task cost; directory policy and child inheritance are validated. Per-trial gates check cache variables, parent/child inheritance and actual Python cache behavior; actual Node API enablement and the Node cache-directory filesystem type are unknown for this cohort. Independent October 10 environment checks observe Node caches on tmpfs for native/host/staged/pVisor VM and overlayfs for Docker. Reference VM output reports ext4, but its environment Ready gate fails. The [cache observation CSV](filesystem-cache-policy.csv) retains types and failure states separately; these historical checks supply no timing or new cache-type evidence for this cohort; cache directories and parent/child inheritance are checked in every new trial. -The workload traverses 2,048 files in 32 directories, reads and SHA256-checks 64 MiB, writes 256 × 64 KiB, runs git status and rg, compiles 64 dependency-free Cargo modules and installs 32 offline npm packages. Operations include validation but exclude launch/exit; the complete task includes all seven and exit. Large repositories, cold disks, online registries and concurrent throughput are unmeasured. +Docker Engine 29.7.2 uses a private rootless overlay2 daemon, classic image storage and a writable bind mount. Firecracker PCI uses a frozen independent custom reference kernel without jailer; QEMU q35/microvm use the same reference kernel and private ext4. pVisor VM uses virtio-fs and frozen pVisor firmware. These Firecracker/QEMU tool samples do not measure stock distribution kernels, and the [independent stock startup results](startup.md) cannot substitute for them. Kernels, storage and staging semantics differ: these are task costs for the stated configurations, not pure VMM or security rankings. -These samples do not verify equal Node/npm compile-cache state across backends. A fresh workspace does not establish an empty tool cache. npm and complete-task differences include each configuration's cache behavior; they cannot all be attributed to FUSE, staging or the VMM. +The workload traverses 2,048 files in 32 directories, reads and SHA256-checks 64 MiB, writes 256 × 64 KiB, runs git status and rg, compiles 64 dependency-free Cargo modules and installs 32 offline npm packages. Operations exclude launch/exit; traversal and reading include their workload checks. Full-byte validation after writing is outside the write timer but remains in complete-task timing. The complete task includes all seven, validation and exit. Large repositories, cold disks, online registries and concurrent throughput are unmeasured. ## Data and analysis {#results} -Measured on 2026-10-06: each backend/workload has 60/60 valid samples and zero measured failures. Outputs, exit and execution records must pass validation; staging also requires unchanged host originals and all 256 upper files with the expected sizes. Complete written bytes were not independently checked. Every valid slow sample is kept, with no timing-based exclusions. Tables normally show P50; separated distributions show cluster medians and counts. P95 is descriptive only. Raw reports, binaries and input/source manifests stay in ignored `.data/`; public CSVs retain workload, cohort and provenance associations. +Measured on 2026-10-12: each backend/workload has 30/30 valid samples, totaling 240 formal tasks with zero formal failures. Capability checks and warmups are excluded. Each sample must pass unique output, exit and execution-record checks; staged/VM also require no written results in the host workspace and complete matching bytes in all 256 upper files. Common input inventories are checked before/after sampling, and parent/child cache policies are verified. Any error or detected same-user Rust build/Python test interference rejects the whole cohort. None was detected, and every valid slow sample remains. Process polling every 0.5 s can miss short, inaccessible or unrecognized host activity. + +### Launch through exit {#complete-task} + +Seconds, N=30 per backend. Normally P50; separated distributions show cluster medians and counts. P95 is descriptive only, with no P99. + +| Runtime | Valid / failed | Completion P50 s | Completion P95 s (reference) | +| --- | --- | --- | --- | +| Native | 30 / 0 | 1.00 | 1.01 | +| pVisor host | 30 / 0 | 1.07 | 1.13 | +| pVisor staged | 30 / 0 | 1.80 | 1.87 | +| pVisor VM | 30 / 0 | 5.46 | 6.40 | +| Docker rootless / overlay2 | 30 / 0 | 1.27 | 1.52 | +| Firecracker PCI | 30 / 0 | 2.67 | 2.74 | +| QEMU q35 | 30 / 0 | 2.19 | 2.53 | +| QEMU microvm | 30 / 0 | 2.35 | 2.79 | + +| Same-cohort completion comparison | Median difference ms | 95% CI ms | Conclusion | +| --- | --- | --- | --- | +| pVisor staged − Docker rootless / overlay2 | +538.35 | [+497.98, +570.62] | Slower | +| pVisor VM − QEMU microvm | +3104.84 | [+2836.53, +3317.20] | Slower | + +pVisor staged minus Docker rootless / overlay2 completion median difference is +538.35 ms, 95% CI [+497.98, +570.62] ms; pVisor VM minus QEMU microvm completion median difference is +3104.84 ms, 95% CI [+2836.53, +3317.20] ms. Intervals use 5,000 same-cohort paired-round bootstrap resamples (seed 20261006). Valid slow samples remain; separated distributions have no single median ranking. Storage and staging capabilities differ, so total gaps cannot identify one component's cost. ### Seven tool operations {#reference-fs} -Milliseconds; P50 or separated-cluster medians and counts. +Milliseconds, N=30 per backend/operation; P50 or separated-cluster medians and counts. Operation times exclude startup and teardown; summing their medians cannot replace complete-task timing. | Operation | Native | pVisor host | pVisor staged | pVisor VM | Docker rootless / overlay2 | Firecracker PCI | QEMU q35 | QEMU microvm | | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| Traverse 2,048 files | 4.64 | 4.63 | 73.53 | 152.97 | 4.62 | 23.43 | 15.92 | 19.91 | -| Read and verify 64 MiB | 32.62 | 31.73 | 66.98 | 116.45 | 32.64 | 79.79 | 37.43 | 40.19 | -| Write 256 files | 3.62 | 3.66 | 26.23 | 135.16 | 3.82 | 43.11 | 5.02 | 4.99 | -| git status | 14.50 | 14.30 | 120.01 | 352.11 (43/60); 665.46 (17/60) | 14.42 | 129.97 | 80.27 | 155.03 | -| Ripgrep search | 7.35 | 7.22 | 84.81 | 434.07 | 6.86 | 15.82 | 12.57 | 13.03 | -| Offline Cargo build | 51.00 | 50.81 | 71.76 | 482.69 | 48.14 | 373.88 | 260.34 | 296.94 | -| Offline npm install | 170.56 | 170.22 | 226.67 | 1320.16 | 215.77 | 546.50 | 399.42 | 421.37 | - -### Launch through exit {#complete-task} +| Traverse 2,048 files | 4.74 | 4.73 | 82.91 | 177.28 | 4.82 | 21.97 | 16.74 | 19.98 | +| Read and verify 64 MiB | 32.18 | 32.83 | 77.54 | 135.60 | 32.51 | 80.22 | 41.18 (n=19); 92.98 (n=11) | 74.23 | +| Write 256 files | 3.71 | 3.79 | 32.00 | 155.56 | 3.87 | 43.01 | 8.04 (n=22); 43.34 (n=8) | 9.77 (n=17); 43.54 (n=13) | +| git status | 15.51 | 16.67 | 163.46 | 503.53 (n=24); 869.80 (n=6) | 15.71 | 82.33 (n=10); 131.00 (n=20) | 89.16 | 168.14 | +| Ripgrep search | 7.83 | 7.85 | 107.10 | 427.32 | 8.33 | 16.05 | 14.67 | 16.13 | +| Offline Cargo build | 52.96 | 58.93 | 90.02 | 579.04 (n=23); 897.47 (n=7) | 48.80 | 386.11 | 373.75 | 430.34 | +| Offline npm install | 212.55 | 225.99 | 244.92 | 1115.77 | 216.83 | 556.76 | 534.19 | 581.33 | -| Runtime | Valid / failed | Completion P50 s | Completion P95 s | -| --- | --- | --- | --- | -| Native | 60 / 0 | 0.45 | 0.46 | -| pVisor host | 60 / 0 | 0.47 | 0.48 | -| pVisor staged | 60 / 0 | 1.09 | 1.13 | -| pVisor VM | 60 / 0 | 4.27 | 4.60 | -| Docker rootless / overlay2 | 60 / 0 | 0.82 | 0.85 | -| Firecracker PCI | 60 / 0 | 2.29 | 2.33 | -| QEMU q35 | 60 / 0 | 1.49 | 1.51 | -| QEMU microvm | 60 / 0 | 1.50 | 1.54 | -Staged minus Docker completion median is +268.81 ms, with a paired-bootstrap 95% interval of [+264.64, +271.15] ms. VM minus QEMU microvm is +2778.04 ms, interval [+2754.17, +2807.74] ms. Staging capabilities and storage configurations differ; total gaps do not identify the cost of one component. +Traversal, Git and search access the staged file view. Reading, compilation and offline installation also include validation, tool execution and each executor's temporary storage. Set timeouts using the actual tool mix and complete waiting, beyond startup speed alone. -Full Ubuntu filesystem workloads have not been retested with the current artifacts. +Full Ubuntu filesystem workloads have not been measured with these current build and resource conditions. -### Where cache optimizations apply {#cache-status} +### Where filesystem optimizations apply {#cache-status} -Immutable-lower physical metadata caching has an independent engineering A/B, with gains limited to Linux HOST FUSE configurations that explicitly declare and maintain lower stability. Host rootfs, OCI extraction directories and lazy-image local projections receive no automatic immutability promise. The experiment does not cover the complete review workflow and cannot be used to rescale staged or VM timings above. See the [immutable-lower cache analysis](../design/filesystem-performance-analysis.md#immutable-lower-cache) for the design, results and confidence intervals. +The frozen build includes host copy-up scheduling, read queues, journal locks, idle dispatch and subsequent VM queue/I/O-fence fixes. Independent engineering A/B is in the [complete evidence scope](../design/filesystem-performance-analysis.md#filesystem-status). Cross-runtime results measure current default configurations; local engineering percentages do not rescale these data. -Extended kernel caching is an explicit Linux HOST API option. Writable long-lived metadata caching requires immutable lowers, exclusive upper/work ownership and stable backing metadata; KEEP_CACHE supports stable read-only regular files only. Configurations incompatible with journals, read-observation metrics or custom path policies are rejected rather than silently omitting observations. Current-source formal timing was contaminated by concurrent build checks, so no speedup magnitude has passed acceptance. This mode is not integrated into default `pvisor run` or VM execution. See the [kernel-cache analysis](../design/filesystem-performance-analysis.md#host-kernel-cache) for capability boundaries and validation status. +All eight backends have now been remeasured with the current frozen artifact and passed correctness, input, budget and interference gates. Earlier unaccepted attempts remain separate and contribute no samples. This new cross-runtime cohort alone does not establish the causal benefit of individual changes. + +Ordinary rootfs/workspace DAX is disabled by default and allocates no window. Immutable shares retain explicit opt-in; see the [DAX comparison](../design/filesystem-performance-analysis.md#readonly-dax-ab). New VM writeback is disabled by default, with explicit `PVISOR_VM_FS_WRITEBACK=1` / `0` configuration. By default, restoring a negotiated snapshot retains its mode; explicit `PVISOR_VM_FS_WRITEBACK=0` rejects a snapshot with writeback enabled. The independent [writeback comparison](../design/filesystem-performance-analysis.md#writeback-ab) retains its own frozen artifact and does not rescale this cohort's task times. + +Immutable-lower physical metadata caching requires an explicit caller declaration and lifetime stability. Host rootfs, OCI extraction directories and lazy-image local projections receive no automatic promise. Extended Linux HOST kernel caching is not integrated into default `pvisor run` or VM, and its formal timing has not passed acceptance. See the [immutable-lower cache analysis](../design/filesystem-performance-analysis.md#immutable-lower-cache) and [kernel-cache analysis](../design/filesystem-performance-analysis.md#host-kernel-cache) for capability boundaries. + +Parallel stage synchronization through `PVISOR_STAGE_SYNC_WORKERS` is disabled and still lacks an independent serial/parallel performance comparison. Implementation availability does not establish it as a source of speedup in these tables. ### Downloads and reproduction {#run} -[Derived statistics CSV](filesystem.csv) · [All runtime statistics](runtime-summary.csv) · [Differences and 95% confidence intervals](runtime-comparisons.csv) · [Source and artifact provenance](runtime-provenance.csv) · [Method](methodology.md) · [Reproduction manual](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/pvisor/README.md) +[Accepted filesystem statistics CSV](filesystem.csv) · [Same-cohort differences and 95% confidence intervals](filesystem-comparisons.csv) · [Measured artifact, source and input provenance](filesystem-provenance.csv) · [Acceptance record](../assets/benchmarks/filesystem-publication-audit.json) · [Method](methodology.md) · [Reproduction manual](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/pvisor/README.md#same-host-runtime-comparison) and [whole-parent resource budget](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/pvisor/README.md#explicit-whole-parent-resource-budget) + +Raw evidence is local at `benchmark/.data/fo5a4e7d2b-f/report.json`; frozen source and build receipts are under `benchmark.new/filesystem/.data/fs-next-20261011/trim/`, with execution/acceptance records under `benchmark.new/filesystem/.data/fs-docs-20261012/`. Raw samples and artifacts are not site downloads. Prior accepted cohorts and rejected attempts remain separate in the [source inventory](evidence-sources.csv). Historical [runtime statistics](runtime-summary.csv) and [provenance](runtime-provenance.csv) are not pooled with this cohort. diff --git a/docs/src/en/benchmarks/index.csv b/docs/src/en/benchmarks/index.csv index 9ae126343..7b40c19a3 100644 --- a/docs/src/en/benchmarks/index.csv +++ b/docs/src/en/benchmarks/index.csv @@ -1,8 +1,8 @@ source_document,section,source_conditions,table_context,row,metric,value -index.md,Conclusions {#conclusions},"This reuses separately registered [startup](startup.md), [filesystem](filesystem.md), [repair](agent-tasks.md) and [complete review workflow](supervision-cost.md) experiments. The first three use 60 samples and three warmups per backend; review uses thirty samples and three warmups per size/condition/backend. Same host, CPUs 0,1, warm caches, randomized interleaving and complete correctness checks, with no speed-based exclusions. Workloads, resources and timing boundaries differ across topics; distributions are not pooled. Docker uses rootless overlay2; workflow controls are native Git/reflink, not container or VM security rankings.","**For frequently created large workspaces with sparse changes, pVisor stage completes the machine workflow sooner: changing twenty of 10,000 files and retaining ten takes 141 ms in stage, 248 ms with Git worktree and 343 ms with btrfs reflink. Native workflows are faster for small workspaces; pVisor VM tools still take longer than the tested Firecracker/QEMU configurations.**","Large workspace, sparse changes, disposable view",Selection implication,Stage has a measured complete-workflow advantage -index.md,Conclusions {#conclusions},"This reuses separately registered [startup](startup.md), [filesystem](filesystem.md), [repair](agent-tasks.md) and [complete review workflow](supervision-cost.md) experiments. The first three use 60 samples and three warmups per backend; review uses thirty samples and three warmups per size/condition/backend. Same host, CPUs 0,1, warm caches, randomized interleaving and complete correctness checks, with no speed-based exclusions. Workloads, resources and timing boundaries differ across topics; distributions are not pooled. Docker uses rootless overlay2; workflow controls are native Git/reflink, not container or VM security rankings.","**For frequently created large workspaces with sparse changes, pVisor stage completes the machine workflow sooner: changing twenty of 10,000 files and retaining ten takes 141 ms in stage, 248 ms with Git worktree and 343 ms with btrfs reflink. Native workflows are faster for small workspaces; pVisor VM tools still take longer than the tested Firecracker/QEMU configurations.**",Small workspace or tool execution alone,Selection implication,Compare native workflows and container costs -index.md,Conclusions {#conclusions},"This reuses separately registered [startup](startup.md), [filesystem](filesystem.md), [repair](agent-tasks.md) and [complete review workflow](supervision-cost.md) experiments. The first three use 60 samples and three warmups per backend; review uses thirty samples and three warmups per size/condition/backend. Same host, CPUs 0,1, warm caches, randomized interleaving and complete correctness checks, with no speed-based exclusions. Workloads, resources and timing boundaries differ across topics; distributions are not pooled. Docker uses rootless overlay2; workflow controls are native Git/reflink, not container or VM security rankings.","**For frequently created large workspaces with sparse changes, pVisor stage completes the machine workflow sooner: changing twenty of 10,000 files and retaining ten takes 141 ms in stage, 248 ms with Git worktree and 343 ms with btrfs reflink. Native workflows are faster for small workspaces; pVisor VM tools still take longer than the tested Firecracker/QEMU configurations.**",VM or concurrent capacity,Selection implication,Require complete tool and resource measurements -index.md,Data and analysis {#results},"This reuses separately registered [startup](startup.md), [filesystem](filesystem.md), [repair](agent-tasks.md) and [complete review workflow](supervision-cost.md) experiments. The first three use 60 samples and three warmups per backend; review uses thirty samples and three warmups per size/condition/backend. Same host, CPUs 0,1, warm caches, randomized interleaving and complete correctness checks, with no speed-based exclusions. Workloads, resources and timing boundaries differ across topics; distributions are not pooled. Docker uses rootless overlay2; workflow controls are native Git/reflink, not container or VM security rankings.","2026-10-06; 1,440 valid startup/filesystem/repair samples and 360 valid review samples, all with zero failures. Topic pages provide median differences and paired-bootstrap 95% intervals.",[Startup](startup.md),P50 level,host 12.72 ms; staged 25.13 ms; VM 100.73 ms; Docker 74.48 ms -index.md,Data and analysis {#results},"This reuses separately registered [startup](startup.md), [filesystem](filesystem.md), [repair](agent-tasks.md) and [complete review workflow](supervision-cost.md) experiments. The first three use 60 samples and three warmups per backend; review uses thirty samples and three warmups per size/condition/backend. Same host, CPUs 0,1, warm caches, randomized interleaving and complete correctness checks, with no speed-based exclusions. Workloads, resources and timing boundaries differ across topics; distributions are not pooled. Docker uses rootless overlay2; workflow controls are native Git/reflink, not container or VM security rankings.","2026-10-06; 1,440 valid startup/filesystem/repair samples and 360 valid review samples, all with zero failures. Topic pages provide median differences and paired-bootstrap 95% intervals.",[Repair completion](agent-tasks.md),P50 level,staged 0.64 s; Docker 0.81 s; VM 3.25 s; QEMU microvm 1.40 s -index.md,Data and analysis {#results},"This reuses separately registered [startup](startup.md), [filesystem](filesystem.md), [repair](agent-tasks.md) and [complete review workflow](supervision-cost.md) experiments. The first three use 60 samples and three warmups per backend; review uses thirty samples and three warmups per size/condition/backend. Same host, CPUs 0,1, warm caches, randomized interleaving and complete correctness checks, with no speed-based exclusions. Workloads, resources and timing boundaries differ across topics; distributions are not pooled. Docker uses rootless overlay2; workflow controls are native Git/reflink, not container or VM security rankings.","2026-10-06; 1,440 valid startup/filesystem/repair samples and 360 valid review samples, all with zero failures. Topic pages provide median differences and paired-bootstrap 95% intervals.",[Seven-tool completion](filesystem.md),P50 level,staged 1.09 s; Docker 0.82 s; VM 4.27 s; Firecracker 2.29 s -index.md,Data and analysis {#results},"This reuses separately registered [startup](startup.md), [filesystem](filesystem.md), [repair](agent-tasks.md) and [complete review workflow](supervision-cost.md) experiments. The first three use 60 samples and three warmups per backend; review uses thirty samples and three warmups per size/condition/backend. Same host, CPUs 0,1, warm caches, randomized interleaving and complete correctness checks, with no speed-based exclusions. Workloads, resources and timing boundaries differ across topics; distributions are not pooled. Docker uses rootless overlay2; workflow controls are native Git/reflink, not container or VM security rankings.","2026-10-06; 1,440 valid startup/filesystem/repair samples and 360 valid review samples, all with zero failures. Topic pages provide median differences and paired-bootstrap 95% intervals.",[Review workflow](supervision-cost.md),P50 level,"10,000 files: stage 141 ms; Git 248 ms; reflink 343 ms" +index.md,Conclusions {#conclusions},"This reuses separately registered [startup](startup.md), [filesystem](filesystem.md), [repair](agent-tasks.md) and [complete review workflow](supervision-cost.md) experiments. Startup and repair use 60 samples per backend, filesystem thirty, each with three warmups; review uses thirty samples and three warmups per size/condition/backend. Same host, CPUs 0,1, warm caches, randomized interleaving and complete correctness checks, with no speed-based exclusions. Workloads, resources and timing boundaries differ across topics; distributions are not pooled. Docker uses rootless overlay2; workflow controls are native Git/reflink, not container or VM security rankings.","The 2026-10-06 startup/filesystem/repair cohorts have 1,440 valid samples and review has 360; the separate 2026-10-07 stock-kernel startup cohort has 240 valid samples. All have zero measured failures. The current-source filesystem comparison on 2026-10-11 adds 240 valid samples and zero failures, with task-local caches and a common parent resource budget. Cohorts are not pooled; topic pages provide median differences and paired-bootstrap 95% intervals.","Large workspace, sparse changes, disposable view",Selection implication,Stage has a measured complete-workflow advantage +index.md,Conclusions {#conclusions},"This reuses separately registered [startup](startup.md), [filesystem](filesystem.md), [repair](agent-tasks.md) and [complete review workflow](supervision-cost.md) experiments. Startup and repair use 60 samples per backend, filesystem thirty, each with three warmups; review uses thirty samples and three warmups per size/condition/backend. Same host, CPUs 0,1, warm caches, randomized interleaving and complete correctness checks, with no speed-based exclusions. Workloads, resources and timing boundaries differ across topics; distributions are not pooled. Docker uses rootless overlay2; workflow controls are native Git/reflink, not container or VM security rankings.","The 2026-10-06 startup/filesystem/repair cohorts have 1,440 valid samples and review has 360; the separate 2026-10-07 stock-kernel startup cohort has 240 valid samples. All have zero measured failures. The current-source filesystem comparison on 2026-10-11 adds 240 valid samples and zero failures, with task-local caches and a common parent resource budget. Cohorts are not pooled; topic pages provide median differences and paired-bootstrap 95% intervals.",Small workspace or tool execution alone,Selection implication,Compare native workflows and container costs +index.md,Conclusions {#conclusions},"This reuses separately registered [startup](startup.md), [filesystem](filesystem.md), [repair](agent-tasks.md) and [complete review workflow](supervision-cost.md) experiments. Startup and repair use 60 samples per backend, filesystem thirty, each with three warmups; review uses thirty samples and three warmups per size/condition/backend. Same host, CPUs 0,1, warm caches, randomized interleaving and complete correctness checks, with no speed-based exclusions. Workloads, resources and timing boundaries differ across topics; distributions are not pooled. Docker uses rootless overlay2; workflow controls are native Git/reflink, not container or VM security rankings.","The 2026-10-06 startup/filesystem/repair cohorts have 1,440 valid samples and review has 360; the separate 2026-10-07 stock-kernel startup cohort has 240 valid samples. All have zero measured failures. The current-source filesystem comparison on 2026-10-11 adds 240 valid samples and zero failures, with task-local caches and a common parent resource budget. Cohorts are not pooled; topic pages provide median differences and paired-bootstrap 95% intervals.",VM or concurrent capacity,Selection implication,Require complete tool and resource measurements +index.md,Data and analysis {#results},"This reuses separately registered [startup](startup.md), [filesystem](filesystem.md), [repair](agent-tasks.md) and [complete review workflow](supervision-cost.md) experiments. Startup and repair use 60 samples per backend, filesystem thirty, each with three warmups; review uses thirty samples and three warmups per size/condition/backend. Same host, CPUs 0,1, warm caches, randomized interleaving and complete correctness checks, with no speed-based exclusions. Workloads, resources and timing boundaries differ across topics; distributions are not pooled. Docker uses rootless overlay2; workflow controls are native Git/reflink, not container or VM security rankings.","The 2026-10-06 startup/filesystem/repair cohorts have 1,440 valid samples and review has 360; the separate 2026-10-07 stock-kernel startup cohort has 240 valid samples. All have zero measured failures. The current-source filesystem comparison on 2026-10-11 adds 240 valid samples and zero failures, with task-local caches and a common parent resource budget. Cohorts are not pooled; topic pages provide median differences and paired-bootstrap 95% intervals.",[Startup](startup.md),P50 level,host 12.72 ms; staged 25.13 ms; VM 100.73 ms; Docker 74.48 ms +index.md,Data and analysis {#results},"This reuses separately registered [startup](startup.md), [filesystem](filesystem.md), [repair](agent-tasks.md) and [complete review workflow](supervision-cost.md) experiments. Startup and repair use 60 samples per backend, filesystem thirty, each with three warmups; review uses thirty samples and three warmups per size/condition/backend. Same host, CPUs 0,1, warm caches, randomized interleaving and complete correctness checks, with no speed-based exclusions. Workloads, resources and timing boundaries differ across topics; distributions are not pooled. Docker uses rootless overlay2; workflow controls are native Git/reflink, not container or VM security rankings.","The 2026-10-06 startup/filesystem/repair cohorts have 1,440 valid samples and review has 360; the separate 2026-10-07 stock-kernel startup cohort has 240 valid samples. All have zero measured failures. The current-source filesystem comparison on 2026-10-11 adds 240 valid samples and zero failures, with task-local caches and a common parent resource budget. Cohorts are not pooled; topic pages provide median differences and paired-bootstrap 95% intervals.",[Repair completion](agent-tasks.md),P50 level,staged 0.64 s; Docker 0.81 s; VM 3.25 s; QEMU microvm 1.40 s +index.md,Data and analysis {#results},"This reuses separately registered [startup](startup.md), [filesystem](filesystem.md), [repair](agent-tasks.md) and [complete review workflow](supervision-cost.md) experiments. Startup and repair use 60 samples per backend, filesystem thirty, each with three warmups; review uses thirty samples and three warmups per size/condition/backend. Same host, CPUs 0,1, warm caches, randomized interleaving and complete correctness checks, with no speed-based exclusions. Workloads, resources and timing boundaries differ across topics; distributions are not pooled. Docker uses rootless overlay2; workflow controls are native Git/reflink, not container or VM security rankings.","The 2026-10-06 startup/filesystem/repair cohorts have 1,440 valid samples and review has 360; the separate 2026-10-07 stock-kernel startup cohort has 240 valid samples. All have zero measured failures. The current-source filesystem comparison on 2026-10-11 adds 240 valid samples and zero failures, with task-local caches and a common parent resource budget. Cohorts are not pooled; topic pages provide median differences and paired-bootstrap 95% intervals.",[Seven-tool completion](filesystem.md),P50 level,staged 1.81 s; Docker 1.23 s; VM 5.78 s; Firecracker 2.65 s +index.md,Data and analysis {#results},"This reuses separately registered [startup](startup.md), [filesystem](filesystem.md), [repair](agent-tasks.md) and [complete review workflow](supervision-cost.md) experiments. Startup and repair use 60 samples per backend, filesystem thirty, each with three warmups; review uses thirty samples and three warmups per size/condition/backend. Same host, CPUs 0,1, warm caches, randomized interleaving and complete correctness checks, with no speed-based exclusions. Workloads, resources and timing boundaries differ across topics; distributions are not pooled. Docker uses rootless overlay2; workflow controls are native Git/reflink, not container or VM security rankings.","The 2026-10-06 startup/filesystem/repair cohorts have 1,440 valid samples and review has 360; the separate 2026-10-07 stock-kernel startup cohort has 240 valid samples. All have zero measured failures. The current-source filesystem comparison on 2026-10-11 adds 240 valid samples and zero failures, with task-local caches and a common parent resource budget. Cohorts are not pooled; topic pages provide median differences and paired-bootstrap 95% intervals.",[Review workflow](supervision-cost.md),P50 level,"10,000 files: stage 141 ms; Git 248 ms; reflink 343 ms" diff --git a/docs/src/en/benchmarks/index.md b/docs/src/en/benchmarks/index.md index 9cfb23e78..d75e3dece 100644 --- a/docs/src/en/benchmarks/index.md +++ b/docs/src/en/benchmarks/index.md @@ -10,13 +10,19 @@ | Small workspace or tool execution alone | Compare native workflows and container costs | | VM or concurrent capacity | Require complete tool and resource measurements | + + +The filesystem overview row uses accepted cohort `fo5a4e7d2b-f` from 2026-10-12; other topics retain their separate cohorts. Timing boundaries and artifacts are listed by topic; see [measurement coverage](#measurement-coverage). + + + ## Motivation {#motivation} Selection requires the total cost of producing the same result, beyond startup or a single command. Review/application, execution boundaries and resource use determine which Agent workflows suit pVisor. ## Experiment design {#interpretation} -This reuses separately registered [startup](startup.md), [filesystem](filesystem.md), [repair](agent-tasks.md) and [complete review workflow](supervision-cost.md) experiments. The first three use 60 samples and three warmups per backend; review uses thirty samples and three warmups per size/condition/backend. Same host, CPUs 0,1, warm caches, randomized interleaving and complete correctness checks, with no speed-based exclusions. Workloads, resources and timing boundaries differ across topics; distributions are not pooled. Docker uses rootless overlay2; workflow controls are native Git/reflink, not container or VM security rankings. +This reuses separately registered [startup](startup.md), [filesystem](filesystem.md), [repair](agent-tasks.md) and [complete review workflow](supervision-cost.md) experiments. Startup and repair use 60 samples per backend, filesystem thirty, each with three warmups; review uses thirty samples and three warmups per size/condition/backend. Same host, CPUs 0,1, warm caches, randomized interleaving and complete correctness checks, with no speed-based exclusions. Workloads, resources and timing boundaries differ across topics; distributions are not pooled. Docker uses rootless overlay2; workflow controls are native Git/reflink, not container or VM security rankings. [Network](network.md) uses thirty independent batches and three warmups per condition; each small-request sample is the median of 256 requests. The origin is outside the payload CPU budget and memory is not identically capped. [Capacity](density.md) uses a shared two-core, 2 GiB, zero-swap budget, five rounds per concurrency and separate idle/useful-tool conditions; every failure, unknown outcome and OOM remains counted. @@ -24,13 +30,13 @@ This reuses separately registered [startup](startup.md), [filesystem](filesystem ## Data and analysis {#results} -The 2026-10-06 startup/filesystem/repair cohorts have 1,440 valid samples and review has 360; the separate 2026-10-07 stock-kernel startup cohort has 240 valid samples. All have zero measured failures. Cohorts are not pooled; topic pages provide median differences and paired-bootstrap 95% intervals. +The 2026-10-06 startup/filesystem/repair cohorts have 1,440 valid samples and review has 360; the separate 2026-10-07 stock-kernel startup cohort has 240 valid samples. All have zero measured failures. The selected-source filesystem comparison on 2026-10-12 adds 240 valid samples and zero failures, with task-local caches and a common parent resource budget. Cohorts are not pooled; topic pages provide median differences and paired-bootstrap 95% intervals. | Question | Measured level (timing is P50) | |---|---| | [Startup](startup.md) | Stock controls: VM 100.91 ms; Firecracker 285.04 ms; QEMU microvm 321.56 ms; q35 702.98 ms | | [Repair completion](agent-tasks.md) | staged 0.64 s; Docker 0.81 s; VM 3.25 s; QEMU microvm 1.40 s | -| [Seven-tool completion](filesystem.md) | staged 1.09 s; Docker 0.82 s; VM 4.27 s; Firecracker 2.29 s | +| [Seven-tool completion](filesystem.md) | staged 1.80 s; Docker 1.27 s; VM 5.46 s; Firecracker 2.67 s | | [Review workflow](supervision-cost.md) | 10,000 files: stage 141 ms; Git 248 ms; reflink 343 ms | | [Network](network.md) | Eight-thread small requests: native 0.69 ms; host proxy 10.09 ms; VM 2.10 ms | | [Active capacity](density.md) | 2 GiB: stage/Podman pass all five rounds at 32; VM at 16 | @@ -44,6 +50,14 @@ Net physical-memory savings and useful-task density from compression, trimmed-ke [End-to-end tasks](agent-tasks.md) combines tool waiting, workspace lifecycle and CLI compatibility to analyze Agent sandbox, container, VM and cloud tradeoffs. [Reinforcement learning](compare-rl-infra.md) maps tool costs, active capacity and historical-prefix preparation to rollouts; full training throughput and reward are unmeasured. +### Measurement coverage of the new framework {#measurement-coverage} + +This table separates new measurements from historical evidence. Data is updated only when metrics, inputs, configurations and correctness gates match. Ready, normal exit, internal tool timings, memory and correctness do not substitute for one another. Unprepared experiments produce no new numbers; historical conclusions apply to their original cohorts. + +{{ table("measurement-coverage.render.json", {"markdown":true}) }} + +[Coverage CSV](measurement-coverage.csv). + ### Downloads and reproduction {#run} -[Runtime statistics](runtime-summary.csv) · [Confidence intervals](runtime-comparisons.csv) · [Runtime provenance](runtime-provenance.csv) · [Workflow statistics](workflow-summary.csv) · [Workflow intervals](workflow-comparisons.csv) · [Workflow provenance](workflow-provenance.csv) · [Method](methodology.md) · [Reproduction manual](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/pvisor/README.md) +[Current filesystem statistics](filesystem.csv) · [Same-cohort filesystem intervals](filesystem-comparisons.csv) · [Current filesystem provenance](filesystem-provenance.csv) · [Historical runtime statistics](runtime-summary.csv) · [Historical runtime intervals](runtime-comparisons.csv) · [Historical runtime provenance](runtime-provenance.csv) · [Workflow statistics](workflow-summary.csv) · [Workflow intervals](workflow-comparisons.csv) · [Workflow provenance](workflow-provenance.csv) · [Method](methodology.md) · [Reproduction manual](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/pvisor/README.md) diff --git a/docs/src/en/benchmarks/isolation-tests.md b/docs/src/en/benchmarks/isolation-tests.md index 8b7da316e..4f6d01447 100644 --- a/docs/src/en/benchmarks/isolation-tests.md +++ b/docs/src/en/benchmarks/isolation-tests.md @@ -10,6 +10,12 @@ | Use an OCI writable workspace mount | Writes reach the host directly; provide review and rollback separately | | Allow access to host paths | Tested host keeps host paths visible and is not a workspace file sandbox | + + +The numbers and conclusions on this page still belong to the original historical cohorts. The new framework has no matching rerun yet; see [measurement coverage](index.md#measurement-coverage). + + + ## Motivation {#motivation} Choosing an execution mode requires knowing both the accessible paths and where writes land. A successful command alone cannot distinguish a staged write from a host modification. The boundary also determines which performance controls offer comparable semantics. diff --git a/docs/src/en/benchmarks/lazy-image-startup.md b/docs/src/en/benchmarks/lazy-image-startup.md index 609b3e069..72cf9d48a 100644 --- a/docs/src/en/benchmarks/lazy-image-startup.md +++ b/docs/src/en/benchmarks/lazy-image-startup.md @@ -10,6 +10,12 @@ | Complete image already cached locally | Docker produces checked output sooner; lazy VM still incurs VM/Job startup and teardown | | First deployment of the image service | Budget upstream download, unpack and indexing separately; cached preparation does not measure these costs | + + +The numbers and conclusions on this page still belong to the original historical cohorts. The new framework has no matching rerun yet; see [measurement coverage](index.md#measurement-coverage). + + + ## Motivation {#motivation} Downloading and unpacking a complete image can cost more than a short task in a disposable environment. Choosing on-demand reads requires distinguishing service preparation from client startup and checking whether the advantage persists after caching. diff --git a/docs/src/en/benchmarks/measurement-coverage.csv b/docs/src/en/benchmarks/measurement-coverage.csv new file mode 100644 index 000000000..07d503e56 --- /dev/null +++ b/docs/src/en/benchmarks/measurement-coverage.csv @@ -0,0 +1,15 @@ +page,available_bindings,missing_metric_adapters,coverage +benchmarks/agent-tasks.md,,agent/agent.json,historical +benchmarks/cluster-scalability.md,,,historical +benchmarks/compare-rl-infra.md,,,historical +benchmarks/density.md,,density/density.json,historical +benchmarks/filesystem.md,B-FS-TOOLS accepted reference cohort fo5a4e7d2b-f,agent/macos.json,full +benchmarks/index.md,,,historical +benchmarks/isolation-tests.md,,isolation/isolation.json,historical +benchmarks/lazy-image-startup.md,,startup/lazy_startup.json,historical +benchmarks/methodology.md,,,historical +benchmarks/network.md,,network/network.json,historical +benchmarks/replay-fidelity.md,replay/replay.json,,partial +benchmarks/startup.md,startup/native_timing.json;startup/pvisor_host_timing.json;startup/pvisor_staged_timing.json;startup/pvisor_vm_timing.json;startup/docker_timing.json;startup/fc_system_timing.json;startup/qemu_timing.json;startup/qemu_microvm_timing.json,agent/macos.json,partial +benchmarks/supervision-cost.md,,workflow/apply.json;workflow/review.json;workflow/workflow.json,historical +benchmarks/vm-memory/index.md,,memory/memory.json,historical diff --git a/docs/src/en/benchmarks/measurement-coverage.render.json b/docs/src/en/benchmarks/measurement-coverage.render.json new file mode 100644 index 000000000..1009c440f --- /dev/null +++ b/docs/src/en/benchmarks/measurement-coverage.render.json @@ -0,0 +1,58 @@ +{ + "[agent-tasks](agent-tasks.md)": { + "Data status": "Historical data", + "Measured scope": "Original metrics and sources retained; no matching new measurements" + }, + "[cluster-scalability](cluster-scalability.md)": { + "Data status": "Historical data", + "Measured scope": "Original metrics and sources retained; no matching new measurements" + }, + "[compare-rl-infra](compare-rl-infra.md)": { + "Data status": "Historical data", + "Measured scope": "Original metrics and sources retained; no matching new measurements" + }, + "[density](density.md)": { + "Data status": "Historical data", + "Measured scope": "Original metrics and sources retained; no matching new measurements" + }, + "[filesystem](filesystem.md)": { + "Data status": "Fully rerun", + "Measured scope": "Eight-backend seven-tool phases and launch through exit; native whole-command probes are separate" + }, + "Overview": { + "Data status": "Historical data", + "Measured scope": "Original metrics and sources retained; no matching new measurements" + }, + "[isolation-tests](isolation-tests.md)": { + "Data status": "Historical data", + "Measured scope": "Original metrics and sources retained; no matching new measurements" + }, + "[lazy-image-startup](lazy-image-startup.md)": { + "Data status": "Historical data", + "Measured scope": "Original metrics and sources retained; no matching new measurements" + }, + "[methodology](methodology.md)": { + "Data status": "Historical data", + "Measured scope": "Original metrics and sources retained; no matching new measurements" + }, + "[network](network.md)": { + "Data status": "Historical data", + "Measured scope": "Original metrics and sources retained; no matching new measurements" + }, + "[replay-fidelity](replay-fidelity.md)": { + "Data status": "Partially rerun", + "Measured scope": "Whole CLI time for fixed task 0; historical 20-task distributions are separate" + }, + "[startup](startup.md)": { + "Data status": "Partially rerun", + "Measured scope": "Same-launch Ready / Exit; whole-command time is separate" + }, + "[supervision-cost](supervision-cost.md)": { + "Data status": "Historical data", + "Measured scope": "Original metrics and sources retained; no matching new measurements" + }, + "[vm-memory](vm-memory/index.md)": { + "Data status": "Historical data", + "Measured scope": "Original metrics and sources retained; no matching new measurements" + } +} diff --git a/docs/src/en/benchmarks/methodology.md b/docs/src/en/benchmarks/methodology.md index c7d310cc2..780c85700 100644 --- a/docs/src/en/benchmarks/methodology.md +++ b/docs/src/en/benchmarks/methodology.md @@ -10,6 +10,12 @@ | Choose an independent guest kernel | Firecracker, QEMU and pVisor startup plus tool data | | Set capacity or timeouts | Success rates, complete-task waiting and matching resource accounting | + + +The numbers and conclusions on this page still belong to the original historical cohorts. The new framework has no matching rerun yet; see [measurement coverage](index.md#measurement-coverage). + + + ## Motivation {#motivation} Faster startup does not guarantee faster compilation. Writable mounts and staged review also provide different workflows. Adoption decisions require speed, execution boundaries and how changes reach the original directory. @@ -22,6 +28,10 @@ B-STARTUP, B-FS-TOOLS and B-AGENT-TASK use one offline tool environment and fixe Downloads, benchmark compilation, image import and input copying are excluded. Timing separates first valid output, internal tools plus validation, returned results and launch-to-process-exit. Successful samples require validated outputs, observed executor and complete staging. Staged modes also verify that original host files remain unchanged. +### JSON tables and hyperfine {#json-tables} + +Tables load resolved named-row JSON before Markdown rendering. Rendering reads data and executes no benchmark. Direct-command startup and replay cohorts use hyperfine statistics; their input preparation and Python hooks are outside timing. Conditions run independently, without the randomized paired-order claim used by historical campaigns. Historical distributions, correctness counts, capacity and physical-memory readings keep their original measurement scope. Reproduction and remaining workload prerequisites are listed in the [JSON benchmark manual](https://github.com/DeepLink-org/pvisor/blob/main/benchmark.new/README.md). + ### Distributions and failures Cohorts are not pooled, and slow samples are not removed after measurement. Failures and invalid outputs are counted separately and never treated as zero latency. Capacity guards are not completed jobs. P95 is descriptive: it is omitted below 30 samples. Public P99 is omitted; small cohorts do not support stable tail-latency claims. diff --git a/docs/src/en/benchmarks/network.md b/docs/src/en/benchmarks/network.md index e3a938555..1916d06e0 100644 --- a/docs/src/en/benchmarks/network.md +++ b/docs/src/en/benchmarks/network.md @@ -10,6 +10,12 @@ | Dependency downloads inside a VM | Budget transmission separately from environment startup | | Blocking direct TCP connections | Host/VM deny-all positive and negative controls are tested; latency is not isolation evidence | + + +The numbers and conclusions on this page still belong to the original historical cohorts. The new framework has no matching rerun yet; see [measurement coverage](index.md#measurement-coverage). + + + ## Motivation {#motivation} Agents request models, download dependencies and consume streams. These workloads respond differently to proxy overhead. Separate request latency, first byte, full transmission and startup to understand the waiting involved. diff --git a/docs/src/en/benchmarks/replay-fidelity.md b/docs/src/en/benchmarks/replay-fidelity.md index 2d3ad72a2..ba8af2324 100644 --- a/docs/src/en/benchmarks/replay-fidelity.md +++ b/docs/src/en/benchmarks/replay-fidelity.md @@ -2,7 +2,7 @@ ## Main conclusions {#conclusions} -**Seven pinned trajectory formats each pass 60/60 prefix checks. Prepare-only executes no tools and leaves the workspace unchanged. Most formats show separated timing clusters: lower-cluster medians are about 6–7 ms and higher-cluster medians about 11–16 ms. This supports binding tasks to historical tool observations; fidelity of subsequent model execution remains unmeasured.** +**In the new framework, each of seven adapters passes 60/60 checks for fixed task 0, with whole prepare-only CLI medians of 5.56–5.74 ms. No tools execute and workspaces remain unchanged. Historical clusters across twenty trajectories use a different input cohort and retain their original scope; consistency of real model continuation remains unmeasured.** | Need | Selection implication | |---|---| @@ -10,6 +10,12 @@ | Restore actual file state | Restore the task environment separately; prefix preparation does not replay file changes | | Compare native resume or RL pipeline speed | No matched-trajectory timing control is available for a ranking | + + +New experiments and historical data are published separately. Each table retains its timing scope and inputs from the design and provenance; distinct cohorts are not pooled. + + + ## Motivation {#motivation} Training and reproduction need tasks to continue at an accurate historical boundary. Missing tool observations or including a future action changes the model's context; executing tools during preparation may introduce additional side effects. These properties determine whether recorded trajectories provide reliable task inputs. @@ -40,6 +46,14 @@ Measured on 2026-10-06. N=60 per adapter, all passing; units are ms. Separated c Mini-SWE-Agent does not trigger the split rule, so its row reports the full-sample P50. Splitting is descriptive: each cluster contains at least 10% of samples; the largest adjacent gap is at least 20% of the overall median and exceeds three times the median adjacent gap; cluster medians differ by at least 1.5×. This does not establish the cause of slower samples. All valid slow samples remain, so preparation should not be budgeted as a fixed few milliseconds. +### Hyperfine: direct prepare-only command {#hyperfine} + +This independent cohort measures one fixed synthetic trajectory (task 0) per adapter, 60 runs without warmups. Hyperfine directly launches `pvisor-replay` with `--shell=none`; Python reset and fidelity checks run outside timing for every execution. All runs must preserve exact arguments, source digests and historical observations, execute zero tools and leave the workspace unchanged. Results cover full command completion, not the twenty-fixture historical distribution above. The binary digest identifies the artifact; its correspondence to current source is unverified. Host caches are retained, CPU affinity and artifact identities are recorded in the provenance download. No paired speedup or tail claim is made. + +{{ table("replay-semspec.render.json") }} + +[Hyperfine data CSV](replay-semspec.csv) · [Batch provenance](../assets/benchmarks/replay-semspec.provenance.json) · [JSON benchmark manual](https://github.com/DeepLink-org/pvisor/blob/main/benchmark.new/README.md#replay) + ### Scope {#acceptance} These results support structure, argument and observation fidelity for pinned short prefixes. Real sessions, newer CLIs, long prefixes, token costs, macOS, remote-connection recovery and subsequent model execution remain unmeasured. Tool replay and actual environment restoration need separate correctness and cost checks. diff --git a/docs/src/en/benchmarks/replay-semspec.csv b/docs/src/en/benchmarks/replay-semspec.csv new file mode 100644 index 000000000..409ba9ce6 --- /dev/null +++ b/docs/src/en/benchmarks/replay-semspec.csv @@ -0,0 +1,15 @@ +batch,benchmark_id,role,runs,warmup,hyperfine_version,metric_scope,evidence_sha256,manifest_sha256,condition,command,N,Median ms,Mean ms,SD ms,Min ms,Max ms +replay-20261010T173216Z-gbh7pkr9,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,5556cb33a1e853ac3074fff9dd34c2fdf722abd2fc9f8f0391bfd89131dedac0,808c779fce917fb38c7845481fb2162f7b2e2b11c387dc60611421caf2532f92,claude-code,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/pvisor-replay --agent claude-code --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/claude-code/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/claude-code/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/claude-code/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/claude-code/output,60,5.813657,5.8942094166666665,0.5230506143534632,5.176242,8.040147000000001 +replay-20261010T173216Z-gbh7pkr9,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,5556cb33a1e853ac3074fff9dd34c2fdf722abd2fc9f8f0391bfd89131dedac0,808c779fce917fb38c7845481fb2162f7b2e2b11c387dc60611421caf2532f92,codex,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/pvisor-replay --agent codex --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/codex/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/codex/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/codex/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/codex/output,60,5.668345500000001,5.793974399999999,0.5887070394240217,5.288943,9.142757 +replay-20261010T173216Z-gbh7pkr9,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,5556cb33a1e853ac3074fff9dd34c2fdf722abd2fc9f8f0391bfd89131dedac0,808c779fce917fb38c7845481fb2162f7b2e2b11c387dc60611421caf2532f92,opencode,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/pvisor-replay --agent opencode --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/opencode/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/opencode/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/opencode/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/opencode/output,60,5.6216435,5.662359433333333,0.25105121611453857,5.226376,6.6708620000000005 +replay-20261010T173216Z-gbh7pkr9,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,5556cb33a1e853ac3074fff9dd34c2fdf722abd2fc9f8f0391bfd89131dedac0,808c779fce917fb38c7845481fb2162f7b2e2b11c387dc60611421caf2532f92,mini-swe-agent,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/pvisor-replay --agent mini-swe-agent --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/mini-swe-agent/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/mini-swe-agent/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/mini-swe-agent/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/mini-swe-agent/output,60,5.660796000000001,5.7662035833333345,0.41099182149198304,5.286918000000001,6.981852 +replay-20261010T173216Z-gbh7pkr9,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,5556cb33a1e853ac3074fff9dd34c2fdf722abd2fc9f8f0391bfd89131dedac0,808c779fce917fb38c7845481fb2162f7b2e2b11c387dc60611421caf2532f92,openhands,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/pvisor-replay --agent openhands --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/openhands/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/openhands/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/openhands/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/openhands/output,60,5.6294830000000005,5.856039483333334,1.2306524438359545,5.282350000000001,14.935868999999999 +replay-20261010T173216Z-gbh7pkr9,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,5556cb33a1e853ac3074fff9dd34c2fdf722abd2fc9f8f0391bfd89131dedac0,808c779fce917fb38c7845481fb2162f7b2e2b11c387dc60611421caf2532f92,pi-agent,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/pvisor-replay --agent pi-agent --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/pi-agent/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/pi-agent/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/pi-agent/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/pi-agent/output,60,5.831475000000001,5.8543261,0.3711018192554459,4.919222,7.126382 +replay-20261010T173216Z-gbh7pkr9,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,5556cb33a1e853ac3074fff9dd34c2fdf722abd2fc9f8f0391bfd89131dedac0,808c779fce917fb38c7845481fb2162f7b2e2b11c387dc60611421caf2532f92,swe-agent,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/pvisor-replay --agent swe-agent --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/swe-agent/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/swe-agent/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/swe-agent/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/swe-agent/output,60,5.645287000000001,5.8619556500000005,0.8858928147291519,5.243638,11.045348 +replay-20261010T194213Z-9zpco2sv,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,34f0c4a59ad1612860c382951158167a4fb3a2f45e47dbf635d0dca793105eca,202c53d97959bbdf76bb10fd5fb071d22eb2c435f194f48e3372800b02dd1c8e,claude-code,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/pvisor-replay --agent claude-code --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/claude-code/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/claude-code/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/claude-code/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/claude-code/output,60,5.7403095,5.8633256833333345,0.8778318015393844,5.114597,12.135782 +replay-20261010T194213Z-9zpco2sv,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,34f0c4a59ad1612860c382951158167a4fb3a2f45e47dbf635d0dca793105eca,202c53d97959bbdf76bb10fd5fb071d22eb2c435f194f48e3372800b02dd1c8e,codex,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/pvisor-replay --agent codex --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/codex/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/codex/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/codex/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/codex/output,60,5.589363,5.668820266666667,0.31781873367499147,5.052701000000001,6.9522770000000005 +replay-20261010T194213Z-9zpco2sv,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,34f0c4a59ad1612860c382951158167a4fb3a2f45e47dbf635d0dca793105eca,202c53d97959bbdf76bb10fd5fb071d22eb2c435f194f48e3372800b02dd1c8e,opencode,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/pvisor-replay --agent opencode --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/opencode/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/opencode/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/opencode/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/opencode/output,60,5.563975500000001,5.6183589833333345,0.2956679622357836,4.985716,6.622702 +replay-20261010T194213Z-9zpco2sv,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,34f0c4a59ad1612860c382951158167a4fb3a2f45e47dbf635d0dca793105eca,202c53d97959bbdf76bb10fd5fb071d22eb2c435f194f48e3372800b02dd1c8e,mini-swe-agent,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/pvisor-replay --agent mini-swe-agent --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/mini-swe-agent/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/mini-swe-agent/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/mini-swe-agent/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/mini-swe-agent/output,60,5.608388000000001,5.723988500000002,0.3963316399933439,5.279214,7.547869 +replay-20261010T194213Z-9zpco2sv,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,34f0c4a59ad1612860c382951158167a4fb3a2f45e47dbf635d0dca793105eca,202c53d97959bbdf76bb10fd5fb071d22eb2c435f194f48e3372800b02dd1c8e,openhands,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/pvisor-replay --agent openhands --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/openhands/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/openhands/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/openhands/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/openhands/output,60,5.675914000000001,5.734144033333331,0.4020236849784177,4.805099,7.400434000000001 +replay-20261010T194213Z-9zpco2sv,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,34f0c4a59ad1612860c382951158167a4fb3a2f45e47dbf635d0dca793105eca,202c53d97959bbdf76bb10fd5fb071d22eb2c435f194f48e3372800b02dd1c8e,pi-agent,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/pvisor-replay --agent pi-agent --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/pi-agent/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/pi-agent/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/pi-agent/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/pi-agent/output,60,5.582430500000001,5.629462633333333,0.33750797150701284,4.734617,6.9348540000000005 +replay-20261010T194213Z-9zpco2sv,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,34f0c4a59ad1612860c382951158167a4fb3a2f45e47dbf635d0dca793105eca,202c53d97959bbdf76bb10fd5fb071d22eb2c435f194f48e3372800b02dd1c8e,swe-agent,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/pvisor-replay --agent swe-agent --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/swe-agent/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/swe-agent/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/swe-agent/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/swe-agent/output,60,5.732465000000001,5.763901283333333,0.39466482164589084,4.707587,7.244041 diff --git a/docs/src/en/benchmarks/replay-semspec.render.json b/docs/src/en/benchmarks/replay-semspec.render.json new file mode 100644 index 000000000..be43ae10f --- /dev/null +++ b/docs/src/en/benchmarks/replay-semspec.render.json @@ -0,0 +1,58 @@ +{ + "claude-code": { + "N": 60, + "Median ms": "5.74", + "Mean ms": "5.86", + "SD ms": "0.88", + "Min ms": "5.11", + "Max ms": "12.14" + }, + "codex": { + "N": 60, + "Median ms": "5.59", + "Mean ms": "5.67", + "SD ms": "0.32", + "Min ms": "5.05", + "Max ms": "6.95" + }, + "opencode": { + "N": 60, + "Median ms": "5.56", + "Mean ms": "5.62", + "SD ms": "0.30", + "Min ms": "4.99", + "Max ms": "6.62" + }, + "mini-swe-agent": { + "N": 60, + "Median ms": "5.61", + "Mean ms": "5.72", + "SD ms": "0.40", + "Min ms": "5.28", + "Max ms": "7.55" + }, + "openhands": { + "N": 60, + "Median ms": "5.68", + "Mean ms": "5.73", + "SD ms": "0.40", + "Min ms": "4.81", + "Max ms": "7.40" + }, + "pi-agent": { + "N": 60, + "Median ms": "5.58", + "Mean ms": "5.63", + "SD ms": "0.34", + "Min ms": "4.73", + "Max ms": "6.93" + }, + "swe-agent": { + "N": 60, + "Median ms": "5.73", + "Mean ms": "5.76", + "SD ms": "0.39", + "Min ms": "4.71", + "Max ms": "7.24" + } +} diff --git a/docs/src/en/benchmarks/startup-hyperfine.csv b/docs/src/en/benchmarks/startup-hyperfine.csv new file mode 100644 index 000000000..a67109fc9 --- /dev/null +++ b/docs/src/en/benchmarks/startup-hyperfine.csv @@ -0,0 +1,6 @@ +batch,benchmark_id,role,runs,warmup,hyperfine_version,metric_scope,evidence_sha256,manifest_sha256,command,time_wall_clock_unit,time_wall_clock_mean,time_wall_clock_stddev,time_wall_clock_median,time_wall_clock_min,time_wall_clock_max,memory_peak_resident_unit,memory_peak_resident_mean,memory_peak_resident_stddev,memory_peak_resident_median,memory_peak_resident_min,memory_peak_resident_max +startup-20261010T140239Z-pzfiqe2v,B-STARTUP,user-facing,60,3,hyperfine 2.0.0,complete command; hooks excluded,e9b1a5d82840710d8b3dd2aef18cdb6b00912b85f197b44a32bf4e243765b309,2a14d8b62556407f6c0029235198249e21b3e2fd70ea16cb1039424b4ec9a8a2,native,s,0.0021688563499999997,0.0002314098403387833,0.002130323,0.0017548470000000001,0.0027290260000000003,B,3712341.3333333335,82850.7755381737,3713024,3522560,3887104 +startup-20261010T140419Z-aaegem48,B-STARTUP,user-facing,60,3,hyperfine 2.0.0,complete command; hooks excluded,d7e327c6e6df30815d967b9444da8665c4af61eb36fb1bbced2530255deca76e,5cb5b8b0bd9b6ccf8fd7b15bf9545f3d5f600e081480b64628ba1d0aa6f9cc3f,qemu,s,0.8967260577333336,0.3190259188267261,0.748406994,0.705032997,2.298454349,B,193738410.66666666,250678.38946311103,193744896,193261568,194342912 +startup-20261010T152527Z-ohjmaem2,B-STARTUP,user-facing,60,3,hyperfine 2.0.0,complete command; hooks excluded,9d1e406f7ecc1f9b85924119616764eee2ba98d7ebf6774e616d76f6cb4b0215,30ea22bb257542678d0f35a79c5bca1febea6f9ae35efae946f822b006d55a37,pvisor-host,s,0.31584145333333336,0.25974104189753316,0.1651246125,0.128760758,1.162888514,B,12082858.666666666,76142.55881572179,12095488,11915264,12197888 +startup-20261010T152713Z-s6p1jnm8,B-STARTUP,user-facing,60,3,hyperfine 2.0.0,complete command; hooks excluded,78a82548faf0abfb71a4e2338e5dbe3f7c90dd03c54b7a85d5479bc659d1ddd0,77fb703e3be244bca0fd2bababc6b26960f0fbef553b3681ad127caaa74218a7,docker,s,0.3810838176166668,0.5093205021505954,0.1672732575,0.128847399,2.692299138,B,28099993.6,180496.4793822766,28110848,27734016,28483584 +startup-20261010T152907Z-qyz2tj6w,B-STARTUP,user-facing,60,3,hyperfine 2.0.0,complete command; hooks excluded,aff95227580927f520cd06327d838fc2490bdcbf08b61d2cd67af38e8957e184,1e17b7b84a8d91d11d2a9eb1f905d70d452cf2c31cd2e4f00217743238749e5e,qemu-microvm,s,0.45113018473333344,0.21992967847736003,0.3537632235,0.30724522400000004,1.126818259,B,189431057.06666666,191731.97540686635,189409280,189075456,189825024 diff --git a/docs/src/en/benchmarks/startup-ready-exit.csv b/docs/src/en/benchmarks/startup-ready-exit.csv new file mode 100644 index 000000000..f9dae1db1 --- /dev/null +++ b/docs/src/en/benchmarks/startup-ready-exit.csv @@ -0,0 +1,2 @@ +batch,benchmark_id,role,runs,warmup,hyperfine_version,metric_scope,evidence_sha256,manifest_sha256,measurement_engine,python_version,condition,command,N,Ready median ms,Ready mean ms,Ready SD ms,Ready min ms,Ready max ms,Exit median ms,Exit mean ms,Exit SD ms,Exit min ms,Exit max ms +pvisor_vm-20261010T191930Z-edvvabx9,B-STARTUP,user-facing,60,3,,same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded,fd22c3d38fd277171ae9026ecdcfc8386c7e522b4ab79476ee24c4b6956865d7,0aa3161db744936f86be6b9f40451d0803d83e404c7574554442ddc85345c436,python-perf_counter_ns,3.12.11,pvisor-vm,"/home/reiase/workspace/pvisor/target/release/pvisor run --no-agent-defaults --overlaynet off --stdio inherit --timeout 30s --stage /home/reiase/workspace/pvisor/benchmark.new/startup/.data/pvisor_vm-20261010T191930Z-edvvabx9/current/stage --vm --rootfs /home/reiase/workspace/pvisor/benchmark/.data/6ra5/rootfs --cpu 2 --memory 128MiB -- /bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,145.532771,163.5235932,28.941141549256155,132.431972,231.477036,280.354604,287.9636302166667,30.578377727311814,245.776707,365.590256 diff --git a/docs/src/en/benchmarks/startup-ready.csv b/docs/src/en/benchmarks/startup-ready.csv new file mode 100644 index 000000000..6d297840a --- /dev/null +++ b/docs/src/en/benchmarks/startup-ready.csv @@ -0,0 +1,3 @@ +batch,benchmark_id,role,runs,warmup,hyperfine_version,metric_scope,evidence_sha256,manifest_sha256,measurement_engine,python_version,condition,command,N,Ready median ms,Ready mean ms,Ready SD ms,Ready min ms,Ready max ms +pvisor_vm-20261010T190837Z-03dall_r,B-STARTUP,user-facing,60,3,,Popen launch to receipt of exact REFERENCE_READY line; perf_counter_ns; preparation and teardown excluded,f20e900c362a2d0d2203c638c33cb901d8ff3a41f8f476254fceda4077973776,9b65738a486a4309e61f9b1d4a7fcbb814094459ff0c2b9a6a0bfbd6d73899a6,python-perf_counter_ns,3.12.11,pvisor-vm,"/home/reiase/workspace/pvisor/target/release/pvisor run --no-agent-defaults --overlaynet off --stdio inherit --timeout 30s --stage /home/reiase/workspace/pvisor/benchmark.new/startup/.data/pvisor_vm-20261010T190837Z-03dall_r/current/stage --vm --rootfs /home/reiase/workspace/pvisor/benchmark/.data/6ra5/rootfs --cpu 2 --memory 128MiB -- /bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,131.736131,135.89581985,20.838798984702635,122.028212,228.284618 +pvisor_vm-20261010T190923Z-a6pqcxmk,B-STARTUP,user-facing,60,3,,Popen launch to receipt of exact REFERENCE_READY line; perf_counter_ns; preparation and teardown excluded,316cd941068ddf87b6d690ba4fdb124a7b7feb0c8b3c3b50179efdf70a78f7c9,be31c9369cb2024ec98c04ed10771972c31970ce830e980cbc9e91bc846a1992,python-perf_counter_ns,3.12.11,pvisor-vm,"/home/reiase/workspace/pvisor/target/release/pvisor run --no-agent-defaults --overlaynet off --stdio inherit --timeout 30s --stage /home/reiase/workspace/pvisor/benchmark.new/startup/.data/pvisor_vm-20261010T190923Z-a6pqcxmk/current/stage --vm --rootfs /home/reiase/workspace/pvisor/benchmark/.data/6ra5/rootfs --cpu 2 --memory 128MiB -- /bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,132.9515445,132.50528831666668,5.088249720425674,122.99712,147.525951 diff --git a/docs/src/en/benchmarks/startup-reboot-comparison.csv b/docs/src/en/benchmarks/startup-reboot-comparison.csv new file mode 100644 index 000000000..5ecafe31f --- /dev/null +++ b/docs/src/en/benchmarks/startup-reboot-comparison.csv @@ -0,0 +1,9 @@ +condition,label,before_batch,after_batch,before_N,after_N,warmup,after_boot_id,pvisor_binary_sha256,before Ready median ms,after Ready median ms,before Ready mean ms,after Ready mean ms,before Ready SD ms,after Ready SD ms,before Ready min ms,after Ready min ms,before Ready max ms,after Ready max ms,before Exit median ms,after Exit median ms,before Exit mean ms,after Exit mean ms,before Exit SD ms,after Exit SD ms,before Exit min ms,after Exit min ms,before Exit max ms,after Exit max ms,Ready median ms change %,Exit median ms change %,before_evidence_sha256,after_evidence_sha256,before_manifest_sha256,after_manifest_sha256 +native,Native,native_timing-20261010T193144Z-rjxju56s,native_timing-20261011T004342Z-8wjyuy21,60,60,3,5ba46bee-f753-405b-9cc8-ff6cdf810b8b,,0.477416,0.4768065,0.4867091666666667,0.4821639,0.027473098763457073,0.024363187201185323,0.45534,0.453769,0.593298,0.554185,0.5262070000000001,0.5247455000000001,0.5548987,0.5674711833333334,0.14369408916641668,0.20638363771035492,0.504061,0.498852,1.626316,1.721113,-0.1276664376560488,-0.2777424093560188,ba0b0607cfde7cc9d8851a838ca4a71b38c08eac81f1361f7ab68f96e08b0f24,355c05aa8af734b85978052114fc98485d339ec681f1e1cebc5bd5b317ac4948,9d7e1087e6897807884761ed201bfd2b97d71fb2b3600fa161e57792baae65ef,571e859d1e439e764b42696e5280aa02bdeb4a3cb49797164d54116286d797e0 +pvisor-host,pVisor host,pvisor_host_timing-20261010T193144Z-7kp2ohaf,pvisor_host_timing-20261011T004343Z-gdenfkzx,60,60,3,5ba46bee-f753-405b-9cc8-ff6cdf810b8b,d3f09687cca60b617629c215fa1359c441ab6ac19f8c63b68f1dcdf7614e6b30,37.453213500000004,33.5245775,37.39181438333333,50.48170388333333,1.3806339289004357,29.699968167718016,33.823985,30.57654,45.32391,128.654942,129.0226385,123.626168,128.38440935,138.93658936666665,2.8130187011713157,27.73345550008831,116.414786,112.022101,132.229654,215.842663,-10.489449723719979,-4.182576455371434,2a58fe62fb73d6c637241854da6fe0de2c3d1436b3f670c760166fd745d02de6,b5ec9fd9f67ad90cfa3b5aaa77041a2aad2a3426f82a7afd2c63efeccd954adb,e3e38d24a5cfcaecad68f5c2a07b270b9654733b23aca3d8ebf13ce07c8c67a6,768f808263dc663e926b58944b46d1e966d0b700f42a4cdacf91a7e0bd7c27fa +pvisor-staged,pVisor staged,pvisor_staged_timing-20261010T193207Z-14b8yji_,pvisor_staged_timing-20261011T004437Z-0_8_myox,60,60,3,5ba46bee-f753-405b-9cc8-ff6cdf810b8b,d3f09687cca60b617629c215fa1359c441ab6ac19f8c63b68f1dcdf7614e6b30,56.317359499999995,50.5909475,83.63402366666666,52.77206473333333,35.80452248684234,17.86787212840543,51.526137,46.178707,151.770448,188.290473,159.076721,146.34664,182.31478523333334,148.0955406,36.35013478894402,17.210223152340454,139.925129,133.553461,246.257413,274.998173,-10.168111663686929,-8.002478879357833,d9697f4bf482937967e3b7459fd4fc6f56d1fcf785f1e261fd3ff40fbac5e2b6,ecad00dd273bd159cd74112059f1a1a09a0bf0a58cf999f596d5a5d7a2027049,1d4b415be94fcfe71048b625e8eff82765e26b2392869a67351bd087abae8764,8a4d6d031779febc3b156bfe29163e68ba7ccaa3f3294ff810cdd913b094e22f +pvisor-vm,pVisor VM,pvisor_vm-20261010T193231Z-oqxsd107,pvisor_vm-20261011T004527Z-4o2qaxr9,60,60,3,5ba46bee-f753-405b-9cc8-ff6cdf810b8b,d3f09687cca60b617629c215fa1359c441ab6ac19f8c63b68f1dcdf7614e6b30,179.21802150000002,130.707348,171.2950154,130.10342321666667,22.10104697452002,6.156610149010636,137.615608,119.004459,207.20476,149.988327,297.7932135,243.53000400000002,293.0082749,248.7508986,23.8399220189266,17.71092203479732,247.049522,218.450697,340.454659,288.919716,-27.067966208967455,-18.221775057341915,f49d48ad7568a601eadf2b470c0513edeef1c5d1864a11ee53dd9fdc418e3a3f,6f1b2e5eb12b8012bb1f221d94944017c3792625ae27038e489d29808ae43de3,11644c889e7b014109709022e6b4222de3865f15fbf636b0aa9366278d2426cb,a63cb47729c4b6cf1f37e259241a0b5ee1105157e58a1eb5b79f5e8da82db252 +docker,Docker,docker_timing-20261010T193254Z-vfew0_j6,docker_timing-20261011T004624Z-8aq0yusi,60,60,3,5ba46bee-f753-405b-9cc8-ff6cdf810b8b,,111.212921,105.027266,112.61178271666667,105.5709834,8.444143141831258,7.221437055108282,91.518154,90.523694,140.077263,126.038887,136.83700499999998,129.3059355,138.7524654,130.3731021,9.00429822650372,7.663377586154465,118.732902,114.191419,169.009196,152.928737,-5.561993106898067,-5.503678993851091,66772bfad50467e9854931366f9c466936f432ebdb8cf8169f3f3d130f85589a,9df027d8154142787594d86cd7572465957be3513333dd83c734debd50558c10,5583225f3e0382e0208f8d8cbe951fa92b7196dbfe65e5824586aa680a9a601f,aea46ba225d18a1fd202e2460933d809a691636e33b0346319fcd4cc01eeb8d5 +fc-system,Firecracker,fc_system_timing-20261010T194324Z-0ngxz1rw,fc_system_timing-20261011T004712Z-97n8gne7,60,60,3,5ba46bee-f753-405b-9cc8-ff6cdf810b8b,,291.2311145,287.127745,291.5582495333333,287.06792748333334,5.8558511887453095,5.885774302854362,282.256479,269.078709,313.902527,301.728159,726.9635,722.0567149999999,728.0146826166666,722.7726551166667,5.32211127846656,6.025874020298251,717.707895,711.091197,740.754322,741.229639,-1.408973593719498,-0.674969926275526,3928edf8392072a5529d79f3d8230829f23a036a11387291dea6707aac6c265d,b0343f5973991391c32b67b4a8431a5e992c80cb6c403338d3dc4965e7ce2251,aaeceeb1b87fe05a7491a51f539e0a4b9a65cbae60a01181c5a7b6cca05ad885,107c830711205462c88ae27f0de0e1088ffee1176a5f9842d483dacc1e8413f3 +qemu,QEMU,qemu_timing-20261010T193411Z-xgtqdjpb,qemu_timing-20261011T004840Z-vh5l4h7y,60,60,3,5ba46bee-f753-405b-9cc8-ff6cdf810b8b,,711.3557575,663.2477994999999,715.5877327166667,667.5293913666667,28.84870286560417,28.729089815188537,676.150134,631.375716,777.811211,742.132208,738.2912145,691.7226949999999,744.53627425,695.4386896,29.35072221760295,28.6305736872296,698.122196,656.021112,804.280468,766.893319,-6.762854941818619,-6.307608513469598,b4d1229af611f7884471e6988eee61219de68aeb1dd7966321196d4091609193,7d8b028d8df05ff7b3cc881e9d30b998fb55de23b51c29329969ed1b86ce1ae0,f5a176f8e7b58ce1ce366827fe4b3b5aaf2fdac052e225642a5fb9abb7288912,1431f57af3f73fac2331982e186a0d61ba8de88d7f07e1213af23751a1c1fad9 +qemu-microvm,QEMU microvm,qemu_microvm_timing-20261010T193436Z-bvzj9q7t,qemu_microvm_timing-20261011T005005Z-hra4_tyy,60,60,3,5ba46bee-f753-405b-9cc8-ff6cdf810b8b,,298.98298450000004,260.10463400000003,299.9279542,259.5273039,30.521712217844783,9.566460105458674,244.717479,243.283605,359.294324,288.390241,321.4902005,281.32751199999996,323.5255617166667,281.99962423333335,29.485465599627037,11.500231438448678,266.138102,261.017615,380.83449,315.552385,-13.00353281475789,-12.492663365022239,a12206db296e544ad323a3781bc9f96cd3370dab5cfc795faa0371d5d74aed65,eb2c5c6f18620ac4f0f30e1d67aa47aa1fcaa223f6b050ad70ec9982f6a3b457,8e217c76c1d10a542fa2ebc9cded9817d74fc0b96924b658f3a2d88c170d0d8b,d5c74b3517c2b8ee545c4a274a8cea36e93d9f3c6b4b9153d0ea7ab4ce1d974a diff --git a/docs/src/en/benchmarks/startup-semspec.csv b/docs/src/en/benchmarks/startup-semspec.csv new file mode 100644 index 000000000..db90d4b46 --- /dev/null +++ b/docs/src/en/benchmarks/startup-semspec.csv @@ -0,0 +1,12 @@ +batch,benchmark_id,role,runs,warmup,hyperfine_version,metric_scope,evidence_sha256,manifest_sha256,condition,command,N,Median ms,Mean ms,SD ms,Min ms,Max ms +native-20261010T165842Z-456ayych,B-STARTUP,user-facing,60,3,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,8c1a7b4106d512085aad8f96e3d7f93a8433dc650d37bed4e00a860775504bdf,728d89ddd4aa83008194d7eb4101e11adcd59faf8101fc6d94cb07236cb6d71a,native,"/bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,0.5428189999999999,0.5448796166666666,0.07744633470708881,0.437788,0.7812990000000001 +pvisor_host-20261010T170208Z-q8e1k8f2,B-STARTUP,user-facing,60,3,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,e4ff64d634d1909b5e7f8ba33ba594faf204f766a03cbee7aab462a53d8539e3,886d1d2f27a0f6c8f40d4d36cec50fb240e0e368c102a4b7dddadb985f111747,pvisor-host,"/home/reiase/workspace/pvisor/target/release/pvisor run --no-agent-defaults --overlaynet off --stdio inherit --timeout 30s --filesystem host -- /bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,177.2584755,172.6504246166667,27.25835190318585,118.760767,224.36950000000002 +pvisor_staged-20261010T170343Z-kthbiapw,B-STARTUP,user-facing,60,3,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,a9a90bc0bb92f45906bc95c26693d867aa8a08e7a3d05fbab38672c8fc298b4a,985bc6975f3eabbf2673f3bc58f1a38089aa2848f8e44ade7f91897d91990d2e,pvisor-staged,"/home/reiase/workspace/pvisor/target/release/pvisor run --no-agent-defaults --overlaynet off --stdio inherit --timeout 30s --stage /home/reiase/workspace/pvisor/benchmark.new/startup/.data/pvisor_staged-20261010T170343Z-kthbiapw/current/stage -- /bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,153.850434,154.5756163166667,5.8784454336238,139.860151,178.11594700000003 +pvisor_vm-20261010T170522Z-_ucnw1fs,B-STARTUP,user-facing,60,3,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,009193d1751dd1ffc5e4f6de7539881471824019372843385e88911dd3e17abc,b64051c916339609d9c76b8ea402e3e8fdd2fbfe9d72cb4c2590d4d04f1271a9,pvisor-vm,"/home/reiase/workspace/pvisor/target/release/pvisor run --no-agent-defaults --overlaynet off --stdio inherit --timeout 30s --stage /home/reiase/workspace/pvisor/benchmark.new/startup/.data/pvisor_vm-20261010T170522Z-_ucnw1fs/current/stage --vm --rootfs /home/reiase/workspace/pvisor/benchmark/.data/6ra5/rootfs --cpu 2 --memory 128MiB -- /bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,276.826408,284.53924739999997,32.07646019571068,244.55794400000002,360.190688 +docker-20261010T170722Z-t1_bih1h,B-STARTUP,user-facing,60,3,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,830cff142a2e0fc5b2e3048f21be9baab881202d9d14fee6db52d7171bb6529f,dfd4d2226e5135c2b74e528f7ae6bcf2636cf80a244dbe4262ebd497acdc5234,docker,"docker run --rm --pull never --network none --name pvisor-startup-0T170722Z-t1_bih1h --label pvisor.startup.batch=docker-20261010T170722Z-t1_bih1h --mount type=bind,source=/home/reiase/workspace/pvisor/benchmark.new/startup/.data/docker-20261010T170722Z-t1_bih1h/workspace,target=/work --workdir /work --entrypoint /bench/affinity sha256:7e73ccc8f9aa6a0883f8339b66ac0c0fc8284ffd61ea11cba43d131b5d06d689 0,1 /bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,163.9206565,168.86826574999998,21.384606046647722,144.00522300000003,285.47211500000003 +qemu-20261010T171009Z-blrhft4p,B-STARTUP,user-facing,60,3,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,d14ab629a2e5c3bab8c91bf922b8ffb5a4d5d764ce2c6146c9f9897334132ffb,f3929e404d8239b906596b2b86144dbe31a3fe10cb7e1c9f4f2b3bac65065ffa,qemu,"qemu-system-x86_64 -machine q35 -accel kvm -cpu host -smp 2 -m 128 -nodefaults -display none -serial stdio -no-reboot -kernel /home/reiase/workspace/pvisor/benchmark/.data/stock-kernel2-20261007/vmlinuz -append 'console=ttyS0 reboot=k panic=1 root=/dev/vda rw init=/bench/init quiet pvbench.mode=ready pvbench.scratch=executor' -drive file=/home/reiase/workspace/pvisor/benchmark.new/startup/.data/qemu-20261010T171009Z-blrhft4p/current/rootfs.ext4,format=raw,if=none,id=root -device virtio-blk-pci,drive=root -initrd /home/reiase/workspace/pvisor/benchmark/.data/stock-kernel2-20261007/initrd",60,722.0847175,711.2354185833335,26.748688365603908,664.49363,762.300203 +qemu_microvm-20261010T171236Z-uwfhnjiw,B-STARTUP,user-facing,60,3,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,181c6eec7cdb8cf339e6fa94bcc2e34fb7614224cf3b5498a82bf0471ea2a864,a99c52fed88a07152688bb0f3f6334a01aa84b359fe3ba889b8e7ce71755bbab,qemu-microvm,"qemu-system-x86_64 -machine microvm,acpi=off,x-option-roms=off,pit=off,pic=off,rtc=on -accel kvm -cpu host -smp 2 -m 128 -nodefaults -display none -serial stdio -no-reboot -kernel /home/reiase/workspace/pvisor/benchmark/.data/stock-kernel2-20261007/vmlinuz -append 'console=ttyS0 reboot=t panic=1 root=/dev/vda rw init=/bench/init quiet pvbench.mode=ready pvbench.scratch=executor pci=off' -drive file=/home/reiase/workspace/pvisor/benchmark.new/startup/.data/qemu_microvm-20261010T171236Z-uwfhnjiw/current/rootfs.ext4,format=raw,if=none,id=root -device virtio-blk-device,drive=root -initrd /home/reiase/workspace/pvisor/benchmark/.data/stock-kernel2-20261007/initrd",60,419.649605,429.2667642333335,42.448996785664086,358.36805300000003,521.7273570000001 +qemu_microvm-20261010T171456Z-btvcjqlb,B-STARTUP,user-facing,60,3,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,318fc8345a5ad21155e992ccd1f51242845a002df72aee3cefdd6ab2bfc17918,fae8d591de1055660a02087934500efff85366ef6ad87e5ec96922f74f39bc68,qemu-microvm,"qemu-system-x86_64 -machine microvm,acpi=off,x-option-roms=off,pit=off,pic=off,rtc=on -accel kvm -cpu host -smp 2 -m 128 -nodefaults -display none -serial stdio -no-reboot -kernel /home/reiase/workspace/pvisor/benchmark/.data/stock-kernel2-20261007/vmlinuz -append 'console=ttyS0 reboot=t panic=1 root=/dev/vda rw init=/bench/init quiet pvbench.mode=ready pvbench.scratch=executor pci=off' -drive file=/home/reiase/workspace/pvisor/benchmark.new/startup/.data/qemu_microvm-20261010T171456Z-btvcjqlb/current/rootfs.ext4,format=raw,if=none,id=root -device virtio-blk-device,drive=root -initrd /home/reiase/workspace/pvisor/benchmark/.data/stock-kernel2-20261007/initrd",60,339.4707245,355.0767046166667,86.49622716268142,302.64781700000003,928.470456 +native-20261010T184227Z-qg9cqk2u,B-STARTUP,user-facing,60,3,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,1504d0bfb38f01b3e1481d4c58854d4c986ecf5e7fb5b0e4ec1237434b497c4f,d312adb83d9dd7a36a163987bc4b2126351170d545e5fa0f1583d94b603e8462,native,"/bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,0.4220775,0.4235270833333332,0.01758734921421555,0.393404,0.515292 +pvisor_vm-20261010T184321Z-6f0yo0wi,B-STARTUP,user-facing,60,3,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,7b9eb8a585dd63280e902e8aefbf2a4a917a74c528341b7440ee9fb839687e91,ab2327fd977f75438a97e84cbc7899fcf5be0e27deda3445e55f9107e81d74a9,pvisor-vm,"/home/reiase/workspace/pvisor/target/release/pvisor run --no-agent-defaults --overlaynet off --stdio inherit --timeout 30s --stage /home/reiase/workspace/pvisor/benchmark.new/startup/.data/pvisor_vm-20261010T184321Z-6f0yo0wi/current/stage --vm --rootfs /home/reiase/workspace/pvisor/benchmark/.data/6ra5/rootfs --cpu 2 --memory 128MiB -- /bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,278.82669549999997,305.76999966666665,63.45494958797474,230.387054,523.62118 +pvisor_host-20261010T184736Z-3xq4m_wt,B-STARTUP,user-facing,60,3,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,01e92e3c7134054451ab05fff2115608a5538654c6a8ff3f08c1fcf09984c161,c14a02b20a9f9794c4158249ff7eb46f8ebda683df147bf055dc2f4d518b9188,pvisor-host,"/home/reiase/workspace/pvisor/target/release/pvisor run --no-agent-defaults --overlaynet off --stdio inherit --timeout 30s --filesystem host -- /bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,208.7871975,217.9388419333333,34.13297007503415,162.078837,335.255299 diff --git a/docs/src/en/benchmarks/startup-timing.csv b/docs/src/en/benchmarks/startup-timing.csv new file mode 100644 index 000000000..fdf33ae1f --- /dev/null +++ b/docs/src/en/benchmarks/startup-timing.csv @@ -0,0 +1,17 @@ +batch,benchmark_id,role,runs,warmup,hyperfine_version,metric_scope,evidence_sha256,manifest_sha256,measurement_engine,python_version,condition,command,N,Ready median ms,Ready mean ms,Ready SD ms,Ready min ms,Ready max ms,Exit median ms,Exit mean ms,Exit SD ms,Exit min ms,Exit max ms +native_timing-20261010T193144Z-rjxju56s,B-STARTUP,user-facing,60,3,,same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded,ba0b0607cfde7cc9d8851a838ca4a71b38c08eac81f1361f7ab68f96e08b0f24,9d7e1087e6897807884761ed201bfd2b97d71fb2b3600fa161e57792baae65ef,python-perf_counter_ns,3.12.11,native,"/bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,0.477416,0.4867091666666667,0.027473098763457073,0.45534,0.593298,0.5262070000000001,0.5548987,0.14369408916641668,0.504061,1.626316 +pvisor_host_timing-20261010T193144Z-7kp2ohaf,B-STARTUP,user-facing,60,3,,same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded,2a58fe62fb73d6c637241854da6fe0de2c3d1436b3f670c760166fd745d02de6,e3e38d24a5cfcaecad68f5c2a07b270b9654733b23aca3d8ebf13ce07c8c67a6,python-perf_counter_ns,3.12.11,pvisor-host,"/home/reiase/workspace/pvisor/target/release/pvisor run --no-agent-defaults --overlaynet off --stdio inherit --timeout 30s --filesystem host -- /bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,37.453213500000004,37.39181438333333,1.3806339289004357,33.823985,45.32391,129.0226385,128.38440935,2.8130187011713157,116.414786,132.229654 +pvisor_staged_timing-20261010T193207Z-14b8yji_,B-STARTUP,user-facing,60,3,,same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded,d9697f4bf482937967e3b7459fd4fc6f56d1fcf785f1e261fd3ff40fbac5e2b6,1d4b415be94fcfe71048b625e8eff82765e26b2392869a67351bd087abae8764,python-perf_counter_ns,3.12.11,pvisor-staged,"/home/reiase/workspace/pvisor/target/release/pvisor run --no-agent-defaults --overlaynet off --stdio inherit --timeout 30s --stage /home/reiase/workspace/pvisor/benchmark.new/startup/.data/pvisor_staged_timing-20261010T193207Z-14b8yji_/current/stage -- /bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,56.317359499999995,83.63402366666666,35.80452248684234,51.526137,151.770448,159.076721,182.31478523333334,36.35013478894402,139.925129,246.257413 +pvisor_vm-20261010T193231Z-oqxsd107,B-STARTUP,user-facing,60,3,,same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded,f49d48ad7568a601eadf2b470c0513edeef1c5d1864a11ee53dd9fdc418e3a3f,11644c889e7b014109709022e6b4222de3865f15fbf636b0aa9366278d2426cb,python-perf_counter_ns,3.12.11,pvisor-vm,"/home/reiase/workspace/pvisor/target/release/pvisor run --no-agent-defaults --overlaynet off --stdio inherit --timeout 30s --stage /home/reiase/workspace/pvisor/benchmark.new/startup/.data/pvisor_vm-20261010T193231Z-oqxsd107/current/stage --vm --rootfs /home/reiase/workspace/pvisor/benchmark/.data/6ra5/rootfs --cpu 2 --memory 128MiB -- /bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,179.21802150000002,171.2950154,22.10104697452002,137.615608,207.20476,297.7932135,293.0082749,23.8399220189266,247.049522,340.454659 +docker_timing-20261010T193254Z-vfew0_j6,B-STARTUP,user-facing,60,3,,same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded,66772bfad50467e9854931366f9c466936f432ebdb8cf8169f3f3d130f85589a,5583225f3e0382e0208f8d8cbe951fa92b7196dbfe65e5824586aa680a9a601f,python-perf_counter_ns,3.12.11,docker,"docker run --rm --pull never --network none --name pvisor-startup-0T193254Z-vfew0_j6 --label pvisor.startup.batch=docker_timing-20261010T193254Z-vfew0_j6 --mount type=bind,source=/home/reiase/workspace/pvisor/benchmark.new/startup/.data/docker_timing-20261010T193254Z-vfew0_j6/workspace,target=/work --workdir /work --entrypoint /bench/affinity sha256:7e73ccc8f9aa6a0883f8339b66ac0c0fc8284ffd61ea11cba43d131b5d06d689 0,1 /bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,111.212921,112.61178271666667,8.444143141831258,91.518154,140.077263,136.83700499999998,138.7524654,9.00429822650372,118.732902,169.009196 +qemu_timing-20261010T193411Z-xgtqdjpb,B-STARTUP,user-facing,60,3,,same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded,b4d1229af611f7884471e6988eee61219de68aeb1dd7966321196d4091609193,f5a176f8e7b58ce1ce366827fe4b3b5aaf2fdac052e225642a5fb9abb7288912,python-perf_counter_ns,3.12.11,qemu,"qemu-system-x86_64 -machine q35 -accel kvm -cpu host -smp 2 -m 128 -nodefaults -display none -serial stdio -no-reboot -kernel /home/reiase/workspace/pvisor/benchmark/.data/stock-kernel2-20261007/vmlinuz -append 'console=ttyS0 reboot=k panic=1 root=/dev/vda rw init=/bench/init quiet pvbench.mode=ready pvbench.scratch=executor' -drive file=/home/reiase/workspace/pvisor/benchmark.new/startup/.data/qemu_timing-20261010T193411Z-xgtqdjpb/current/rootfs.ext4,format=raw,if=none,id=root -device virtio-blk-pci,drive=root -initrd /home/reiase/workspace/pvisor/benchmark/.data/stock-kernel2-20261007/initrd",60,711.3557575,715.5877327166667,28.84870286560417,676.150134,777.811211,738.2912145,744.53627425,29.35072221760295,698.122196,804.280468 +qemu_microvm_timing-20261010T193436Z-bvzj9q7t,B-STARTUP,user-facing,60,3,,same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded,a12206db296e544ad323a3781bc9f96cd3370dab5cfc795faa0371d5d74aed65,8e217c76c1d10a542fa2ebc9cded9817d74fc0b96924b658f3a2d88c170d0d8b,python-perf_counter_ns,3.12.11,qemu-microvm,"qemu-system-x86_64 -machine microvm,acpi=off,x-option-roms=off,pit=off,pic=off,rtc=on -accel kvm -cpu host -smp 2 -m 128 -nodefaults -display none -serial stdio -no-reboot -kernel /home/reiase/workspace/pvisor/benchmark/.data/stock-kernel2-20261007/vmlinuz -append 'console=ttyS0 reboot=t panic=1 root=/dev/vda rw init=/bench/init quiet pvbench.mode=ready pvbench.scratch=executor pci=off' -drive file=/home/reiase/workspace/pvisor/benchmark.new/startup/.data/qemu_microvm_timing-20261010T193436Z-bvzj9q7t/current/rootfs.ext4,format=raw,if=none,id=root -device virtio-blk-device,drive=root -initrd /home/reiase/workspace/pvisor/benchmark/.data/stock-kernel2-20261007/initrd",60,298.98298450000004,299.9279542,30.521712217844783,244.717479,359.294324,321.4902005,323.5255617166667,29.485465599627037,266.138102,380.83449 +fc_system_timing-20261010T194324Z-0ngxz1rw,B-STARTUP,user-facing,60,3,,same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded,3928edf8392072a5529d79f3d8230829f23a036a11387291dea6707aac6c265d,aaeceeb1b87fe05a7491a51f539e0a4b9a65cbae60a01181c5a7b6cca05ad885,python-perf_counter_ns,3.12.11,fc-system,firecracker --no-api --config-file /home/reiase/workspace/pvisor/benchmark.new/startup/.data/fc_system_timing-20261010T194324Z-0ngxz1rw/current/firecracker.json,60,291.2311145,291.5582495333333,5.8558511887453095,282.256479,313.902527,726.9635,728.0146826166666,5.32211127846656,717.707895,740.754322 +native_timing-20261011T004342Z-8wjyuy21,B-STARTUP,user-facing,60,3,,same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded,355c05aa8af734b85978052114fc98485d339ec681f1e1cebc5bd5b317ac4948,571e859d1e439e764b42696e5280aa02bdeb4a3cb49797164d54116286d797e0,python-perf_counter_ns,3.12.11,native,"/bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,0.4768065,0.4821639,0.024363187201185323,0.453769,0.554185,0.5247455000000001,0.5674711833333334,0.20638363771035492,0.498852,1.721113 +pvisor_host_timing-20261011T004343Z-gdenfkzx,B-STARTUP,user-facing,60,3,,same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded,b5ec9fd9f67ad90cfa3b5aaa77041a2aad2a3426f82a7afd2c63efeccd954adb,768f808263dc663e926b58944b46d1e966d0b700f42a4cdacf91a7e0bd7c27fa,python-perf_counter_ns,3.12.11,pvisor-host,"/home/reiase/workspace/pvisor/target/release/pvisor run --no-agent-defaults --overlaynet off --stdio inherit --timeout 30s --filesystem host -- /bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,33.5245775,50.48170388333333,29.699968167718016,30.57654,128.654942,123.626168,138.93658936666665,27.73345550008831,112.022101,215.842663 +pvisor_staged_timing-20261011T004437Z-0_8_myox,B-STARTUP,user-facing,60,3,,same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded,ecad00dd273bd159cd74112059f1a1a09a0bf0a58cf999f596d5a5d7a2027049,8a4d6d031779febc3b156bfe29163e68ba7ccaa3f3294ff810cdd913b094e22f,python-perf_counter_ns,3.12.11,pvisor-staged,"/home/reiase/workspace/pvisor/target/release/pvisor run --no-agent-defaults --overlaynet off --stdio inherit --timeout 30s --stage /home/reiase/workspace/pvisor/benchmark.new/startup/.data/pvisor_staged_timing-20261011T004437Z-0_8_myox/current/stage -- /bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,50.5909475,52.77206473333333,17.86787212840543,46.178707,188.290473,146.34664,148.0955406,17.210223152340454,133.553461,274.998173 +pvisor_vm-20261011T004527Z-4o2qaxr9,B-STARTUP,user-facing,60,3,,same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded,6f1b2e5eb12b8012bb1f221d94944017c3792625ae27038e489d29808ae43de3,a63cb47729c4b6cf1f37e259241a0b5ee1105157e58a1eb5b79f5e8da82db252,python-perf_counter_ns,3.12.11,pvisor-vm,"/home/reiase/workspace/pvisor/target/release/pvisor run --no-agent-defaults --overlaynet off --stdio inherit --timeout 30s --stage /home/reiase/workspace/pvisor/benchmark.new/startup/.data/pvisor_vm-20261011T004527Z-4o2qaxr9/current/stage --vm --rootfs /home/reiase/workspace/pvisor/benchmark/.data/6ra5/rootfs --cpu 2 --memory 128MiB -- /bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,130.707348,130.10342321666667,6.156610149010636,119.004459,149.988327,243.53000400000002,248.7508986,17.71092203479732,218.450697,288.919716 +docker_timing-20261011T004624Z-8aq0yusi,B-STARTUP,user-facing,60,3,,same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded,9df027d8154142787594d86cd7572465957be3513333dd83c734debd50558c10,aea46ba225d18a1fd202e2460933d809a691636e33b0346319fcd4cc01eeb8d5,python-perf_counter_ns,3.12.11,docker,"docker run --rm --pull never --network none --name pvisor-startup-1T004624Z-8aq0yusi --label pvisor.startup.batch=docker_timing-20261011T004624Z-8aq0yusi --mount type=bind,source=/home/reiase/workspace/pvisor/benchmark.new/startup/.data/docker_timing-20261011T004624Z-8aq0yusi/workspace,target=/work --workdir /work --entrypoint /bench/affinity sha256:7e73ccc8f9aa6a0883f8339b66ac0c0fc8284ffd61ea11cba43d131b5d06d689 0,1 /bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,105.027266,105.5709834,7.221437055108282,90.523694,126.038887,129.3059355,130.3731021,7.663377586154465,114.191419,152.928737 +fc_system_timing-20261011T004712Z-97n8gne7,B-STARTUP,user-facing,60,3,,same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded,b0343f5973991391c32b67b4a8431a5e992c80cb6c403338d3dc4965e7ce2251,107c830711205462c88ae27f0de0e1088ffee1176a5f9842d483dacc1e8413f3,python-perf_counter_ns,3.12.11,fc-system,firecracker --no-api --config-file /home/reiase/workspace/pvisor/benchmark.new/startup/.data/fc_system_timing-20261011T004712Z-97n8gne7/current/firecracker.json,60,287.127745,287.06792748333334,5.885774302854362,269.078709,301.728159,722.0567149999999,722.7726551166667,6.025874020298251,711.091197,741.229639 +qemu_timing-20261011T004840Z-vh5l4h7y,B-STARTUP,user-facing,60,3,,same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded,7d8b028d8df05ff7b3cc881e9d30b998fb55de23b51c29329969ed1b86ce1ae0,1431f57af3f73fac2331982e186a0d61ba8de88d7f07e1213af23751a1c1fad9,python-perf_counter_ns,3.12.11,qemu,"qemu-system-x86_64 -machine q35 -accel kvm -cpu host -smp 2 -m 128 -nodefaults -display none -serial stdio -no-reboot -kernel /home/reiase/workspace/pvisor/benchmark/.data/stock-kernel2-20261007/vmlinuz -append 'console=ttyS0 reboot=k panic=1 root=/dev/vda rw init=/bench/init quiet pvbench.mode=ready pvbench.scratch=executor' -drive file=/home/reiase/workspace/pvisor/benchmark.new/startup/.data/qemu_timing-20261011T004840Z-vh5l4h7y/current/rootfs.ext4,format=raw,if=none,id=root -device virtio-blk-pci,drive=root -initrd /home/reiase/workspace/pvisor/benchmark/.data/stock-kernel2-20261007/initrd",60,663.2477994999999,667.5293913666667,28.729089815188537,631.375716,742.132208,691.7226949999999,695.4386896,28.6305736872296,656.021112,766.893319 +qemu_microvm_timing-20261011T005005Z-hra4_tyy,B-STARTUP,user-facing,60,3,,same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded,eb2c5c6f18620ac4f0f30e1d67aa47aa1fcaa223f6b050ad70ec9982f6a3b457,d5c74b3517c2b8ee545c4a274a8cea36e93d9f3c6b4b9153d0ea7ab4ce1d974a,python-perf_counter_ns,3.12.11,qemu-microvm,"qemu-system-x86_64 -machine microvm,acpi=off,x-option-roms=off,pit=off,pic=off,rtc=on -accel kvm -cpu host -smp 2 -m 128 -nodefaults -display none -serial stdio -no-reboot -kernel /home/reiase/workspace/pvisor/benchmark/.data/stock-kernel2-20261007/vmlinuz -append 'console=ttyS0 reboot=t panic=1 root=/dev/vda rw init=/bench/init quiet pvbench.mode=ready pvbench.scratch=executor pci=off' -drive file=/home/reiase/workspace/pvisor/benchmark.new/startup/.data/qemu_microvm_timing-20261011T005005Z-hra4_tyy/current/rootfs.ext4,format=raw,if=none,id=root -device virtio-blk-device,drive=root -initrd /home/reiase/workspace/pvisor/benchmark/.data/stock-kernel2-20261007/initrd",60,260.10463400000003,259.5273039,9.566460105458674,243.283605,288.390241,281.32751199999996,281.99962423333335,11.500231438448678,261.017615,315.552385 diff --git a/docs/src/en/benchmarks/startup-timing.exit.render.json b/docs/src/en/benchmarks/startup-timing.exit.render.json new file mode 100644 index 000000000..1462d62e6 --- /dev/null +++ b/docs/src/en/benchmarks/startup-timing.exit.render.json @@ -0,0 +1,66 @@ +{ + "Native": { + "N": 60, + "Exit median ms": "0.52", + "Exit mean ms": "0.57", + "Exit SD ms": "0.21", + "Exit min ms": "0.50", + "Exit max ms": "1.72" + }, + "pVisor host": { + "N": 60, + "Exit median ms": "123.63", + "Exit mean ms": "138.94", + "Exit SD ms": "27.73", + "Exit min ms": "112.02", + "Exit max ms": "215.84" + }, + "pVisor staged": { + "N": 60, + "Exit median ms": "146.35", + "Exit mean ms": "148.10", + "Exit SD ms": "17.21", + "Exit min ms": "133.55", + "Exit max ms": "275.00" + }, + "pVisor VM": { + "N": 60, + "Exit median ms": "243.53", + "Exit mean ms": "248.75", + "Exit SD ms": "17.71", + "Exit min ms": "218.45", + "Exit max ms": "288.92" + }, + "Docker": { + "N": 60, + "Exit median ms": "129.31", + "Exit mean ms": "130.37", + "Exit SD ms": "7.66", + "Exit min ms": "114.19", + "Exit max ms": "152.93" + }, + "Firecracker": { + "N": 60, + "Exit median ms": "722.06", + "Exit mean ms": "722.77", + "Exit SD ms": "6.03", + "Exit min ms": "711.09", + "Exit max ms": "741.23" + }, + "QEMU": { + "N": 60, + "Exit median ms": "691.72", + "Exit mean ms": "695.44", + "Exit SD ms": "28.63", + "Exit min ms": "656.02", + "Exit max ms": "766.89" + }, + "QEMU microvm": { + "N": 60, + "Exit median ms": "281.33", + "Exit mean ms": "282.00", + "Exit SD ms": "11.50", + "Exit min ms": "261.02", + "Exit max ms": "315.55" + } +} diff --git a/docs/src/en/benchmarks/startup-timing.ready.render.json b/docs/src/en/benchmarks/startup-timing.ready.render.json new file mode 100644 index 000000000..64f061151 --- /dev/null +++ b/docs/src/en/benchmarks/startup-timing.ready.render.json @@ -0,0 +1,66 @@ +{ + "Native": { + "N": 60, + "Ready median ms": "0.48", + "Ready mean ms": "0.48", + "Ready SD ms": "0.02", + "Ready min ms": "0.45", + "Ready max ms": "0.55" + }, + "pVisor host": { + "N": 60, + "Ready median ms": "33.52", + "Ready mean ms": "50.48", + "Ready SD ms": "29.70", + "Ready min ms": "30.58", + "Ready max ms": "128.65" + }, + "pVisor staged": { + "N": 60, + "Ready median ms": "50.59", + "Ready mean ms": "52.77", + "Ready SD ms": "17.87", + "Ready min ms": "46.18", + "Ready max ms": "188.29" + }, + "pVisor VM": { + "N": 60, + "Ready median ms": "130.71", + "Ready mean ms": "130.10", + "Ready SD ms": "6.16", + "Ready min ms": "119.00", + "Ready max ms": "149.99" + }, + "Docker": { + "N": 60, + "Ready median ms": "105.03", + "Ready mean ms": "105.57", + "Ready SD ms": "7.22", + "Ready min ms": "90.52", + "Ready max ms": "126.04" + }, + "Firecracker": { + "N": 60, + "Ready median ms": "287.13", + "Ready mean ms": "287.07", + "Ready SD ms": "5.89", + "Ready min ms": "269.08", + "Ready max ms": "301.73" + }, + "QEMU": { + "N": 60, + "Ready median ms": "663.25", + "Ready mean ms": "667.53", + "Ready SD ms": "28.73", + "Ready min ms": "631.38", + "Ready max ms": "742.13" + }, + "QEMU microvm": { + "N": 60, + "Ready median ms": "260.10", + "Ready mean ms": "259.53", + "Ready SD ms": "9.57", + "Ready min ms": "243.28", + "Ready max ms": "288.39" + } +} diff --git a/docs/src/en/benchmarks/startup.md b/docs/src/en/benchmarks/startup.md index 5beaed5fd..c082d1e52 100644 --- a/docs/src/en/benchmarks/startup.md +++ b/docs/src/en/benchmarks/startup.md @@ -2,7 +2,9 @@ ## Conclusions {#conclusions} -**With prepared environments, pVisor host and staged produce first output in 12.72 ms and 25.13 ms respectively. With Linux/KVM and 2 vCPU / 128 MiB, pVisor VM takes 100.91 ms, ahead of Firecracker (285.04 ms), QEMU (702.98 ms) and microvm (321.56 ms) using the full Fedora kernel.** +**In the independent-condition retest after the reboot on 2026-10-11, median Ready times are 33.52 ms for pVisor host, 50.59 ms for staged, 130.71 ms for VM, 105.03 ms for Docker, 287.13 ms for Firecracker MMIO, 663.25 ms for QEMU and 260.10 ms for microvm. Ready and normal Exit share sixty launches per condition. Use these tables for the measured artifacts; historical randomized-pair rankings and intervals do not carry over to the new cohorts.** + +The selection summary below retains the scope of its historical cohorts. | Need | Selection implication | | --- | --- | @@ -10,28 +12,76 @@ | Frequently created disposable VMs | pVisor VM waits about 0.1 seconds for the first command | | Complete Agent tool tasks | Use tool and repair results to choose an execution mode | + + +New experiments and historical data are published separately. Each table retains its timing scope and inputs from the design and provenance; distinct cohorts are not pooled. + + + ## Motivation {#motivation} Disposable environments repeatedly pay for the first command. Choosing host, staged, containers or VMs requires knowing their startup waits and the cost of the isolation you need. Short tasks also pay for exit and tools. ## Experiment design {#interpretation} -Linux x86_64 / KVM, AMD Ryzen 7 9700X, Fedora host. The controls are native, pVisor host, pVisor staged, pVisor VM, rootless Docker / overlay2, Firecracker PCI, QEMU q35 and QEMU microvm. Launch trees are pinned to CPUs 0,1; every VM has 2 vCPU / 128 MiB. Processes and containers have no memory limit. +The historical Ready / Exit comparison uses Linux x86_64 / KVM, AMD Ryzen 7 9700X, Fedora host. The controls are native, pVisor host, pVisor staged, pVisor VM, rootless Docker / overlay2, Firecracker PCI, QEMU q35 and QEMU microvm. Launch trees are pinned to CPUs 0,1; every VM has 2 vCPU / 128 MiB. Processes and containers have no memory limit. Firecracker, QEMU and microvm use the full Fedora distribution kernel, Linux 7.2.8-200.fc44.x86_64. pVisor's virtualization is based on libkrun and uses its companion libkrunfw project's customized kernel, Linux 6.12.109. Prepared userspace environments run the same `/bin/sh` output probe. Each execution uses a fresh environment and private workspace; every VM boots afresh, without snapshots or environment pools. Warm page caches, three warmups and sixty measured samples per backend, with randomized interleaving within each measurement group. Environment preparation and input copying are outside startup timing. Kernel, device and storage paths differ, so results cover each complete startup configuration. This probe does not cover full distribution boot, cold images, Agent CLI initialization, concurrent capacity or snapshot restoration. -Ready ends at the checked first shell marker. Exit includes execution and teardown, requiring correct output and a normal zero exit. Firecracker is stopped under control after checking markers and results and confirming no panic; only Ready is reported. Failures, panic, mismatched output or changed artifacts are excluded from timing distributions; valid slow samples are retained. +Historical Ready ends at the checked first shell marker. Exit includes execution and teardown, requiring correct output and a normal zero exit. Firecracker is stopped under control after checking markers and results and confirming no panic; only Ready is reported. Failures, panic, mismatched output or changed artifacts are excluded from timing distributions; valid slow samples are retained. + +The historical hyperfine measurements used the independent JSON templates available at the time, executed by semspec bench, with three warmups and sixty samples. They measure the complete command through normal exit, not first-output Ready; input preparation and correctness checks are outside timing. Conditions sample independently, without pooling batches or deriving paired improvement percentages. Those batches validate the observed executor in each Run Bundle: host and staged use `rootless_process` on this machine, and VM uses `vm`; artifact digests and batch provenance are recorded separately. ## Data and analysis {#results} +### Python: Ready / Exit from the same launch {#python-ready-exit} + +Each process launch records its start, receipt of the complete `REFERENCE_READY` line and +normal exit. Three warmups and sixty formal samples are fixed; both tables below project +columns from one command's output, without repeated sampling. Inputs are a prepared rootfs, +2 vCPU / 128 MiB, warm caches and CPU 0,1 affinity. Python `perf_counter_ns` excludes +interpreter startup and input preparation. Ready includes process creation, pipe reading and +observer scheduling; Exit uses the same start through observed normal exit. Output, Run Bundle +and unchanged inputs must pass for every sample. +New Firecracker uses the stock kernel, the same initrd and virtio-MMIO. Two PCI preflights failed to exit normally after successful guest markers and were rejected. Historical Firecracker PCI Ready and new MMIO use different configurations; subtracting them does not establish an improvement. + +#### Ready + +{{ table("startup-timing.ready.render.json") }} + +#### Exit + +{{ table("startup-timing.exit.render.json") }} + +The [paired-sample statistics CSV](startup-timing.csv) and [provenance JSON](../assets/benchmarks/startup-timing.provenance.json) +identify each condition’s latest successful independent batch. Standard deviation uses the formal samples and sample formula. These are +Python event-clock data, independent of the hyperfine completion experiment. Historical results +from different artifacts are not pooled. Rerun the entire table with `just benchmark startup/timing`, +or just VM with `just benchmark startup/timing "pvisor vm"`. +The [earlier VM-only cohort](startup-ready-exit.csv) remains separate. + +All eight conditions were remeasured after the reboot on 2026-10-11. The +[before/after comparison CSV](startup-reboot-comparison.csv) retains full-precision statistics +and provenance digests for both independent cohorts. The measured pVisor binary SHA-256 is +`d3f09687cca60b617629c215fa1359c441ab6ac19f8c63b68f1dcdf7614e6b30`, unchanged across the reboot; +userspace inputs, measured commands, timing and statistics functions are also unchanged. +Failure handling and provenance fields were added to the harness outside timing. +VM median Ready fell from 179.22 ms to 130.71 ms (a descriptive change of -27.07%), and +Exit from 297.79 ms to 243.53 ms. Host median Ready fell from 37.45 ms to 33.52 ms, but +its standard deviation rose from 1.38 ms to 29.70 ms, with the final seventeen formal +samples noticeably slower. All valid slow samples are retained. These independently sampled, +warm-cache cohorts do not establish a causal reboot effect on their own. The historical VM +Ready of about 101 ms used different binary and firmware artifacts; attributing the remaining +gap requires controls with fixed artifacts. + ### Startup and exit times {#reference-startup} -Unit ms, N=60 per backend, zero measured failures. Process/container and VM measurements were collected separately, with each row retaining its own statistics; all four VM controls were randomly interleaved within one group. P95 describes the distribution rather than guaranteeing stable tail latency. +Historical controls: unit ms, N=60 per backend, zero measured failures. Process/container and VM measurements were collected separately, with each row retaining its own statistics; all four VM controls were randomly interleaved within one group. P95 describes the distribution rather than guaranteeing stable tail latency. | Runtime | Valid / failed | Ready P50 ms | Ready P95 ms | Exit P50 ms | Exit P95 ms | | --- | --- | --- | --- | --- | --- | @@ -56,6 +106,64 @@ Differences are differences between the two medians, with 95% intervals from 5,0 For cold-image client waiting and content downloads, see the separate [on-demand image startup](lazy-image-startup.md) experiment. Image-service preparation is recorded separately, and its samples are not pooled with these prepared-environment results. + + +### Historical Python: Ready-only data {#python-ready} + +This experiment uses a prepared rootfs, 2 vCPU / 128 MiB, warm caches and CPU 0,1 affinity. +Three warmups and sixty formal samples are fixed in the script. Python `perf_counter_ns` +starts immediately before Popen and stops on receipt of the complete `REFERENCE_READY` line. +Process creation, pipe reading and observer scheduling are included; interpreter startup, +input preparation, subsequent exit and cleanup are excluded. Correct output, normal zero exit +and a valid Run Bundle remain required after the clock stops. + +| | N | Ready median ms | Ready mean ms | Ready SD ms | Ready min ms | Ready max ms | +| --- | --- | --- | --- | --- | --- | --- | +| pVisor VM | 60 | 132.95 | 132.51 | 5.09 | 123.00 | 147.53 | + +The [Ready statistics CSV](startup-ready.csv) and [provenance JSON](../assets/benchmarks/startup-ready.provenance.json) +retain the latest successful batch for this condition. Standard deviation uses formal samples +and the sample formula. These are Python clock measurements, separate from hyperfine completion. +Historical Ready used a different binary artifact; timing boundaries alone cannot explain the difference. +The current Ready / Exit experiment uses `just benchmark startup/timing "pvisor vm"`; this section retains the older Ready-only cohort. + +### Historical Hyperfine: direct launch to normal exit {#hyperfine-startup} + +Each row uses that condition's latest successful independent batch, without pooling samples. Three warmups and sixty measured samples; median, mean and standard deviation come from the same raw hyperfine export, in ms. Python preparation and checks, semspec processing and external CPU affinity setup are outside timing. + +| | N | Median ms | Mean ms | SD ms | +| --- | --- | --- | --- | --- | +| Native | 60 | 0.42 | 0.42 | 0.02 | +| pVisor host | 60 | 208.79 | 217.94 | 34.13 | +| pVisor staged | 60 | 153.85 | 154.58 | 5.88 | +| pVisor VM | 60 | 278.83 | 305.77 | 63.45 | +| Docker | 60 | 163.92 | 168.87 | 21.38 | +| Firecracker | — | — | — | — | +| QEMU | 60 | 722.08 | 711.24 | 26.75 | +| QEMU microvm | 60 | 339.47 | 355.08 | 86.50 | + +Empty conditions have no validated new batch. This measures the command through normal exit, not first-output Ready. Rows show independent levels, without paired improvement percentages or confidence intervals. See the [statistics CSV](startup-semspec.csv) and [per-row provenance JSON](../assets/benchmarks/startup-semspec.provenance.json) for commands, extrema and sources. + +
+Historical complete commands (including wrappers) + +| | N | Median ms | Mean ms | SD ms | +| --- | --- | --- | --- | --- | +| Native | 60 | 2.13 | 2.17 | 0.23 | +| pVisor host | 60 | 165.12 | 315.84 | 259.74 | +| pVisor staged | — | — | — | — | +| pVisor VM | — | — | — | — | +| Docker | 60 | 167.27 | 381.08 | 509.32 | +| Firecracker | — | — | — | — | +| QEMU | 60 | 748.41 | 896.73 | 319.03 | +| QEMU microvm | 60 | 353.76 | 451.13 | 219.93 | + +The [historical CSV](startup-hyperfine.csv) includes an outer shell, taskset and timeout. Its original scope is preserved. These are different batches and scopes; subtracting them cannot estimate wrapper cost or establish product performance improvements. + +
+ + + ### Downloads and reproduction {#run} -[Startup statistics](startup.csv) · [VM differences and 95% intervals](startup-stock-comparisons.csv) · [VM source and artifact provenance](startup-stock-provenance.csv) · [Method](methodology.md) · [Reproduction manual](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/pvisor/README.md#explicit-firecracker-kernel-controls). Binaries, complete inputs and raw logs stay in local ignored `.data/`; CSVs retain statistical sources and sample counts, and provenance records retain CPU/RAM configuration and report/artifact digests. +[Startup statistics](startup.csv) · [VM differences and 95% intervals](startup-stock-comparisons.csv) · [VM source and artifact provenance](startup-stock-provenance.csv) · [Method](methodology.md) · [Historical reproduction manual](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/pvisor/README.md#explicit-firecracker-kernel-controls). See the [startup manual](https://github.com/DeepLink-org/pvisor/blob/main/benchmark.new/startup/README.md) to reproduce the current Ready / Exit experiment. Binaries, complete inputs and raw logs stay in local ignored `.data/`; CSVs retain statistical sources and sample counts, and provenance records retain CPU/RAM configuration and report/artifact digests. diff --git a/docs/src/en/benchmarks/supervision-cost.md b/docs/src/en/benchmarks/supervision-cost.md index 8fb7332b3..617b4eff6 100644 --- a/docs/src/en/benchmarks/supervision-cost.md +++ b/docs/src/en/benchmarks/supervision-cost.md @@ -12,6 +12,12 @@ Applying prepared changes alone takes pVisor apply P50 of **17 ms, 0.93 s and 14 | Small workspace or a prepared, reused Git worktree | Git is faster for small workspaces and review of a prepared twenty-file view; long-term reuse is unmeasured | | Estimate human supervision cost | Machine timings exclude human reading and decisions | + + +The numbers and conclusions on this page still belong to the original historical cohorts. The new framework has no matching rerun yet; see [measurement coverage](index.md#measurement-coverage). + + + ## Motivation {#motivation} An Agent task needs a private workspace, diff review, selective application and disposal as well as tool execution. Individual filesystem timings omit these costs. If you frequently create task workspaces, compare the entire workflow that produces the same result. diff --git a/docs/src/en/benchmarks/vm-memory/index.md b/docs/src/en/benchmarks/vm-memory/index.md index 4215635b2..f921334de 100644 --- a/docs/src/en/benchmarks/vm-memory/index.md +++ b/docs/src/en/benchmarks/vm-memory/index.md @@ -14,6 +14,12 @@ | KSM | After 60 seconds, savings are 63.6% for repeated contents, 51.7% for identical random contents and 16.0% for unique random contents | | Daemon memory pool | About 61–124 MiB resident per instance; repeated / identical random / unique random savings are 53.7% / 39.6% / 6.5% | + + +The numbers and conclusions on this page still belong to the original historical cohorts. The new framework has no matching rerun yet; see [measurement coverage](../index.md#measurement-coverage). + + + ## Motivation {#motivation} 512 MiB is the guest's available capacity, not physical memory already consumed by the VM on the host. Choose compression, offload or sharing from actual host usage and the next tool's recovery cost. Guest free memory and compression ratios alone cannot establish how much host RAM was saved. diff --git a/docs/src/en/design/concurrency-filesystem-summary.csv b/docs/src/en/design/concurrency-filesystem-summary.csv new file mode 100644 index 000000000..4a30c08ad --- /dev/null +++ b/docs/src/en/design/concurrency-filesystem-summary.csv @@ -0,0 +1,67 @@ +benchmark_id,batch,backend,metric,reference,condition,unit,n,failures,reference_p50_ms,candidate_p50_ms,reference_distribution,candidate_distribution,reference_clusters,candidate_clusters,median_difference_ms,ci95_low_ms,ci95_high_ms,change_percent,ci95_low_percent,ci95_high_percent,interpretation +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,ready_ms,before,baseline,ms,30,0,229.91647,229.98571349999997,unsplit,unsplit,"{""n"": 30, ""p50"": 229.91647, ""p95_reference"": 238.32857479999998, ""minimum"": 225.00713, ""maximum"": 239.060502, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 229.98571349999997, ""p95_reference"": 239.74732755, ""minimum"": 225.925142, ""maximum"": 241.800046, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.06924349999997048,-3.7560089999999775,1.5340036000000123,0.030116807203928175,-1.600658088796314,0.6705387744533059,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,result_ms,before,baseline,ms,30,0,1432.675709,1452.1272185,unsplit,unsplit,"{""n"": 30, ""p50"": 1432.675709, ""p95_reference"": 1483.4033009999998, ""minimum"": 1410.577951, ""maximum"": 1508.418277, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 1452.1272185, ""p95_reference"": 1482.17247615, ""minimum"": 1425.4714, ""maximum"": 1493.164094, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",19.45150949999993,1.4766259999998965,33.376978000000236,1.357704983605612,0.1027493854155681,2.341979201568112,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,completion_ms,before,baseline,ms,30,0,1736.98509,1755.9002185,unsplit,unsplit,"{""n"": 30, ""p50"": 1736.98509, ""p95_reference"": 1806.5802087, ""minimum"": 1709.818704, ""maximum"": 1843.440248, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 1755.9002185, ""p95_reference"": 1801.23240945, ""minimum"": 1710.367224, ""maximum"": 1822.801743, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",18.915128500000037,5.857880962499979,37.47388799999999,1.0889632046294784,0.334793959152343,2.161752363391889,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,worker_ms,before,baseline,ms,30,0,1200.7365585,1221.6369465,unsplit,unsplit,"{""n"": 30, ""p50"": 1200.7365585, ""p95_reference"": 1249.71875875, ""minimum"": 1183.925077, ""maximum"": 1272.398219, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 1221.6369465, ""p95_reference"": 1251.0438589999999, ""minimum"": 1197.346921, ""maximum"": 1254.41953, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",20.90038800000002,4.867007324999998,28.62760000000003,1.7406306031116037,0.40108238884013325,2.3982232479462073,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,metadata,before,baseline,ms,30,0,66.8129905,66.747274,unsplit,unsplit,"{""n"": 30, ""p50"": 66.8129905, ""p95_reference"": 68.5091225, ""minimum"": 65.630669, ""maximum"": 70.287737, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 66.747274, ""p95_reference"": 67.72837894999999, ""minimum"": 65.793975, ""maximum"": 69.340539, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.06571649999999352,-0.5632655,0.33756449999999916,-0.09835886630459312,-0.8391419689548707,0.5074020396881318,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,read,before,baseline,ms,30,0,66.82559699999999,67.200728,unsplit,unsplit,"{""n"": 30, ""p50"": 66.82559699999999, ""p95_reference"": 84.0615196, ""minimum"": 64.394717, ""maximum"": 86.841035, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 67.200728, ""p95_reference"": 87.6224648, ""minimum"": 64.505854, ""maximum"": 89.45771, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.3751310000000103,-0.246721462499999,1.3834005000000076,0.5613582471998058,-0.3686651121435963,2.070130098516554,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,write,before,baseline,ms,30,0,25.8121755,27.7569145,unsplit,unsplit,"{""n"": 30, ""p50"": 25.8121755, ""p95_reference"": 27.6612622, ""minimum"": 24.89516, ""maximum"": 38.060812, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 27.7569145, ""p95_reference"": 31.669841, ""minimum"": 26.927324, ""maximum"": 46.557162, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",1.944739000000002,1.5309370000000015,2.6798775,7.534192536386564,5.865745079707496,10.41130974741223,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,git,before,baseline,ms,30,0,139.85539599999998,142.645583,unsplit,unsplit,"{""n"": 30, ""p50"": 139.85539599999998, ""p95_reference"": 159.10001265, ""minimum"": 137.212516, ""maximum"": 160.940306, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 142.645583, ""p95_reference"": 151.54488995, ""minimum"": 138.077973, ""maximum"": 167.480336, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",2.790187000000003,0.23915699999997742,4.193277000000023,1.9950513743495435,0.16996744285950882,3.011356100774054,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,rg,before,baseline,ms,30,0,106.22186500000001,107.2148145,unsplit,unsplit,"{""n"": 30, ""p50"": 106.22186500000001, ""p95_reference"": 109.24850185, ""minimum"": 103.694053, ""maximum"": 111.563961, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 107.2148145, ""p95_reference"": 109.80437595000001, ""minimum"": 105.088613, ""maximum"": 114.976122, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.9929494999999946,0.17080017499999797,2.0470097124999853,0.9347882378077266,0.16032914825811095,1.9489317087555635,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,cargo,before,baseline,ms,30,0,69.4859085,71.7917695,unsplit,unsplit,"{""n"": 30, ""p50"": 69.4859085, ""p95_reference"": 89.68605625000001, ""minimum"": 66.90582, ""maximum"": 97.637597, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 71.7917695, ""p95_reference"": 94.70548355, ""minimum"": 68.274296, ""maximum"": 94.887111, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",2.3058610000000073,1.0185160000000053,3.925326999999996,3.318458446866246,1.4565101933340818,5.645965992760083,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,npm,before,baseline,ms,30,0,229.7664595,230.2638835,unsplit,unsplit,"{""n"": 30, ""p50"": 229.7664595, ""p95_reference"": 238.9916174, ""minimum"": 223.771816, ""maximum"": 244.285887, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 230.2638835, ""p95_reference"": 235.71617395, ""minimum"": 222.969277, ""maximum"": 238.44851, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.4974239999999952,-1.4246772249999993,2.522163000000006,0.21649112802732962,-0.6176680833597179,1.0962831613243518,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,ready_ms,baseline,candidate,ms,30,0,229.98571349999997,219.2519275,unsplit,unsplit,"{""n"": 30, ""p50"": 229.98571349999997, ""p95_reference"": 239.74732755, ""minimum"": 225.925142, ""maximum"": 241.800046, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 219.2519275, ""p95_reference"": 225.1728297, ""minimum"": 216.247081, ""maximum"": 227.081684, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-10.73378599999998,-11.616524499999997,-9.230597999999986,-4.667153379507627,-5.040109487846952,-4.0275789223127605,faster +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,result_ms,baseline,candidate,ms,30,0,1452.1272185,1424.0447745,unsplit,unsplit,"{""n"": 30, ""p50"": 1452.1272185, ""p95_reference"": 1482.17247615, ""minimum"": 1425.4714, ""maximum"": 1493.164094, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 1424.0447745, ""p95_reference"": 1475.2823721, ""minimum"": 1410.463259, ""maximum"": 1503.673034, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-28.082444000000123,-38.423754500000086,-8.237163025000063,-1.933883177880813,-2.627129382665516,-0.5668043495671742,faster +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,completion_ms,baseline,candidate,ms,30,0,1755.9002185,1668.1645155,unsplit,unsplit,"{""n"": 30, ""p50"": 1755.9002185, ""p95_reference"": 1801.23240945, ""minimum"": 1710.367224, ""maximum"": 1822.801743, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 1668.1645155, ""p95_reference"": 1718.1817415, ""minimum"": 1640.05777, ""maximum"": 1745.974529, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-87.73570299999983,-99.79265829999989,-66.70573049999985,-4.99662236359587,-5.645611656690141,-3.801581220258332,faster +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,worker_ms,baseline,candidate,ms,30,0,1221.6369465,1204.6902355,unsplit,unsplit,"{""n"": 30, ""p50"": 1221.6369465, ""p95_reference"": 1251.0438589999999, ""minimum"": 1197.346921, ""maximum"": 1254.41953, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 1204.6902355, ""p95_reference"": 1254.5410703, ""minimum"": 1192.402507, ""maximum"": 1284.236761, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-16.94671100000005,-21.77219178749984,0.02503700000011122,-1.3872133655217667,-1.7787872004971146,0.002047580554398465,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,metadata,baseline,candidate,ms,30,0,66.747274,66.34579550000001,unsplit,unsplit,"{""n"": 30, ""p50"": 66.747274, ""p95_reference"": 67.72837894999999, ""minimum"": 65.793975, ""maximum"": 69.340539, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 66.34579550000001, ""p95_reference"": 67.6513001, ""minimum"": 64.847312, ""maximum"": 69.493501, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.4014784999999961,-0.7117880000000127,0.025973675000000262,-0.6014904818434919,-1.0658511874154009,0.03905143622526831,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,read,baseline,candidate,ms,30,0,67.200728,67.6185575,unsplit,unsplit,"{""n"": 30, ""p50"": 67.200728, ""p95_reference"": 87.6224648, ""minimum"": 64.505854, ""maximum"": 89.45771, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 67.6185575, ""p95_reference"": 86.2219173, ""minimum"": 64.034195, ""maximum"": 91.423127, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.4178294999999963,-0.821345000000008,1.465355925000001,0.6217633535160472,-1.199008245463562,2.2030811609936567,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,write,baseline,candidate,ms,30,0,27.7569145,26.6695915,unsplit,unsplit,"{""n"": 30, ""p50"": 27.7569145, ""p95_reference"": 31.669841, ""minimum"": 26.927324, ""maximum"": 46.557162, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 26.6695915, ""p95_reference"": 28.10140115, ""minimum"": 25.499008, ""maximum"": 42.255647, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-1.0873230000000014,-1.8405830000000023,-0.6787167250000019,-3.9173050016059974,-6.469080863044775,-2.44476937509307,faster +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,git,baseline,candidate,ms,30,0,142.645583,141.874509,unsplit,unsplit,"{""n"": 30, ""p50"": 142.645583, ""p95_reference"": 151.54488995, ""minimum"": 138.077973, ""maximum"": 167.480336, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 141.874509, ""p95_reference"": 146.1360852, ""minimum"": 138.46138, ""maximum"": 166.552746, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.7710739999999987,-2.0200437624999963,1.264147237499974,-0.5405523141925772,-1.4056665800519654,0.897091209424094,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,rg,baseline,candidate,ms,30,0,107.2148145,107.5758025,unsplit,unsplit,"{""n"": 30, ""p50"": 107.2148145, ""p95_reference"": 109.80437595000001, ""minimum"": 105.088613, ""maximum"": 114.976122, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 107.5758025, ""p95_reference"": 109.17498499999999, ""minimum"": 104.800776, ""maximum"": 109.830677, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.36098799999999187,-0.41142899999999827,1.2563604999999853,0.33669600762120666,-0.38314212460381913,1.1816846254704583,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,cargo,baseline,candidate,ms,30,0,71.7917695,70.61406149999999,unsplit,unsplit,"{""n"": 30, ""p50"": 71.7917695, ""p95_reference"": 94.70548355, ""minimum"": 68.274296, ""maximum"": 94.887111, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 70.61406149999999, ""p95_reference"": 87.73722915, ""minimum"": 67.557371, ""maximum"": 92.931381, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-1.1777080000000097,-2.770897000000005,0.5887617624999866,-1.6404498847183424,-3.7814851081183347,0.8251468207752486,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,npm,baseline,candidate,ms,30,0,230.2638835,230.2610775,unsplit,unsplit,"{""n"": 30, ""p50"": 230.2638835, ""p95_reference"": 235.71617395, ""minimum"": 222.969277, ""maximum"": 238.44851, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 230.2610775, ""p95_reference"": 242.12161375, ""minimum"": 222.594656, ""maximum"": 251.761305, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.0028059999999925367,-2.284809999999993,2.131650499999978,-0.00121860187421996,-0.9857928417418615,0.9299392205147416,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,ready_ms,before,candidate,ms,30,0,229.91647,219.2519275,unsplit,unsplit,"{""n"": 30, ""p50"": 229.91647, ""p95_reference"": 238.32857479999998, ""minimum"": 225.00713, ""maximum"": 239.060502, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 219.2519275, ""p95_reference"": 225.1728297, ""minimum"": 216.247081, ""maximum"": 227.081684, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-10.66454250000001,-14.508426999999983,-9.276924499999978,-4.638442169888924,-6.201985461067475,-4.057051099123587,faster +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,result_ms,before,candidate,ms,30,0,1432.675709,1424.0447745,unsplit,unsplit,"{""n"": 30, ""p50"": 1432.675709, ""p95_reference"": 1483.4033009999998, ""minimum"": 1410.577951, ""maximum"": 1508.418277, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 1424.0447745, ""p95_reference"": 1475.2823721, ""minimum"": 1410.463259, ""maximum"": 1503.673034, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-8.630934500000194,-24.200380499999937,11.341323000000102,-0.6024346225584121,-1.6726311854847076,0.791700062704126,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,completion_ms,before,candidate,ms,30,0,1736.98509,1668.1645155,unsplit,unsplit,"{""n"": 30, ""p50"": 1736.98509, ""p95_reference"": 1806.5802087, ""minimum"": 1709.818704, ""maximum"": 1843.440248, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 1668.1645155, ""p95_reference"": 1718.1817415, ""minimum"": 1640.05777, ""maximum"": 1745.974529, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-68.8205744999998,-78.74124916250011,-48.03843048749988,-3.9620705379802557,-4.508901934704912,-2.7709160549363636,faster +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,worker_ms,before,candidate,ms,30,0,1200.7365585,1204.6902355,unsplit,unsplit,"{""n"": 30, ""p50"": 1200.7365585, ""p95_reference"": 1249.71875875, ""minimum"": 1183.925077, ""maximum"": 1272.398219, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 1204.6902355, ""p95_reference"": 1254.5410703, ""minimum"": 1192.402507, ""maximum"": 1284.236761, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",3.9536769999999706,-10.102030037499992,18.527396437499945,0.32927097721910936,-0.8336866554901787,1.5440042793461561,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,metadata,before,candidate,ms,30,0,66.8129905,66.34579550000001,unsplit,unsplit,"{""n"": 30, ""p50"": 66.8129905, ""p95_reference"": 68.5091225, ""minimum"": 65.630669, ""maximum"": 70.287737, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 66.34579550000001, ""p95_reference"": 67.6513001, ""minimum"": 64.847312, ""maximum"": 69.493501, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.4671949999999896,-0.8462214250000003,-0.09388767500000254,-0.6992577289292146,-1.2609061646048372,-0.14090263251865448,faster +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,read,before,candidate,ms,30,0,66.82559699999999,67.6185575,unsplit,unsplit,"{""n"": 30, ""p50"": 66.82559699999999, ""p95_reference"": 84.0615196, ""minimum"": 64.394717, ""maximum"": 86.841035, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 67.6185575, ""p95_reference"": 86.2219173, ""minimum"": 64.034195, ""maximum"": 91.423127, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.7929605000000066,-0.044775099999996834,1.700723999999994,1.186611920578895,-0.0667637260475182,2.555513183817193,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,write,before,candidate,ms,30,0,25.8121755,26.6695915,unsplit,unsplit,"{""n"": 30, ""p50"": 25.8121755, ""p95_reference"": 27.6612622, ""minimum"": 24.89516, ""maximum"": 38.060812, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 26.6695915, ""p95_reference"": 28.10140115, ""minimum"": 25.499008, ""maximum"": 42.255647, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.8574160000000006,0.4421379999999999,1.353806500000001,3.321750233722076,1.693633447021649,5.299561377085471,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,git,before,candidate,ms,30,0,139.85539599999998,141.874509,unsplit,unsplit,"{""n"": 30, ""p50"": 139.85539599999998, ""p95_reference"": 159.10001265, ""minimum"": 137.212516, ""maximum"": 160.940306, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 141.874509, ""p95_reference"": 146.1360852, ""minimum"": 138.46138, ""maximum"": 166.552746, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",2.0191130000000044,0.8281499999999937,3.2326907500000104,1.443714763783599,0.5872585675185338,2.3259170570486765,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,rg,before,candidate,ms,30,0,106.22186500000001,107.5758025,unsplit,unsplit,"{""n"": 30, ""p50"": 106.22186500000001, ""p95_reference"": 109.24850185, ""minimum"": 103.694053, ""maximum"": 111.563961, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 107.5758025, ""p95_reference"": 109.17498499999999, ""minimum"": 104.800776, ""maximum"": 109.830677, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",1.3539374999999865,0.4326582374999969,2.420104500000008,1.2746316401053503,0.4060921276966667,2.29953303821655,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,cargo,before,candidate,ms,30,0,69.4859085,70.61406149999999,unsplit,unsplit,"{""n"": 30, ""p50"": 69.4859085, ""p95_reference"": 89.68605625000001, ""minimum"": 66.90582, ""maximum"": 97.637597, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 70.61406149999999, ""p95_reference"": 87.73722915, ""minimum"": 67.557371, ""maximum"": 92.931381, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",1.1281529999999975,-0.19087100000000135,2.7702473749999963,1.6235709143818777,-0.2695991190721614,4.008438674779477,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,npm,before,candidate,ms,30,0,229.7664595,230.2610775,unsplit,unsplit,"{""n"": 30, ""p50"": 229.7664595, ""p95_reference"": 238.9916174, ""minimum"": 223.771816, ""maximum"": 244.285887, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 230.2610775, ""p95_reference"": 242.12161375, ""minimum"": 222.594656, ""maximum"": 251.761305, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.49461800000000267,-1.3394670000000133,2.3905861500000043,0.2152698879881454,-0.5818155197230057,1.0426051565434238,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,ready_ms,before,baseline,ms,30,0,701.0899955,701.291806,unsplit,unsplit,"{""n"": 30, ""p50"": 701.0899955, ""p95_reference"": 732.14511455, ""minimum"": 686.675537, ""maximum"": 873.145236, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 701.291806, ""p95_reference"": 731.1870892, ""minimum"": 686.521945, ""maximum"": 789.906315, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.20181049999996503,-6.143235000000004,7.4441127000000655,0.028785248868956614,-0.8710870179678643,1.0656154816754777,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,result_ms,before,baseline,ms,30,0,4297.801669,4278.449968,unsplit,unsplit,"{""n"": 30, ""p50"": 4297.801669, ""p95_reference"": 5080.3648109000005, ""minimum"": 4074.466407, ""maximum"": 5228.259533, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 4278.449968, ""p95_reference"": 5340.32934275, ""minimum"": 4069.431508, ""maximum"": 5474.773979, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-19.351701000000503,-130.84436600000026,98.9951629999996,-0.45026975394384383,-2.969052346742973,2.347429750542762,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,completion_ms,before,baseline,ms,30,0,4544.0489055,4535.762954,unsplit,unsplit,"{""n"": 30, ""p50"": 4544.0489055, ""p95_reference"": 5334.32411415, ""minimum"": 4317.653732, ""maximum"": 5526.082774, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 4535.762954, ""p95_reference"": 5577.58314435, ""minimum"": 4314.00466, ""maximum"": 5766.958044, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-8.28595150000001,-116.6800845000007,104.18260949999967,-0.18234732222998096,-2.5157048607849952,2.3138633248019977,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,worker_ms,before,baseline,ms,30,0,3589.173895,3578.175109,unsplit,unsplit,"{""n"": 30, ""p50"": 3589.173895, ""p95_reference"": 4356.26645515, ""minimum"": 3375.558433, ""maximum"": 4522.132202, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 3578.175109, ""p95_reference"": 4633.7424845000005, ""minimum"": 3367.664205, ""maximum"": 4777.305497, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-10.99878600000011,-130.28897150000012,74.88542894999988,-0.30644338563038387,-3.514336657179251,2.101642282507704,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,metadata,before,baseline,ms,30,0,133.4563125,157.8011775,unsplit,unsplit,"{""n"": 30, ""p50"": 133.4563125, ""p95_reference"": 192.7246791, ""minimum"": 108.503417, ""maximum"": 194.824494, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 157.8011775, ""p95_reference"": 189.13611625000001, ""minimum"": 112.822408, ""maximum"": 198.907503, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",24.344865,12.818983487499969,38.27126057499998,18.241823518089497,9.172649821108937,31.596964415085505,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,read,before,baseline,ms,30,0,115.795024,117.672345,unsplit,unsplit,"{""n"": 30, ""p50"": 115.795024, ""p95_reference"": 136.34799395000002, ""minimum"": 110.380387, ""maximum"": 609.142557, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 117.672345, ""p95_reference"": 141.71966129999998, ""minimum"": 112.567169, ""maximum"": 142.482554, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",1.8773210000000091,-0.32354749999998944,4.782931500000004,1.621244968177571,-0.2742023414077699,4.172260618919554,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,write,before,baseline,ms,30,0,131.376992,131.0506595,unsplit,unsplit,"{""n"": 30, ""p50"": 131.376992, ""p95_reference"": 280.5306881000002, ""minimum"": 96.834779, ""maximum"": 499.98355, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 131.0506595, ""p95_reference"": 148.0101782, ""minimum"": 97.027691, ""maximum"": 173.047176, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.3263325000000066,-8.5659527125,6.082268499999998,-0.24839395013702692,-6.360689513400716,4.755462668297984,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,git,before,baseline,ms,30,0,,,separated-clusters,separated-clusters,"{""n"": 30, ""p50"": """", ""p95_reference"": 733.711548, ""minimum"": 347.08043, ""maximum"": 745.646024, ""distribution"": ""separated-clusters"", ""low_n"": 21, ""low_p50"": 374.894303, ""high_n"": 9, ""high_p50"": 723.043237}","{""n"": 30, ""p50"": """", ""p95_reference"": 764.0261530500001, ""minimum"": 353.68035, ""maximum"": 1143.341075, ""distribution"": ""separated-clusters"", ""low_n"": 24, ""low_p50"": 375.053872, ""high_n"": 6, ""high_p50"": 749.922686}",,,,,,,separated clusters; no whole-distribution ranking +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,rg,before,baseline,ms,30,0,424.35992799999997,426.7428575,unsplit,unsplit,"{""n"": 30, ""p50"": 424.35992799999997, ""p95_reference"": 445.81724305, ""minimum"": 373.277409, ""maximum"": 449.78536, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 426.7428575, ""p95_reference"": 448.67384825000005, ""minimum"": 379.105913, ""maximum"": 461.008823, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",2.3829295000000457,-10.78591141250001,13.572753500000033,0.5615349948876469,-2.499321157216366,3.2639920232744855,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,cargo,before,baseline,ms,30,0,483.237283,501.3797235,unsplit,unsplit,"{""n"": 30, ""p50"": 483.237283, ""p95_reference"": 551.8527210000001, ""minimum"": 460.089429, ""maximum"": 1005.241583, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 501.3797235, ""p95_reference"": 806.8878312000002, ""minimum"": 474.732044, ""maximum"": 913.063239, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",18.14244050000002,5.078862000000015,25.685059500000023,3.7543544627536507,1.036280621480845,5.358434870249336,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,npm,before,baseline,ms,30,0,928.2279195,923.2233120000001,unsplit,unsplit,"{""n"": 30, ""p50"": 928.2279195, ""p95_reference"": 1017.8536178, ""minimum"": 750.15394, ""maximum"": 1168.682019, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 923.2233120000001, ""p95_reference"": 1051.5576092000001, ""minimum"": 761.841513, ""maximum"": 1353.648422, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-5.004607499999906,-17.426156374999987,9.47210236249997,-0.5391571827203512,-1.8600305012168472,1.0223747436623447,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,ready_ms,baseline,candidate,ms,30,0,701.291806,693.9176055,unsplit,unsplit,"{""n"": 30, ""p50"": 701.291806, ""p95_reference"": 731.1870892, ""minimum"": 686.521945, ""maximum"": 789.906315, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 693.9176055, ""p95_reference"": 727.06945975, ""minimum"": 654.316747, ""maximum"": 728.832386, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-7.374200499999915,-15.10067749999996,0.21714737500001527,-1.05151670629956,-2.1385968354064477,0.030946466821430672,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,result_ms,baseline,candidate,ms,30,0,4278.449968,4446.6029255,unsplit,unsplit,"{""n"": 30, ""p50"": 4278.449968, ""p95_reference"": 5340.32934275, ""minimum"": 4069.431508, ""maximum"": 5474.773979, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 4446.6029255, ""p95_reference"": 4849.9728442000005, ""minimum"": 4296.389126, ""maximum"": 5433.299961, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",168.1529575000004,65.6855170000008,210.05962100000033,3.9302307788492197,1.4993552919430941,4.928041852411542,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,completion_ms,baseline,candidate,ms,30,0,4535.762954,4635.260874,unsplit,unsplit,"{""n"": 30, ""p50"": 4535.762954, ""p95_reference"": 5577.58314435, ""minimum"": 4314.00466, ""maximum"": 5766.958044, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 4635.260874, ""p95_reference"": 5053.494531, ""minimum"": 4486.242774, ""maximum"": 5650.125408, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",99.4979199999998,-1.9010724999998274,148.12697500000013,2.1936313914344696,-0.041062386808854434,3.278578269622737,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,worker_ms,baseline,candidate,ms,30,0,3578.175109,3749.0687745,unsplit,unsplit,"{""n"": 30, ""p50"": 3578.175109, ""p95_reference"": 4633.7424845000005, ""minimum"": 3367.664205, ""maximum"": 4777.305497, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 3749.0687745, ""p95_reference"": 4155.79789515, ""minimum"": 3601.525824, ""maximum"": 4739.00818, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",170.89366550000022,107.47650010000024,210.4752810750002,4.776000623059518,2.9590088269350274,5.923245553099513,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,metadata,baseline,candidate,ms,30,0,157.8011775,138.862869,unsplit,unsplit,"{""n"": 30, ""p50"": 157.8011775, ""p95_reference"": 189.13611625000001, ""minimum"": 112.822408, ""maximum"": 198.907503, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 138.862869, ""p95_reference"": 200.8448491, ""minimum"": 111.11752, ""maximum"": 203.982042, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-18.938308500000005,-35.205424174999976,1.4454382249999898,-12.001373373782343,-22.0605940758005,0.9223912548634038,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,read,baseline,candidate,ms,30,0,117.672345,116.8579125,unsplit,unsplit,"{""n"": 30, ""p50"": 117.672345, ""p95_reference"": 141.71966129999998, ""minimum"": 112.567169, ""maximum"": 142.482554, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 116.8579125, ""p95_reference"": 128.33210245, ""minimum"": 113.424067, ""maximum"": 139.308489, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.8144325000000094,-3.518810712499991,0.18627950000001192,-0.6921188661618061,-2.9508338240847722,0.15837790391393103,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,write,baseline,candidate,ms,30,0,131.0506595,163.677244,unsplit,unsplit,"{""n"": 30, ""p50"": 131.0506595, ""p95_reference"": 148.0101782, ""minimum"": 97.027691, ""maximum"": 173.047176, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 163.677244, ""p95_reference"": 193.86429005, ""minimum"": 130.111318, ""maximum"": 200.878049, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",32.62658450000001,15.380851000000007,42.35225749999998,24.89616200672382,11.71693302314134,33.32361947440947,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,git,baseline,candidate,ms,30,0,,,separated-clusters,separated-clusters,"{""n"": 30, ""p50"": """", ""p95_reference"": 764.0261530500001, ""minimum"": 353.68035, ""maximum"": 1143.341075, ""distribution"": ""separated-clusters"", ""low_n"": 24, ""low_p50"": 375.053872, ""high_n"": 6, ""high_p50"": 749.922686}","{""n"": 30, ""p50"": """", ""p95_reference"": 806.4616766, ""minimum"": 382.933572, ""maximum"": 820.942533, ""distribution"": ""separated-clusters"", ""low_n"": 25, ""low_p50"": 399.041918, ""high_n"": 5, ""high_p50"": 798.597088}",,,,,,,separated clusters; no whole-distribution ranking +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,rg,baseline,candidate,ms,30,0,426.7428575,450.55341599999997,unsplit,unsplit,"{""n"": 30, ""p50"": 426.7428575, ""p95_reference"": 448.67384825000005, ""minimum"": 379.105913, ""maximum"": 461.008823, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 450.55341599999997, ""p95_reference"": 462.7012209, ""minimum"": 427.947427, ""maximum"": 469.690888, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",23.810558499999956,16.56280799999996,36.91811001250002,5.579603286037416,3.8592397706755044,8.922258802187594,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,cargo,baseline,candidate,ms,30,0,501.3797235,502.76696549999997,unsplit,unsplit,"{""n"": 30, ""p50"": 501.3797235, ""p95_reference"": 806.8878312000002, ""minimum"": 474.732044, ""maximum"": 913.063239, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 502.76696549999997, ""p95_reference"": 524.2727038, ""minimum"": 478.655504, ""maximum"": 895.64709, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",1.387241999999958,-6.339356500000008,10.583257549999944,0.2766849026753615,-1.2563193692766594,2.137455146647018,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,npm,baseline,candidate,ms,30,0,923.2233120000001,973.343816,unsplit,unsplit,"{""n"": 30, ""p50"": 923.2233120000001, ""p95_reference"": 1051.5576092000001, ""minimum"": 761.841513, ""maximum"": 1353.648422, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 973.343816, ""p95_reference"": 1030.30126, ""minimum"": 911.547435, ""maximum"": 1201.185718, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",50.12050399999987,31.983457087499954,62.587000812500015,5.4288603145670855,3.417355788988163,6.780859620762852,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,ready_ms,before,candidate,ms,30,0,701.0899955,693.9176055,unsplit,unsplit,"{""n"": 30, ""p50"": 701.0899955, ""p95_reference"": 732.14511455, ""minimum"": 686.675537, ""maximum"": 873.145236, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 693.9176055, ""p95_reference"": 727.06945975, ""minimum"": 654.316747, ""maximum"": 728.832386, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-7.17238999999995,-14.30879517499997,-0.8413703500000779,-1.023034139131418,-2.030073917528008,-0.12121943873517678,faster +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,result_ms,before,candidate,ms,30,0,4297.801669,4446.6029255,unsplit,unsplit,"{""n"": 30, ""p50"": 4297.801669, ""p95_reference"": 5080.3648109000005, ""minimum"": 4074.466407, ""maximum"": 5228.259533, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 4446.6029255, ""p95_reference"": 4849.9728442000005, ""minimum"": 4296.389126, ""maximum"": 5433.299961, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",148.8012564999999,32.44331499999953,206.4904338374998,3.4622643844480328,0.7361868415249218,4.836304780587501,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,completion_ms,before,candidate,ms,30,0,4544.0489055,4635.260874,unsplit,unsplit,"{""n"": 30, ""p50"": 4544.0489055, ""p95_reference"": 5334.32411415, ""minimum"": 4317.653732, ""maximum"": 5526.082774, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 4635.260874, ""p95_reference"": 5053.494531, ""minimum"": 4486.242774, ""maximum"": 5650.125408, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",91.21196849999978,-13.554260000000795,145.19304476250045,2.0072840411026194,-0.293499352432236,3.211141901839739,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,worker_ms,before,candidate,ms,30,0,3589.173895,3749.0687745,unsplit,unsplit,"{""n"": 30, ""p50"": 3589.173895, ""p95_reference"": 4356.26645515, ""minimum"": 3375.558433, ""maximum"": 4522.132202, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 3749.0687745, ""p95_reference"": 4155.79789515, ""minimum"": 3601.525824, ""maximum"": 4739.00818, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",159.89487950000012,36.23646000000008,205.12455,4.454921499422082,0.9676407995443004,5.762080107641683,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,metadata,before,candidate,ms,30,0,133.4563125,138.862869,unsplit,unsplit,"{""n"": 30, ""p50"": 133.4563125, ""p95_reference"": 192.7246791, ""minimum"": 108.503417, ""maximum"": 194.824494, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 138.862869, ""p95_reference"": 200.8448491, ""minimum"": 111.11752, ""maximum"": 203.982042, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",5.4065564999999935,-11.300286999999997,28.476527612499915,4.0511807937147815,-8.231074393742155,22.348529030444993,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,read,before,candidate,ms,30,0,115.795024,116.8579125,unsplit,unsplit,"{""n"": 30, ""p50"": 115.795024, ""p95_reference"": 136.34799395000002, ""minimum"": 110.380387, ""maximum"": 609.142557, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 116.8579125, ""p95_reference"": 128.33210245, ""minimum"": 113.424067, ""maximum"": 139.308489, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",1.0628884999999997,-1.493983,3.1831505000000107,0.9179051597242971,-1.274610870267967,2.777127987017969,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,write,before,candidate,ms,30,0,131.376992,163.677244,unsplit,unsplit,"{""n"": 30, ""p50"": 131.376992, ""p95_reference"": 280.5306881000002, ""minimum"": 96.834779, ""maximum"": 499.98355, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 163.677244, ""p95_reference"": 193.86429005, ""minimum"": 130.111318, ""maximum"": 200.878049, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",32.300252,15.337790000000012,40.707677062499975,24.585927496345782,11.674571038067684,31.651474103201487,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,git,before,candidate,ms,30,0,,,separated-clusters,separated-clusters,"{""n"": 30, ""p50"": """", ""p95_reference"": 733.711548, ""minimum"": 347.08043, ""maximum"": 745.646024, ""distribution"": ""separated-clusters"", ""low_n"": 21, ""low_p50"": 374.894303, ""high_n"": 9, ""high_p50"": 723.043237}","{""n"": 30, ""p50"": """", ""p95_reference"": 806.4616766, ""minimum"": 382.933572, ""maximum"": 820.942533, ""distribution"": ""separated-clusters"", ""low_n"": 25, ""low_p50"": 399.041918, ""high_n"": 5, ""high_p50"": 798.597088}",,,,,,,separated clusters; no whole-distribution ranking +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,rg,before,candidate,ms,30,0,424.35992799999997,450.55341599999997,unsplit,unsplit,"{""n"": 30, ""p50"": 424.35992799999997, ""p95_reference"": 445.81724305, ""minimum"": 373.277409, ""maximum"": 449.78536, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 450.55341599999997, ""p95_reference"": 462.7012209, ""minimum"": 427.947427, ""maximum"": 469.690888, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",26.193488000000002,18.683151862499987,38.547659287500025,6.172469705952066,4.353792075138041,9.336122466372625,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,cargo,before,candidate,ms,30,0,483.237283,502.76696549999997,unsplit,unsplit,"{""n"": 30, ""p50"": 483.237283, ""p95_reference"": 551.8527210000001, ""minimum"": 460.089429, ""maximum"": 1005.241583, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 502.76696549999997, ""p95_reference"": 524.2727038, ""minimum"": 478.655504, ""maximum"": 895.64709, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",19.52968249999998,9.177695000000028,26.30648309999997,4.041427097420369,1.8714947485005462,5.516458189109752,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,npm,before,candidate,ms,30,0,928.2279195,973.343816,unsplit,unsplit,"{""n"": 30, ""p50"": 928.2279195, ""p95_reference"": 1017.8536178, ""minimum"": 750.15394, ""maximum"": 1168.682019, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 973.343816, ""p95_reference"": 1030.30126, ""minimum"": 911.547435, ""maximum"": 1201.185718, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",45.11589649999996,28.81408799999997,56.54230879999987,4.860433041520884,3.0909553950674518,6.092084323792668,slower diff --git a/docs/src/en/design/concurrency-retest-provenance.csv b/docs/src/en/design/concurrency-retest-provenance.csv new file mode 100644 index 000000000..f5479737d --- /dev/null +++ b/docs/src/en/design/concurrency-retest-provenance.csv @@ -0,0 +1,24 @@ +field,value +filesystem_report_sha256,39684288c49c075115731488f57c2fe85db432da07959ccd169266ea678e61a3 +filesystem_raw,benchmark/.data/concurrency-formal2-20261011/report.json +filesystem_launch_sha256,67b115314b2906cdedf8fa614fc397d411d64880c67e60bf2e88d59f9e96e2b8 +filesystem_guard_sha256,8bf04bf4f018f58e9e2b86321e1586566396228204177f6624ed29f27462ff59 +filesystem_protocol,30 seeded shuffled paired rounds; 3 warmups; fresh private user/mount/PID namespace and pivot_root per trial; fresh Host listener and VM launch timed; full 256 x 64 KiB written bytes verified; unchanged seven-operation workload; no profiling +filesystem_before_scope,"same frozen Oct10 source, reverses only 65e65c93 copy-up/dispatch and 09bd1d59 read/journal changes; stage sync default unchanged; not literal cf8 parent" +filesystem_baseline_scope,prior candidate6 optimized overlay and worker-pool queues +filesystem_binaries,"{""before"": ""b074594368e5655c0e6949dcd45e6eb958816f3cdfca53011911f6d059fcbfe8"", ""baseline"": ""378ee13ba8b8aafc0a588d51a9c3a79c1410c000857f323970cc13fc65d279e4"", ""candidate"": ""87ebb0658f2df6a84f9e4eca29c8d3d8bc01923817f04432780272c8b1b30309""}" +filesystem_source_manifests,"{""before"": ""1068608e3b4e611871e0e00a755b8ddb4e9d9c1a5e48b4b46597d353937b603e"", ""baseline"": ""aa30eda66bc1f1ee853404b7d41e8847f0722ce72adfb79fa6816e06258655d6"", ""candidate"": ""07a216161d0b64313ba03787630658f300a19788f705d318dfd88f9c5397726c""}" +startup_report_sha256,"{""pvisor-host"": ""30a46f312063eda97c46e7ec92a1ffdb777cc9b974aa2b7f5fb423071c966ded"", ""pvisor-staged"": ""f017d8c0e06575fa488642cf47cbf8f694896932917061b031f4b950d4d7edca"", ""pvisor-vm"": ""db50555e5a6466d7fd9e3578428e4c7605e66cc94dda939b6427b0d67e4b4963""}" +startup_raw,benchmark.new/startup/.data/concurrency-retest2-20261011 +startup_launch_sha256,693047df4814c012a4f19ef01924d4cddd04901f5280278ff3de5f3be3fa2e35 +startup_guard_sha256,3f40c557c2acc1bedfa314818441d5d8d1c0d558e64fe7ec40ea41a20a98b5d0 +startup_source_receipt_sha256,3f79d1fbb1c5e5b1d9ad3539b316654b2ecd37b96356b342deb3f116a3d6566b +startup_protocol,60 seeded shuffled paired rounds; 3 warmups; long-lived private namespaces and warmed Host listener per arm; exact Ready and normal zero Exit; GNU release/gateway and identical firmware; original startup workload and correctness gates +startup_before_scope,archived historical GNU old-pvisor build before cf8 changes +startup_baseline_scope,archived cf8 GNU gnu-pvisor build; version comparison includes intervening source changes; not single-change attribution +startup_binaries,"{""before"": ""51972e17777cfbab79b5ca18031d6d37ba47477917628d9e3bf0a81e3353fdff"", ""baseline"": ""b074594368e5655c0e6949dcd45e6eb958816f3cdfca53011911f6d059fcbfe8"", ""candidate"": ""87ebb0658f2df6a84f9e4eca29c8d3d8bc01923817f04432780272c8b1b30309""}" +candidate_scope,"frozen current dirty source build2; queue-local execution, hiprio lane, stealing, default staged guest writeback, leased I/O, Ready/Exit fixes, descriptor publication race fix" +budget,"private whole-parent cgroup: CPUs 0,1; CPUQuota 200%; MemoryMax 16 GiB; swap 0" +statistics,5000 paired-round bootstrap resamples; median difference and median ratio CI95; separated distributions have no global median ranking; P95 descriptive only; no P99 +interference,no detected same-UID builds/tests outside owned service; sampled every 0.5 s; may miss short/inaccessible/unrecognized activity +scope,"Linux KVM/FUSE, warm host caches, sequential jobs; not cold storage, concurrency throughput, other runtimes or macOS; failed/preflight batches retained separately" diff --git a/docs/src/en/design/concurrency-startup-summary.csv b/docs/src/en/design/concurrency-startup-summary.csv new file mode 100644 index 000000000..1c95d7082 --- /dev/null +++ b/docs/src/en/design/concurrency-startup-summary.csv @@ -0,0 +1,19 @@ +benchmark_id,batch,backend,metric,reference,condition,unit,n,failures,reference_p50_ms,candidate_p50_ms,reference_distribution,candidate_distribution,reference_clusters,candidate_clusters,median_difference_ms,ci95_low_ms,ci95_high_ms,change_percent,ci95_low_percent,ci95_high_percent,interpretation +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-host,ready_ms,before,baseline,ms,60,0,13.210854,29.141069,unsplit,unsplit,"{""n"": 60, ""p50"": 13.210854, ""p95_reference"": 14.969226149999999, ""minimum"": 11.852243, ""maximum"": 205.123838, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 60, ""p50"": 29.141069, ""p95_reference"": 30.828012049999998, ""minimum"": 26.50353, ""maximum"": 31.261772, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",15.930215000000002,15.297128,16.4309922625,120.5842937935731,112.0264088185669,127.09639105401754,slower +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-host,exit_ms,before,baseline,ms,60,0,23.914116999999997,99.162519,unsplit,unsplit,"{""n"": 60, ""p50"": 23.914116999999997, ""p95_reference"": 24.6613889, ""minimum"": 23.231012, ""maximum"": 215.171903, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 60, ""p50"": 99.162519, ""p95_reference"": 100.65071339999999, ""minimum"": 97.331325, ""maximum"": 101.4216, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",75.248402,74.98108948749999,75.53936,314.6610096454743,312.27151498760674,318.21856071435786,slower +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-host,ready_ms,baseline,candidate,ms,60,0,29.141069,20.032587499999998,unsplit,unsplit,"{""n"": 60, ""p50"": 29.141069, ""p95_reference"": 30.828012049999998, ""minimum"": 26.50353, ""maximum"": 31.261772, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 60, ""p50"": 20.032587499999998, ""p95_reference"": 21.203905149999997, ""minimum"": 18.304547, ""maximum"": 21.709512, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-9.108481500000003,-9.505201824999999,-8.718452,-31.256511214465064,-32.34063201302348,-30.252958401952807,faster +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-host,exit_ms,baseline,candidate,ms,60,0,99.162519,24.1611885,unsplit,unsplit,"{""n"": 60, ""p50"": 99.162519, ""p95_reference"": 100.65071339999999, ""minimum"": 97.331325, ""maximum"": 101.4216, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 60, ""p50"": 24.1611885, ""p95_reference"": 25.4893269, ""minimum"": 22.370415, ""maximum"": 26.040997, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-75.0013305,-75.23979940000001,-74.59203987500001,-75.63475722112302,-75.82453689709905,-75.30913279068344,faster +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-host,ready_ms,before,candidate,ms,60,0,13.210854,20.032587499999998,unsplit,unsplit,"{""n"": 60, ""p50"": 13.210854, ""p95_reference"": 14.969226149999999, ""minimum"": 11.852243, ""maximum"": 205.123838, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 60, ""p50"": 20.032587499999998, ""p95_reference"": 21.203905149999997, ""minimum"": 18.304547, ""maximum"": 21.709512, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",6.821733499999999,6.159386462499999,7.267630299999997,51.63733926663636,45.122763251403676,56.37405576931433,slower +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-host,exit_ms,before,candidate,ms,60,0,23.914116999999997,24.1611885,unsplit,unsplit,"{""n"": 60, ""p50"": 23.914116999999997, ""p95_reference"": 24.6613889, ""minimum"": 23.231012, ""maximum"": 215.171903, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 60, ""p50"": 24.1611885, ""p95_reference"": 25.4893269, ""minimum"": 22.370415, ""maximum"": 26.040997, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.2470715000000041,0.022403000000000617,0.6303448375000009,1.0331617094622603,0.09355227592020565,2.6470244424196063,slower +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-staged,ready_ms,before,baseline,ms,60,0,25.0450905,41.23049,unsplit,unsplit,"{""n"": 60, ""p50"": 25.0450905, ""p95_reference"": 48.783252, ""minimum"": 22.323295, ""maximum"": 66.539693, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 60, ""p50"": 41.23049, ""p95_reference"": 44.29052765, ""minimum"": 38.807522, ""maximum"": 52.209267, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",16.185399500000003,15.648150999999995,16.617075000000003,64.62503898718195,61.857315182368744,66.76458729971617,slower +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-staged,exit_ms,before,baseline,ms,60,0,,118.4108385,separated-clusters,unsplit,"{""n"": 60, ""p50"": """", ""p95_reference"": 74.42863514999999, ""minimum"": 33.176303, ""maximum"": 84.36309, ""distribution"": ""separated-clusters"", ""low_n"": 53, ""low_p50"": 34.117038, ""high_n"": 7, ""high_p50"": 74.426694}","{""n"": 60, ""p50"": 118.4108385, ""p95_reference"": 139.05603995, ""minimum"": 107.356436, ""maximum"": 141.341628, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",,,,,,,separated clusters; no whole-distribution ranking +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-staged,ready_ms,baseline,candidate,ms,60,0,41.23049,,unsplit,separated-clusters,"{""n"": 60, ""p50"": 41.23049, ""p95_reference"": 44.29052765, ""minimum"": 38.807522, ""maximum"": 52.209267, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 60, ""p50"": """", ""p95_reference"": 154.09938019999998, ""minimum"": 30.150605, ""maximum"": 164.8574, ""distribution"": ""separated-clusters"", ""low_n"": 19, ""low_p50"": 32.167219, ""high_n"": 41, ""high_p50"": 121.953181}",,,,,,,separated clusters; no whole-distribution ranking +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-staged,exit_ms,baseline,candidate,ms,60,0,118.4108385,118.6742705,unsplit,unsplit,"{""n"": 60, ""p50"": 118.4108385, ""p95_reference"": 139.05603995, ""minimum"": 107.356436, ""maximum"": 141.341628, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 60, ""p50"": 118.6742705, ""p95_reference"": 163.34460224999998, ""minimum"": 39.08468, ""maximum"": 175.515683, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.26343200000000877,-31.53614085000001,16.60314992500001,0.22247287776786528,-26.828364218695413,14.937975299367043,no detected difference +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-staged,ready_ms,before,candidate,ms,60,0,25.0450905,,unsplit,separated-clusters,"{""n"": 60, ""p50"": 25.0450905, ""p95_reference"": 48.783252, ""minimum"": 22.323295, ""maximum"": 66.539693, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 60, ""p50"": """", ""p95_reference"": 154.09938019999998, ""minimum"": 30.150605, ""maximum"": 164.8574, ""distribution"": ""separated-clusters"", ""low_n"": 19, ""low_p50"": 32.167219, ""high_n"": 41, ""high_p50"": 121.953181}",,,,,,,separated clusters; no whole-distribution ranking +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-staged,exit_ms,before,candidate,ms,60,0,,118.6742705,separated-clusters,unsplit,"{""n"": 60, ""p50"": """", ""p95_reference"": 74.42863514999999, ""minimum"": 33.176303, ""maximum"": 84.36309, ""distribution"": ""separated-clusters"", ""low_n"": 53, ""low_p50"": 34.117038, ""high_n"": 7, ""high_p50"": 74.426694}","{""n"": 60, ""p50"": 118.6742705, ""p95_reference"": 163.34460224999998, ""minimum"": 39.08468, ""maximum"": 175.515683, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",,,,,,,separated clusters; no whole-distribution ranking +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-vm,ready_ms,before,baseline,ms,60,0,174.06437799999998,119.4055905,unsplit,unsplit,"{""n"": 60, ""p50"": 174.06437799999998, ""p95_reference"": 216.6176647, ""minimum"": 131.159754, ""maximum"": 231.683627, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 60, ""p50"": 119.4055905, ""p95_reference"": 126.87861115, ""minimum"": 112.32518, ""maximum"": 148.467629, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-54.658787499999974,-59.42378157499999,-45.55883091250003,-31.401478078415323,-33.28718065830785,-27.613199631421672,faster +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-vm,exit_ms,before,baseline,ms,60,0,211.83177,218.71427549999999,unsplit,unsplit,"{""n"": 60, ""p50"": 211.83177, ""p95_reference"": 257.17307515, ""minimum"": 155.286827, ""maximum"": 296.54068, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 60, ""p50"": 218.71427549999999, ""p95_reference"": 242.96695565, ""minimum"": 189.475487, ""maximum"": 251.073247, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",6.882505499999979,-4.323434649999996,23.11530400000001,3.249043096793258,-2.0062220876725156,11.166021081097233,no detected difference +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-vm,ready_ms,baseline,candidate,ms,60,0,119.4055905,115.24632700000001,unsplit,unsplit,"{""n"": 60, ""p50"": 119.4055905, ""p95_reference"": 126.87861115, ""minimum"": 112.32518, ""maximum"": 148.467629, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 60, ""p50"": 115.24632700000001, ""p95_reference"": 137.31941615, ""minimum"": 101.220694, ""maximum"": 162.491764, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-4.1592634999999945,-7.0784748624999905,0.7727227500000008,-3.4833071739635124,-5.906450779487837,0.6509769176801421,no detected difference +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-vm,exit_ms,baseline,candidate,ms,60,0,218.71427549999999,170.4499275,unsplit,unsplit,"{""n"": 60, ""p50"": 218.71427549999999, ""p95_reference"": 242.96695565, ""minimum"": 189.475487, ""maximum"": 251.073247, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 60, ""p50"": 170.4499275, ""p95_reference"": 201.5738823, ""minimum"": 140.112464, ""maximum"": 224.770585, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-48.264347999999984,-67.4287749125,-33.355708362500025,-22.06730579870173,-29.305294564855153,-15.698035238930327,faster +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-vm,ready_ms,before,candidate,ms,60,0,174.06437799999998,115.24632700000001,unsplit,unsplit,"{""n"": 60, ""p50"": 174.06437799999998, ""p95_reference"": 216.6176647, ""minimum"": 131.159754, ""maximum"": 231.683627, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 60, ""p50"": 115.24632700000001, ""p95_reference"": 137.31941615, ""minimum"": 101.220694, ""maximum"": 162.491764, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-58.81805099999997,-64.26843900000003,-47.911247949999996,-33.79097531374281,-36.16021254706435,-28.866040152361748,faster +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-vm,exit_ms,before,candidate,ms,60,0,211.83177,170.4499275,unsplit,unsplit,"{""n"": 60, ""p50"": 211.83177, ""p95_reference"": 257.17307515, ""minimum"": 155.286827, ""maximum"": 296.54068, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 60, ""p50"": 170.4499275, ""p95_reference"": 201.5738823, ""minimum"": 140.112464, ""maximum"": 224.770585, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-41.381842500000005,-53.93050220000001,-27.752718999999985,-19.53523897760945,-24.942263955132848,-13.402372886512527,faster diff --git a/docs/src/en/design/direct-queue-filesystem-summary.csv b/docs/src/en/design/direct-queue-filesystem-summary.csv new file mode 100644 index 000000000..387d61fb7 --- /dev/null +++ b/docs/src/en/design/direct-queue-filesystem-summary.csv @@ -0,0 +1,67 @@ +benchmark_id,batch,backend,metric,reference,condition,unit,n,failures,reference_p50_ms,candidate_p50_ms,reference_distribution,candidate_distribution,reference_clusters,candidate_clusters,median_difference_ms,ci95_low_ms,ci95_high_ms,change_percent,ci95_low_percent,ci95_high_percent,interpretation +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,ready_ms,before,baseline,ms,10,0,229.153498,218.766278,unsplit,unsplit,"{""n"": 10, ""p50"": 229.153498, ""p95_reference"": """", ""minimum"": 226.907656, ""maximum"": 235.116278, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 218.766278, ""p95_reference"": """", ""minimum"": 217.461773, ""maximum"": 223.010901, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-10.387220000000013,-12.487494999999996,-9.296683499999972,-4.532865564199251,-5.392903353957301,-4.071565519151276,faster +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,result_ms,before,baseline,ms,10,0,1444.5803395,1432.6245764999999,unsplit,unsplit,"{""n"": 10, ""p50"": 1444.5803395, ""p95_reference"": """", ""minimum"": 1437.881595, ""maximum"": 1468.744135, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 1432.6245764999999, ""p95_reference"": """", ""minimum"": 1423.942204, ""maximum"": 1485.340626, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-11.955763000000161,-24.857361000000083,-4.528592500000059,-0.8276288049260283,-1.7058429220306839,-0.3147141149056343,faster +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,completion_ms,before,baseline,ms,10,0,1754.5811735,1668.7888515,unsplit,unsplit,"{""n"": 10, ""p50"": 1754.5811735, ""p95_reference"": """", ""minimum"": 1732.989469, ""maximum"": 1780.666738, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 1668.7888515, ""p95_reference"": """", ""minimum"": 1662.500618, ""maximum"": 1742.622511, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-85.79232200000001,-100.13619300000005,-62.81696950000014,-4.889618291575726,-5.665124330067917,-3.5929848342111104,faster +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,worker_ms,before,baseline,ms,10,0,1214.5357079999999,1213.940276,unsplit,unsplit,"{""n"": 10, ""p50"": 1214.5357079999999, ""p95_reference"": """", ""minimum"": 1208.78866, ""maximum"": 1236.847748, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 1213.940276, ""p95_reference"": """", ""minimum"": 1205.377403, ""maximum"": 1266.145527, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.5954319999998461,-15.07675899999981,6.464432000000215,-0.04902548324251521,-1.2297231433468503,0.5336936876742415,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,metadata,before,baseline,ms,10,0,66.186594,66.306904,unsplit,unsplit,"{""n"": 10, ""p50"": 66.186594, ""p95_reference"": """", ""minimum"": 65.803535, ""maximum"": 67.278247, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 66.306904, ""p95_reference"": """", ""minimum"": 65.649597, ""maximum"": 66.767604, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.12031000000000347,-0.2652100000000104,0.2874724999999927,0.18177397072283163,-0.3997165160069982,0.43433644583674713,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,read,before,baseline,ms,10,0,67.97466700000001,68.0428395,unsplit,unsplit,"{""n"": 10, ""p50"": 67.97466700000001, ""p95_reference"": """", ""minimum"": 66.813841, ""maximum"": 88.24187, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 68.0428395, ""p95_reference"": """", ""minimum"": 66.569334, ""maximum"": 87.879223, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.06817249999998864,-1.3143540000000087,1.7080007499999905,0.10029103930730976,-1.9222466994853415,2.5195059224219505,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,write,before,baseline,ms,10,0,28.212829499999998,27.667371000000003,unsplit,unsplit,"{""n"": 10, ""p50"": 28.212829499999998, ""p95_reference"": """", ""minimum"": 27.059161, ""maximum"": 28.757852, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 27.667371000000003, ""p95_reference"": """", ""minimum"": 25.565924, ""maximum"": 33.203189, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.5454584999999952,-1.8269304999999996,0.8011204999999997,-1.9333704192980528,-6.463596467734812,2.8670908869605993,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,git,before,baseline,ms,10,0,142.2296785,143.3659445,unsplit,unsplit,"{""n"": 10, ""p50"": 142.2296785, ""p95_reference"": """", ""minimum"": 140.683512, ""maximum"": 142.917783, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 143.3659445, ""p95_reference"": """", ""minimum"": 140.2054, ""maximum"": 161.597772, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",1.1362660000000062,-1.1790165000000172,3.5052984999999808,0.7988951476115558,-0.830296189130042,2.4803955406863087,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,rg,before,baseline,ms,10,0,106.5825365,106.638556,unsplit,unsplit,"{""n"": 10, ""p50"": 106.5825365, ""p95_reference"": """", ""minimum"": 104.669916, ""maximum"": 107.633067, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 106.638556, ""p95_reference"": """", ""minimum"": 105.771502, ""maximum"": 107.456758, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.056019499999990785,-0.8575905000000006,1.1857430000000022,0.052559736181545524,-0.8005183675214744,1.1193511385933386,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,cargo,before,baseline,ms,10,0,77.55028250000001,77.498756,unsplit,unsplit,"{""n"": 10, ""p50"": 77.55028250000001, ""p95_reference"": """", ""minimum"": 75.865877, ""maximum"": 96.866359, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 77.498756, ""p95_reference"": """", ""minimum"": 75.108835, ""maximum"": 100.508157, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.05152650000000847,-7.921887725000003,1.2402804999999972,-0.06644269800050173,-9.309745385197033,1.6210217107728075,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,npm,before,baseline,ms,10,0,230.13633249999998,226.985576,unsplit,unsplit,"{""n"": 10, ""p50"": 230.13633249999998, ""p95_reference"": """", ""minimum"": 225.186954, ""maximum"": 233.312622, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 226.985576, ""p95_reference"": """", ""minimum"": 224.502827, ""maximum"": 232.975762, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-3.1507564999999715,-4.94182049999999,1.7339465000000303,-1.3690826067196404,-2.1471197427962805,0.7556910873117939,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,ready_ms,baseline,candidate,ms,10,0,218.766278,217.922999,unsplit,unsplit,"{""n"": 10, ""p50"": 218.766278, ""p95_reference"": """", ""minimum"": 217.461773, ""maximum"": 223.010901, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 217.922999, ""p95_reference"": """", ""minimum"": 216.724477, ""maximum"": 224.184182, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.8432789999999954,-2.0738920000000007,2.8129865000000223,-0.385470287152756,-0.9468303297746616,1.2847693340192423,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,result_ms,baseline,candidate,ms,10,0,1432.6245764999999,1431.14254,unsplit,unsplit,"{""n"": 10, ""p50"": 1432.6245764999999, ""p95_reference"": """", ""minimum"": 1423.942204, ""maximum"": 1485.340626, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 1431.14254, ""p95_reference"": """", ""minimum"": 1426.265762, ""maximum"": 1499.086515, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-1.482036499999822,-9.493872499999952,15.92296350000015,-0.10344904899094454,-0.6594381172062014,1.112087893300659,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,completion_ms,baseline,candidate,ms,10,0,1668.7888515,1667.3669375,unsplit,unsplit,"{""n"": 10, ""p50"": 1668.7888515, ""p95_reference"": """", ""minimum"": 1662.500618, ""maximum"": 1742.622511, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 1667.3669375, ""p95_reference"": """", ""minimum"": 1663.385239, ""maximum"": 1732.171492, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-1.4219140000000152,-20.763048000000026,28.91245650000019,-0.08520634583110143,-1.2305334450607441,1.7352342748735872,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,worker_ms,baseline,candidate,ms,10,0,1213.940276,1213.163396,unsplit,unsplit,"{""n"": 10, ""p50"": 1213.940276, ""p95_reference"": """", ""minimum"": 1205.377403, ""maximum"": 1266.145527, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 1213.163396, ""p95_reference"": """", ""minimum"": 1202.128989, ""maximum"": 1274.682132, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.7768800000001193,-7.0755079999999,16.04047350000019,-0.06399655859182785,-0.5797981066362867,1.3219537365593226,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,metadata,baseline,candidate,ms,10,0,66.306904,65.25977950000001,unsplit,unsplit,"{""n"": 10, ""p50"": 66.306904, ""p95_reference"": """", ""minimum"": 65.649597, ""maximum"": 66.767604, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 65.25977950000001, ""p95_reference"": """", ""minimum"": 64.547881, ""maximum"": 65.985975, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-1.0471244999999954,-1.3180705000000046,-0.30603550000000723,-1.579208855837988,-1.9779505425664867,-0.4649780385276192,faster +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,read,baseline,candidate,ms,10,0,68.0428395,68.746929,unsplit,unsplit,"{""n"": 10, ""p50"": 68.0428395, ""p95_reference"": """", ""minimum"": 66.569334, ""maximum"": 87.879223, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 68.746929, ""p95_reference"": """", ""minimum"": 66.530903, ""maximum"": 88.036867, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.7040894999999949,-1.3006580000000127,4.918150999999995,1.0347738353864377,-1.8581818451443133,7.1338735580186885,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,write,baseline,candidate,ms,10,0,27.667371000000003,27.1156815,unsplit,unsplit,"{""n"": 10, ""p50"": 27.667371000000003, ""p95_reference"": """", ""minimum"": 25.565924, ""maximum"": 33.203189, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 27.1156815, ""p95_reference"": """", ""minimum"": 26.0406, ""maximum"": 28.266014, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.5516895000000019,-1.6412134999999992,0.5558670000000028,-1.9940076706240095,-5.843610396315935,2.0963679767739274,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,git,baseline,candidate,ms,10,0,143.3659445,141.312361,unsplit,unsplit,"{""n"": 10, ""p50"": 143.3659445, ""p95_reference"": """", ""minimum"": 140.2054, ""maximum"": 161.597772, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 141.312361, ""p95_reference"": """", ""minimum"": 139.59504, ""maximum"": 158.597863, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-2.053583500000002,-5.070566000000014,1.3457075000000032,-1.4324067735626,-3.484313690633445,0.9519678346596727,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,rg,baseline,candidate,ms,10,0,106.638556,106.0887095,unsplit,unsplit,"{""n"": 10, ""p50"": 106.638556, ""p95_reference"": """", ""minimum"": 105.771502, ""maximum"": 107.456758, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 106.0887095, ""p95_reference"": """", ""minimum"": 104.835946, ""maximum"": 108.707052, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.549846500000001,-1.783239000000009,0.6942710000000005,-0.5156169781593833,-1.6650277414053782,0.6532974481806786,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,cargo,baseline,candidate,ms,10,0,77.498756,76.5364195,unsplit,unsplit,"{""n"": 10, ""p50"": 77.498756, ""p95_reference"": """", ""minimum"": 75.108835, ""maximum"": 100.508157, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 76.5364195, ""p95_reference"": """", ""minimum"": 75.187111, ""maximum"": 102.151344, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.9623365000000064,-1.8989100999999988,9.935165999999995,-1.2417444481302442,-2.4417307286991865,12.833699515010522,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,npm,baseline,candidate,ms,10,0,226.985576,230.30114500000002,unsplit,unsplit,"{""n"": 10, ""p50"": 226.985576, ""p95_reference"": """", ""minimum"": 224.502827, ""maximum"": 232.975762, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 230.30114500000002, ""p95_reference"": """", ""minimum"": 225.368933, ""maximum"": 239.572046, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",3.3155690000000106,0.6215554999999711,6.673878499999972,1.46069589902047,0.26870357347081875,2.9456143146573854,slower +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,ready_ms,before,candidate,ms,10,0,229.153498,217.922999,unsplit,unsplit,"{""n"": 10, ""p50"": 229.153498, ""p95_reference"": """", ""minimum"": 226.907656, ""maximum"": 235.116278, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 217.922999, ""p95_reference"": """", ""minimum"": 216.724477, ""maximum"": 224.184182, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-11.230499000000009,-13.345687000000027,-8.016419000000013,-4.900863001445432,-5.795577660349471,-3.5073220895628254,faster +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,result_ms,before,candidate,ms,10,0,1444.5803395,1431.14254,unsplit,unsplit,"{""n"": 10, ""p50"": 1444.5803395, ""p95_reference"": """", ""minimum"": 1437.881595, ""maximum"": 1468.744135, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 1431.14254, ""p95_reference"": """", ""minimum"": 1426.265762, ""maximum"": 1499.086515, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-13.437799499999983,-26.121981000000005,-0.828896499999928,-0.9302216797891005,-1.7936987420358985,-0.057617785344832306,faster +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,completion_ms,before,candidate,ms,10,0,1754.5811735,1667.3669375,unsplit,unsplit,"{""n"": 10, ""p50"": 1754.5811735, ""p95_reference"": """", ""minimum"": 1732.989469, ""maximum"": 1780.666738, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 1667.3669375, ""p95_reference"": """", ""minimum"": 1663.385239, ""maximum"": 1732.171492, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-87.21423600000003,-102.02723887499991,-57.22989500000017,-4.970658372335491,-5.774483061629977,-3.2617410846737416,faster +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,worker_ms,before,candidate,ms,10,0,1214.5357079999999,1213.163396,unsplit,unsplit,"{""n"": 10, ""p50"": 1214.5357079999999, ""p95_reference"": """", ""minimum"": 1208.78866, ""maximum"": 1236.847748, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 1213.163396, ""p95_reference"": """", ""minimum"": 1202.128989, ""maximum"": 1274.682132, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-1.3723119999999653,-15.082394000000022,10.879147999999986,-0.11299066721223161,-1.2278611706684206,0.8992147687199914,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,metadata,before,candidate,ms,10,0,66.186594,65.25977950000001,unsplit,unsplit,"{""n"": 10, ""p50"": 66.186594, ""p95_reference"": """", ""minimum"": 65.803535, ""maximum"": 67.278247, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 65.25977950000001, ""p95_reference"": """", ""minimum"": 64.547881, ""maximum"": 65.985975, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.9268144999999919,-1.3706619999999958,-0.44021650000000534,-1.4003054757584121,-2.0603253594512094,-0.6674864575038408,faster +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,read,before,candidate,ms,10,0,67.97466700000001,68.746929,unsplit,unsplit,"{""n"": 10, ""p50"": 67.97466700000001, ""p95_reference"": """", ""minimum"": 66.813841, ""maximum"": 88.24187, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 68.746929, ""p95_reference"": """", ""minimum"": 66.530903, ""maximum"": 88.036867, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.7722619999999836,-0.3650870000000168,3.934369000000004,1.136102660127758,-0.5325435168712422,5.644306404155475,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,write,before,candidate,ms,10,0,28.212829499999998,27.1156815,unsplit,unsplit,"{""n"": 10, ""p50"": 28.212829499999998, ""p95_reference"": """", ""minimum"": 27.059161, ""maximum"": 28.757852, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 27.1156815, ""p95_reference"": """", ""minimum"": 26.0406, ""maximum"": 28.266014, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-1.0971479999999971,-2.0724234999999993,-0.5905829999999987,-3.8888265354596774,-7.3045638502453825,-2.134984580202981,faster +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,git,before,candidate,ms,10,0,142.2296785,141.312361,unsplit,unsplit,"{""n"": 10, ""p50"": 142.2296785, ""p95_reference"": """", ""minimum"": 140.683512, ""maximum"": 142.917783, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 141.312361, ""p95_reference"": """", ""minimum"": 139.59504, ""maximum"": 158.597863, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.9173174999999958,-2.1900235000000237,1.6218834999999956,-0.644955054159102,-1.5381712664529479,1.14757302022368,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,rg,before,candidate,ms,10,0,106.5825365,106.0887095,unsplit,unsplit,"{""n"": 10, ""p50"": 106.5825365, ""p95_reference"": """", ""minimum"": 104.669916, ""maximum"": 107.633067, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 106.0887095, ""p95_reference"": """", ""minimum"": 104.835946, ""maximum"": 108.707052, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.49382700000001023,-1.8981907750000104,0.9848100250000014,-0.46332824890127355,-1.771328627173161,0.9253499391059903,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,cargo,before,candidate,ms,10,0,77.55028250000001,76.5364195,unsplit,unsplit,"{""n"": 10, ""p50"": 77.55028250000001, ""p95_reference"": """", ""minimum"": 75.865877, ""maximum"": 96.866359, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 76.5364195, ""p95_reference"": """", ""minimum"": 75.187111, ""maximum"": 102.151344, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-1.0138630000000148,-8.576559500000002,9.034319674999958,-1.3073620976171374,-9.917914123319392,11.44591497004314,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,npm,before,candidate,ms,10,0,230.13633249999998,230.30114500000002,unsplit,unsplit,"{""n"": 10, ""p50"": 230.13633249999998, ""p95_reference"": """", ""minimum"": 225.186954, ""maximum"": 233.312622, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 230.30114500000002, ""p95_reference"": """", ""minimum"": 225.368933, ""maximum"": 239.572046, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.1648125000000391,-1.9061581749999885,4.023910999999998,0.07161515881028979,-0.8239097968774294,1.7558586825401346,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,ready_ms,before,baseline,ms,10,0,701.6391005,691.963534,unsplit,unsplit,"{""n"": 10, ""p50"": 701.6391005, ""p95_reference"": """", ""minimum"": 690.82293, ""maximum"": 712.260488, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 691.963534, ""p95_reference"": """", ""minimum"": 679.212557, ""maximum"": 700.735585, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-9.675566500000059,-15.009504250000077,-2.2094994999999926,-1.378994769975772,-2.128561365252646,-0.31659921452842354,faster +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,result_ms,before,baseline,ms,10,0,4322.3996565,4408.2079945000005,unsplit,unsplit,"{""n"": 10, ""p50"": 4322.3996565, ""p95_reference"": """", ""minimum"": 4148.19369, ""maximum"": 4634.98185, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 4408.2079945000005, ""p95_reference"": """", ""minimum"": 4327.636532, ""maximum"": 4875.767963, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",85.80833800000073,-75.06041950000053,190.58705650000047,1.985201388561153,-1.6958545855973228,4.476708091747295,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,completion_ms,before,baseline,ms,10,0,4560.905778,4595.446841,unsplit,unsplit,"{""n"": 10, ""p50"": 4560.905778, ""p95_reference"": """", ""minimum"": 4400.953022, ""maximum"": 4878.763604, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 4595.446841, ""p95_reference"": """", ""minimum"": 4506.06905, ""maximum"": 5064.932774, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",34.54106299999967,-143.22989999999936,133.20583400000032,0.75732901930603,-3.0089414851627305,2.951110582589078,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,worker_ms,before,baseline,ms,10,0,3621.289471,3707.288059,unsplit,unsplit,"{""n"": 10, ""p50"": 3621.289471, ""p95_reference"": """", ""minimum"": 3433.253015, ""maximum"": 3944.006877, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 3707.288059, ""p95_reference"": """", ""minimum"": 3635.668228, ""maximum"": 4183.811464, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",85.99858799999993,-70.33717099999967,203.0250715000002,2.3748056787145533,-1.8876871408780582,5.701304364284399,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,metadata,before,baseline,ms,10,0,123.04792850000001,148.9697515,unsplit,unsplit,"{""n"": 10, ""p50"": 123.04792850000001, ""p95_reference"": """", ""minimum"": 113.761069, ""maximum"": 173.507349, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 148.9697515, ""p95_reference"": """", ""minimum"": 111.944401, ""maximum"": 189.904797, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",25.92182299999999,-2.951689150000013,32.67203500000001,21.066444040136755,-2.409605705167983,27.683774560670326,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,read,before,baseline,ms,10,0,118.0688145,116.5817545,unsplit,unsplit,"{""n"": 10, ""p50"": 118.0688145, ""p95_reference"": """", ""minimum"": 113.033845, ""maximum"": 142.850213, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 116.5817545, ""p95_reference"": """", ""minimum"": 112.623695, ""maximum"": 136.255042, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-1.4870599999999996,-9.452190000000002,4.780556500000003,-1.2594858399293862,-7.499717665346894,4.158361006664624,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,write,before,baseline,ms,10,0,118.641548,152.998639,unsplit,unsplit,"{""n"": 10, ""p50"": 118.641548, ""p95_reference"": """", ""minimum"": 96.60634, ""maximum"": 149.088464, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 152.998639, ""p95_reference"": """", ""minimum"": 105.266935, ""maximum"": 173.889485, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",34.357091,13.240334999999988,57.9698425,28.95873459102203,10.481857588238054,56.164296908366374,slower +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,git,before,baseline,ms,10,0,,404.511602,separated-clusters,unsplit,"{""n"": 10, ""p50"": """", ""p95_reference"": """", ""minimum"": 354.844523, ""maximum"": 765.855372, ""distribution"": ""separated-clusters"", ""low_n"": 5, ""low_p50"": 364.978813, ""high_n"": 5, ""high_p50"": 731.631807}","{""n"": 10, ""p50"": 404.511602, ""p95_reference"": """", ""minimum"": 365.490143, ""maximum"": 824.110585, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",,,,,,,separated clusters; no whole-distribution ranking +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,rg,before,baseline,ms,10,0,422.51628949999997,453.05335,unsplit,unsplit,"{""n"": 10, ""p50"": 422.51628949999997, ""p95_reference"": """", ""minimum"": 392.012682, ""maximum"": 449.120192, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 453.05335, ""p95_reference"": """", ""minimum"": 431.242878, ""maximum"": 482.024183, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",30.537060500000052,11.138871687499986,50.36638599999998,7.227427973519607,2.5624897126117268,12.559394275873625,slower +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,cargo,before,baseline,ms,10,0,501.842823,508.938783,unsplit,unsplit,"{""n"": 10, ""p50"": 501.842823, ""p95_reference"": """", ""minimum"": 481.152788, ""maximum"": 529.957093, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 508.938783, ""p95_reference"": """", ""minimum"": 497.104239, ""maximum"": 531.244318, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",7.095959999999991,-8.212645000000066,15.285581999999977,1.413980568174833,-1.5841152415438153,3.0584737200522127,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,npm,before,baseline,ms,10,0,924.1405775000001,975.212892,unsplit,unsplit,"{""n"": 10, ""p50"": 924.1405775000001, ""p95_reference"": """", ""minimum"": 825.165724, ""maximum"": 951.471934, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 975.212892, ""p95_reference"": """", ""minimum"": 956.093243, ""maximum"": 994.02812, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",51.07231449999995,40.48251049999999,80.33377199999995,5.526465966699745,4.347094558481546,9.021085987970977,slower +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,ready_ms,baseline,candidate,ms,10,0,691.963534,693.3607625,unsplit,unsplit,"{""n"": 10, ""p50"": 691.963534, ""p95_reference"": """", ""minimum"": 679.212557, ""maximum"": 700.735585, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 693.3607625, ""p95_reference"": """", ""minimum"": 684.282021, ""maximum"": 726.019, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",1.3972284999999829,-7.613195999999903,5.321734500000048,0.2019222735514914,-1.0891083563037585,0.7712365914626328,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,result_ms,baseline,candidate,ms,10,0,4408.2079945000005,4303.31926,unsplit,unsplit,"{""n"": 10, ""p50"": 4408.2079945000005, ""p95_reference"": """", ""minimum"": 4327.636532, ""maximum"": 4875.767963, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 4303.31926, ""p95_reference"": """", ""minimum"": 4114.404626, ""maximum"": 4733.887275, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-104.88873450000028,-198.579205,264.49603149999984,-2.379396222475594,-4.4135739916857375,6.080819350509237,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,completion_ms,baseline,candidate,ms,10,0,4595.446841,4484.42847,unsplit,unsplit,"{""n"": 10, ""p50"": 4595.446841, ""p95_reference"": """", ""minimum"": 4506.06905, ""maximum"": 5064.932774, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 4484.42847, ""p95_reference"": """", ""minimum"": 4284.771117, ""maximum"": 4925.370465, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-111.01837100000012,-222.17086666250032,248.69843200000105,-2.4158340818896673,-4.7596864032378186,5.482919937523523,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,worker_ms,baseline,candidate,ms,10,0,3707.288059,3606.7074435,unsplit,unsplit,"{""n"": 10, ""p50"": 3707.288059, ""p95_reference"": """", ""minimum"": 3635.668228, ""maximum"": 4183.811464, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 3606.7074435, ""p95_reference"": """", ""minimum"": 3424.74753, ""maximum"": 4007.710068, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-100.58061550000002,-200.09853924999973,264.8636459999998,-2.7130509930520574,-5.283209164000757,7.245092923180008,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,metadata,baseline,candidate,ms,10,0,148.9697515,142.879463,unsplit,unsplit,"{""n"": 10, ""p50"": 148.9697515, ""p95_reference"": """", ""minimum"": 111.944401, ""maximum"": 189.904797, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 142.879463, ""p95_reference"": """", ""minimum"": 109.248394, ""maximum"": 193.687111, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-6.090288500000014,-34.00228049999998,57.33683100000002,-4.088271906662888,-22.10593881840791,43.986512348584064,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,read,baseline,candidate,ms,10,0,116.5817545,116.239688,unsplit,unsplit,"{""n"": 10, ""p50"": 116.5817545, ""p95_reference"": """", ""minimum"": 112.623695, ""maximum"": 136.255042, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 116.239688, ""p95_reference"": """", ""minimum"": 113.463571, ""maximum"": 143.747386, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.3420665000000014,-5.796062000000006,2.4831729999999936,-0.29341340887094614,-4.841108476027922,2.151856179849432,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,write,baseline,candidate,ms,10,0,152.998639,157.081132,unsplit,unsplit,"{""n"": 10, ""p50"": 152.998639, ""p95_reference"": """", ""minimum"": 105.266935, ""maximum"": 173.889485, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 157.081132, ""p95_reference"": """", ""minimum"": 109.271257, ""maximum"": 170.078216, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",4.0824929999999995,-21.790348499999993,28.414495000000016,2.668319814269715,-13.211373730440778,21.335000388714633,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,git,baseline,candidate,ms,10,0,404.511602,387.065142,unsplit,unsplit,"{""n"": 10, ""p50"": 404.511602, ""p95_reference"": """", ""minimum"": 365.490143, ""maximum"": 824.110585, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 387.065142, ""p95_reference"": """", ""minimum"": 366.41647, ""maximum"": 786.817882, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-17.446460000000002,-43.1554385,362.8267168374993,-4.312969001072064,-10.15133314100217,91.48113097145973,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,rg,baseline,candidate,ms,10,0,453.05335,418.8727155,unsplit,unsplit,"{""n"": 10, ""p50"": 453.05335, ""p95_reference"": """", ""minimum"": 431.242878, ""maximum"": 482.024183, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 418.8727155, ""p95_reference"": """", ""minimum"": 395.836215, ""maximum"": 436.257845, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-34.1806345,-52.35176000000001,-19.29760799999997,-7.544505409793345,-11.387569735656433,-4.329230687039798,faster +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,cargo,baseline,candidate,ms,10,0,508.938783,488.0712105,unsplit,unsplit,"{""n"": 10, ""p50"": 508.938783, ""p95_reference"": """", ""minimum"": 497.104239, ""maximum"": 531.244318, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 488.0712105, ""p95_reference"": """", ""minimum"": 479.838543, ""maximum"": 519.067537, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-20.867572499999994,-30.005172999999957,-11.134416000000044,-4.100212677248449,-5.849091368688708,-2.1933129399490214,faster +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,npm,baseline,candidate,ms,10,0,975.212892,929.924457,unsplit,unsplit,"{""n"": 10, ""p50"": 975.212892, ""p95_reference"": """", ""minimum"": 956.093243, ""maximum"": 994.02812, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 929.924457, ""p95_reference"": """", ""minimum"": 712.545115, ""maximum"": 955.363464, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-45.28843500000005,-142.84756531250002,-34.50912100000005,-4.643953681449076,-14.757461850438,-3.5390971840836283,faster +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,ready_ms,before,candidate,ms,10,0,701.6391005,693.3607625,unsplit,unsplit,"{""n"": 10, ""p50"": 701.6391005, ""p95_reference"": """", ""minimum"": 690.82293, ""maximum"": 712.260488, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 693.3607625, ""p95_reference"": """", ""minimum"": 684.282021, ""maximum"": 726.019, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-8.278338000000076,-15.691415499999948,-2.56550900000002,-1.179856994015982,-2.2075007869827967,-0.36862457696940076,faster +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,result_ms,before,candidate,ms,10,0,4322.3996565,4303.31926,unsplit,unsplit,"{""n"": 10, ""p50"": 4322.3996565, ""p95_reference"": """", ""minimum"": 4148.19369, ""maximum"": 4634.98185, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 4303.31926, ""p95_reference"": """", ""minimum"": 4114.404626, ""maximum"": 4733.887275, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-19.08039649999955,-208.33696899999995,352.35256200000003,-0.4414306407623969,-4.647749548701285,8.112365261407707,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,completion_ms,before,candidate,ms,10,0,4560.905778,4484.42847,unsplit,unsplit,"{""n"": 10, ""p50"": 4560.905778, ""p95_reference"": """", ""minimum"": 4400.953022, ""maximum"": 4878.763604, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 4484.42847, ""p95_reference"": """", ""minimum"": 4284.771117, ""maximum"": 4925.370465, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-76.47730800000045,-290.97629099999995,262.68939899999987,-1.6768008751440688,-6.101023686832433,5.809290056528682,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,worker_ms,before,candidate,ms,10,0,3621.289471,3606.7074435,unsplit,unsplit,"{""n"": 10, ""p50"": 3621.289471, ""p95_reference"": """", ""minimum"": 3433.253015, ""maximum"": 3944.006877, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 3606.7074435, ""p95_reference"": """", ""minimum"": 3424.74753, ""maximum"": 4007.710068, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-14.582027500000095,-202.75019199999997,356.52362300000004,-0.40267500338694306,-5.349564986425682,9.831150421507196,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,metadata,before,candidate,ms,10,0,123.04792850000001,142.879463,unsplit,unsplit,"{""n"": 10, ""p50"": 123.04792850000001, ""p95_reference"": """", ""minimum"": 113.761069, ""maximum"": 173.507349, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 142.879463, ""p95_reference"": """", ""minimum"": 109.248394, ""maximum"": 193.687111, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",19.831534499999975,-30.704955000000012,64.7004125,16.116918620048093,-20.591589894426665,54.068835933254064,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,read,before,candidate,ms,10,0,118.0688145,116.239688,unsplit,unsplit,"{""n"": 10, ""p50"": 118.0688145, ""p95_reference"": """", ""minimum"": 113.033845, ""maximum"": 142.850213, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 116.239688, ""p95_reference"": """", ""minimum"": 113.463571, ""maximum"": 143.747386, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-1.829126500000001,-7.181691000000015,0.24210399999999765,-1.549203748463146,-5.698219657006776,0.2087146918508198,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,write,before,candidate,ms,10,0,118.641548,157.081132,unsplit,unsplit,"{""n"": 10, ""p50"": 118.641548, ""p95_reference"": """", ""minimum"": 96.60634, ""maximum"": 149.088464, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 157.081132, ""p95_reference"": """", ""minimum"": 109.271257, ""maximum"": 170.078216, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",38.439583999999996,9.882897174999991,63.73562249999999,32.39976605834576,7.535407135453076,64.42140771151135,slower +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,git,before,candidate,ms,10,0,,387.065142,separated-clusters,unsplit,"{""n"": 10, ""p50"": """", ""p95_reference"": """", ""minimum"": 354.844523, ""maximum"": 765.855372, ""distribution"": ""separated-clusters"", ""low_n"": 5, ""low_p50"": 364.978813, ""high_n"": 5, ""high_p50"": 731.631807}","{""n"": 10, ""p50"": 387.065142, ""p95_reference"": """", ""minimum"": 366.41647, ""maximum"": 786.817882, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",,,,,,,separated clusters; no whole-distribution ranking +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,rg,before,candidate,ms,10,0,422.51628949999997,418.8727155,unsplit,unsplit,"{""n"": 10, ""p50"": 422.51628949999997, ""p95_reference"": """", ""minimum"": 392.012682, ""maximum"": 449.120192, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 418.8727155, ""p95_reference"": """", ""minimum"": 395.836215, ""maximum"": 436.257845, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-3.643573999999944,-26.00949750000001,16.516374150000015,-0.8623511307248477,-5.889376948386782,4.098432434016597,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,cargo,before,candidate,ms,10,0,501.842823,488.0712105,unsplit,unsplit,"{""n"": 10, ""p50"": 501.842823, ""p95_reference"": """", ""minimum"": 481.152788, ""maximum"": 529.957093, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 488.0712105, ""p95_reference"": """", ""minimum"": 479.838543, ""maximum"": 519.067537, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-13.771612500000003,-28.893867500000056,-6.115591000000052,-2.744208319583763,-5.563656181729161,-1.2188892407220542,faster +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,npm,before,candidate,ms,10,0,924.1405775000001,929.924457,unsplit,unsplit,"{""n"": 10, ""p50"": 924.1405775000001, ""p95_reference"": """", ""minimum"": 825.165724, ""maximum"": 951.471934, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 929.924457, ""p95_reference"": """", ""minimum"": 712.545115, ""maximum"": 955.363464, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",5.783879499999898,-101.11153649999994,37.297305999999935,0.6258657655360711,-10.925673042081986,4.174025872447418,no detected difference diff --git a/docs/src/en/design/direct-queue-provenance.csv b/docs/src/en/design/direct-queue-provenance.csv new file mode 100644 index 000000000..75b356d09 --- /dev/null +++ b/docs/src/en/design/direct-queue-provenance.csv @@ -0,0 +1,20 @@ +field,value +filesystem_report_sha256,76121114242e2372287f734ea14764d4072e14bee658cbfbe340302f3bddc26b +filesystem_raw,benchmark/.data/direct-queue-quick-20261011/report.json +filesystem_launch_sha256,e4e4e817fb556b102058d9febd8612901350ff33f2455a2357c7efa6b9574c4d +filesystem_guard_sha256,1fa71afe6f70756cab5446ac49315bd484f3e51ff78439a436ffcb388932e526 +filesystem_protocol,10 seeded shuffled paired rounds per arm/backend; exploratory 60-sample batch; cancelled original formal batch retained separately; 3 warmups; fresh private user/mount/PID namespace and pivot_root per trial; fresh Host listener and VM launch timed; full 256 x 64 KiB written bytes verified; unchanged seven-operation workload; no profiling +filesystem_before_scope,"prior candidate6 overlay optimization and shared worker-pool queues; engineering recovery reference, not literal cf8 parent" +filesystem_baseline_scope,"previous concurrency build2 with kick forwarder, busy-based stealing and mount-wide I/O drain" +filesystem_binaries,"{""before"": ""378ee13ba8b8aafc0a588d51a9c3a79c1410c000857f323970cc13fc65d279e4"", ""baseline"": ""87ebb0658f2df6a84f9e4eca29c8d3d8bc01923817f04432780272c8b1b30309"", ""candidate"": ""ae221676e4bb04231c88f2ce17c2570824f4064f8a3a63d6ef32817b0e2afa98""}" +filesystem_source_manifests,"{""before"": ""aa30eda66bc1f1ee853404b7d41e8847f0722ce72adfb79fa6816e06258655d6"", ""baseline"": ""07a216161d0b64313ba03787630658f300a19788f705d318dfd88f9c5397726c"", ""candidate"": ""f5d1759332f5393bf9ff56569018ccb443d889f4a028ee6df4563569329a5590""}" +candidate_scope,"direct per-queue kick wait/execute/publish; steal only heavyweight operation running >=2 ms with pending descriptors, revalidated under ring lock; scoped handle I/O fences and I/O admission outside namespace guard; writeback unchanged" +candidate_build_receipt_sha256,dd6121706bbc4dcff61a4b537611703bb181c5af02735c71806b8313fd6897bf +publication_script_sha256,a8759c3cb72866890ab8de267537a81b1ab5f50bd8a5c65b6d6150baa6a15355 +source_note,measurement build includes final production implementation; worktree subsequently only moves existing cfg(test) device tests to satisfy Clippy; no production behavior change +budget,"whole owned parent CPUs 0,1; CPUQuota 200%; RAM 16 GiB; swap 0; VM 2 vCPU/16 GiB" +statistics,5000 paired-round bootstrap resamples of median difference and median ratio CI95; separated distributions have no global median ranking; no P95/P99 at N=10; intervals exploratory +interference,none detected outside owned service; same-UID build/test polling every 0.5 s may miss short/inaccessible/unrecognized activity +scope,"B-FS-ENG exploratory; only 10 paired rounds per cell, not formal 30-round performance acceptance; Linux KVM/FUSE/Btrfs warm caches, sequential jobs; same seven-operation workload; unchanged staged control; no startup/cold/concurrency-throughput/macOS claim" +correctness,"all 60 exploratory measured tasks and 18 warmups pass original workload, Run Bundle, isolation and full 256x64KiB output bytes; inputs and artifacts reverified; preflight separate" +validation,363 VM library tests pass; 8 skipped; Clippy all targets and warnings-denied rustdoc pass diff --git a/docs/src/en/design/experimental/guest-stage-cache/index.md b/docs/src/en/design/experimental/guest-stage-cache/index.md new file mode 100644 index 000000000..9c0f2992e --- /dev/null +++ b/docs/src/en/design/experimental/guest-stage-cache/index.md @@ -0,0 +1,637 @@ +# EXP-002 (design stage): guest kernel filesystem cache and deferred backend binding + +Let the guest kernel use a versioned filesystem view to complete cached path resolution, attribute queries and ordinary read-only open locally. Call the VMM for missing data, stage mutations and durability. The host retains authoritative stage, baseline observation, access policy, review/apply and restoration state. + +First reuse FUSE caching and read-only zero-message open, then add paged metadata import and stage consistency. + +| Item | Current status | +|---|---| +| Stage | **Design stage (RFC), 2026-10-11; not implemented or accepted** | +| Implementation audience | Guest-kernel, VMM, filesystem and performance researchers | +| Initial scope | Linux x86_64/KVM; exclusively owned, physically immutable read-only export; regular files and static access grants | +| Main mechanism | Metadata import, object references, guest open state, data binding on first backend access | +| Stage extension | Host commits mutations; guest updates caches through the protocol; current writable-open/copy-up contract retained initially | +| Switch | No runtime switch or CLI interface yet; disabled by default after prototype integration | +| Languages | Rust for new host and guest-userspace code; small C patches for necessary guest-kernel extensions | +| EROFS | Optional immutable-lower representation; not required initially and does not own stage writes | +| Performance | No real VM measurements for this proposal; request reduction and end-to-end benefit accepted separately | +| Later profile | Frozen execution baseline, exclusive stage, protocol-managed cache updates | +| Interface status | Types, fields and messages below express proposed semantics, not a wire ABI or public Rust API | +| Implementation entry | [Milestones, responsibilities and acceptance matrix](#implementation-plan) | + +First validate existing FUSE caching and zero-message open, then investigate paged metadata import and mixed stage views. Initial results apply only to the named read-only profile. Writable stage, live baseline and other platforms require separate acceptance. + +## 1. Problem statement {#problem} + +### 1.1 Why move filesystem requests into the guest? + +File traversal and tool initialization repeatedly resolve paths, query attributes and open files. Some files are opened only to inspect metadata, with no content read. Existing guest FUSE caches some entries, attributes and pages, but metadata misses and current ordinary OPEN still traverse the guest kernel, virtqueue, VMM and host service; OPEN also creates a backend handle. A prepared host lazy-image index does not establish guest-local access to that knowledge. + +Identify requests the guest kernel can complete from a stable view by supplying immutable-lower metadata and protocol-managed stage state, while preserving host mutation, authorization and durability ownership. Traversal, open/fstat/close and mixed stage views expose metadata delivery, open lifetime and cache coherence as separate problems. + +The existing [Writeback comparison](../../filesystem-performance-analysis.md#writeback-ab) supports improvement for that write workload with writeback disabled, without detected full-task acceleration. This proposal targets structural metadata/open costs and requires independent end-to-end evidence. Writeback, copy-up and durability costs still need separate diagnosis. + +### 1.2 Foundations and design gaps + +Guest virtio-fs/virtqueue calls `FilesystemService` directly in the host runner, sharing OverlayCore with host FUSE. VM lazy images have no intermediate host FUSE mount; the host image backend still supplies metadata and block reads. See [Current architecture](https://github.com/DeepLink-org/pvisor/blob/main/crates/pvisor-vm/README.md) and [Shared service](../../overlayfs.md). + +| Foundation | Reusable behavior | Additions or verification | +|---|---|---| +| Guest FUSE cache | Paths, attributes and data pages | Admission, validity, metadata import and local-open conditions | +| FUSE zero-message open | No-OPEN prototype on a read-only connection | Handleless data service, FLUSH/RELEASE and connection-wide limits | +| VM OverlayFs | Inodes/handles, object I/O leases, mutation/freeze | Unbound opens, object retention and update protocol | +| OverlayCore | Lower/upper composition, copy-up, preimages and conflicts | Explicit cache semantics while retaining current contract | +| Lazy-image paged index | Versioned attributes, directory index, content descriptors/checksums | Guest consumer, stable object mapping and permission completeness | +| Existing snapshot | CPU/device/RAM quiescence and filesystem rebinding | Cache modes, object references and unbound FD save/restore | + +Read-only open observes the baseline in live mode; writable open performs copy-up. See [Open classification/preparation](https://github.com/DeepLink-org/pvisor/blob/main/crates/pvisor-overlay-core/src/service.rs) and [Preimage observation](https://github.com/DeepLink-org/pvisor/blob/main/crates/pvisor-overlay-core/src/core.rs). Local-open admission must include observation requirements as well as data access. + +Ordinary OverlayFs open currently requires a backend handle. Existing FUSE flags do not establish that this proposal can simply be enabled. See the [VM open implementation](https://github.com/DeepLink-org/pvisor/blob/main/crates/pvisor-vm/src/devices/virtio/fs/overlay.rs). + +Host OverlayFs already provides owned-view caching, mutation-effect collection and notification-failure handling. Reuse its semantic lessons and test cases from the [Host cache contract](https://github.com/DeepLink-org/pvisor/blob/main/crates/pvisor-overlayfs/README.md). Its mount adapter is not guest update transport; keep private state within its API boundary. + +### 1.3 Goals and measurement boundaries + +Complete path resolution, attributes and ordinary read-only open without filesystem VMM requests when metadata, permissions, retention and versions are complete. Valid data hits also stay local; missing data, stage mutation and required host observation/effects use the backend. Evaluate cold start, steady state and complete Attempts. + +Zero requests means no filesystem VMM requests within the specified operation window. Metadata import, first protocol probes, reference reclamation and teardown are recorded separately. It does not mean a VM has no hardware exits, scheduling or other device activity. + +## 2. Core design {#core-design} + +### 2.1 Architecture and responsibilities + +1. Use guest VFS dentry, inode, page cache and open state to cache the exported namespace and metadata. +2. Keep stage mutation, copy-up, data service, baseline observation and durability in the VMM; guest caches are execution-view replicas. +3. Ordinary read-only open creates guest open state; bind backend resources on first access requiring them. A valid guest data hit does not force backend access merely because read was called. +4. Retain current host paths initially for writable open, creation, truncation, removal, rename and other operations with existing side effects. +5. Demonstrate fewer requests under the same contract before extending to stage and recovery; accept each stage separately. + +```mermaid +flowchart LR + subgraph Guest[Guest] + App[Tool processes] --> VFS[VFS paths and open state] + VFS --> Cache[Namespace metadata and data cache] + Cache --> Local[Valid hit completed locally] + Cache --> Remote[Miss or required host operation] + end + subgraph Host[Host] + Metadata[Immutable metadata and access grants] --> Transport[virtio-fs and proposed cache protocol] + Transport --> Objects[Object references and backend bindings] + Objects --> Core[FilesystemService / OverlayCore] + Core --> Data[Native files / lazy content blocks] + Core --> Stage[Upper preimages and sealing] + Stage --> Apply[Review / apply / restore] + end + Remote --> Transport + Transport --> Cache +``` + +The guest owns caches, local open state and protocol-granted object references. The VMM owns sessions, trusted mappings, native handles, authorization and request lifecycle. OverlayCore owns composition, copy-up, preimages and stage semantics. Runtime/supervisor continues to arbitrate Attempt, freeze, seal, save and restore. + +Metadata construction/import fixes the source revision, validates indexes and preserves raw Unix path bytes/hardlink identity. Caches retain image/backend content validation and never treat holes in sparse lazy placeholders as real file content. + +### 2.2 Local and VMM operation boundaries + +Local completion requires valid entries, complete metadata, established object retention, locally decidable access, and no required host observation or side effect. + +| Operation | Conditional guest-local path | Required backend access | +|---|---|---| +| LOOKUP, GETATTR, stat/lstat | Valid directory entries and attributes | Missing pages/fields, policy or observation requirements | +| Negative lookup | Complete directory index and valid stage covering state prove absence | Incomplete index/version gaps; errors do not mean absence | +| READDIR | Valid directory view and version-bound cookies | Missing pages, changed stage version or unsupported cursor semantics | +| O_PATH | Valid path cache and object references | Missing path, permission or retention conditions | +| Regular-file O_RDONLY open | Complete static permissions, retained object, compatible observation contract | Live observation, dynamic policy, special files or unknown capability | +| O_WRONLY/O_RDWR open | Current VMM behavior initially | Copy-up, permissions and error timing | +| O_CREAT/O_TRUNC/O_TMPFILE and similar | Cached preparation inputs | Host creation/truncation/temporary-object effects | +| read, pread, mmap faults | Valid versioned data pages | Missing/invalid data or absent required backend binding | +| write, truncate, shared writable mmap | Existing path or explicit rejection of uncovered forms | Stage mutation, copy-up, preimages and writes | +| unlink, rename, link, mkdir, attribute changes | Atomic application of host-returned cache effects | Mutation authorization, preimages and stage commit | +| fsync, seal, checkpoint | Existing flush/quiescence contract | Host synchronization, sealing and capture | + +Linux usually handles O_PATH in VFS already; operations already lacking FUSE OPEN do not count as a new reduction. Flags such as O_TRUNC can produce effects before any read/write. See [Linux open semantics](https://man7.org/linux/man-pages/man2/open.2.html). + +Initial exclusions are live/mutable lower, external upper writers, shared mutable stage across VMs, dynamic host policy, special files and unaccepted locking/I/O forms. Extensions require independent profiles/cases. A complete guest upper, full in-kernel overlay and changed apply semantics remain separate architecture evaluations. + +Target operation example: + +```text +open("a", O_RDONLY) → guest validates view/permissions, retains object A, creates local open state +fstat(fd) → returns valid cached attributes of A +read(fd) → returns a cache hit; otherwise fetches data for A +close(fd) → releases open state; cached object references follow reclamation protocol +``` + +### 2.3 Objects, versions and references + +The following semantic model supports review; it is not a final serialization layout. + +| Concept | Required information | Lifetime/use | +|---|---|---| +| Session identity | Attempt, stage, base revision, protocol, permission semantics, policy version | Host validates before mount/restore; tokens do not cross sessions | +| Object identity | Host-assigned/recognized object ID, incarnation, type, hardlink group | Stable while referenced despite path/inode-number reuse | +| Backing identity | Physical/remote backing identity and generation | Copy-up/replacement may change backing; existing opens retain current VM semantics | +| Directory version | Directory and relevant covering state | Creation, removal, rename, whiteout/opaque updates | +| Attribute version | Owner, mode, size, times, link count and other attributes | Validated independently of directory/data versions | +| Data version | Data and valid ranges | Updated by writes/truncation; directory version alone cannot validate pages | +| Object reference | Granted retention/access scope, owner, budget, reclaim state | May precede a data FD; eviction retains references needed by opens | +| Open state | Object/backing binding, flags, credentials context, offset, binding state | dup/fork share one open description; separate opens are independent | + +Directory entries are isolated by session, parent, raw basename, relevant directory version and policy version. Attributes use object identity/attribute version; data pages use object/backing identity/data version. Implementations may combine/compress fields while preserving these distinctions. + +#### 2.3.1 Deferred binding retains the old object + +```text +fd = open("a", O_RDONLY) # locally opens object A +unlink("a") # VMM mutates stage +create("a") # creates object B under the same name +read(fd) # must still access A +``` + +An unlinked open file persists until its relevant references are released. First read carries A's reference rather than looking up path a again. See [Linux unlink semantics](https://man7.org/linux/man-pages/man2/unlink.2.html). + +An immutable lower can retain its entire revision without a host data FD per guest open. Stage may retain a native object, descriptor or private backing; prove lifetime after unlink and resource bounds. Data FDs can be deferred, but object retention is already established. Missing retention falls back to the original host open path. + +Stage metadata carries an already-effective host retention reference before guest-local opens acquire references within it. The host may conservatively retain cached objects until the guest releases both cache and open references, at an object/FD cost. Delivering a path and pinning only at first read does not meet this requirement. + +Incarnation differs from data version. Writes to the same mutable file remain visible to other opens under the existing contract; each write does not create a new object. Preserve current VM behavior in which existing lower handles retain their original backing after copy-up. + +#### 2.3.2 Reclamation + +Caches and opens hold separate references. Eviction releases only the cache portion; in-flight I/O retains object/RAM leases. Use bounded batch release or existing FORGET-like mechanisms, with session teardown as final reclamation boundary. + +Removed stage objects await reclamation until the last recognized reference is released. Budget exhaustion rejects admission or uses the original path; referenced objects remain valid. Existing supervisor ownership terminates a guest that does not respond to reclamation. + +### 2.4 Metadata packages and merged caches + +#### 2.4.1 Physical and view stability + +Physical immutability covers data, metadata, namespace, ancestors, mount identity and hardlink aliases for the agreed lifetime. Read-only mounts, digest paths and frozen baseline express other properties and do not independently prove physical stability. See the [Lower contract](https://github.com/DeepLink-org/pvisor/blob/main/crates/pvisor-overlay-core/README.md). + +A read-only export can be cached as a whole. A mixed view additionally requires one owner of upper/work, all mutations through VMM, provable/handled higher-priority lower changes and fixed policy or reliable policy updates. Reject admission before enablement if these conditions fail; a longer TTL cannot resolve uncontrolled external changes. + +#### 2.4.2 Paged import + +Bootstrap delivers bounded version/checksum metadata, root/necessary pages and capabilities; fetch additional metadata pages on demand. Represent directory indexes, inode attributes, symlinks, hardlink groups and content locations explicitly. Package construction, page delivery and inode/dentry creation are separate costs. + +[pvisor-paged-v1](../../shared-image-cache-storage.md) provides fields/building foundations, while the guest format requires review. It currently omits xattr, so it does not establish complete ACL/security input. Incomplete permissions exclude local open; missing attribute fields require backend queries. + +Before kernel consumption, limit bytes, records, page references, depth, name/target lengths and offset arithmetic. Reject cycles, duplicates, out-of-bounds references and unknown required features. Retain both host validation and kernel boundary checks. Fix format version/endian layout; do not map native Rust/C struct memory. + +#### 2.4.3 Merged lookup and negative cache + +Check valid stage covering state before the explicit lower order. Whiteout, opaque and non-directory ancestors hide lower results; directory rename updates relevant subtrees/path caches. Synthetic configuration and mounts keep their existing owners rather than being replaced by package metadata. + +Absence requires a complete validated index and valid stage state; unloaded pages do not establish absence. Readdir cookies bind a view/version, with explicit restore/seek/concurrent-mutation semantics. Network, integrity and version errors never become negative entries. + +### 2.5 Local open and deferred binding + +#### 2.5.1 Local-open steps + +1. Obtain a consistent cache view and resolve components/symlinks. +2. Validate type, flags, permission inputs, path grants and observation requirements. +3. Acquire an open reference from established retention; the object survives until future I/O. +4. Create guest open state and return an FD without reading content. + +Missing conditions use the original open path. O_CREAT, O_TRUNC, O_TMPFILE, permission clearing and current writable copy-up remain host operations. Deferred copy-up or shifted preimage timing is a separate contract migration. + +#### 2.5.2 Open state machine + +```mermaid +stateDiagram-v2 + [*] --> Unbound: Local open and retention succeed + Unbound --> Unbound: Valid attribute or data cache hit + Unbound --> Binding: First backend resource required + Binding --> Bound: Host validates and binds + Binding --> Unbound: Retryable failure without binding effects + Binding --> Failed: Invalid object or unknown result + Bound --> Bound: Data and supported control operations + Unbound --> Closed: Last open reference released + Bound --> Closing: Last open reference released + Closing --> Closed: In-flight I/O drained and backend released + Failed --> Closed: Cleanup under failure contract +``` + +Concurrent first accesses in Binding share one result. Wait outside inode-table/global-view locks. Close, cancel and freeze cannot leak unknown handles. Async requests retain open state; current INTERRUPT gives no actual cancellation guarantee, so cleanup awaits results or enters session failure. + +#### 2.5.3 Data requests + +Requests carry recognized object references, backing/data-version conditions, open context, offset and length. The VMM validates session, grant, lifetime and bounds, then supplies data from retained native backing or lazy backend. + +The proposed protocol can combine BIND and first READ to avoid an extra round trip. The initial read-only FUSE prototype may instead serve inode plus fh=0 and cache read-only backing per object. This differs from a new BIND RPC and requires separate verification. + +Read, mmap faults and executable loading can request data; valid guest pages complete locally. See [FUSE I/O caching](https://docs.kernel.org/filesystems/fuse/fuse-io.html). Writes initially retain existing paths with correct partial success, sizes/times, errors and preimage order. + +Mixed-stage data caching has separate admission. Lower old-open and copied-up new-open bindings may use different backing, while an ordinary FUSE inode generally shares one page-cache address space. Prove pages are not reused across incompatible backing; initially restrict data caching if needed, then investigate page ownership or a separate semantics migration. Conceptual data-version fields alone do not solve this. + +#### 2.5.4 Errors and close + +Local open preserves existence, permissions, type, flags and promised observation/effect errors. Deferring host FDs shifts host resource-exhaustion errors; profiles need explicit semantic review, beyond final data checks. + +An unbound read-only FD can release open state locally, with separate FLUSH, RELEASE, FORGET, atime and reference-reclamation verification. Zero-message open alone does not establish a request-free open/fstat/close window. First connection probes count in startup, with steady-state counts reported separately. + +### 2.6 Stage mutation and consistency + +#### 2.6.1 Mutation effects + +Host versioned results cover affected entries, attributes, backing, hardlink aliases, data ranges, whiteout/opaque and subtree changes. Guest caches apply authoritative effects rather than guessed local outcomes. + +Namespace mutation, data writes and policy changes update relevant versions. Old metadata/read replies fill caches only under valid object/version conditions. Failures may have partial data/visible effects; report or recover actual effects. + +#### 2.6.2 Visibility order + +Complex mutations use a verifiable preparation, fence and publication protocol: + +1. Determine scope/expected versions; widen the fence when aliases/subtrees are uncertain. +2. Guest blocks new cached reads/opens in scope and coordinates admitted work; VMM coordinates native I/O. +3. VMM records preimages, performs copy-up/mutation under the current contract, and generates versions/effects. +4. Guest atomically applies effects, rejects stale replies and acknowledges agreed visibility. +5. Release the fence before returning the result; host requests follow the same publication order. + +Define the actual linearization point, cross-queue order and ACK in executable interleaving tests before implementation. Initial stage PoC may use a conservative whole-view fence, then measure/refine it. Never await guest ACK while holding locks needed by response threads. + +Coordinate object retention with mutation fences. New opens cannot acquire an invalid reference after deletion committed but before cache update; existing opens retain their old object. Metadata and I/O in one causal chain cannot combine incompatible versions. + +#### 2.6.3 Transport and unknown commits + +Mixed stage requires reliable updates, sequence detection, gap recovery and backpressure. Evaluate reply extensions or a separate control channel; host `/dev/fuse` notification does not establish an existing guest virtio-fs capability. + +Lost updates, sequence gaps and token/version mismatch first disable affected cache paths, then enter verified resynchronization. Retain existing open bindings; dropping all FDs does not rebuild valid semantics. Fail the session if consistency cannot be reestablished. + +Request IDs correlate operations; initial scope provides no durable exactly-once guarantee. READ can retry under explicit object conditions. Mutation timeout/cancel does not blindly resend; reconcile trusted stage/log state or use existing failure recovery. + +#### 2.6.4 Minimum message categories for review + +These categories specify information and causality. Encoding, errors, limits, compatibility and reuse of existing opcodes are milestone-review decisions. + +| Category | Required information | Success/failure boundary | +|---|---|---| +| View initialization | Session, protocol/profile, base/stage, policy version, budgets, index root and initial sequence | Local operations start only after full admission; failure leaves no partly usable view | +| Metadata-page fetch | View, page/object and expected version; reply checksums, completeness and reference grants | Old pages do not replace newer state; missing page differs from absence | +| Data access/binding | Retained object reference, backing/data conditions, authorized open context, offset/length | May combine first READ; distinguish no effects, acquired resources and unknown result | +| Stage mutation/effects | Existing operation input, expected versions and correlation; actual effects/sequence in reply | Host retains preimages/authorization; partial success publishes actual effects | +| Update ACK/resync | Applied sequence, gaps, fence scope and recovery view | Disable affected caches during gaps; ACK does not establish durability | +| Reference release | Session, object/binding references, batch and owner | Explicit duplicate/late-release contract; retain live references | + +Existing control owners continue to schedule quiescence, seal and snapshot. These messages do not create separate terminal-state or Host-authority control channels. + +### 2.7 Semantic and permission invariants {#invariants} + +1. **Host authority is preserved.** Guest caches cannot grant reads, stage mutation, apply, seal or Host-control privileges. +2. **Execution view and conflict baseline stay separate.** Preserve visible compose B, apply baseline A and upper C independently. Modification of B still applies against A; B's digest cannot replace A. +3. **Object identity is stable.** Open retains object identity/lifetime; rename, unlink, same-name recreation and eviction do not retarget an old FD. +4. **Equal content does not mean one inode.** Model deduplication, hardlink groups, entries and open state separately. +5. **Merged caches respect coverage.** Upper, whiteout, opaque, directory rename and policy determine visibility. Immutable lower alone does not establish an immutable merged result. +6. **Observation contracts remain intact.** Live content-open and negative-lookup observations do not silently disappear on a hit; initial profiles permit skipping these observations. +7. **Errors do not mean absence.** Propagate timeouts, corruption, denied access, version mismatch and backend failures distinctly. +8. **Commit and visibility have explicit ordering.** Stage effects and cache updates share a linearization rule; late responses cannot replace newer state. +9. **Lifetimes are bounded.** Object references, backend handles, in-flight requests and caches have owners, budgets and release protocols. +10. **Snapshots retain open semantics.** Freeze/rebind does not replace the original object by reopening its current path. + +#### 2.7.1 Permissions, baseline observation and trust + +The host trims invisible paths, protected aliases and object grants before metadata delivery. Mode/owner/ACL/xattr inputs must be complete. Preserve raw path bytes; symlinks cannot request arbitrary host paths. Content digests do not grant cross-tenant access. + +Guest Linux checks permissions locally; VMM still validates host-recognized authorization for data/mutations. Session tokens, guest uid and mutation lists cannot replace Host-control credentials or alter host permission ledgers. See [Host/Guest authority](../../decisions/0008-host-guest-control-authority.md). + +Profiles requiring per-operation host policy/audit callbacks exclude local open. Policy changes revoke new cache admission and coordinate references; resume only after rebuilding permission/retention contracts. + +Live baseline observes apply baseline on first content-open; negative lookup may record Absent. Moving these events to read/write changes current semantics. Initial stable read-only profiles need no such read observations; frozen-stage mutations still record true baseline preimages on the host. Guest digests/events do not become sole conflict evidence. + +Verify atime, force-sync attribute queries and access checks under the profile; incomplete semantics use backend access. Read-only mounts or modified timestamps do not establish source stability. + +### 2.8 Lifecycle and recovery + +#### 2.8.1 Failures and mode changes + +| Event | Required behavior | +|---|---| +| Import failure/unsupported capability | Reject before cache enablement; preserve existing configuration without a partial view | +| Incomplete metadata | Admit verified ranges/capabilities, fetch missing fields; do not infer absence or permission success | +| Integrity/protocol error | Explicit error, no cache contamination or conversion to ENOENT | +| Object/incarnation mismatch | Resynchronize or return object error; old FDs do not reopen the current path | +| Resource exhaustion | Reject admission or reclaim unreferenced state under budgets; preserve valid FDs/in-flight requests | +| Unknown mutation result | Stop affected progress and reconcile actual effects; do not report success or safe rollback | +| Guest/VMM exit | Existing supervisor ends session and drains/releases references/handles | +| seal/apply/drop | Existing Host lifecycle boundary, with no concurrent stage writers | + +Connection-wide no-open cannot revert the original path per inode through a return value. Define admission/fallback before mount; live protocol-mode changes require drain, preserved bindings and a separate design. + +#### 2.8.2 Snapshot, fork and rebinding + +Capture binds base revision, stage identity/state, protocol/cache mode, permissions/policy, object references and open bindings. Guest RAM inode/dentry/FD/page-cache state and VMM devices correspond to the same quiescent cut. + +Reuse existing control ownership: stop admission, park CPUs, drain executed device accesses and publish completion, save consistent RAM/device/Host filesystem state, then finish existing backing commit/seal. Unexecuted ring requests follow current save/restore semantics. Virtqueue drain does not persist guest dirty pages; seal still requires stopped writers and data/directory synchronization. + +Required guest flush/ACK runs while CPUs and its device path still execute. Do not park CPUs or close that path before awaiting a guest reply. Capturing whole-VM dirty state and exporting durable stage are separate acceptance targets. + +Before resume, validate revision, mode and permissions and reconstruct trusted mappings. Unbound FDs retain the original object for future binding; Bound FDs retain backing, flags and offset. Deleted stage objects with live references need independently saved backing, beyond the current namespace. + +Native FD numbers are not persistent restore identifiers. Separate evictable caches from required open references. Fork uses independent upper/reference owners while sharing valid immutable base. Reject unsupported snapshot profiles rather than extending capability from one read-only PoC. + + +### 2.9 Implementation and acceptance {#implementation-plan} + +Researchers working on guest kernels, VMMs, filesystems and performance first validate read-only caching/local open in real VMs, then import metadata, then integrate stage updates and recovery. All milestones remain incomplete. Commands below are future implementation-validation entry points, not tests executed for this proposal. + +Preserve the [Design invariants](#invariants). Record stage scope, protocol mode and accepted profiles independently; read-only results do not accept writable workspaces. + +#### 2.9.1 Milestones and decision gates + +| Stage | Deliverable | Gate for continuation | +|---|---|---| +| M0: contract/baseline | Current behavior, scope, diagnostics, frozen inputs/artifacts | Local operations and necessary host work identified | +| M1: read-only prototype | FUSE cache, connection-wide no-open, handleless data service | Real KVM semantics, complete interpretable counts and bounded reclamation | +| M2: metadata import | Versioned package, kernel importer, complete permissions, bounded paging | Cold/warm correctness, errors/misses/negative-cache semantics | +| M3: stage awareness | Retention, mixed opens, effects, update transport/fences | Identity, aliases, partial failures and interleavings pass | +| M4: lifecycle | Seal, snapshot, restore, fork, exit and compatibility | Consistent RAM/device/Host state, recoverable opens and contained failure | +| M5: performance/integration | Registered A/B, resources, maintenance decision, experimental switch | Useful net benefit and preregistered non-target/resource budgets met | + +M1 can run without guest-kernel changes. It validates primed mechanisms, not packaged metadata or cold-start improvement. M2 first accepts package import. M3/M4 research can be divided after shared semantics settle; jointly review object retention and update draining rather than assuming the other work package owns them. + +##### M0: fix the contract and baseline + +1. Pin Linux x86_64/KVM, guest kernel/configuration, VMM, rootfs, lower, upper/work and tools. Record sources and artifacts. +2. Review open, copy-up, live/frozen baseline, negative lookup, directory cookies, permissions and error timing. Separate current guarantees, prototype scope and proposed changes. +3. Define read-only admission evidence: physically immutable source, stable revision, static grants, retention, complete permission fields and exclusively owned session lifecycle. Read-only mounts/content digests alone are insufficient. +4. In independent diagnostics record INIT, LOOKUP, GETATTR, READDIR, OPEN, READ, FLUSH, RELEASE, FORGET and failures, split into initialization, priming, operation window and teardown. +5. Review existing FUSE hits and O_PATH behavior; discuss reductions only for requests that actually occurred. + +Deliver contract/admission/capability matrices, diagnostic harness, full counts and review questions. Unproved source stability excludes M1; failures do not become performance baselines. + +##### M1: minimal prototype on an independent read-only connection + +1. Use a restricted read-only share, retaining current writable workspace paths. Negotiate/verify no-open per connection; do not return ENOSYS globally on a mixed writable mount. +2. Serve fh=0 READ and supported data requests using trusted inode/object identity. Retain the immutable revision from local open through first read. +3. Reuse bounded read-only backing per object. Define owners for concurrent first access, failure, eviction, FORGET and teardown; repeated opens do not grow host FDs without bound. +4. Verify the full open/fstat/close window. Record first OPEN/FLUSH probes separately; omit FLUSH/control operations only when the profile permits. +5. Validate read, concurrent pread, mmap faults and exec. Valid guest hits create no new data binding. Retain full byte verification. +6. Record resource exhaustion/error timing; review contract changes separately. Reject or exclude unsupported locks, special files and I/O forms. + +Exit gate: real KVM read-only semantics pass, declared steady metadata/open request targets are met, no hidden FLUSH/error access/FD leak, with probing/priming/resources recorded. Expand to import/stage after mechanism benefit; explain absent performance improvement before enlarging scope. + +##### M2: metadata packages in the guest kernel + +1. Choose eager or bounded paged import. Reuse lazy-index construction experience but define guest format, version, checksums, completeness and capabilities independently. +2. Import root/necessary inode and directory records, raw Unix names, symlinks, hardlink groups and content descriptors. Separate unloaded, complete absence and errors. +3. Kernel import checks lengths, offsets, counts, depth, references and cycles. Existing xattr omissions do not establish ACL/security support; initial inputs satisfy an explicit permission profile. +4. Maintain small C patches on the pinned kernel, reusing VFS/FUSE. Prefer Rust for protocol/host parsing; a complete filesystem rewrite is outside initial scope. +5. Give import/fetch explicit locking and budgets. Coalesce page misses outside global-view locks; eviction retains objects needed by FDs/I/O. +6. Measure construction, delivery, parsing, inode/dentry creation, first lookup and steady queries separately. A warm traversal is not accepted package import. + +Exit gate: new-guest cold queries are correct, complete indexes prove negative results, misses/old versions are handled, corrupt packages cannot cause kernel out-of-bounds access, and import/memory meet preregistered budgets. Paged packages explicitly identify first queries still requiring VMM. + +##### M3: stage-aware mixed views + +1. Begin with frozen baseline, exclusive upper/work and VMM-mediated mutations. Exclude external writers, live mutable lower and shared mutable stage. +2. Grant effective retention with stage metadata. Validate local open followed by unlink/rename/recreation/eviction; first read binds the old object rather than reopening a path. +3. Review per-object local-open capability/kernel extension. Connection-wide no-open cannot selectively skip qualifying reads; preserve O_WRONLY/O_RDWR, creation, truncation and current VMM/copy-up behavior. +4. Collect actual pre/post effects on entries, attributes, data, backing, aliases, whiteout/opaque and subtrees. Partial failures still publish actual state. +5. Implement sequences, fences, reliable transport, ACK, gap recovery and bounded backpressure. Start with a whole-view fence, then use diagnostics to refine scope. +6. Reuse host owned-view semantics/test lessons through explicit input/result contracts. Keep private cache state private; host FUSE notifications do not establish guest transport. +7. Accept data caches separately. Retain lower old-open and copied-up new-open semantics, proving shared FUSE page caches do not confuse backing; restrict data capability until proven. +8. Add executable interleavings: late pages, mutation/local-open races, cross-queue publication, ACK waits, lost updates, repeated release, unknown commit and partial failure. + +Exit gate: sequential semantics, concurrent linearization, lifetimes and invalidation coverage pass. Cache faults cannot leave usable stale views. If complexity/fencing cost is excessive, record limits and retain M2 read-only capability. + +##### M4: restoration and runtime lifecycle + +1. Reuse Attempt/supervisor, CPU/device quiescence and filesystem-freeze ownership. Required guest flush/ACK completes while CPUs/device paths remain available. +2. Save protocol mode, base/stage, policy, references and bindings; separate caches from required references. Native FD numbers are not persistent identities. +3. Cover Unbound, Bound, Binding, in-flight I/O and undrained updates. Frozen state is explicit, without threads awaiting the frozen guest. +4. Deleted open stage objects retain restorable backing; unsupported capture explicitly rejects the profile instead of reopening the current name. +5. Fork gets independent upper/reference owners, sharing only valid immutable revision. Parent/child writes, eviction, release and exit remain isolated. +6. Accept whole-VM capture/restore separately from seal/export. Virtqueue drain does not flush guest dirty pages; update ACK does not mean durable commit. +7. Reject unknown required features/incompatible snapshot modes. Upgrades/live switching need separate designs; per-inode fallback cannot bypass connection-wide no-open. + +Exit gate: enabled profiles retain existing seal/snapshot/fork contracts, with reviewable cleanup/restoration. Record KVM, HVF, architectures and mount profiles separately. + +##### M5: performance acceptance and product integration + +Register protocols/budgets before freezing artifacts. After formal performance passes, decide actual experimental-switch integration: disabled by default, capability checks, consistent CLI/Host request/runner spec/restore mode. + +Expand only with scoped net benefit, permitted non-target regression and acceptable resources. Retire routes whose benefit is absent or maintenance exceeds gains, preserving conclusions. Update user performance articles only when user-facing conclusions change. + +#### 2.9.2 Work packages and integration points + +| Work package | Main integration | Deliverable | +|---|---|---| +| Guest-kernel cache | [fw](https://github.com/DeepLink-org/pvisor/blob/main/fw/README.md), FUSE/VFS patches/configuration | Import, local open, page ownership, version checks, kernel budgets | +| VMM filesystem | [virtio-fs](https://github.com/DeepLink-org/pvisor/tree/main/crates/pvisor-vm/src/devices/virtio/fs), [VM API](https://github.com/DeepLink-org/pvisor/blob/main/crates/pvisor-vm/README.md) | Trusted references, handleless service, binding, queues/updates and capture | +| Stage/observation | [OverlayCore](https://github.com/DeepLink-org/pvisor/blob/main/crates/pvisor-overlay-core/README.md), [FilesystemService](https://github.com/DeepLink-org/pvisor/blob/main/crates/pvisor-overlay-core/src/service.rs) | Composition/copy-up, baseline, actual effects, partial failures and seal | +| Host-cache lessons | [OverlayFs](https://github.com/DeepLink-org/pvisor/blob/main/crates/pvisor-overlayfs/README.md) | Owned-view contracts, invalidation/failure cases within adapter boundaries | +| Image/import format | [Shared-image design](../../shared-image-cache-storage.md) | Revision, index, content checks, complete permissions, format/build evidence | +| Guest userspace | [pvisor-guest](https://github.com/DeepLink-org/pvisor/blob/main/crates/pvisor-guest/README.md) | Necessary initialization/ACK, keeping ordinary stat/open in kernel | +| Lifecycle/integration | [runtime](https://github.com/DeepLink-org/pvisor/tree/main/crates/pvisor/src/runtime), [environment snapshot](https://github.com/DeepLink-org/pvisor/tree/main/crates/pvisor/src/environment_snapshot) | Admission/freeze/restore/fork/configuration/Host authority | +| Validation/performance | [semspec](https://github.com/DeepLink-org/pvisor/blob/main/tools/semspec/DESIGN.md), [benchmark](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/README.md) | Candidate cases, fault/interleaving tests, real VM harness, raw evidence/statistics | + +Review shared identity/profile/failure contracts before splitting work. Owners define shared APIs without duplicate DTOs. `pvisor-vm`, `pvisor-overlayfs` and `pvisor-journal` retain current boundaries; unconverted crates receive necessary local changes rather than workspace-wide rewrites. Read each directory README before edits. + +#### 2.9.3 Correctness acceptance matrix + +These are candidate cases, not human-approved semspec. Record profile, initial state, interleavings, result, errors and release behavior; distinguish unit and real VM tests. + +| Case group | Operations/interference | Required verification | +|---|---|---| +| Metadata hits | lookup/stat/lstat/readdir, complete/missing pages | Fields/order/cookies; missing pages do not establish absence | +| Local read-only open | O_RDONLY → fstat → close without data | Retention/permissions; declared complete-window counts | +| Existing local paths | O_PATH, existing dentry/attribute hits | Comparison with legacy behavior; no credit for existing zero requests | +| Side-effect opens | O_CREAT/O_EXCL/O_TRUNC/O_TMPFILE, writable open | Errors/copy-up/preimages/effect timing preserved | +| Permissions/policy | Denied paths/aliases, mode/uid/gid, absent ACL/xattr | No unauthorized success; dynamic policy callbacks preserved | +| First access | Concurrent first reads, failure/close races | One controlled binding, correct bytes, no leaks/deadlocks | +| Valid data hit | read/pread/mmap/exec, EOF/short reads | Correct page version/backing; no forced host data FD | +| Old-FD lifetime | Local open then unlink/rename/recreate/eviction | Old FD retains its object; new open sees the new object | +| Identity versus content | Equal-content files, hardlinks, reused inode numbers | Deduplication does not create aliases; true links stay coherent | +| Copy-up/cache | Lower FD, new upper FD, write/truncate, late pages | Binding semantics preserved; no incompatible page reuse | +| Merged coverage | Whiteout/opaque, directory rename, non-directory ancestors | Lower hiding and complete subtree/alias updates | +| B/A/C and observation | Compose B, baseline A, upper C; live/negative lookup | Apply compares A; admission excludes or preserves host observation | +| Update interleavings | lookup/open versus mutation, cross-queue reordering | Linearization and stale-fill rejection | +| Update faults | Lost updates, sequence gaps, delayed ACK, queue saturation | Affected caches disabled, resync/failure rather than stale access | +| Partial/unknown mutation | Short write, later failure, timeout, INTERRUPT | Actual effects reported; no blind replay or claimed rollback | +| Format/path | Non-UTF-8 names, symlinks, bounds/cycles/corruption | Raw bytes and grants correct; illegal imports rejected | +| Budgets/reclamation | Large trees, FD/reference pressure, FORGET, close/exit | Bounded state/queues, valid references retained, final owner baseline | +| Snapshot/fork | Unbound/Bound/deleted open objects, in-flight work | Original object restored, mode/permissions valid, stage isolation | +| Seal/exit | Dirty pages, pending updates, guest/VMM crash | Durability preserved, bounded failure; drain alone is insufficient | +| Admission rejection | Mutable lower, external writer, special files | Reject before enablement or scoped backend path, no partial mode | + +Controlled interleavings first validate protocol/lifetimes, then real KVM validates syscall/VFS behavior. Host mocks do not accept kernel semantics. + +#### 2.9.4 Validation entry points after implementation + +Choose commands for actual edits, covering target crates, affected consumer compilation and necessary platform checks: + +```bash +just test pvisor-vm +just test pvisor-overlay-core +just test pvisor-overlayfs +just test pvisor-guest +just test pvisor +``` + +For firmware patches/configuration: + +```bash +just fw build +just fw test +``` + +Firmware tests check build/bundle contracts, not real guest filesystems. Record kernel, patches, configuration, VMM/firmware hashes and `libkrunfw.SOURCE`. Missing `/dev/kvm`, `/dev/fuse`, permissions or platform produce explicit failures/skips and uncovered scope. + +Public API edits require external tests through `api`, existing boundary guards and warnings-denied rustdoc. Keep internals private. Follow [Semspec design](https://github.com/DeepLink-org/pvisor/blob/main/tools/semspec/DESIGN.md) for case changes with `just semspec lint docs/src/zh/cases`; runner edits use `just test-semspec`. AI does not approve/revoke, edit real `REVIEWED.toml`/`.approved/`, or weaken claims/checks/xfail. + +#### 2.9.5 Performance protocol + +##### 2.9.5.1 Registry and reproduction + +Formal engineering comparisons belong to [B-FS-ENG](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/README.md#b-fs-eng); request diagnostics belong to [B-FS-DIAG](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/README.md#b-fs-diag). Recheck the [Registry](https://github.com/DeepLink-org/pvisor/blob/main/benchmark.new/registry.csv) and [Filesystem entry](https://github.com/DeepLink-org/pvisor/blob/main/benchmark.new/filesystem/README.md) before implementation/measurement. `filesystem/ab.json` and `diagnostic.json` require concrete single-condition commands; prepare the repeatable harness before measuring. + +Existing paired DAX experiments test a separate mechanism. Independent condition samplers do not automatically form randomized pairs; generate explicit ordering, execution records and same-round statistics. + +Store inputs, harness, frozen sources, artifacts, logs, samples and validation in the experiment's `.data/`; derived CSV/conclusions belong to engineering reports. Research A/B does not update user tables. + +##### 2.9.5.2 Controls and budgets + +Prefer identical binaries, firmware, inputs and configuration with only the real cache mechanism toggled. Required kernel/artifact differences use one frozen baseline plus the tested change, recording identities and differences. + +Fix complete VM-group CPU affinity/quota, guest vCPU/RAM, host memory/swap limits, temporary storage, tool caches, I/O concurrency, writeback, sync, baseline, permissions and seal. Additional guest memory/host-thread budget does not establish cache benefit. + +Preregister target workloads, minimum useful improvement, allowed regressions, import/update cost and memory/FD limits. Count guest metadata/inodes/dentries/pages, host references/FDs, requests/update queues and helper/node costs. Explain actual shared-memory charging without omission or double counting. + +##### 2.9.5.3 Workloads and cache conditions + +| Workload | Question | Required separation | +|---|---|---| +| lookup/stat, negative lookup, traversal | Does metadata import remove requests? | New guest, imported/uninstantiated, steady, updated version | +| open/fstat/close | Does local open work? | Initial probes, priming, steady; all FLUSH/control requests | +| Small-file open/read/close | Does binding/reuse help? | First data access, warm guest, reference/FD reclamation | +| Sequential/concurrent reads, mmap, exec | Data service/page-cache cost | Miss/hit, same FD/reopen, full bytes | +| create/write/truncate/rename | Does stage maintenance regress? | Legacy path, fences/effects, partial failures and seal | +| git/rg/Cargo/npm tools | Does request reduction help tools? | Each tool/full task, input/tool-cache conditions | +| Large directories/multiple VMs | Do import/references reduce density? | Peaks, eviction, quotas, throughput/tails | +| checkpoint/restore/fork | Added lifecycle cost? | Save/restore, bound/unbound opens, private stage | + +Define host and guest cache temperatures separately. New guest does not mean cold host disk; primed data/interpreters affect first access. Cold experiments use isolated reproducible inputs/environments rather than casual drop_caches on a shared machine. + +##### 2.9.5.4 Sampling and validity + +At least 30 formal samples per condition, preregistered seed with randomized balanced pairs, and three recorded warmups per group. Diagnostics, tuning and failed reruns remain separate from the frozen formal batch. + +Every run validates output, all data, unchanged host originals, merged upper and durability boundaries. Retain failures and handling: verification, nonzero exit, changed artifacts/inputs, budget mismatch, OOM or external build/test interference follow the declared protocol. Filtering failures cannot establish zero failures; valid slow runs are retained. + +Preflight confirms instruments/workloads before freezing. New issues stop the batch, with repair/refreeze and retained failure evidence; do not append until significance appears. + +##### 2.9.5.5 Timing and statistics + +Report internal tool time and complete Attempt time separately. The latter includes per-run metadata preparation/delivery, startup/import, tools, seal, requested checkpoint/export and teardown. Offline image construction is separate; first-use/reuse costs include new import rather than moving it outside timing. + +Publish samples, condition medians, same-round paired changes and 95% bootstrap confidence intervals. Examine distributions first. Separated clusters require proportions/conditions and no overall tool ranking. Intervals crossing zero mean no detected difference, not proved equality; 30 samples do not establish reliable P99. + +Report CPU, IO, peak/steady memory, references/FDs, hits and reclamation. Concurrent throughput/density require separate workloads/budgets, beyond single-VM extrapolation. + +##### 2.9.5.6 Independent diagnostics and mechanism evidence + +Disable performance-affecting profiling/tracing in formal timing; run separate diagnostics. Record final counts, sequences and resources for all PID/component/instances. Explain missing/extra records and failures rather than ignoring thread instances. Inclusive spans cannot sum into exact attribution. + +Metadata/open zero-request acceptance applies to the declared steady window, counting FLUSH/RELEASE/FORGET and all other operations. Initialization, priming, import, reclamation and teardown have independent records/costs. Data hits also establish no implicit host FD creation. + +Report stages as semantic correctness, fewer requests, improved target workloads, then complete-task/resource net benefit. Earlier gates cannot replace later ones; metadata/open improvement alone does not solve writeback regression. + +#### 2.9.6 Review and handoff + +Each stage submits small independently reviewable changes with: + +1. Supported profiles, admission evidence, operation/error contracts and exclusions. +2. Identity/versions, linearization, message limits, ownership and recovery. +3. Owning crates/APIs, kernel patches/configuration, formats and compatibility. +4. Correctness passes/failures/skips, real platforms and uncovered scope. +5. Frozen sources/artifacts/inputs, reproducible commands, raw evidence and statistics. +6. Cold/warm lifecycle performance, budgets, maintenance cost and continuation/retirement advice. + +The first handoff is M0 contracts plus the restricted M1 read-only loop. Select later routes using retention/import/update evidence. EROFS remains an independent lower comparison rather than automatically expanding into a full writable-filesystem rewrite. + +### 2.10 Implementation and maintenance rules {#maintenance} + +1. Distinguish existing implementation, proposed protocol, restricted prototype and accepted capability. Message fields/names remain semantic drafts before review. +2. State object retention, permissions, baseline observation and version conditions for every local operation; references cannot rely only on paths. +3. Separate directory, attribute and data versions from backing identity. Lost updates and unknown commits enter explicit recovery/failure paths. +4. Follow [Benchmark rules](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/README.md), locate the registered experiment before measurement, and store raw data in its `.data/` directory. +5. Keep implementation in existing crates and `fw/`; follow directory READMEs and API boundaries. Registration does not create an empty CLI switch. +6. Follow the [Semspec design](https://github.com/DeepLink-org/pvisor/blob/main/tools/semspec/DESIGN.md). Candidate acceptance, test PASS and human approval remain separate; AI does not edit real approval ledgers/snapshots. + +## 3. Alternatives and comparison {#alternatives} + +### 3.1 Filesystem routes + +| Route | Expected mechanism benefit | Cost and boundaries | Choice | +|---|---|---|---| +| Extend existing FUSE TTL only | Fewer repeated cached metadata requests | Does not import cold metadata or directly remove ordinary OPEN; requires stability/invalidation | Low-cost control | +| FUSE cache + metadata import + deferred binding | More local metadata operations and ordinary read-only opens | Retention, complete permissions, stage updates and page ownership | Recommended route, independent read-only prototype first | +| EROFS immutable lower + host stage | Native read-only directory/inode/open path | Image/block service, mixed view, device snapshots and stage coherence | Independent lower comparison | +| Complete guest upper/native writable filesystem + stage export | Potentially more guest-local writes/namespace operations | Changes preimages, authoritative stage, seal/apply and restore contracts; broader engineering scope | Separate architecture evaluation | + +These are mechanism hypotheses requiring validation. Compare identical inputs, permissions, budgets and lifecycle boundaries; read-only lower results do not establish writable-workspace benefit. + +#### 3.1.1 First extend the existing FUSE path + +Linux FUSE has connection-wide no-open, allowing subsequent open/release in the kernel. See the [Linux 6.12 implementation](https://github.com/torvalds/linux/blob/v6.12/fs/fuse/file.c). It fits an independent read-only prototype. Mixed writable views require per-object/operation capability or kernel extensions to skip only qualifying opens; preserve existing writable open. + +First use known inputs and priming to validate local operations and handleless data service, including priming cost. Then add paged metadata import. A primed prototype does not establish package import or improved cold startup. + +#### 3.1.2 EROFS as an optional lower + +EROFS supplies guest-native read-only inode/directory/content layout and is an alternative immutable lower. See [EROFS documentation](https://docs.kernel.org/filesystems/erofs.html). Existing host mechanisms still own stage. Format, data-block service and mixed-view integration need adaptation; EROFS alone does not supply remote lazy access or stage coherence. + +Current x86 firmware enables OverlayFS/tmpfs/virtio-blk but not EROFS/ext4; VMM blk is also outside default features. See [Firmware configuration](https://github.com/DeepLink-org/pvisor/blob/main/fw/config-libkrunfw_x86_64) and [VMM features](https://github.com/DeepLink-org/pvisor/blob/main/crates/pvisor-vm/Cargo.toml). Device and snapshot lifecycle require separate review. + +### 3.2 Eager versus paged import + +| Strategy | Advantage | Cost and conditions | +|---|---|---| +| Eager import | Local first metadata queries after import; complete absence proofs | Startup absorbs construction/delivery/kernel-object cost, including unvisited trees | +| Demand paging | Less metadata for small working sets and bounded allocation | First misses call VMM; completeness, coalescing and stale-page rejection are more complex | +| Root/necessary pages first, then paging/bounded prefetch | Fast entry with cache expansion as needed | Distinguish delivered, validated and instantiated state; bound prefetch/eviction | + +Start with bounded root/necessary pages and retain eager import as a control. Choose using cold start, first lookup, traversal, peak memory and multi-VM density rather than primed query latency alone. + +### 3.3 C, Rust and API ownership + +| Location | Preferred route | Reason and cost | +|---|---|---| +| Guest FUSE/VFS extension | Small C patches | Reuse kernel interfaces/caches; explicitly maintain lifetimes and input bounds | +| Host protocol/objects and guest-userspace control | Rust | Existing crates/ownership/API model; explicit protocol/object budgets remain necessary | +| Custom Rust kernel filesystem | Separate research package | Safe filesystem abstractions, no_std tools and pinned-kernel adaptation add initial maintenance cost | + +Use Rust for new host object management, data service, stage updates and restoration; guest-userspace control remains in `pvisor-guest`. Prefer small C patches for necessary guest FUSE/VFS extensions, reusing kernel caches. + +Firmware pins [Linux 6.12.109](https://github.com/DeepLink-org/pvisor/blob/main/fw/Makefile). That series lacks complete filesystem abstractions in its Rust kernel crate; kernel Rust requires safe abstractions and no_std tooling. See the [Series source](https://github.com/torvalds/linux/blob/v6.12/rust/kernel/lib.rs) and [Kernel Rust guidance](https://www.kernel.org/doc/html/v6.12/rust/general-information.html). A custom Rust kernel module separately budgets interfaces, backports and cross-architecture validation. + +Cross-crate contracts belong to owner APIs; avoid equivalent DTOs or exposure of private VMM/Core state. Migrate one boundary at a time, with uniform declarations and explicit Unsupported errors. Kernel patches follow Linux licensing and corresponding-source delivery. + +### 3.4 Budgets and net benefit + +Budget metadata pages, guest inodes/dentries/open state, retained objects, host FDs, content pages, in-flight I/O and update queues. Existing node/cache/helper costs count toward the machine budget. Shared metadata/content does not make per-VM VFS/page-cache state free. + +Wait for metadata/data misses outside global-view locks. Coalesce per-object/block requests with bounded prefetch. Update overload suspends admission or resynchronizes rather than accumulating without bound. Report eviction/reclamation costs separately. + +Expected early benefit is metadata-heavy traversal and open followed only by attribute checks. New-file WRITE/CREATE still calls VMM; this proposal does not establish that existing small-file write regression is solved. First cold reads still depend on data service, copy-up, hashing and durability. + +```text +Full cost = metadata preparation/delivery + VM startup/import + tools + + seal + requested checkpoint/export + teardown +Candidate net benefit = backend work removed - added import/maintenance/reclaim/restore cost +``` + +Report offline shared-image construction separately from per-Attempt import, including first-use and reuse costs. Formal A/B compares total latency, CPU, memory, IO and throughput; diagnostics explain request reductions. Register benefit/tolerance gates before measurement and evaluate confidence intervals with complete correctness. + +### 3.5 Research questions and decision gates + +| Question | Required evidence | +|---|---| +| Eager metadata or demand paging? | Cold start, first lookup, traversal, peak memory and multi-VM density | +| Which open/close requests disappear on read-only connections? | Complete INIT/LOOKUP/GETATTR/OPEN/FLUSH/RELEASE/FORGET counts and semantics | +| How to retain unbound stage objects? | Unlink/rename/recreation/eviction interleavings and FD/reference bounds | +| How to update mixed views precisely? | Alias/subtree coverage, reordered replies, partial failures, linearization/backpressure | +| Which kernel extensions are needed? | Minimal pinned-kernel patch, build/restore compatibility and maintenance cost | +| FUSE cache or EROFS? | Controlled engineering A/B with identical inputs, permissions, budgets and cold/warm states | +| Does product lifecycle retain net benefit? | End-to-end startup/seal/save/restore and resource results | + +Each question produces reviewable code, tests and raw evidence through the [Implementation work packages](#implementation-plan). Decide stage extensions and product scope after read-only mechanisms succeed. diff --git a/docs/src/en/design/experimental/index.md b/docs/src/en/design/experimental/index.md new file mode 100644 index 000000000..a90505d32 --- /dev/null +++ b/docs/src/en/design/experimental/index.md @@ -0,0 +1,51 @@ +# pVisor experimental features + +Register implementable, verifiable capabilities that support higher execution density and lower supervision cost. Designs, implementation plans and validation records live here. Feature code stays in existing crates, and measurements stay with the benchmark registry. + +## Capability registry + +| ID | Capability | Status | Goal | +|---|---|---|---| +| EXP-001 | [vCPU idle observation and automatic RAM offload](vcpu-idle-offload/index.md) | **Observation prototype stage** (M0; KVM measured, HVF not validated) | Find VM waiting windows without changing the Agent or guest kernel; offload and restore on host events/deadlines | +| EXP-002 | [Guest kernel filesystem cache and deferred backend binding](guest-stage-cache/index.md) | **Design stage** (RFC, not implemented) | Complete valid cached metadata operations and ordinary read-only open locally; retain VMM authority over stage, data supply and durability | + +## CLI feature entry points + +```bash +pvisor feature +pvisor feature list --json +pvisor run --executor vm --feature workload-aware-memory-offloading -- /bin/sleep 10 +``` + +The runtime registry in `crates/pvisor/src/features.rs` defines stable names, stages, defaults and descriptions. `--feature NAME` accepts repeated or comma-separated values. Features are disabled by default; unknown names are rejected. Arguments after `--` belong to the workload. Query output `enabled` reflects registry defaults plus CLI selections; it does not load Run configuration or validate execution. + +The first registered feature is `workload-aware-memory-offloading`, stage `experimental`, default `false`. It enables EXP-001 M0 observation. Automatic offload is not enabled. The effective executor must be VM; supported integration platforms are Linux x86_64/KVM and Apple Silicon macOS/HVF, with HVF runtime validation outstanding. Configuration accepts `workload-aware-memory-offloading = true` under `[features]`; CLI enable overrides configuration false, while omission preserves configuration. There is no CLI disable or persistent feature-enable operation. Existing cold RAM parameters remain outside this migration. + +Feature selection travels explicitly through Host requests, RunConfig and runner spec. The native runner calls `VcpuObservationControl`. New experimental features enter the registry after real integration and support checks; empty switches are not registered. See the [CLI reference](../../reference/cli.md#features). + +## Stages and promotion + +- **Design stage:** defines the problem, existing foundations, gaps, scope, failures and acceptance gates; implementation remains pending. +- **Observation prototype stage:** collects opportunities and rejection reasons without automatically changing execution state. +- **Restricted PoC:** disabled by default, explicitly enabled, with a complete loop for named platforms/profiles. +- **Experimentally usable:** passes correctness and fault gates, with traceable artifacts and declared scope. +- **Stable or retired:** reviewed for API, documentation, delivery and maintenance; retired if net benefit is absent or maintenance cost is excessive. + +Record each platform separately. Promotion includes source/artifact identity, commands, passes, failures, skips and uncovered cases; one platform's PASS does not establish another's support. + +## Directory rules + +1. Give each capability a stable ID and descriptive directory. Its single `index.md` combines current status, design, implementation plans and validation records. +2. Separate design from measurement, implemented from proposed interfaces, and heuristics from deterministic guarantees. +3. Keep features disabled by default. Unvalidated options do not enter official CLI examples or claims of delivered capability. +4. Reuse Runtime, control owners, CPU/device quiescence, storage and evidence models; preserve existing terminal-state ownership. +5. Follow `crates/pvisor-vm/README.md` for public VM APIs. State backend scope and distinguish KVM/HVF and mapping profiles. +6. Read directory READMEs before implementation. Follow `benchmark/README.md` and register or explicitly reuse the measurement protocol; store raw data in `.data/`. Manual-operation results do not establish automatic-policy benefit. +7. Follow `tools/semspec/DESIGN.md`; keep PASS separate from human approval. AI does not edit real approval ledgers/snapshots or weaken claims/checks/xfail. +8. Keep credentials, user trajectories, VM images and build artifacts outside this directory. + +## First capability + +EXP-001 first uses a host-driven route: backends expose vCPU observations, and the host aggregates waiting windows and arbitrates resource actions. Guest PV cooperation remains an optional extension. + +EXP-001 has a default-disabled M0 observation API, backend integration points and real VM experiments. KVM measurements found no reliable waiting signal; automatic offload and wake/deadline integration remain pending. See [Initial implementation and validation](vcpu-idle-offload/index.md#validation). diff --git a/docs/src/en/design/experimental/vcpu-idle-offload/index.md b/docs/src/en/design/experimental/vcpu-idle-offload/index.md new file mode 100644 index 000000000..bc1fca517 --- /dev/null +++ b/docs/src/en/design/experimental/vcpu-idle-offload/index.md @@ -0,0 +1,306 @@ +# EXP-001 (observation prototype): vCPU idle observation and automatic RAM offload + +| Item | Current status | +|---|---| +| Stage | **Observation prototype stage (M0); not all exit gates passed** | +| Main goal | Reduce residency during VM waiting windows and restore automatically on host events | +| Default behavior | Existing behavior retained; no automatic-offload switch | +| Preferred route | pVisor vCPU/host observation without Agent or guest-kernel changes | +| HVF | Wait/timeout observation and all-vCPU aggregation integrated; compilation/runtime validation pending | +| KVM | Real VM API and experiments validated; KVM_RUN remains Unknown, with no reliable idle signal | +| Foundations | Pause/resume, RAM offload, device memory gate and Host backing commit | +| Main additions | Waiting-window observation, all-vCPU aggregation, policy owner, wake/deadline and lost-wakeup prevention | +| Pending | Complete idle sources, wake/deadline, automatic offload, HVF validation and offload-benefit experiments | + +An Agent waiting for a model response, input or an external tool may retain resident VM RAM without useful execution. Short waits allow CPU sleep; longer waiting windows can justify offload to reduce memory-time cost. Resume continues the same live Attempt rather than creating a persistent checkpoint or restarting it. + +The host decides whether to offload using window length, memory pressure, device safety, restoration budget and supported profiles. A blocked thread alone does not trigger immediate offload. + +## 1. Problem statement {#problem} + +### 1.1 Waiting windows and resident memory + +An Agent waiting for a model, network or external tool may retain resident VM RAM without useful computation. CPU sleep reduces execution cost; memory reclamation additionally needs a sufficiently long window, safe parking and timely restoration of the same Attempt. + +Three questions guide the design: identify useful waiting across relevant vCPUs, restore reliably on network/control/timer events, and establish whether memory-time savings cover observation, commit and restore costs. + +### 1.2 Backend foundations and observation gaps + +| Backend | Existing integration | Proposed route | Limits | +|---|---|---|---| +| HVF | WaitForEvent/Timeout and wait channel in `vmm/macos/vstate.rs`; virtual timer read in `hvf/mod.rs` | Wait enter/leave sequence, available deadline, independent owner aggregating all vCPUs | WFE may be synchronization; other timer/IRQ sources need validation, beyond a single register | +| KVM | vCPU threads and `vmm/linux/vstate.rs` run loop | First observe candidate CPU-time/scheduling windows; investigate optional host KVM events | An unreturned KVM_RUN may be executing; low CPU may mean host preemption; Hlt currently means Stopped and retains that semantics | + +KVM tracepoint/eBPF or userspace halt handling are candidate routes. Kernel interfaces, privileges, overhead and compatibility remain unverified. The first stage changes neither irqchip nor global sysctl and requires no privileged tracing. Ambiguous states remain Unknown and reject automatic actions. + +Current real KVM M0 experiments found no reliable waiting signal; HVF wait integration has no real-hardware validation. [Validation](#validation) preserves results and uncovered scope separately. Automatic-policy wake/deadline integration remains pending. + +### 1.3 Goals and scope + +Discover waiting windows from pVisor vCPU and host events, offload RAM automatically, and restore when execution is needed, without changing the Agent or guest kernel. Observation determines when offload may help; existing CPU/device quiescence and backing commit determine how to offload safely. + +Excluded scope includes proof that all Linux processes are blocked, general checkpoint/migration, frozen guest time, arbitrary devices or every memory-mechanism combination, and promised production-density multipliers. + +## 2. Core design {#core-design} + +### 2.1 Architecture, ownership and integration + +```mermaid +flowchart TD + Backend[KVM / HVF vCPU observation] --> Aggregate[All-vCPU windows and epochs] + Events[Host network / control / deadline] --> Wake[Independent wake latch] + Aggregate --> Owner[Host policy owner] + Wake --> Owner + Owner --> Control[Existing pause/offload/resume control] + Control --> Boundary[CPU park / device gate drain] + Boundary --> Commit[RAM writeback / Host backing commit] + Commit --> Park[Offloaded] + Park --> Owner +``` + +- `pvisor-vm` owns backend observation and CPU/device control. Public contracts belong in `api.rs`; the experiment does not expose private VMM modules. +- `pvisor` owns policy, Host backing commit, Attempt lifecycle, configuration and explainable events. +- Event sources such as OverlayNet notify wake before guest RAM access or potentially blocking channels. +- One control owner serializes automatic policy with manual pause/resume/checkpoint/cancel; existing completion ownership remains intact. + +Observation states, snapshots and `VcpuObservationControl` are implemented in `api.rs`; see [Validation](#validation). The Host automatic-policy owner, wake/deadline and automatic-offload state machine remain proposed. + +### 2.2 Aggregation and candidate windows + +M0 defines Executing, HandlingExit, WaitingForEvent, HostDescheduled, ManualPaused, Stopped and Unknown. Candidates require every online vCPU to wait and policy conditions to hold. HostDescheduled is not guest idle; waiting states are opportunities, not Linux runqueue truth. + +### 2.3 Wake, deadlines and commit order + +Required information includes Attempt identity, CPU topology generation, observation sequence, idle epoch, pending wake sequence, park reason, available deadline and clock domain. + +Latch wake throughout Candidate/Quiescing/Offloading/Offloaded; preserve already-pending events. Revalidate observation and event sequences after entering the stop boundary. Wake arriving during Host backing commit waits until commit completes, then restores immediately; RAM writers do not reopen earlier. + +Network data, EOF, errors and connection completion are semantic wake sources. Socket writable/ordinary ACK events do not unconditionally trigger wake. Wake precedes virtqueue/memory-gate access and is handled by an independent owner. Device threads do not synchronously resume while holding queue/proxy locks. Recheck pending events/rings after restoration rather than relying only on fresh EPOLLET edges. + +The earliest valid timer deadline must cover the profile, with explicit guest/host time conversion and advance margin. Unknown or short deadlines skip offload. A watchdog provides PoC fault tolerance, not exact timer support. Existing pause retains wall time; freezing time does not hide expired timers. + +### 2.4 States and failures + +```mermaid +stateDiagram-v2 + Observing --> Candidate: All-vCPU waiting window + Candidate --> Observing: Expired or policy rejected + Candidate --> Quiescing: Wake registered and profile supported + Quiescing --> Offloading: Safety boundary revalidated + Offloading --> Offloaded: Runner and Host commit complete + Offloading --> Resuming: Process latched wake after commit + Offloaded --> Resuming: Event or deadline + Resuming --> Observing: Gate and CPU restored + Quiescing --> Failed: Transition failed + Offloading --> Failed: Writeback or commit unknown + Resuming --> Failed: Resume failed +``` + +Busy, unsupported and short-window results are normal rejections before a destructive transition. Existing drain timeout calls fail_control; a poisoned runner cannot continue as though policy merely rejected offload. Cancelling an initiated transition requires proof of restored state before returning to Observing. + +Automatic wake releases only the automatic park owned by that owner/epoch. Manual pause, snapshot freeze and failure states preserve their control semantics. The guest cannot forge Host authority or bypass limits. Misclassification can harm performance; lost wake, missing timers or skipped commit can break correctness. + +### 2.5 Initial profiles + +- Ordinary writable shared file-backed RAM, raw offload, same live Attempt. +- One vCPU first; expand after SMP aggregation/epoch acceptance. +- Network wake initially uses existing OverlayNet IPv4 TCP, with explicit timer/control-wake support. +- No in-flight virtio-fs requests; skip slow I/O and preserve existing RAM leases during discard. +- Exclude cold pager/pool, restored private COW, unguarded devices and unknown wake sources. +- Remain observe-only until backend deadline/wake is validated; clearer HVF integration points do not establish macOS-wide support. + + +### 2.6 Current constraints {#constraints} + +- Guest-kernel notifications are not required initially; PV notifications remain a possible precision improvement. +- Observation supplies opportunity signals rather than proof that every Linux process is blocked. Safe offload still requires CPU/device quiescence and backing commit. +- Initial scope is ordinary file-backed RAM. Cold pager/pool and whole-VM offload are mutually exclusive; private restored COW does not support this discard route. +- Platforms/devices with incomplete deadline/wake support remain observe-only. +- Automatic wake does not release manual pause, snapshot freeze or failure stops, or grant guest Host-control authority. + +Existing manual-offload data supports baseline cost analysis. Automatic policy has no established production-density benefit; observation results do not authorize automatic offload, and a design does not establish acceptance. + +### 2.7 Implementation and acceptance {#implementation-plan} + +> M0 observation API, backend integration and experiments are implemented. Automatic idle/offload remains pending. Completed and unvalidated work are recorded separately in [Validation](#validation); no fixed schedule is promised. + +#### M0: baseline and observe-only + +- [ ] Review current backend idle, IRQ, timer, offload, device gate and Host commit paths. +- [x] Define cross-backend observations and Unknown semantics within the VM public API boundary. +- [x] Register vCPU identity/topology generation, enter/exit sequence, waiting windows and rejection reasons. +- [ ] Collect HVF wait/timeout observations; validate low-cost KVM candidates and distinguish guest wait from host preemption. +- [x] Integrate one VM-local collector that records without pause/offload; the Host policy owner remains pending. +- [ ] Validate cross-platform overhead, exit cleanup and capacity. KVM bounded sampling and engineering A/B are complete; HVF remains unvalidated. + +Exit gate: explain real workload waiting windows without interpreting absent KVM exits, low CPU or manual pause as precise idle. Platforms lacking reliable information return Unknown. + +#### M1: controlled wake loop + +- [ ] Add an independent wake latch covering candidate, drain, offload and park windows. +- [ ] Register network data/EOF/errors/connection completion before RAM access or bounded-send waits. +- [ ] Define deadline clock domain, restoration margin and timer cancel/reprogram rules. +- [ ] Deliver wake to the existing Host control owner, preserving backing-commit order after runner ACK. +- [ ] Arbitrate automatic, manual, snapshot, cancel and failure states. +- [ ] Recheck queue/backend after resume, retaining consumed edges. + +Exit gate: fault injection and controlled interleavings prove no lost wake; profiles with unknown deadlines reject automatic deep parking. Mock passes still require real VM validation. + +#### M2: restricted single-vCPU raw-offload PoC + +- [ ] Keep disabled by default; name and connect proposed enable configuration only after implementation review. +- [ ] Check ordinary file-backed RAM support; exclude pager/pool/private COW. +- [ ] Reject in-flight filesystem I/O and unguarded devices early; real drain failure is not routine policy rejection. +- [ ] Define initial grace, deadline guard, reentry cooldown and wake-frequency suppression. +- [ ] Restore the same Attempt under real delayed-network, timer and control wake, verifying all content. +- [ ] Preserve fail-stop/unknown behavior for offload/store/restore failures and actual terminal state. + +Exit gate: correctness, authority and lifecycle negative controls pass. Mark only validated platforms/profiles as restricted PoC. + +#### M3: SMP and additional events + +- [ ] Aggregate all online vCPUs; test cross-CPU wake/IPI and migration/topology races. +- [ ] Keep RAM resident when one CPU runs; do not reuse old epochs after exit/restore. +- [ ] Expand to vsock, terminals and signals without synchronous transitions under proxy/queue locks. +- [ ] Cover multiple connections, streaming, backpressure, restore storms and deadline races. + +Exit gate: record each support-matrix cell. SMP mocks do not replace real guest validation. + +#### M4: benefit experiments and continuation decision + +- [ ] Register the automatic-policy question, role, runner and statistics under `benchmark/README.md`. +- [ ] Use identical tasks/hardware/budgets/artifacts, randomized paired on/off runs across rounds; manual policy can be a separate control. +- [ ] Separate waiting, compute, short blocking, streaming, timer-heavy and restore-storm workloads. +- [ ] Measure memory-time integral, cgroup/PSS/cache, complete-group peaks, CPU/IO, restore and full-task latency, failures and useful throughput. +- [ ] Explain observer/global-scanner costs outside the measured group; low PSS or configured capacity does not establish production density. +- [ ] Retain failures, interference and provenance; correctness failures do not count as benefit and samples are not selected for appearance. + +Promotion gate: net resource benefit on explicit workloads while meeting QoS, with reproducible tests and explainable actions. Adjust or retire policies when thrash, CPU/IO or restore cost offsets savings. + +#### Suggested code ownership + +| Owner | Responsibilities | Validation | +|---|---|---| +| `crates/pvisor-vm/src/api.rs` and private backends | Observation contracts, wait/deadline, CPU control and device gate | API boundary, states/epochs, timers and RAM correctness | +| `crates/pvisor/src/executor/vm/` | Observation/control channels, backing commit and profiles | Drain/commit/wake concurrency and fail-stop | +| `crates/pvisor/src/runtime/` | Attempt arbitration, automatic policy and events | Authority preserved across manual/automatic/snapshot/cancel | +| `crates/pvisor-overlaynet/src/vm.rs` and later device sources | Wake-before-RAM and external socket lifecycle | Data/EOF/errors/backpressure/already-pending events | +| Existing crate and product integration tests | Mock interleavings and real VM validation | Separate platform passes, failures and skips | +| `benchmark/` | Registered policy experiments | Independent data/receipts/samples outside this directory | + +These responsibilities do not predefine new files or dependencies. Implementation follows the existing code structure. + +#### Required negative controls + +| Scenario | Expected behavior | +|---|---| +| Compute busy, host CPU congestion | Low CPU does not establish guest wait; misclassification still preserves the safety boundary | +| Sleep/futex deadline, timer cancellation | Timers retained, time not frozen; unknown deadlines reject offload | +| Wake in any offload window | Latched and eventually delivered without requiring a repeated guest event | +| Slow filesystem I/O retaining RAM lease | Skipped early; actual transition failure is fail-stop | +| Manual pause or snapshot freeze | Network/automatic deadline cannot release it | +| Network arrival during backing commit | Resume after commit; RAM writers do not open early | +| KVM Hlt/guest shutdown | Shutdown semantics preserved | +| Cancel/exit while offloaded | Correct cleanup and terminal state; no permanently unwakeable owner | +| Unsupported pager/pool/COW | Explicit rejection without runtime-state changes | + +#### Validation record template + +Record date, platform, stage/profile, source SHA and dirty manifest, binary/firmware/rootfs identity, commands, results, failures/skips, uncovered cases, evidence paths and conclusion. See [Initial implementation and validation](#validation) and the [KVM engineering report](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/pvisor/vcpu_idle_report.md). + +Use targeted `just test PACKAGE` by default; real VM/special runners follow existing contracts. PASS and semantic approval stay separate; AI does not edit real ledgers. Update user documentation and formal support matrices after successful experimental implementation. + +### 2.8 Initial implementation and validation {#validation} + +#### Current conclusion + +**M0 observe-only observation and real VM experiments are delivered; the automatic-offload loop remains pending.** KVM measurements show current userspace integration points cannot identify waiting: sleep/busy/short-timer/SMP observations are almost entirely Unknown. Low CPU, unreturned KVM_RUN and shutdown HLT retain their existing meaning rather than being reclassified as idle to complete the policy. + +HVF wait paths are integrated, but this Linux workspace has no actual macOS compilation or runtime validation. M0 has outstanding exit gates; M1/M2 are unimplemented, with no new automatic-offload CLI switch. + +#### Actual code and API + +- `crates/pvisor-vm/src/api.rs`: `VcpuObservationControl`, states/rejection reasons, per-CPU records and aggregate snapshots; public contracts are concentrated here. +- `crates/pvisor-vm/src/vcpu_observation.rs`: default-disabled per-VM collector, no background threads/event queue, O(vCPU count) memory, session/sequence/topology generation/idle epoch, accumulated waits and rejection reasons. +- `crates/pvisor-vm/src/vmm/{builder,mod}.rs`: collector lifecycle and CPU injection. +- `crates/pvisor-vm/src/vmm/{linux,macos}/vstate.rs`: backend observation; manual park and stop retain existing control semantics. +- `crates/pvisor/examples/vm_vcpu_observe.rs`: real SDK VM, bounded sampling and guest correctness workload. + +```rust +use pvisor_vm::api::VcpuObservationControl; + +handle.set_vcpu_observation(true)?; +let snapshot = handle.vcpu_observation()?; +handle.set_vcpu_observation(false)?; +``` + +This API does not pause/resume/offload. Enabling during execution does not kick CPUs; state remains conservatively Unknown until the next observation point. KVM Executing marks entry, KVM_RUN stays Unknown, and return becomes HandlingExit; HLT remains stopped. HVF WaitingForEvent represents a wait/select window that may immediately consume ready events rather than precise sleep or Linux runqueue state. + +Configured CPU online state reflects backend lifecycle, not guest Linux CPU-online. Stopped/unstarted slots do not count as all-CPU idle. Even a valid all-waiting result rejects automatic action with WakeDeadlineUnavailable. + +#### Experiments and results + +Registered as **B-VCPU-IDLE-ENG**, engineering A/B, outside user benchmark articles. See the [Protocol](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/pvisor/vcpu_idle_plan.md), [Report](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/pvisor/vcpu_idle_report.md) and [Reproduction entry](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/pvisor/README.md#vcpu-observation-m0). + +Matrix: sleep, busy and 2 ms short timers with 1/2 vCPU, plus a 2-vCPU case with only CPU 0 busy. Each fresh VM has 256 MiB RAM, a 3-second workload and 10 ms sampling. Observer off/on uses seeded randomized pairs, five pairs per case, 70 VMs total. Guest multiprocessing and affinity checks prevent GIL serialization; each worker verifies SHA-256, execution interval, exit and output. + +| Item | Actual result | +|---|---| +| Original preflight | 0/14 valid; all passes/failures retained; Python 3.14 forkserver bootstrap error | +| Independent preflight after explicit-fork fix | 14/14 valid, 0 failures | +| Formal batch | 70/70 valid, 0 failures/timeouts, 35 complete pairs | +| Periodic sampling | 10,613 snapshots, 16,666 CPU-state records | +| Unknown | 16,657 records, 99.946% | +| WaitingForEvent / all-waiting epochs | 0 / 0 | +| Paired task wall-time difference | 95% CI includes 0 for every case; no difference detected | +| Median paired VM CPU difference | +48.253–+88.676 ms; 2-vCPU busy CI includes 0, others positive | +| Snapshot plus JSON Value construction time | Descriptive median 24.566 µs; excludes JSONL writing | + +Overhead includes collector, sampler and evidence I/O rather than pure integration-point cost. GNU debug, one host and n=5 per case, without cgroup limits or continuous interference detection, establish no release, P95/P99, production density or machine-wide saving. Offload was not called; no reclamation or restore-latency result exists. + +#### Validation and artifact boundaries + +- Final `just test pvisor-vm`: 345 passed, 8 skipped, including real KVM busy-loop/OUT/HLT special tests and SMP/session/concurrent-sampling/exit/capacity/statistics regressions. +- Python runner: 16 unit tests passed, covering explicit fork with default forkserver, timeout, provenance, topology and output rejection. +- Example debug build and formatting checks passed; Linux VM library Clippy passed. Tests Clippy was blocked by existing dependency warnings in pvisor; unrelated code was retained. +- macOS/HVF compilation and runtime remain unvalidated; lost-wakeup, complete timer/deadline and automatic restoration are untested. + +The measurement binds frozen binary SHA-256 `af6830774dfd9fef042893c7491c38077377cf7032afeb1add3af433ca83f239` and report SHA-256 `036094ed87fcc5ffd7d7c73d28cd932350bbd16499fae6b8ae28f4655e5bf339`. Full provenance/assets are in the report; raw evidence remains at `benchmark/pvisor/.data/vcpu-m0-fork-pairs5-20261007/`. + +**Two issues were fixed after measurement: snapshot timestamps consistent under the lock, and per-CPU wait accounting truncated by disable.** Final unit tests cover these fixes, but the frozen binary was not replaced and real A/B was not rerun. Results above belong to the earlier frozen version. Concurrent external development means old receipts reject current sources; future measurement needs a new build/cohort rather than edited historical results. + +#### Gates and experiments for the next version + +1. **KVM signal first:** investigate host KVM halt/wake interfaces, VM/vCPU correlation, preemption separation and bounded privilege/overhead. Preserve irqchip, HLT shutdown and global sysctl initially; remain Unknown without a reliable source. +2. **HVF on real hardware:** validate wait integration, short timers, single-busy-CPU SMP and pending events under the same matrix. Add host-congestion controls and lower-cost aggregate-only sampling; a wait window does not establish a future deadline. +3. **M1 correctness:** inject wake-before-RAM events during candidate/drain/commit/park; cover timer reprogram/cancel, manual pause, cancel and failure. Prove events retained and authority preserved. +4. **M2/M4 reclamation A/B:** after the loop passes, compare off/on for ordinary file-backed raw RAM, one vCPU and explicit network/timer profile. Register/complete an independent protocol; measure waits of 0.1/1/5/30/60 seconds, compute/streaming/short timers/restore storms and repeated/random working sets. Fix group budgets; include cgroup/PSS/cache/helper, memory-time integral, CPU/IO/peaks, full-task latency and valid results. These experiments remain unmeasured, with no assumed saving multiplier. + +The experiment exposed the Linux observation gap before automatic-policy implementation. It supplies no benefit claim for an automatic-offload policy that remains pending. + +## 3. Alternatives and comparison {#alternatives} + +### 3.1 Waiting-observation routes + +| Route | Advantage | Cost and limits | Choice | +|---|---|---|---| +| Existing userspace vCPU/host points | Reuse VMM without Agent/guest-kernel changes | HVF waits need timer/IRQ validation; KVM_RUN does not establish waiting | M0 route, observe-only with incomplete information | +| Host KVM observation extension | Potential halt/wake sources | Interfaces, permissions, VM/vCPU correlation, preemption separation and overhead unvalidated | Next Linux research focus | +| Guest PV cooperation | Potentially clearer waits/deadlines | Guest protocol/kernel adaptation; Host retains authority and safety control | Optional, independently accepted extension | +| Low-CPU/fixed-time heuristics | Simple candidate discovery | Does not prove guest waiting or supply complete wake/deadline | Insufficient standalone offload basis | + +Every route uses existing quiescence and backing-commit boundaries. Observation changes opportunity precision/cost while retaining safe park/wake protocols. + +### 3.2 Policy and full cost + +Start with explainable grace, deadline guard, minimum re-park interval and thrash suppression, then investigate pressure-aware policy. Experiments determine parameters; one manual-offload result does not set production defaults. + +The target is reduced memory-time integral while preserving task QoS. Include complete cgroup/PSS/cache/helper costs, CPU/IO, peaks, restore latency, throughput, failures and valid results. Short blocking uses CPU sleep; longer windows reclaim RAM; longer parks may later justify compressed backing. + +### 3.3 Implementation choices and gates + +First establish whether host-only observation is useful. Guest PV remains optional; precision ambitions should not indefinitely expand the first version. + +Retain the host-driven observe-only route first. After reliable observation and wake/deadline pass, validate single-vCPU raw offload with ordinary file-backed RAM. Compressed backing, additional devices and SMP expand under separate benefit/correctness gates. + +Automatic policy requires reliable signals or explicit rejection, no lost wake, complete timer support and real VM restoration. Then compare memory-time, CPU/IO, full-task latency and useful throughput under identical tasks/budgets; adjust or retire routes when costs offset benefit. See [Implementation](#implementation-plan) and existing [Validation](#validation). diff --git a/docs/src/en/design/filesystem-evidence-overview.csv b/docs/src/en/design/filesystem-evidence-overview.csv new file mode 100644 index 000000000..0d56cea90 --- /dev/null +++ b/docs/src/en/design/filesystem-evidence-overview.csv @@ -0,0 +1,12 @@ +benchmark_id,batch,backend,metric,reference,condition,n,unit,candidate_p50_ms,change_percent,ci95_low_percent,ci95_high_percent,interpretation,role,reference_p50_ms,source_csv,source_csv_sha256,evidence_section +B-FS-ENG,fr10fixf3,pvisor-staged,completion_ms,baseline,candidate,30,ms,1774.147928,-2.862063347700472,-3.5741408369402943,-1.2217776782787793,faster,engineering A/B,1826.4212615000001,host-idle-dispatch-summary.csv,90bb16084577d55021246c32f0e3f33054be0cb7e31c3c51c5bda9ecf5949177,host-idle-dispatch +B-FS-ENG,fr10vmfixf,pvisor-vm,completion_ms,baseline,candidate,30,ms,4592.3552565,-3.153046526049663,-6.139649356853594,-1.2345936261486057,faster,engineering A/B,4741.868579,vm-completion-dispatch-summary.csv,4f43be23fb31b82f643745f69c31a4c98148f7ca068b107e9b0aaf20a30ecc20,vm-completion-dispatch +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,worker_ms,baseline,candidate,30,ms,3749.0687745,4.776000623059518,2.9590088269350274,5.923245553099513,slower,engineering A/B,3578.175109,concurrency-filesystem-summary.csv,115a25eb7ec87484eeb3da87156fb49d67b5d7f37feb086784a740efca759119,concurrency-retest +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,worker_ms,baseline,candidate,10,ms,3606.7074435,-2.7130509930520574,-5.283209164000757,7.245092923180008,no detected difference,engineering A/B,3707.288059,direct-queue-filesystem-summary.csv,032c3a218c413524a2aaabf82f4f733909b4685d51a6a3f590ceb69679bc54ee,direct-queue-fast +B-FS-ENG,writeback-quick-20261011,pvisor-vm,write,enabled,disabled,30,ms,131.6145675,-18.80685324106817,-25.058474230843046,-12.86551705502134,faster,engineering A/B,162.10058700000002,writeback-filesystem-summary.csv,77c425a2580cd9f36fbf9790ae1d3d87d4524dd02160e5b3ec6483a7ecbf275c,writeback-ab +B-FS-ENG,dax-20261011-final,immutable-share,read_repeat,dax=never,dax=inode,30,ms,4.580477,-17.384227766361672,-21.395220652225067,-10.101683557067531,faster,engineering A/B,5.544313,readonly-dax-summary.csv,439eb91eb306051feb296f9ff49d45bbc73881134874098fe469c93d5a189729,readonly-dax-ab +B-FS-ENG,dax-20261011-final,immutable-share,parallel_first,dax=never,dax=inode,30,ms,38.2735545,-29.687926607405725,-32.155415115930666,-28.250685731462653,faster,engineering A/B,54.43383,readonly-dax-summary.csv,439eb91eb306051feb296f9ff49d45bbc73881134874098fe469c93d5a189729,readonly-dax-ab +B-FS-ENG,dax-20261011-final,immutable-share,mmap_first,dax=never,dax=inode,30,ms,104.75073,339.5902132918816,328.766260843115,352.398630591051,slower,engineering A/B,23.829177,readonly-dax-summary.csv,439eb91eb306051feb296f9ff49d45bbc73881134874098fe469c93d5a189729,readonly-dax-ab +B-FS-ENG,dax-20261011-final,immutable-share,small_repeat,dax=never,dax=inode,30,ms,36.6305735,44.36788056884071,36.46610040529619,47.98132060479314,slower,engineering A/B,25.373077000000002,readonly-dax-summary.csv,439eb91eb306051feb296f9ff49d45bbc73881134874098fe469c93d5a189729,readonly-dax-ab +B-FS-ENG,fof1,pvisor-vm,completion_ms,baseline,selected,30,ms,4467.5308425,-1.7337445136365037,-5.698320121926485,-0.22006512668154682,faster,engineering A/B,4546.352988,filesystem-optimization-n30-summary.csv,65b0287892724c266ebc39db20adcc138ac180aedb6b455cc0f713517d6b25dd,filesystem-optimization-n30 +B-FS-ENG,fof1,pvisor-vm,write,baseline,selected,30,ms,137.107249,-14.18593265348862,-17.580605675377935,-9.820266917526087,faster,engineering A/B,159.77246300000002,filesystem-optimization-n30-summary.csv,65b0287892724c266ebc39db20adcc138ac180aedb6b455cc0f713517d6b25dd,filesystem-optimization-n30 diff --git a/docs/src/en/design/filesystem-measurement-status.csv b/docs/src/en/design/filesystem-measurement-status.csv new file mode 100644 index 000000000..1128458fe --- /dev/null +++ b/docs/src/en/design/filesystem-measurement-status.csv @@ -0,0 +1,7 @@ +benchmark_id,batch,role,state,formal_rows_retained,expected_formal_rows,published_samples,measured_failures,interference_count,reason,binary_sha256,report_sha256,acceptance_record,acceptance_sha256 +B-FS-TOOLS,f10f2,user-facing,accepted,240,240,240,0,0,"Complete same-cohort correctness, input and interference gates passed",c6965986ac119544f0886e06806fcf41b814f54b54ad655978046a2449797c42,c2f544b79356db3fcda8f4ed34d944861a64f2190c18747c30441201271502ff,benchmark/pvisor/.data/filesystem-current-20261010/formal2-launch.json,ef557fc359cf7806bf2f6526acce2a4a76af4b0ac679caf6072ce208bdc02227 +B-FS-TOOLS,f11f,user-facing,rejected,102,240,0,0,3,External build/test interference; incomplete cohort; no final input gate,a4cfdbc6e2657f9ff6a43a4c640cd472c1c94c965c9d388924e58727dc55556d,d0c41cc5cc100115f47922577f4eb19087cdd6ee76d89ae7152c673de5a140c1,benchmark/pvisor/.data/filesystem-retest-20261011/ACCEPTANCE.json,7f68dc6572a457985ec1c302193b8183b0e1b857f1a120449cd8e57649b09652 +B-FS-TOOLS,f11f2,user-facing,rejected,192,240,0,0,1,External build/test interference; incomplete cohort; no final input gate,a4cfdbc6e2657f9ff6a43a4c640cd472c1c94c965c9d388924e58727dc55556d,01873d904892451fd16930e469c9794d12af5054d7895218ccdd623fe70ea103,benchmark/pvisor/.data/filesystem-retest-20261011/ACCEPTANCE.json,7f68dc6572a457985ec1c302193b8183b0e1b857f1a120449cd8e57649b09652 +B-FS-TOOLS,fs11f4,user-facing,accepted,240,240,240,0,0,"Current source/defaults; full correctness, before/after input, budget, retained output and interference gates passed",939f72289a72f73ba64d645c891842ed4a9408c7eaef4c54d5fa04aed0371e85,99eb2ee32bd9e595694c39434f33bc4a914b949e88d3c2b345733932e6884369,benchmark.new/filesystem/.data/cross-runtime-20261011T052947Z/attempt4/formal-launch.json,b799e35c825a4cb9a5a152728993a6d2cf19a038588097773026d5f8e8f92142 +B-FS-TOOLS,fo8e2d57ff-f,user-facing,accepted,240,240,240,0,0,"Independent complete full cohort: bytes, outputs, bundles, input/build identities, resource budget and interference gates passed",8840b16cdc0610e381eec66b0f75f1e86efba76feb358c9eae5b3d923854b592,d37b961370f9d8ba3487fd1a6a7a85cf5e85e5de6b32ae91285eb69b9dbfdb92,benchmark.new/filesystem/.data/fs-opt-report-20261011/publication-audit.json,ec38a6bc0311c07e45a756ed6ac0099270c680e432a3aa02252e78ea07f66528 +B-FS-TOOLS,fo5a4e7d2b-f,user-facing,accepted,240,240,240,0,0,"Independent complete full cohort: bytes, outputs, bundles, input/build identities, resource budget and interference gates passed",de4889d932ed8bb5ab376cb09ae43327084a2d017f66db5c54946b78b50446ac,0127dbdd8a04317be06a0235e5b1cec75dc298b3c6e312f22f1382b8d19f7262,benchmark.new/filesystem/.data/fs-docs-20261012/publication-audit.json,45f479ab7709723e781bfd6b78d8ace3dfa2a8914a687173babfd9ee23daf24e diff --git a/docs/src/en/design/filesystem-optimization-n30-provenance.csv b/docs/src/en/design/filesystem-optimization-n30-provenance.csv new file mode 100644 index 000000000..03916a385 --- /dev/null +++ b/docs/src/en/design/filesystem-optimization-n30-provenance.csv @@ -0,0 +1,18 @@ +benchmark_id,batch,field,value +B-FS-ENG,fof1,raw_location,benchmark/.data/fof1/report.json +B-FS-ENG,fof1,report_sha256,df8ddd01aa86ef0a61a04d4157dc8e02a6c935ebacecf3ccfb448ab624ded7b1 +B-FS-ENG,fof1,acceptance_record,benchmark/.data/fof1/derived/audit.json +B-FS-ENG,fof1,acceptance_sha256,3a599448f7a7737f38e52b7d2943555d0a8cc4d74e030ebabbea5461d25fca29 +B-FS-ENG,fof1,summary_sha256,65b0287892724c266ebc39db20adcc138ac180aedb6b455cc0f713517d6b25dd +B-FS-ENG,fof1,role,independent formal engineering A/B; not pooled; not user-facing phase/whole-command replacement +B-FS-ENG,fof1,samples_per_arm,30 +B-FS-ENG,fof1,warmups_per_arm,3 +B-FS-ENG,fof1,failures,0 +B-FS-ENG,fof1,arguments,"{""backends"": ""pvisor-vm"", ""env"": [], ""output"": ""/home/reiase/workspace/pvisor/benchmark/.data/fof1"", ""preflight"": false, ""samples"": 30, ""seed"": 20261011, ""variants"": ""baseline=/home/reiase/workspace/pvisor/benchmark.new/filesystem/.data/cross-runtime-20261011T052947Z/build,selected=/home/reiase/workspace/pvisor/benchmark.new/filesystem/.data/fs-opt-build3-20261011/candidate"", ""warmups"": 3}" +B-FS-ENG,fof1,receipts,"{""baseline"": {""cargo"": ""cargo 1.98.1 (797e8a9bc 2026-08-05)\n"", ""command"": [""cargo"", ""build"", ""--locked"", ""--offline"", ""--release"", ""-p"", ""pvisor-cli"", ""--bin"", ""pvisor"", ""--features"", ""gateway"", ""--target-dir"", ""/home/reiase/workspace/pvisor/target/filesystem-current-build-20261010""], ""compiled_units"": {""cli"": {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""gateway\""]"", ""features"": ""[\""default\"", \""gateway\""]"", ""profile"": 4686446429936418732, ""rustc"": 1625334936438085654, ""rustflags"": []}, ""libraries"": [{""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__disabled\"", \""static\""]"", ""features"": ""[]"", ""name"": ""bzip2_sys"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__docs_rs\"", \""codec\"", \""compat\"", \""default\"", \""full\"", \""futures-io\"", \""futures-util\"", \""hashbrown\"", \""io\"", \""io-util\"", \""join-map\"", \""net\"", \""rt\"", \""slab\"", \""time\"", \""tracing\""]"", ""features"": ""[\""codec\"", \""default\"", \""futures-util\"", \""io\"", \""rt\""]"", ""name"": ""tokio_util"", ""profile"": 10043413591317300293, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__internal_bench\"", \""alloc\"", \""arbitrary\"", \""clock\"", \""core-error\"", \""default\"", \""defmt\"", \""iana-time-zone\"", \""js-sys\"", \""libc\"", \""now\"", \""oldtime\"", \""pure-rust-locales\"", \""rkyv\"", \""rkyv-16\"", \""rkyv-32\"", \""rkyv-64\"", \""rkyv-validation\"", \""serde\"", \""std\"", \""unstable-locales\"", \""wasm-bindgen\"", \""wasmbind\"", \""winapi\"", \""windows-link\""]"", ""features"": ""[\""alloc\"", \""clock\"", \""default\"", \""iana-time-zone\"", \""js-sys\"", \""now\"", \""oldtime\"", \""serde\"", \""std\"", \""wasm-bindgen\"", \""wasmbind\"", \""winapi\"", \""windows-link\""]"", ""name"": ""chrono"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__internal_happy_eyeballs_tests\"", \""client\"", \""client-legacy\"", \""client-pool\"", \""client-proxy\"", \""client-proxy-system\"", \""default\"", \""full\"", \""http1\"", \""http2\"", \""server\"", \""server-auto\"", \""server-graceful\"", \""service\"", \""tokio\"", \""tracing\""]"", ""features"": ""[\""client\"", \""client-legacy\"", \""client-proxy\"", \""default\"", \""http1\"", \""http2\"", \""server\"", \""service\"", \""tokio\""]"", ""name"": ""hyper_util"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__internal_use_only_features_that_work_on_stable\"", \""alloc\"", \""derive\"", \""float-nightly\"", \""simd\"", \""simd-nightly\"", \""std\"", \""zerocopy-derive\""]"", ""features"": ""[\""derive\"", \""simd\"", \""zerocopy-derive\""]"", ""name"": ""zerocopy"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__private\"", \""__private_docs\"", \""default\"", \""form\"", \""http1\"", \""http2\"", \""json\"", \""macros\"", \""matched-path\"", \""multipart\"", \""original-uri\"", \""query\"", \""tokio\"", \""tower-log\"", \""tracing\"", \""ws\""]"", ""features"": ""[\""http1\"", \""http2\"", \""json\"", \""tokio\""]"", ""name"": ""axum"", ""profile"": 7938159781367629850, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__private_docs\"", \""tracing\""]"", ""features"": ""[]"", ""name"": ""axum_core"", ""profile"": 9498297305443351059, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__rustls\"", \""__rustls-ring\"", \""__tls\"", \""blocking\"", \""brotli\"", \""charset\"", \""cookies\"", \""default\"", \""default-tls\"", \""deflate\"", \""gzip\"", \""h2\"", \""hickory-dns\"", \""http2\"", \""http3\"", \""json\"", \""macos-system-configuration\"", \""multipart\"", \""native-tls\"", \""native-tls-alpn\"", \""native-tls-vendored\"", \""rustls-tls\"", \""rustls-tls-manual-roots\"", \""rustls-tls-manual-roots-no-provider\"", \""rustls-tls-native-roots\"", \""rustls-tls-native-roots-no-provider\"", \""rustls-tls-no-provider\"", \""rustls-tls-webpki-roots\"", \""rustls-tls-webpki-roots-no-provider\"", \""socks\"", \""stream\"", \""system-proxy\"", \""trust-dns\"", \""zstd\""]"", ""features"": ""[\""__rustls\"", \""__rustls-ring\"", \""__tls\"", \""blocking\"", \""deflate\"", \""gzip\"", \""h2\"", \""http2\"", \""json\"", \""rustls-tls\"", \""rustls-tls-native-roots\"", \""rustls-tls-native-roots-no-provider\"", \""rustls-tls-webpki-roots\"", \""rustls-tls-webpki-roots-no-provider\"", \""stream\""]"", ""name"": ""reqwest"", ""profile"": 17166782826556458903, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__test_helpers\"", \""default\""]"", ""features"": ""[\""default\""]"", ""name"": ""matchit"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""_netsim\"", \""_proto-fragmentation\"", \""alloc\"", \""assembler-max-segment-count-1\"", \""assembler-max-segment-count-16\"", \""assembler-max-segment-count-2\"", \""assembler-max-segment-count-3\"", \""assembler-max-segment-count-32\"", \""assembler-max-segment-count-4\"", \""assembler-max-segment-count-8\"", \""async\"", \""auto-icmp-echo-reply\"", \""default\"", \""defmt\"", \""dns-max-name-size-128\"", \""dns-max-name-size-255\"", \""dns-max-name-size-64\"", \""dns-max-result-count-1\"", \""dns-max-result-count-16\"", \""dns-max-result-count-2\"", \""dns-max-result-count-3\"", \""dns-max-result-count-32\"", \""dns-max-result-count-4\"", \""dns-max-result-count-8\"", \""dns-max-server-count-1\"", \""dns-max-server-count-16\"", \""dns-max-server-count-2\"", \""dns-max-server-count-3\"", \""dns-max-server-count-32\"", \""dns-max-server-count-4\"", \""dns-max-server-count-8\"", \""fragmentation-buffer-size-1024\"", \""fragmentation-buffer-size-1500\"", \""fragmentation-buffer-size-16384\"", \""fragmentation-buffer-size-2048\"", \""fragmentation-buffer-size-256\"", \""fragmentation-buffer-size-32768\"", \""fragmentation-buffer-size-4096\"", \""fragmentation-buffer-size-512\"", \""fragmentation-buffer-size-65536\"", \""fragmentation-buffer-size-8192\"", \""iface-max-addr-count-1\"", \""iface-max-addr-count-2\"", \""iface-max-addr-count-3\"", \""iface-max-addr-count-4\"", \""iface-max-addr-count-5\"", \""iface-max-addr-count-6\"", \""iface-max-addr-count-7\"", \""iface-max-addr-count-8\"", \""iface-max-multicast-group-count-1\"", \""iface-max-multicast-group-count-1024\"", \""iface-max-multicast-group-count-128\"", \""iface-max-multicast-group-count-16\"", \""iface-max-multicast-group-count-2\"", \""iface-max-multicast-group-count-256\"", \""iface-max-multicast-group-count-3\"", \""iface-max-multicast-group-count-32\"", \""iface-max-multicast-group-count-4\"", \""iface-max-multicast-group-count-5\"", \""iface-max-multicast-group-count-512\"", \""iface-max-multicast-group-count-6\"", \""iface-max-multicast-group-count-64\"", \""iface-max-multicast-group-count-7\"", \""iface-max-multicast-group-count-8\"", \""iface-max-prefix-count-1\"", \""iface-max-prefix-count-2\"", \""iface-max-prefix-count-3\"", \""iface-max-prefix-count-4\"", \""iface-max-prefix-count-5\"", \""iface-max-prefix-count-6\"", \""iface-max-prefix-count-7\"", \""iface-max-prefix-count-8\"", \""iface-max-route-count-0\"", \""iface-max-route-count-1\"", \""iface-max-route-count-1024\"", \""iface-max-route-count-128\"", \""iface-max-route-count-16\"", \""iface-max-route-count-2\"", \""iface-max-route-count-256\"", \""iface-max-route-count-3\"", \""iface-max-route-count-32\"", \""iface-max-route-count-4\"", \""iface-max-route-count-5\"", \""iface-max-route-count-512\"", \""iface-max-route-count-6\"", \""iface-max-route-count-64\"", \""iface-max-route-count-7\"", \""iface-max-route-count-8\"", \""iface-max-sixlowpan-address-context-count-1\"", \""iface-max-sixlowpan-address-context-count-1024\"", \""iface-max-sixlowpan-address-context-count-128\"", \""iface-max-sixlowpan-address-context-count-16\"", \""iface-max-sixlowpan-address-context-count-2\"", \""iface-max-sixlowpan-address-context-count-256\"", \""iface-max-sixlowpan-address-context-count-3\"", \""iface-max-sixlowpan-address-context-count-32\"", \""iface-max-sixlowpan-address-context-count-4\"", \""iface-max-sixlowpan-address-context-count-5\"", \""iface-max-sixlowpan-address-context-count-512\"", \""iface-max-sixlowpan-address-context-count-6\"", \""iface-max-sixlowpan-address-context-count-64\"", \""iface-max-sixlowpan-address-context-count-7\"", \""iface-max-sixlowpan-address-context-count-8\"", \""iface-neighbor-cache-count-1\"", \""iface-neighbor-cache-count-1024\"", \""iface-neighbor-cache-count-128\"", \""iface-neighbor-cache-count-16\"", \""iface-neighbor-cache-count-2\"", \""iface-neighbor-cache-count-256\"", \""iface-neighbor-cache-count-3\"", \""iface-neighbor-cache-count-32\"", \""iface-neighbor-cache-count-4\"", \""iface-neighbor-cache-count-5\"", \""iface-neighbor-cache-count-512\"", \""iface-neighbor-cache-count-6\"", \""iface-neighbor-cache-count-64\"", \""iface-neighbor-cache-count-7\"", \""iface-neighbor-cache-count-8\"", \""ipv6-hbh-max-options-1\"", \""ipv6-hbh-max-options-16\"", \""ipv6-hbh-max-options-2\"", \""ipv6-hbh-max-options-3\"", \""ipv6-hbh-max-options-32\"", \""ipv6-hbh-max-options-4\"", \""ipv6-hbh-max-options-8\"", \""libc\"", \""log\"", \""medium-ethernet\"", \""medium-ieee802154\"", \""medium-ip\"", \""multicast\"", \""packetmeta-id\"", \""phy-raw_socket\"", \""phy-tuntap_interface\"", \""proto-dhcpv4\"", \""proto-dns\"", \""proto-ipsec\"", \""proto-ipsec-ah\"", \""proto-ipsec-esp\"", \""proto-ipv4\"", \""proto-ipv4-fragmentation\"", \""proto-ipv6\"", \""proto-ipv6-fragmentation\"", \""proto-ipv6-hbh\"", \""proto-ipv6-routing\"", \""proto-ipv6-slaac\"", \""proto-rpl\"", \""proto-sixlowpan\"", \""proto-sixlowpan-fragmentation\"", \""reassembly-buffer-count-1\"", \""reassembly-buffer-count-16\"", \""reassembly-buffer-count-2\"", \""reassembly-buffer-count-3\"", \""reassembly-buffer-count-32\"", \""reassembly-buffer-count-4\"", \""reassembly-buffer-count-8\"", \""reassembly-buffer-size-1024\"", \""reassembly-buffer-size-1500\"", \""reassembly-buffer-size-16384\"", \""reassembly-buffer-size-2048\"", \""reassembly-buffer-size-256\"", \""reassembly-buffer-size-32768\"", \""reassembly-buffer-size-4096\"", \""reassembly-buffer-size-512\"", \""reassembly-buffer-size-65536\"", \""reassembly-buffer-size-8192\"", \""rpl-parents-buffer-count-16\"", \""rpl-parents-buffer-count-2\"", \""rpl-parents-buffer-count-32\"", \""rpl-parents-buffer-count-4\"", \""rpl-parents-buffer-count-8\"", \""rpl-relations-buffer-count-1\"", \""rpl-relations-buffer-count-128\"", \""rpl-relations-buffer-count-16\"", \""rpl-relations-buffer-count-2\"", \""rpl-relations-buffer-count-32\"", \""rpl-relations-buffer-count-4\"", \""rpl-relations-buffer-count-64\"", \""rpl-relations-buffer-count-8\"", \""socket\"", \""socket-dhcpv4\"", \""socket-dns\"", \""socket-icmp\"", \""socket-mdns\"", \""socket-raw\"", \""socket-tcp\"", \""socket-tcp-cubic\"", \""socket-tcp-pause-synack\"", \""socket-tcp-reno\"", \""socket-udp\"", \""std\"", \""verbose\""]"", ""features"": ""[\""alloc\"", \""log\"", \""medium-ethernet\"", \""proto-dhcpv4\"", \""proto-dns\"", \""proto-ipv4\"", \""socket\"", \""socket-tcp\"", \""socket-tcp-pause-synack\"", \""socket-udp\"", \""std\""]"", ""name"": ""smoltcp"", ""profile"": 13333777962520875826, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""aarch64\"", \""alloc\"", \""default\"", \""derive\"", \""serde\"", \""simd\"", \""std\"", \""zeroize_derive\""]"", ""features"": ""[\""alloc\"", \""default\""]"", ""name"": ""zeroize"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""abi-7-10\"", \""abi-7-11\"", \""abi-7-12\"", \""abi-7-13\"", \""abi-7-14\"", \""abi-7-15\"", \""abi-7-16\"", \""abi-7-17\"", \""abi-7-18\"", \""abi-7-19\"", \""abi-7-20\"", \""abi-7-21\"", \""abi-7-22\"", \""abi-7-23\"", \""abi-7-24\"", \""abi-7-25\"", \""abi-7-26\"", \""abi-7-27\"", \""abi-7-28\"", \""abi-7-29\"", \""abi-7-30\"", \""abi-7-31\"", \""abi-7-9\"", \""default\"", \""libfuse\"", \""macfuse-4-compat\"", \""macfuse-5\"", \""pkg-config\"", \""serde\"", \""serializable\""]"", ""features"": ""[\""abi-7-10\"", \""abi-7-11\"", \""abi-7-12\"", \""abi-7-13\"", \""abi-7-14\"", \""abi-7-15\"", \""abi-7-16\"", \""abi-7-17\"", \""abi-7-18\"", \""abi-7-19\"", \""abi-7-20\"", \""abi-7-21\"", \""abi-7-22\"", \""abi-7-23\"", \""abi-7-24\"", \""abi-7-25\"", \""abi-7-26\"", \""abi-7-27\"", \""abi-7-28\"", \""abi-7-29\"", \""abi-7-30\"", \""abi-7-31\"", \""abi-7-9\""]"", ""name"": ""fuser"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""acct\"", \""aio\"", \""default\"", \""dir\"", \""env\"", \""event\"", \""fanotify\"", \""feature\"", \""fs\"", \""hostname\"", \""inotify\"", \""ioctl\"", \""kmod\"", \""memoffset\"", \""mman\"", \""mount\"", \""mqueue\"", \""net\"", \""personality\"", \""pin-utils\"", \""poll\"", \""process\"", \""pthread\"", \""ptrace\"", \""quota\"", \""reboot\"", \""resource\"", \""sched\"", \""signal\"", \""socket\"", \""syslog\"", \""term\"", \""time\"", \""ucontext\"", \""uio\"", \""user\"", \""zerocopy\""]"", ""features"": ""[\""default\"", \""fs\"", \""ioctl\"", \""memoffset\"", \""net\"", \""poll\"", \""socket\"", \""term\""]"", ""name"": ""nix"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""acct\"", \""aio\"", \""default\"", \""dir\"", \""env\"", \""event\"", \""fanotify\"", \""feature\"", \""fs\"", \""hostname\"", \""inotify\"", \""ioctl\"", \""kmod\"", \""memoffset\"", \""mman\"", \""mount\"", \""mqueue\"", \""net\"", \""personality\"", \""pin-utils\"", \""poll\"", \""process\"", \""pthread\"", \""ptrace\"", \""quota\"", \""reboot\"", \""resource\"", \""sched\"", \""signal\"", \""socket\"", \""term\"", \""time\"", \""ucontext\"", \""uio\"", \""user\"", \""zerocopy\""]"", ""features"": ""[\""default\"", \""feature\"", \""fs\"", \""user\""]"", ""name"": ""nix"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""add-extension\"", \""async-compression\"", \""auth\"", \""base64\"", \""catch-panic\"", \""compression-br\"", \""compression-deflate\"", \""compression-full\"", \""compression-gzip\"", \""compression-zstd\"", \""cors\"", \""decompression-br\"", \""decompression-deflate\"", \""decompression-full\"", \""decompression-gzip\"", \""decompression-zstd\"", \""default\"", \""follow-redirect\"", \""fs\"", \""full\"", \""futures-core\"", \""futures-util\"", \""httpdate\"", \""iri-string\"", \""limit\"", \""map-request-body\"", \""map-response-body\"", \""metrics\"", \""mime\"", \""mime_guess\"", \""normalize-path\"", \""percent-encoding\"", \""propagate-header\"", \""redirect\"", \""request-id\"", \""sensitive-headers\"", \""set-header\"", \""set-status\"", \""timeout\"", \""tokio\"", \""tokio-util\"", \""tower\"", \""trace\"", \""tracing\"", \""util\"", \""uuid\"", \""validate-request\""]"", ""features"": ""[\""async-compression\"", \""decompression-deflate\"", \""decompression-gzip\"", \""follow-redirect\"", \""futures-core\"", \""futures-util\"", \""iri-string\"", \""tokio\"", \""tokio-util\"", \""tower\""]"", ""name"": ""tower_http"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""ahash\"", \""alloc\"", \""allocator-api2\"", \""compiler_builtins\"", \""core\"", \""default\"", \""equivalent\"", \""inline-more\"", \""nightly\"", \""raw\"", \""rayon\"", \""rkyv\"", \""rustc-dep-of-std\"", \""rustc-internal-api\"", \""serde\""]"", ""features"": ""[\""raw\""]"", ""name"": ""hashbrown"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""align\"", \""const-extern-fn\"", \""default\"", \""extra_traits\"", \""rustc-dep-of-std\"", \""rustc-std-workspace-core\"", \""std\"", \""use_std\""]"", ""features"": ""[\""default\"", \""extra_traits\"", \""std\""]"", ""name"": ""libc"", ""profile"": 4248016837003788142, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""align\"", \""const-extern-fn\"", \""default\"", \""extra_traits\"", \""rustc-dep-of-std\"", \""rustc-std-workspace-core\"", \""std\"", \""use_std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""libc"", ""profile"": 4793633013790561351, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""all-algorithms\"", \""brotli\"", \""bzip2\"", \""deflate\"", \""deflate64\"", \""flate2\"", \""gzip\"", \""libzstd\"", \""lz4\"", \""lzma\"", \""memchr\"", \""xz\"", \""xz-parallel\"", \""xz2\"", \""zlib\"", \""zstd\"", \""zstd-safe\"", \""zstdmt\""]"", ""features"": ""[\""flate2\"", \""gzip\"", \""memchr\"", \""zlib\""]"", ""name"": ""compression_codecs"", ""profile"": 4107725171516170900, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""all-apis\"", \""alloc\"", \""core\"", \""default\"", \""event\"", \""fs\"", \""io_uring\"", \""libc\"", \""libc_errno\"", \""linux_4_11\"", \""linux_5_1\"", \""linux_5_11\"", \""linux_latest\"", \""mm\"", \""mount\"", \""net\"", \""param\"", \""pipe\"", \""process\"", \""pty\"", \""rand\"", \""runtime\"", \""rustc-dep-of-std\"", \""rustc-std-workspace-alloc\"", \""shm\"", \""std\"", \""stdio\"", \""system\"", \""termios\"", \""thread\"", \""time\"", \""try_close\"", \""use-explicitly-provided-auxv\"", \""use-libc\"", \""use-libc-auxv\""]"", ""features"": ""[\""alloc\"", \""default\"", \""fs\"", \""std\""]"", ""name"": ""rustix"", ""profile"": 4272393398390303732, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""all\"", \""all-algorithms\"", \""all-implementations\"", \""brotli\"", \""bzip2\"", \""deflate\"", \""deflate64\"", \""futures-io\"", \""gzip\"", \""lz4\"", \""lzma\"", \""tokio\"", \""xz\"", \""xz-parallel\"", \""xz2\"", \""zlib\"", \""zstd\"", \""zstdmt\""]"", ""features"": ""[\""gzip\"", \""tokio\"", \""zlib\""]"", ""name"": ""async_compression"", ""profile"": 4107725171516170900, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""all\""]"", ""features"": ""[\""all\""]"", ""name"": ""socket2"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""allocator-api2\"", \""core\"", \""default\"", \""default-hasher\"", \""equivalent\"", \""inline-more\"", \""nightly\"", \""raw-entry\"", \""rayon\"", \""rustc-dep-of-std\"", \""rustc-internal-api\"", \""serde\""]"", ""features"": ""[\""allocator-api2\"", \""default\"", \""default-hasher\"", \""equivalent\"", \""inline-more\"", \""raw-entry\""]"", ""name"": ""hashbrown"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""allocator-api2\"", \""core\"", \""default\"", \""default-hasher\"", \""equivalent\"", \""inline-more\"", \""nightly\"", \""raw-entry\"", \""rayon\"", \""rustc-dep-of-std\"", \""rustc-internal-api\"", \""serde\""]"", ""features"": ""[]"", ""name"": ""hashbrown"", ""profile"": 10067188661882979502, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""arbitrary\"", \""bytemuck\"", \""ctutils\"", \""extra-sizes\"", \""serde\"", \""subtle\"", \""zerocopy\"", \""zeroize\""]"", ""features"": ""[]"", ""name"": ""hybrid_array"", ""profile"": 7230693119043292679, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""arbitrary_precision\"", \""default\"", \""float_roundtrip\"", \""indexmap\"", \""preserve_order\"", \""raw_value\"", \""std\"", \""unbounded_depth\""]"", ""features"": ""[\""default\"", \""raw_value\"", \""std\""]"", ""name"": ""serde_json"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""async-await\"", \""async-await-macro\"", \""bilock\"", \""cfg-target-has-atomic\"", \""channel\"", \""compat\"", \""default\"", \""futures-channel\"", \""futures-io\"", \""futures-macro\"", \""futures-sink\"", \""futures_01\"", \""io\"", \""io-compat\"", \""libc\"", \""memchr\"", \""portable-atomic\"", \""sink\"", \""slab\"", \""spin\"", \""std\"", \""tokio-io\"", \""unstable\"", \""write-all-vectored\""]"", ""features"": ""[\""alloc\"", \""async-await\"", \""async-await-macro\"", \""default\"", \""futures-io\"", \""futures-macro\"", \""futures-sink\"", \""io\"", \""memchr\"", \""sink\"", \""slab\"", \""std\""]"", ""name"": ""futures_util"", ""profile"": 8157235479358073570, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""async-await\"", \""bilock\"", \""cfg-target-has-atomic\"", \""compat\"", \""default\"", \""executor\"", \""futures-executor\"", \""io-compat\"", \""spin\"", \""std\"", \""thread-pool\"", \""unstable\"", \""write-all-vectored\""]"", ""features"": ""[\""alloc\""]"", ""name"": ""futures"", ""profile"": 8157235479358073570, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""atomic-polyfill\"", \""critical-section\"", \""default\"", \""parking_lot\"", \""portable-atomic\"", \""race\"", \""std\"", \""unstable\""]"", ""features"": ""[\""alloc\"", \""default\"", \""race\"", \""std\""]"", ""name"": ""once_cell"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""aws-lc-rs\"", \""aws-lc-rs-fips\"", \""aws-lc-rs-unstable\"", \""default\"", \""ring\"", \""std\""]"", ""features"": ""[\""alloc\"", \""ring\"", \""std\""]"", ""name"": ""webpki"", ""profile"": 13987763031497049980, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""baked\"", \""deserialize_bincode_1\"", \""deserialize_json\"", \""deserialize_postcard_1\"", \""export\"", \""logging\"", \""serde\"", \""std\"", \""sync\"", \""zerotrie\""]"", ""features"": ""[\""baked\""]"", ""name"": ""icu_provider"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""bincode_derive\"", \""default\"", \""derive\"", \""serde\"", \""std\""]"", ""features"": ""[\""bincode_derive\"", \""derive\""]"", ""name"": ""bincode"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""blobby\"", \""block-api\"", \""default\"", \""dev\"", \""getrandom\"", \""mac\"", \""oid\"", \""rand_core\"", \""zeroize\""]"", ""features"": ""[\""alloc\"", \""block-api\"", \""default\"", \""oid\""]"", ""name"": ""digest"", ""profile"": 2606296542320589278, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""blobby\"", \""block-buffer\"", \""const-oid\"", \""core-api\"", \""default\"", \""dev\"", \""mac\"", \""oid\"", \""rand_core\"", \""std\"", \""subtle\""]"", ""features"": ""[\""block-buffer\"", \""core-api\"", \""default\""]"", ""name"": ""digest"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""block-boundary\"", \""core\"", \""default\"", \""rustc-dep-of-std\"", \""serde\"", \""simd\"", \""simd-adler32\"", \""std\"", \""with-alloc\""]"", ""features"": ""[\""default\"", \""simd\"", \""simd-adler32\"", \""std\"", \""with-alloc\""]"", ""name"": ""miniz_oxide"", ""profile"": 13333777962520875826, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""bytes\"", \""defmt\"", \""embedded-io-v0.7\"", \""mpmc_large\"", \""nightly\"", \""portable-atomic\"", \""portable-atomic-critical-section\"", \""portable-atomic-unsafe-assume-single-core\"", \""serde\"", \""ufmt\"", \""zeroize\""]"", ""features"": ""[]"", ""name"": ""heapless"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""cfg-target-has-atomic\"", \""default\"", \""futures-sink\"", \""sink\"", \""std\"", \""unstable\""]"", ""features"": ""[\""alloc\"", \""default\"", \""futures-sink\"", \""sink\"", \""std\""]"", ""name"": ""futures_channel"", ""profile"": 8157235479358073570, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""cfg-target-has-atomic\"", \""default\"", \""portable-atomic\"", \""std\"", \""unstable\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""futures_core"", ""profile"": 8157235479358073570, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""cfg-target-has-atomic\"", \""default\"", \""std\"", \""unstable\""]"", ""features"": ""[\""alloc\"", \""std\""]"", ""name"": ""futures_task"", ""profile"": 8157235479358073570, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""chacha\"", \""default\"", \""log\"", \""serde\"", \""simd_support\"", \""std\"", \""std_rng\"", \""sys_rng\"", \""thread_rng\"", \""unbiased\""]"", ""features"": ""[\""alloc\"", \""std\"", \""std_rng\"", \""sys_rng\"", \""thread_rng\""]"", ""name"": ""rand"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""compiled_data\"", \""datagen\"", \""default\"", \""harfbuzz_traits\"", \""serde\"", \""unicode_bidi\""]"", ""features"": ""[\""compiled_data\""]"", ""name"": ""icu_properties"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""compiled_data\"", \""default\"", \""std\""]"", ""features"": ""[\""alloc\"", \""compiled_data\"", \""std\""]"", ""name"": ""idna"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""core\"", \""default\"", \""libc\"", \""logging\"", \""rustc-dep-of-std\"", \""std\"", \""use_std\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""memchr"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""databake\"", \""default\"", \""dense\"", \""litemap\"", \""serde\"", \""yoke\"", \""zerofrom\"", \""zerovec\""]"", ""features"": ""[\""yoke\"", \""zerofrom\""]"", ""name"": ""zerotrie"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""databake\"", \""default\"", \""serde\"", \""std\"", \""zerovec\""]"", ""features"": ""[\""zerovec\""]"", ""name"": ""tinystr"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""databake\"", \""default\"", \""serde\"", \""testing\"", \""yoke\""]"", ""features"": ""[]"", ""name"": ""litemap"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""databake\"", \""default\"", \""serde\"", \""writeable\"", \""zerovec\""]"", ""features"": ""[\""zerovec\""]"", ""name"": ""potential_utf"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""databake\"", \""derive\"", \""hashmap\"", \""schemars\"", \""serde\"", \""std\"", \""yoke\""]"", ""features"": ""[\""derive\"", \""yoke\""]"", ""name"": ""zerovec"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""databake\"", \""serde\"", \""zerovec\""]"", ""features"": ""[\""zerovec\""]"", ""name"": ""icu_locale_core"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""databake\"", \""serde\""]"", ""features"": ""[]"", ""name"": ""icu_collections"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""debug\"", \""default\"", \""simd\"", \""std\"", \""unstable-doc\"", \""unstable-recover\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""winnow"", ""profile"": 16551665274674009540, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""defmt\"", \""js\"", \""logging\"", \""perf-inline\"", \""serde\"", \""static\"", \""static-tz\"", \""std\"", \""tz-fat\"", \""tz-system\"", \""tzdb-bundle-always\"", \""tzdb-bundle-platform\"", \""tzdb-concatenated\"", \""tzdb-zoneinfo\""]"", ""features"": ""[\""alloc\"", \""std\""]"", ""name"": ""jiff"", ""profile"": 7947306171983741744, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""defmt\"", \""logging\"", \""std\"", \""tz-fat\""]"", ""features"": ""[\""alloc\"", \""std\""]"", ""name"": ""jiff_core"", ""profile"": 7947306171983741744, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""derive\"", \""rc\"", \""serde_derive\"", \""std\"", \""unstable\""]"", ""features"": ""[\""default\"", \""derive\"", \""rc\"", \""serde_derive\"", \""std\""]"", ""name"": ""serde"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""derive\"", \""serde\"", \""zerofrom\""]"", ""features"": ""[\""derive\"", \""zerofrom\""]"", ""name"": ""yoke"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""derive\""]"", ""features"": ""[\""derive\""]"", ""name"": ""zerofrom"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""dev_urandom_fallback\"", \""less-safe-getrandom-custom-or-rdrand\"", \""less-safe-getrandom-espidf\"", \""slow_tests\"", \""std\"", \""test_logging\"", \""unstable-testing-arm-no-hw\"", \""unstable-testing-arm-no-neon\"", \""wasm32_unknown_unknown_js\""]"", ""features"": ""[\""alloc\"", \""default\"", \""dev_urandom_fallback\"", \""std\""]"", ""name"": ""ring"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""dfa\"", \""dfa-build\"", \""dfa-onepass\"", \""dfa-search\"", \""hybrid\"", \""internal-instrument\"", \""internal-instrument-pikevm\"", \""logging\"", \""meta\"", \""nfa\"", \""nfa-backtrack\"", \""nfa-pikevm\"", \""nfa-thompson\"", \""perf\"", \""perf-inline\"", \""perf-literal\"", \""perf-literal-multisubstring\"", \""perf-literal-substring\"", \""std\"", \""syntax\"", \""unicode\"", \""unicode-age\"", \""unicode-bool\"", \""unicode-case\"", \""unicode-gencat\"", \""unicode-perl\"", \""unicode-script\"", \""unicode-segment\"", \""unicode-word-boundary\""]"", ""features"": ""[\""alloc\"", \""dfa-onepass\"", \""hybrid\"", \""meta\"", \""nfa\"", \""nfa-backtrack\"", \""nfa-pikevm\"", \""nfa-thompson\"", \""perf\"", \""perf-inline\"", \""perf-literal\"", \""perf-literal-multisubstring\"", \""perf-literal-substring\"", \""std\"", \""syntax\"", \""unicode\"", \""unicode-age\"", \""unicode-bool\"", \""unicode-case\"", \""unicode-gencat\"", \""unicode-perl\"", \""unicode-script\"", \""unicode-segment\"", \""unicode-word-boundary\""]"", ""name"": ""regex_automata"", ""profile"": 2547513045437502204, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""either\""]"", ""features"": ""[]"", ""name"": ""writeable"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""fresh-rust\"", \""nightly\"", \""serde\"", \""std\""]"", ""features"": ""[\""alloc\""]"", ""name"": ""allocator_api2"", ""profile"": 3361788016818567686, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""getrandom\"", \""js\"", \""std\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""fastrand"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""log\"", \""nightly\"", \""os_rng\"", \""serde\"", \""simd_support\"", \""small_rng\"", \""std\"", \""std_rng\"", \""thread_rng\"", \""unbiased\""]"", ""features"": ""[\""alloc\"", \""default\"", \""os_rng\"", \""small_rng\"", \""std\"", \""std_rng\"", \""thread_rng\""]"", ""name"": ""rand"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""map\"", \""std\""]"", ""features"": ""[\""alloc\"", \""map\"", \""std\""]"", ""name"": ""managed"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""memchr\"", \""serde\"", \""std\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""iri_string"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""oid\"", \""zeroize\""]"", ""features"": ""[\""alloc\"", \""default\"", \""oid\""]"", ""name"": ""sha2"", ""profile"": 8048078827458334496, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""rc\"", \""result\"", \""std\"", \""unstable\""]"", ""features"": ""[\""alloc\"", \""default\"", \""rc\"", \""result\"", \""std\""]"", ""name"": ""serde_core"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""serde\"", \""std\"", \""unicode\""]"", ""features"": ""[\""alloc\"", \""std\""]"", ""name"": ""bstr"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""std\"", \""web\"", \""web-time\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""rustls_pki_types"", ""profile"": 8134619797147437084, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""base64"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""form_urlencoded"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""futures_sink"", ""profile"": 8157235479358073570, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""percent_encoding"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""toml_write"", ""profile"": 11768715044625735406, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""features"": ""[]"", ""name"": ""stable_deref_trait"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""kv\"", \""kv_serde\"", \""kv_std\"", \""kv_sval\"", \""kv_unstable\"", \""kv_unstable_serde\"", \""kv_unstable_std\"", \""kv_unstable_sval\"", \""max_level_debug\"", \""max_level_error\"", \""max_level_info\"", \""max_level_off\"", \""max_level_trace\"", \""max_level_warn\"", \""release_max_level_debug\"", \""release_max_level_error\"", \""release_max_level_info\"", \""release_max_level_off\"", \""release_max_level_trace\"", \""release_max_level_warn\"", \""serde\"", \""serde_core\"", \""std\"", \""sval\"", \""sval_ref\"", \""value-bag\""]"", ""features"": ""[\""alloc\"", \""std\""]"", ""name"": ""log"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""amd-sev\"", \""aws-nitro\"", \""bitfield\"", \""blk\"", \""default\"", \""efi\"", \""gpu\"", \""init-blob\"", \""input\"", \""iocuddle\"", \""kbs-types\"", \""krun_display\"", \""krun_input\"", \""net\"", \""pw\"", \""rutabaga_gfx\"", \""serde\"", \""snd\"", \""tdx\"", \""tee\"", \""test_utils\"", \""thiserror\"", \""virgl_resource_map2\"", \""zerocopy\""]"", ""features"": ""[\""default\"", \""init-blob\"", \""net\""]"", ""name"": ""pvisor_vm"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""ansi\"", \""bitflags\"", \""cursor-icon\"", \""default\"", \""log\"", \""serde\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""vte"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""any_c_zlib\"", \""any_impl\"", \""any_zlib\"", \""cloudflare-zlib-sys\"", \""cloudflare_zlib\"", \""default\"", \""document-features\"", \""libz-ng-sys\"", \""libz-sys\"", \""miniz-sys\"", \""miniz_oxide\"", \""rust_backend\"", \""zlib\"", \""zlib-default\"", \""zlib-ng\"", \""zlib-ng-compat\"", \""zlib-rs\""]"", ""features"": ""[\""any_impl\"", \""default\"", \""miniz_oxide\"", \""rust_backend\""]"", ""name"": ""flate2"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""async-tokio\"", \""default\"", \""document-features\"", \""encoding\"", \""encoding_rs\"", \""escape-html\"", \""overlapped-lists\"", \""serde\"", \""serde-types\"", \""serialize\"", \""tokio\""]"", ""features"": ""[\""default\"", \""overlapped-lists\"", \""serde\"", \""serialize\""]"", ""name"": ""quick_xml"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""atomic\"", \""borsh\"", \""bytemuck\"", \""default\"", \""fast-rng\"", \""js\"", \""macro-diagnostics\"", \""md5\"", \""rng\"", \""rng-getrandom\"", \""rng-rand\"", \""serde\"", \""sha1\"", \""slog\"", \""std\"", \""uuid-rng-internal-lib\"", \""v1\"", \""v3\"", \""v4\"", \""v5\"", \""v6\"", \""v7\"", \""v8\"", \""zerocopy\""]"", ""features"": ""[\""default\"", \""rng\"", \""std\"", \""v4\""]"", ""name"": ""uuid"", ""profile"": 5623565614784616014, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""bincode\"", \""const_generics\"", \""const_new\"", \""debugger_visualizer\"", \""drain_filter\"", \""drain_keep_rest\"", \""impl_bincode\"", \""malloc_size_of\"", \""may_dangle\"", \""serde\"", \""specialization\"", \""union\"", \""unty\"", \""write\""]"", ""features"": ""[\""const_generics\"", \""const_new\""]"", ""name"": ""smallvec"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""borsh\"", \""default\"", \""quickcheck\"", \""rayon\"", \""serde\"", \""std\"", \""sval\"", \""test_debug\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""indexmap"", ""profile"": 10937888587037481829, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""bytemuck\"", \""example_generated\"", \""serde\"", \""serde_core\"", \""std\""]"", ""features"": ""[\""std\""]"", ""name"": ""bitflags"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""db\""]"", ""features"": ""[]"", ""name"": ""const_oid"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""default\"", \""log\"", \""serde\"", \""serde1\"", \""simd-accel\""]"", ""features"": ""[\""default\"", \""log\""]"", ""name"": ""globset"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""default\"", \""std\"", \""unicode\"", \""unicode-age\"", \""unicode-bool\"", \""unicode-case\"", \""unicode-gencat\"", \""unicode-perl\"", \""unicode-script\"", \""unicode-segment\""]"", ""features"": ""[\""default\"", \""std\"", \""unicode\"", \""unicode-age\"", \""unicode-bool\"", \""unicode-case\"", \""unicode-gencat\"", \""unicode-perl\"", \""unicode-script\"", \""unicode-segment\""]"", ""name"": ""regex_syntax"", ""profile"": 2547513045437502204, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""inline\"", \""raw-api\"", \""rayon\"", \""serde\"", \""typesize\""]"", ""features"": ""[]"", ""name"": ""dashmap"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arc-swap\"", \""backend-atomic\"", \""backend-bitmap\"", \""backend-mmap\"", \""bitflags\"", \""default\"", \""rawfd\"", \""vmm-sys-util\"", \""xen\""]"", ""features"": ""[\""backend-mmap\"", \""default\"", \""rawfd\""]"", ""name"": ""vm_memory"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arc_lock\"", \""atomic_usize\"", \""default\"", \""nightly\"", \""owning_ref\"", \""serde\""]"", ""features"": ""[\""atomic_usize\"", \""default\""]"", ""name"": ""lock_api"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arc_lock\"", \""deadlock_detection\"", \""default\"", \""hardware-lock-elision\"", \""nightly\"", \""owning_ref\"", \""send_guard\"", \""serde\""]"", ""features"": ""[\""default\""]"", ""name"": ""parking_lot"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arrays\"", \""bindgen\"", \""debug\"", \""default\"", \""doc-cfg\"", \""experimental\"", \""fat-lto\"", \""legacy\"", \""no_asm\"", \""pkg-config\"", \""seekable\"", \""std\"", \""thin\"", \""thin-lto\"", \""zdict_builder\"", \""zstdmt\""]"", ""features"": ""[\""arrays\"", \""legacy\"", \""std\"", \""zdict_builder\""]"", ""name"": ""zstd_safe"", ""profile"": 10592522859712577474, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arrays\"", \""bindgen\"", \""debug\"", \""default\"", \""doc-cfg\"", \""experimental\"", \""fat-lto\"", \""legacy\"", \""no_asm\"", \""pkg-config\"", \""thin\"", \""thin-lto\"", \""wasm\"", \""zdict_builder\"", \""zstdmt\""]"", ""features"": ""[\""arrays\"", \""default\"", \""legacy\"", \""zdict_builder\""]"", ""name"": ""zstd"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""asm\"", \""default\"", \""force-soft\"", \""loongarch64_asm\"", \""md5-asm\"", \""oid\"", \""std\""]"", ""features"": ""[]"", ""name"": ""md5"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""async-await\"", \""attributes\"", \""default\"", \""log\"", \""log-always\"", \""max_level_debug\"", \""max_level_error\"", \""max_level_info\"", \""max_level_off\"", \""max_level_trace\"", \""max_level_warn\"", \""release_max_level_debug\"", \""release_max_level_error\"", \""release_max_level_info\"", \""release_max_level_off\"", \""release_max_level_trace\"", \""release_max_level_warn\"", \""std\"", \""tracing-attributes\"", \""valuable\""]"", ""features"": ""[\""attributes\"", \""default\"", \""std\"", \""tracing-attributes\""]"", ""name"": ""tracing"", ""profile"": 1647937577980832904, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""async-await\""]"", ""features"": ""[]"", ""name"": ""tracing_attributes"", ""profile"": 6207960371462447473, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""async\"", \""default\"", \""sync\"", \""sync-wrappers\"", \""vm-memory\""]"", ""features"": ""[\""async\"", \""default\"", \""sync-wrappers\""]"", ""name"": ""imago"", ""profile"": 15916455528278280289, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""auto-color\"", \""color\"", \""default\"", \""humantime\"", \""kv\"", \""regex\"", \""unstable-kv\""]"", ""features"": ""[\""auto-color\"", \""color\"", \""default\"", \""humantime\"", \""regex\""]"", ""name"": ""env_logger"", ""profile"": 10632806285984276979, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""auto\"", \""default\"", \""test\"", \""wincon\""]"", ""features"": ""[\""auto\"", \""default\"", \""wincon\""]"", ""name"": ""anstream"", ""profile"": 4227688284201523262, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""auto\"", \""default\"", \""test\"", \""wincon\""]"", ""features"": ""[\""auto\"", \""wincon\""]"", ""name"": ""anstream"", ""profile"": 10632806285984276979, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""auxvec\"", \""bootparam\"", \""btrfs\"", \""core\"", \""default\"", \""elf\"", \""elf_uapi\"", \""errno\"", \""general\"", \""if_arp\"", \""if_ether\"", \""if_packet\"", \""if_tun\"", \""image\"", \""io_uring\"", \""ioctl\"", \""landlock\"", \""loop_device\"", \""mempolicy\"", \""net\"", \""netlink\"", \""no_std\"", \""prctl\"", \""ptrace\"", \""rustc-dep-of-std\"", \""std\"", \""system\"", \""vm_sockets\"", \""xdp\""]"", ""features"": ""[\""auxvec\"", \""default\"", \""elf\"", \""errno\"", \""general\"", \""ioctl\"", \""no_std\"", \""std\""]"", ""name"": ""linux_raw_sys"", ""profile"": 14036798396267011297, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""aws-lc-rs\"", \""aws_lc_rs\"", \""brotli\"", \""custom-provider\"", \""default\"", \""fips\"", \""hashbrown\"", \""log\"", \""logging\"", \""prefer-post-quantum\"", \""read_buf\"", \""ring\"", \""rustversion\"", \""std\"", \""tls12\"", \""zlib\""]"", ""features"": ""[\""ring\"", \""std\"", \""tls12\""]"", ""name"": ""rustls"", ""profile"": 16187626775398413875, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""aws-lc-rs\"", \""aws_lc_rs\"", \""brotli\"", \""default\"", \""early-data\"", \""fips\"", \""logging\"", \""ring\"", \""tls12\"", \""zlib\""]"", ""features"": ""[\""ring\"", \""tls12\""]"", ""name"": ""tokio_rustls"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""aws-lc-rs\"", \""default\"", \""fips\"", \""http1\"", \""http2\"", \""log\"", \""logging\"", \""native-tokio\"", \""ring\"", \""rustls-native-certs\"", \""rustls-platform-verifier\"", \""tls12\"", \""webpki-roots\"", \""webpki-tokio\""]"", ""features"": ""[\""http1\"", \""http2\"", \""native-tokio\"", \""ring\"", \""rustls-native-certs\"", \""tls12\"", \""webpki-roots\"", \""webpki-tokio\""]"", ""name"": ""hyper_rustls"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""aws\"", \""azure\"", \""base64\"", \""cloud\"", \""default\"", \""form_urlencoded\"", \""fs\"", \""gcp\"", \""http\"", \""http-body-util\"", \""httparse\"", \""hyper\"", \""integration\"", \""md-5\"", \""quick-xml\"", \""rand\"", \""reqwest\"", \""ring\"", \""rustls-pki-types\"", \""serde\"", \""serde_json\"", \""serde_urlencoded\"", \""tls-webpki-roots\"", \""tokio\"", \""walkdir\""]"", ""features"": ""[\""aws\"", \""base64\"", \""cloud\"", \""form_urlencoded\"", \""http-body-util\"", \""hyper\"", \""md-5\"", \""quick-xml\"", \""rand\"", \""reqwest\"", \""ring\"", \""serde\"", \""serde_json\"", \""serde_urlencoded\"", \""tls-webpki-roots\"", \""tokio\""]"", ""name"": ""object_store"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""backtrace\"", \""deadlock_detection\"", \""nightly\"", \""petgraph\""]"", ""features"": ""[]"", ""name"": ""parking_lot_core"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""backtrace\"", \""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""anyhow"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""balance\"", \""buffer\"", \""discover\"", \""filter\"", \""full\"", \""futures-core\"", \""futures-util\"", \""hdrhistogram\"", \""hedge\"", \""indexmap\"", \""limit\"", \""load\"", \""load-shed\"", \""log\"", \""make\"", \""pin-project-lite\"", \""ready-cache\"", \""reconnect\"", \""retry\"", \""slab\"", \""spawn-ready\"", \""steer\"", \""sync_wrapper\"", \""timeout\"", \""tokio\"", \""tokio-stream\"", \""tokio-util\"", \""tracing\"", \""util\""]"", ""features"": ""[\""futures-core\"", \""futures-util\"", \""make\"", \""pin-project-lite\"", \""retry\"", \""sync_wrapper\"", \""timeout\"", \""tokio\"", \""util\""]"", ""name"": ""tower"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""bindgen\"", \""debug\"", \""default\"", \""experimental\"", \""fat-lto\"", \""legacy\"", \""no_asm\"", \""no_wasm_shim\"", \""non-cargo\"", \""pkg-config\"", \""seekable\"", \""std\"", \""thin\"", \""thin-lto\"", \""zdict_builder\"", \""zstdmt\""]"", ""features"": ""[\""legacy\"", \""std\"", \""zdict_builder\""]"", ""name"": ""zstd_sys"", ""profile"": 10592522859712577474, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""borsh\"", \""default\"", \""serde\"", \""std\"", \""zeroize\""]"", ""features"": ""[]"", ""name"": ""arrayvec"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""bytes\"", \""default\"", \""fs\"", \""full\"", \""io-std\"", \""io-uring\"", \""io-util\"", \""libc\"", \""macros\"", \""mio\"", \""net\"", \""parking_lot\"", \""process\"", \""rt\"", \""rt-multi-thread\"", \""signal\"", \""signal-hook-registry\"", \""socket2\"", \""sync\"", \""taskdump\"", \""test-util\"", \""time\"", \""tokio-macros\"", \""tracing\"", \""windows-sys\""]"", ""features"": ""[\""bytes\"", \""default\"", \""fs\"", \""io-util\"", \""libc\"", \""macros\"", \""mio\"", \""net\"", \""process\"", \""rt\"", \""rt-multi-thread\"", \""signal\"", \""signal-hook-registry\"", \""socket2\"", \""sync\"", \""time\"", \""tokio-macros\""]"", ""name"": ""tokio"", ""profile"": 10043413591317300293, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""bzimage\"", \""default\"", \""elf\"", \""pe\""]"", ""features"": ""[\""bzimage\"", \""default\"", \""elf\"", \""pe\""]"", ""name"": ""linux_loader"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""capi\"", \""client\"", \""default\"", \""ffi\"", \""full\"", \""http1\"", \""http2\"", \""nightly\"", \""server\"", \""tracing\""]"", ""features"": ""[\""client\"", \""default\"", \""http1\"", \""http2\"", \""server\""]"", ""name"": ""hyper"", ""profile"": 5907227947356068632, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""cargo\"", \""color\"", \""debug\"", \""default\"", \""deprecated\"", \""derive\"", \""env\"", \""error-context\"", \""help\"", \""std\"", \""string\"", \""suggestions\"", \""unicode\"", \""unstable-derive-ui-tests\"", \""unstable-doc\"", \""unstable-ext\"", \""unstable-markdown\"", \""unstable-styles\"", \""unstable-v5\"", \""usage\"", \""wrap_help\""]"", ""features"": ""[\""color\"", \""default\"", \""derive\"", \""env\"", \""error-context\"", \""help\"", \""std\"", \""string\"", \""suggestions\"", \""usage\""]"", ""name"": ""clap"", ""profile"": 4628411354643344498, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""cargo\"", \""color\"", \""debug\"", \""default\"", \""deprecated\"", \""env\"", \""error-context\"", \""help\"", \""std\"", \""string\"", \""suggestions\"", \""unicode\"", \""unstable-doc\"", \""unstable-ext\"", \""unstable-styles\"", \""unstable-v5\"", \""usage\"", \""wrap_help\""]"", ""features"": ""[\""color\"", \""env\"", \""error-context\"", \""help\"", \""std\"", \""string\"", \""suggestions\"", \""usage\""]"", ""name"": ""clap_builder"", ""profile"": 4628411354643344498, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""channel\"", \""default\"", \""full\""]"", ""features"": ""[\""default\""]"", ""name"": ""http_body_util"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""cipher\"", \""default\"", \""legacy\"", \""rng\"", \""xchacha\"", \""zeroize\""]"", ""features"": ""[\""rng\""]"", ""name"": ""chacha20"", ""profile"": 4285365006624603084, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""cjk\"", \""core\"", \""default\"", \""no_std\"", \""rustc-dep-of-std\"", \""std\""]"", ""features"": ""[\""cjk\"", \""default\""]"", ""name"": ""unicode_width"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""cli\"", \""default\""]"", ""features"": ""[]"", ""name"": ""pvisor_overlayfs"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""clone-impls\"", \""default\"", \""derive\"", \""extra-traits\"", \""fold\"", \""full\"", \""parsing\"", \""printing\"", \""proc-macro\"", \""test\"", \""visit\"", \""visit-mut\""]"", ""features"": ""[\""clone-impls\"", \""default\"", \""derive\"", \""extra-traits\"", \""fold\"", \""full\"", \""parsing\"", \""printing\"", \""proc-macro\"", \""visit\"", \""visit-mut\""]"", ""name"": ""syn"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""compiled_data\"", \""datagen\"", \""default\"", \""harfbuzz_traits\"", \""icu_properties\"", \""serde\"", \""utf16_iter\"", \""utf8_iter\"", \""write16\""]"", ""features"": ""[\""compiled_data\""]"", ""name"": ""icu_normalizer"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""compiled_data\""]"", ""features"": ""[\""compiled_data\""]"", ""name"": ""idna_adapter"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""compiler_builtins\"", \""core\"", \""custom\"", \""js\"", \""js-sys\"", \""linux_disable_fallback\"", \""rdrand\"", \""rustc-dep-of-std\"", \""std\"", \""test-in-browser\"", \""wasm-bindgen\""]"", ""features"": ""[]"", ""name"": ""getrandom"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""compiler_builtins\"", \""core\"", \""default\"", \""example_generated\"", \""rustc-dep-of-std\""]"", ""features"": ""[\""default\""]"", ""name"": ""bitflags"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""const-generics\"", \""core_hint_black_box\"", \""default\"", \""i128\"", \""nightly\"", \""std\""]"", ""features"": ""[]"", ""name"": ""subtle"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""const-generics\"", \""default\"", \""nightly\"", \""std\""]"", ""features"": ""[]"", ""name"": ""simd_adler32"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""const-generics\"", \""i128\"", \""scale-info\"", \""scale_info\"", \""strict\""]"", ""features"": ""[\""const-generics\""]"", ""name"": ""typenum"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""core\"", \""default\"", \""rustc-dep-of-std\"", \""std\""]"", ""features"": ""[]"", ""name"": ""adler2"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""core\"", \""default\"", \""utf8\""]"", ""features"": ""[\""default\"", \""utf8\""]"", ""name"": ""anstyle_parse"", ""profile"": 10632806285984276979, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""core\"", \""default\"", \""utf8\""]"", ""features"": ""[\""default\"", \""utf8\""]"", ""name"": ""anstyle_parse"", ""profile"": 4227688284201523262, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""core\"", \""rustc-dep-of-std\""]"", ""features"": ""[]"", ""name"": ""cfg_if"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""core\"", \""rustc-dep-of-std\""]"", ""features"": ""[]"", ""name"": ""cfg_if"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""count_instructions_test\"", \""default\"", \""std\""]"", ""features"": ""[\""std\""]"", ""name"": ""constant_time_eq"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""debug\"", \""default\"", \""deprecated\"", \""raw-deprecated\"", \""unstable-markdown\"", \""unstable-v5\""]"", ""features"": ""[\""default\""]"", ""name"": ""clap_derive"", ""profile"": 5346920246396443047, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""debugger_visualizer\"", \""default\"", \""expose_internals\"", \""serde\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""url"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""deserialize_in_place\""]"", ""features"": ""[\""default\""]"", ""name"": ""serde_derive"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""digest\"", \""mmap\"", \""neon\"", \""no_avx2\"", \""no_avx512\"", \""no_neon\"", \""no_sse2\"", \""no_sse41\"", \""prefer_intrinsics\"", \""pure\"", \""rayon\"", \""serde\"", \""std\"", \""traits-preview\"", \""wasm32_simd\"", \""zeroize\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""blake3"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""display\"", \""indexmap\"", \""parse\"", \""preserve_order\"", \""unbounded\""]"", ""features"": ""[\""default\"", \""display\"", \""parse\""]"", ""name"": ""toml"", ""profile"": 11768715044625735406, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""display\"", \""parse\"", \""perf\"", \""serde\"", \""unbounded\"", \""unstable-debug\""]"", ""features"": ""[\""default\"", \""display\"", \""parse\"", \""serde\""]"", ""name"": ""toml_edit"", ""profile"": 11768715044625735406, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""echo-server\""]"", ""features"": ""[]"", ""name"": ""pvisor_gateway"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""extra-platforms\"", \""serde\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""bytes"", ""profile"": 11003502350373712158, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""fs\"", \""full\"", \""io-util\"", \""net\"", \""signal\"", \""sync\"", \""time\"", \""tokio-util\""]"", ""features"": ""[\""default\"", \""time\""]"", ""name"": ""tokio_stream"", ""profile"": 10043413591317300293, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""gateway\""]"", ""features"": ""[\""default\"", \""gateway\""]"", ""name"": ""pvisor_cli"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""gateway\""]"", ""features"": ""[\""gateway\""]"", ""name"": ""pvisor"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""getrandom\"", \""nightly\""]"", ""features"": ""[\""default\"", \""getrandom\""]"", ""name"": ""tempfile"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""hashbrown\"", \""nightly\""]"", ""features"": ""[\""default\"", \""hashbrown\""]"", ""name"": ""lru"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""heapless\"", \""json\"", \""schemars\"", \""schemars08\"", \""schemars1\"", \""ser_as_str\"", \""serde\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""ipnet"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""i128\"", \""libm\"", \""std\""]"", ""features"": ""[]"", ""name"": ""num_traits"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""i128\"", \""std\""]"", ""features"": ""[]"", ""name"": ""byteorder"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""is_sync\""]"", ""features"": ""[\""default\""]"", ""name"": ""maybe_async"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""libbz2-rs-sys\"", \""static\""]"", ""features"": ""[\""default\""]"", ""name"": ""bzip2"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""log\"", \""net\"", \""os-ext\"", \""os-poll\""]"", ""features"": ""[\""net\"", \""os-ext\"", \""os-poll\""]"", ""name"": ""mio"", ""profile"": 16626080924472169626, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""logging\"", \""pattern\"", \""perf\"", \""perf-backtrack\"", \""perf-cache\"", \""perf-dfa\"", \""perf-dfa-full\"", \""perf-inline\"", \""perf-literal\"", \""perf-onepass\"", \""std\"", \""unicode\"", \""unicode-age\"", \""unicode-bool\"", \""unicode-case\"", \""unicode-gencat\"", \""unicode-perl\"", \""unicode-script\"", \""unicode-segment\"", \""unstable\"", \""use_std\""]"", ""features"": ""[\""default\"", \""perf\"", \""perf-backtrack\"", \""perf-cache\"", \""perf-dfa\"", \""perf-inline\"", \""perf-literal\"", \""perf-onepass\"", \""std\"", \""unicode\"", \""unicode-age\"", \""unicode-bool\"", \""unicode-case\"", \""unicode-gencat\"", \""unicode-perl\"", \""unicode-script\"", \""unicode-segment\""]"", ""name"": ""regex"", ""profile"": 2547513045437502204, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""logging\"", \""perf-literal\"", \""std\""]"", ""features"": ""[\""default\"", \""perf-literal\"", \""std\""]"", ""name"": ""aho_corasick"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""loom\"", \""nightly\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""crossbeam_utils"", ""profile"": 10433883304768107316, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""nightly\"", \""proc-macro\"", \""span-locations\""]"", ""features"": ""[\""default\"", \""proc-macro\""]"", ""name"": ""proc_macro2"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""nightly\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""crc32fast"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""nightly\""]"", ""features"": ""[\""default\""]"", ""name"": ""utf8parse"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""no_simd\"", \""simd\"", \""std\""]"", ""features"": ""[\""simd\"", \""std\""]"", ""name"": ""ppv_lite86"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""once_cell\"", \""std\"", \""valuable\""]"", ""features"": ""[\""once_cell\"", \""std\""]"", ""name"": ""tracing_core"", ""profile"": 1647937577980832904, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""os_rng\"", \""serde\"", \""std\""]"", ""features"": ""[\""std\""]"", ""name"": ""rand_chacha"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""proc-macro\""]"", ""features"": ""[\""default\"", \""proc-macro\""]"", ""name"": ""quote"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""proc-macro\""]"", ""features"": ""[\""default\"", \""proc-macro\""]"", ""name"": ""synstructure"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""regex\"", \""std\""]"", ""features"": ""[\""regex\"", \""std\""]"", ""name"": ""env_filter"", ""profile"": 10632806285984276979, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""serde\"", \""std\"", \""use_std\""]"", ""features"": ""[\""std\"", \""use_std\""]"", ""name"": ""either"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""serde\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""semver"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""serde\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""slab"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""set\""]"", ""features"": ""[\""default\""]"", ""name"": ""hostname"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\"", \""unstable\""]"", ""features"": ""[\""std\""]"", ""name"": ""futures_io"", ""profile"": 8157235479358073570, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""anstyle"", ""profile"": 10632806285984276979, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""crossbeam_channel"", ""profile"": 10433883304768107316, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""errno"", ""profile"": 14231071289633644017, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""fnv"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""http"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""httparse"", ""profile"": 1675385711784277470, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""shlex"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""thiserror"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[]"", ""name"": ""displaydoc"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[]"", ""name"": ""foldhash"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""unstable_const\"", \""unstable_offset_of\""]"", ""features"": ""[\""default\""]"", ""name"": ""memoffset"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""unsupported\""]"", ""features"": ""[\""default\"", \""unsupported\""]"", ""name"": ""xattr"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""use_alloc\"", \""use_std\""]"", ""features"": ""[\""default\"", \""use_alloc\"", \""use_std\""]"", ""name"": ""itertools"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""use_std\""]"", ""features"": ""[]"", ""name"": ""scopeguard"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""xattr\""]"", ""features"": ""[\""default\"", \""xattr\""]"", ""name"": ""tar"", ""profile"": 4339154717131654023, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\""]"", ""features"": ""[\""default\""]"", ""name"": ""is_terminal_polyfill"", ""profile"": 5812794364236305338, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""fallback\""]"", ""features"": ""[\""fallback\""]"", ""name"": ""iana_time_zone"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""fam-wrappers\"", \""serde\"", \""vmm-sys-util\""]"", ""features"": ""[\""fam-wrappers\"", \""serde\"", \""vmm-sys-util\""]"", ""name"": ""kvm_bindings"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""futures\"", \""futures-core\""]"", ""features"": ""[\""futures\"", \""futures-core\""]"", ""name"": ""sync_wrapper"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""getrandom\"", \""rand_core\"", \""std\""]"", ""features"": ""[]"", ""name"": ""crypto_common"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""getrandom\"", \""rand_core\"", \""zeroize\""]"", ""features"": ""[]"", ""name"": ""crypto_common"", ""profile"": 2606296542320589278, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""jobserver\"", \""parallel\""]"", ""features"": ""[\""parallel\""]"", ""name"": ""cc"", ""profile"": 12458195255332598410, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""more_lengths\"", \""serde\"", \""zeroize\""]"", ""features"": ""[\""more_lengths\""]"", ""name"": ""generic_array"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""mu\""]"", ""features"": ""[]"", ""name"": ""humantime"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""no-panic\"", \""small\""]"", ""features"": ""[]"", ""name"": ""ryu"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""no-panic\""]"", ""features"": ""[]"", ""name"": ""itoa"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""no-panic\""]"", ""features"": ""[]"", ""name"": ""zmij"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""no_std\"", \""spin\""]"", ""features"": ""[]"", ""name"": ""page_size"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""os_rng\"", \""serde\"", \""std\""]"", ""features"": ""[\""os_rng\"", \""std\""]"", ""name"": ""rand_core"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""portable-atomic\""]"", ""features"": ""[]"", ""name"": ""atomic_waker"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""proc-macro2\""]"", ""features"": ""[]"", ""name"": ""virtue"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""serde\"", \""serde_derive\"", \""with-serde\""]"", ""features"": ""[\""serde\"", \""serde_derive\"", \""with-serde\""]"", ""name"": ""vmm_sys_util"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""serde\"", \""serde_support\""]"", ""features"": ""[]"", ""name"": ""caps"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""serde\""]"", ""features"": ""[\""serde\""]"", ""name"": ""serde_spanned"", ""profile"": 11768715044625735406, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""serde\""]"", ""features"": ""[\""serde\""]"", ""name"": ""toml_datetime"", ""profile"": 11768715044625735406, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""std\"", \""sys_rng\"", \""wasm_js\""]"", ""features"": ""[\""std\"", \""sys_rng\""]"", ""name"": ""getrandom"", ""profile"": 7847730558161019686, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""std\"", \""wasm_js\""]"", ""features"": ""[\""std\""]"", ""name"": ""getrandom"", ""profile"": 3240661740686181567, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""stream\"", \""unstable\""]"", ""features"": ""[]"", ""name"": ""h2"", ""profile"": 13333777962520875826, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""zeroize\""]"", ""features"": ""[]"", ""name"": ""block_buffer"", ""profile"": 2548229083023945596, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""anstyle_query"", ""profile"": 8192080853972711267, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""arrayref"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""async_trait"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""autocfg"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""bincode_derive"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""block_buffer"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""cfg_aliases"", ""profile"": 11980061553486606077, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""clap_lex"", ""profile"": 4628411354643344498, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""colorchoice"", ""profile"": 10632806285984276979, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""compression_core"", ""profile"": 4107725171516170900, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""cpufeatures"", ""profile"": 2548229083023945596, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""dirs"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""dirs_sys"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""equivalent"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""filetime"", ""profile"": 4796329815077315148, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""find_msvc_tools"", ""profile"": 12458195255332598410, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""fs2"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""futures_macro"", ""profile"": 1218413102171684145, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""hash32"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""heck"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""http_body"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""httpdate"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""icu_normalizer_data"", ""profile"": 18083946540466509289, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""icu_properties_data"", ""profile"": 18083946540466509289, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""jobserver"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""kvm_ioctls"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""libloading"", ""profile"": 12556093047751754406, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""libloading"", ""profile"": 6679834303860230680, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""mime"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""openssl_probe"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""option_ext"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""pin_project_lite"", ""profile"": 14034241328553317513, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""pkg_config"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""pvisor_core"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""pvisor_guest"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""pvisor_journal"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""pvisor_overlay_core"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""pvisor_overlaynet"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""pvisor_replay"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""rand_core"", ""profile"": 11804347602484681322, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""rustc_version"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""rustls_native_certs"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""serde_path_to_error"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""serde_urlencoded"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""serde_yaml"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""signal_hook_registry"", ""profile"": 8111324945914507537, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""strsim"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""thiserror_impl"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""tokio_macros"", ""profile"": 5029862450655259090, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""tower_layer"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""tower_service"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""try_lock"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""unicode_ident"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""unsafe_libyaml"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""untrusted"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""unty"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""utf8_iter"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""version_check"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""virtio_bindings"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""vt100"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""want"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""webpki_roots"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""yoke_derive"", ""profile"": 4981139003136920394, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""zerocopy_derive"", ""profile"": 16810066265625103578, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""zerofrom_derive"", ""profile"": 4981139003136920394, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""zerovec_derive"", ""profile"": 4981139003136920394, ""rustc"": 1625334936438085654, ""rustflags"": []}]}, ""environment"": {""CARGO_BUILD_JOBS"": ""2"", ""CARGO_NET_OFFLINE"": ""true""}, ""firmware_sha256"": ""b61f68dac3ef20a88e1ee387733e4baed2f7c02edac940c8882e0b549dac95e4"", ""pvisor_sha256"": ""939f72289a72f73ba64d645c891842ed4a9408c7eaef4c54d5fa04aed0371e85"", ""rustc"": ""rustc 1.98.1 (48a229cea 2026-09-01)\nbinary: rustc\ncommit-hash: 48a229ceaefd4985c50990b14116b6d856af0985\ncommit-date: 2026-09-01\nhost: x86_64-unknown-linux-gnu\nrelease: 1.98.1\nLLVM version: 22.1.8\n"", ""source_identity"": {""head"": ""b3e2f03509396b8ebe840a9ccdefa63424bc1afe"", ""manifest_sha256"": ""07b27144366d96dbac053bd18288a50c7468b231d0e8570b3f400b38c28d0f8f"", ""recorded_at"": ""2026-10-11T05:29:47.568168+00:00"", ""scope"": ""complete current tracked/unignored files excluding generated target and raw .data"", ""status"": "" M .github/workflows/ci.yml\n M .github/workflows/docs.yml\n M .github/workflows/pvisor-benchmark.yml\n M README.md\n M benchmark/README.md\n M crates/pvisor-cli/README.md\n M crates/pvisor-cli/src/cli/host_process.rs\n M crates/pvisor-cli/src/cli/host_service.rs\n M crates/pvisor-cli/src/cli/runtime.rs\n M crates/pvisor-overlay-core/src/core.rs\n M crates/pvisor-overlayfs/README.md\n M crates/pvisor-overlayfs/src/api.rs\n M crates/pvisor-overlayfs/src/dispatch.rs\n M crates/pvisor-overlayfs/src/fs.rs\n M crates/pvisor-vm/Cargo.toml\n M crates/pvisor-vm/README.md\n M crates/pvisor-vm/src/api.rs\n M crates/pvisor-vm/src/builder.rs\n M crates/pvisor-vm/src/contract_tests/vm_snapshot_fs.rs\n M crates/pvisor-vm/src/devices/virtio/device.rs\n M crates/pvisor-vm/src/devices/virtio/fs/augment_fs.rs\n M crates/pvisor-vm/src/devices/virtio/fs/device.rs\n M crates/pvisor-vm/src/devices/virtio/fs/filesystem.rs\n M crates/pvisor-vm/src/devices/virtio/fs/fuse.rs\n M crates/pvisor-vm/src/devices/virtio/fs/linux/passthrough.rs\n M crates/pvisor-vm/src/devices/virtio/fs/linux/passthrough_snapshot.rs\n M crates/pvisor-vm/src/devices/virtio/fs/macos/passthrough.rs\n M crates/pvisor-vm/src/devices/virtio/fs/mod.rs\n M crates/pvisor-vm/src/devices/virtio/fs/overlay.rs\n M crates/pvisor-vm/src/devices/virtio/fs/read_only.rs\n M crates/pvisor-vm/src/devices/virtio/fs/server.rs\n M crates/pvisor-vm/src/devices/virtio/fs/snapshot.rs\n M crates/pvisor-vm/src/devices/virtio/fs/worker.rs\n M crates/pvisor-vm/src/devices/virtio/gpu/device.rs\n M crates/pvisor-vm/src/devices/virtio/mmio.rs\n M crates/pvisor-vm/src/portable.rs\n M crates/pvisor-vm/src/vmm/builder.rs\n M crates/pvisor-vm/src/vmm/linux/vstate.rs\n M crates/pvisor-vm/tests/api_contract.rs\n M docs/README.md\n M docs/src/en/benchmarks/agent-tasks.md\n M docs/src/en/benchmarks/cluster-scalability.md\n M docs/src/en/benchmarks/compare-rl-infra.md\n M docs/src/en/benchmarks/density.md\n M docs/src/en/benchmarks/evidence-sources.csv\n M docs/src/en/benchmarks/filesystem.csv\n M docs/src/en/benchmarks/filesystem.md\n M docs/src/en/benchmarks/index.csv\n M docs/src/en/benchmarks/index.md\n M docs/src/en/benchmarks/isolation-tests.md\n M docs/src/en/benchmarks/lazy-image-startup.md\n M docs/src/en/benchmarks/methodology.md\n M docs/src/en/benchmarks/network.md\n M docs/src/en/benchmarks/replay-fidelity.md\n M docs/src/en/benchmarks/startup.md\n M docs/src/en/benchmarks/supervision-cost.md\n M docs/src/en/benchmarks/vm-memory/index.md\n M docs/src/en/design/agent-task-performance-analysis.md\n M docs/src/en/design/benchmark-methodology-evidence.md\n M docs/src/en/design/cluster-performance-analysis.md\n M docs/src/en/design/filesystem-performance-analysis.md\n M docs/src/en/design/index.md\n M docs/src/en/design/vm-memory-performance-analysis.md\n M docs/src/en/design/vm-startup-performance-analysis.md\n M docs/src/zh/benchmarks/agent-tasks.md\n M docs/src/zh/benchmarks/cluster-scalability.md\n M docs/src/zh/benchmarks/compare-rl-infra.md\n M docs/src/zh/benchmarks/density.md\n M docs/src/zh/benchmarks/evidence-sources.csv\n M docs/src/zh/benchmarks/filesystem.csv\n M docs/src/zh/benchmarks/filesystem.md\n M docs/src/zh/benchmarks/index.csv\n M docs/src/zh/benchmarks/index.md\n M docs/src/zh/benchmarks/isolation-tests.md\n M docs/src/zh/benchmarks/lazy-image-startup.md\n M docs/src/zh/benchmarks/methodology.md\n M docs/src/zh/benchmarks/network.md\n M docs/src/zh/benchmarks/replay-fidelity.md\n M docs/src/zh/benchmarks/startup.md\n M docs/src/zh/benchmarks/supervision-cost.md\n M docs/src/zh/benchmarks/vm-memory/index.md\n M docs/src/zh/design/agent-task-performance-analysis.md\n M docs/src/zh/design/benchmark-methodology-evidence.md\n M docs/src/zh/design/cluster-performance-analysis.md\n M docs/src/zh/design/filesystem-performance-analysis.md\n M docs/src/zh/design/index.md\n M docs/src/zh/design/vm-memory-performance-analysis.md\n M docs/src/zh/design/vm-startup-performance-analysis.md\n M docs/translations.json\n M docs/zensical.en.toml\n M docs/zensical.zh.toml\n D experimental/README.md\n D experimental/vcpu-idle-offload/README.md\n D experimental/vcpu-idle-offload/design.md\n D experimental/vcpu-idle-offload/implementation-plan.md\n D experimental/vcpu-idle-offload/validation.md\n M justfile\n M tests/justfile\n M tests/test_docs.py\n M tools/semspec/Cargo.lock\n M tools/semspec/Cargo.toml\n M tools/semspec/DESIGN.md\n M tools/semspec/README.md\n M tools/semspec/src/helpers.rs\n M tools/semspec/src/lib.rs\n M tools/semspec/src/main.rs\n?? benchmark.new/\n?? crates/pvisor-vm/src/devices/virtio/fs/dax.rs\n?? crates/pvisor-vm/src/devices/virtio/fs/worker/\n?? crates/pvisor-vm/src/probes/readonly_dax_case.rs\n?? docs/semspec_tables.py\n?? docs/src/en/benchmarks/filesystem-cache-policy.csv\n?? docs/src/en/benchmarks/filesystem-comparisons.csv\n?? docs/src/en/benchmarks/filesystem-provenance.csv\n?? docs/src/en/benchmarks/filesystem-semspec.csv\n?? docs/src/en/benchmarks/filesystem-semspec.render.json\n?? docs/src/en/benchmarks/filesystem-semspec.render.provenance.json\n?? docs/src/en/benchmarks/measurement-coverage.csv\n?? docs/src/en/benchmarks/measurement-coverage.render.json\n?? docs/src/en/benchmarks/replay-semspec.csv\n?? docs/src/en/benchmarks/replay-semspec.render.json\n?? docs/src/en/benchmarks/replay-semspec.render.provenance.json\n?? docs/src/en/benchmarks/startup-hyperfine-legacy.render.json\n?? docs/src/en/benchmarks/startup-hyperfine.csv\n?? docs/src/en/benchmarks/startup-ready-exit.csv\n?? docs/src/en/benchmarks/startup-ready-exit.exit.render.json\n?? docs/src/en/benchmarks/startup-ready-exit.provenance.json\n?? docs/src/en/benchmarks/startup-ready-exit.ready.render.json\n?? docs/src/en/benchmarks/startup-ready-exit.render.json\n?? docs/src/en/benchmarks/startup-ready.csv\n?? docs/src/en/benchmarks/startup-ready.provenance.json\n?? docs/src/en/benchmarks/startup-ready.render.json\n?? docs/src/en/benchmarks/startup-reboot-comparison.csv\n?? docs/src/en/benchmarks/startup-semspec.csv\n?? docs/src/en/benchmarks/startup-semspec.provenance.json\n?? docs/src/en/benchmarks/startup-semspec.render.json\n?? docs/src/en/benchmarks/startup-timing.csv\n?? docs/src/en/benchmarks/startup-timing.exit.render.json\n?? docs/src/en/benchmarks/startup-timing.provenance.json\n?? docs/src/en/benchmarks/startup-timing.ready.render.json\n?? docs/src/en/benchmarks/startup-timing.render.json\n?? docs/src/en/benchmarks/tables/\n?? docs/src/en/benchmarks/vm-memory/tables/\n?? docs/src/en/design/concurrency-filesystem-summary.csv\n?? docs/src/en/design/concurrency-retest-provenance.csv\n?? docs/src/en/design/concurrency-startup-summary.csv\n?? docs/src/en/design/direct-queue-filesystem-summary.csv\n?? docs/src/en/design/direct-queue-provenance.csv\n?? docs/src/en/design/filesystem-evidence-overview.csv\n?? docs/src/en/design/filesystem-measurement-status.csv\n?? docs/src/en/design/host-copy-up-summary.csv\n?? docs/src/en/design/host-idle-dispatch-provenance.csv\n?? docs/src/en/design/host-idle-dispatch-summary.csv\n?? docs/src/en/design/host-read-summary.csv\n?? docs/src/en/design/journal-lock-summary.csv\n?? docs/src/en/design/readonly-dax-provenance.csv\n?? docs/src/en/design/readonly-dax-summary.csv\n?? docs/src/en/design/tables/\n?? docs/src/en/design/vm-completion-dispatch-provenance.csv\n?? docs/src/en/design/vm-completion-dispatch-summary.csv\n?? docs/src/en/design/writeback-filesystem-summary.csv\n?? docs/src/en/design/writeback-provenance.csv\n?? docs/src/zh/benchmarks/filesystem-cache-policy.csv\n?? docs/src/zh/benchmarks/filesystem-comparisons.csv\n?? docs/src/zh/benchmarks/filesystem-provenance.csv\n?? docs/src/zh/benchmarks/filesystem-semspec.csv\n?? docs/src/zh/benchmarks/filesystem-semspec.render.json\n?? docs/src/zh/benchmarks/filesystem-semspec.render.provenance.json\n?? docs/src/zh/benchmarks/measurement-coverage.csv\n?? docs/src/zh/benchmarks/measurement-coverage.render.json\n?? docs/src/zh/benchmarks/replay-semspec.csv\n?? docs/src/zh/benchmarks/replay-semspec.render.json\n?? docs/src/zh/benchmarks/replay-semspec.render.provenance.json\n?? docs/src/zh/benchmarks/startup-hyperfine-legacy.render.json\n?? docs/src/zh/benchmarks/startup-hyperfine.csv\n?? docs/src/zh/benchmarks/startup-ready-exit.csv\n?? docs/src/zh/benchmarks/startup-ready-exit.exit.render.json\n?? docs/src/zh/benchmarks/startup-ready-exit.provenance.json\n?? docs/src/zh/benchmarks/startup-ready-exit.ready.render.json\n?? docs/src/zh/benchmarks/startup-ready-exit.render.json\n?? docs/src/zh/benchmarks/startup-ready.csv\n?? docs/src/zh/benchmarks/startup-ready.provenance.json\n?? docs/src/zh/benchmarks/startup-ready.render.json\n?? docs/src/zh/benchmarks/startup-reboot-comparison.csv\n?? docs/src/zh/benchmarks/startup-semspec.csv\n?? docs/src/zh/benchmarks/startup-semspec.provenance.json\n?? docs/src/zh/benchmarks/startup-semspec.render.json\n?? docs/src/zh/benchmarks/startup-timing.csv\n?? docs/src/zh/benchmarks/startup-timing.exit.render.json\n?? docs/src/zh/benchmarks/startup-timing.provenance.json\n?? docs/src/zh/benchmarks/startup-timing.ready.render.json\n?? docs/src/zh/benchmarks/startup-timing.render.json\n?? docs/src/zh/benchmarks/tables/\n?? docs/src/zh/benchmarks/vm-memory/tables/\n?? docs/src/zh/design/concurrency-filesystem-summary.csv\n?? docs/src/zh/design/concurrency-retest-provenance.csv\n?? docs/src/zh/design/concurrency-startup-summary.csv\n?? docs/src/zh/design/direct-queue-filesystem-summary.csv\n?? docs/src/zh/design/direct-queue-provenance.csv\n?? docs/src/zh/design/experimental/\n?? docs/src/zh/design/filesystem-evidence-overview.csv\n?? docs/src/zh/design/filesystem-measurement-status.csv\n?? docs/src/zh/design/host-copy-up-summary.csv\n?? docs/src/zh/design/host-idle-dispatch-provenance.csv\n?? docs/src/zh/design/host-idle-dispatch-summary.csv\n?? docs/src/zh/design/host-read-summary.csv\n?? docs/src/zh/design/journal-lock-summary.csv\n?? docs/src/zh/design/readonly-dax-provenance.csv\n?? docs/src/zh/design/readonly-dax-summary.csv\n?? docs/src/zh/design/tables/\n?? docs/src/zh/design/vm-completion-dispatch-provenance.csv\n?? docs/src/zh/design/vm-completion-dispatch-summary.csv\n?? docs/src/zh/design/writeback-filesystem-summary.csv\n?? docs/src/zh/design/writeback-provenance.csv\n?? tests/test_semspec_tables.py\n?? tools/semspec/src/table.rs\n?? tools/semspec/tests/tables.rs\n""}, ""source_manifest_sha256"": ""07b27144366d96dbac053bd18288a50c7468b231d0e8570b3f400b38c28d0f8f""}, ""selected"": {""cargo"": ""cargo 1.98.1 (797e8a9bc 2026-08-05)\n"", ""command"": [""cargo"", ""build"", ""--locked"", ""--offline"", ""--release"", ""-p"", ""pvisor-cli"", ""--bin"", ""pvisor"", ""--features"", ""gateway"", ""--target-dir"", ""/home/reiase/workspace/pvisor/target/filesystem-current-build-20261010""], ""compiled_units"": {""cli"": {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""gateway\""]"", ""features"": ""[\""default\"", \""gateway\""]"", ""profile"": 4686446429936418732, ""rustc"": 1625334936438085654, ""rustflags"": []}, ""libraries"": [{""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__disabled\"", \""static\""]"", ""features"": ""[]"", ""name"": ""bzip2_sys"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__docs_rs\"", \""codec\"", \""compat\"", \""default\"", \""full\"", \""futures-io\"", \""futures-util\"", \""hashbrown\"", \""io\"", \""io-util\"", \""join-map\"", \""net\"", \""rt\"", \""slab\"", \""time\"", \""tracing\""]"", ""features"": ""[\""codec\"", \""default\"", \""futures-util\"", \""io\"", \""rt\""]"", ""name"": ""tokio_util"", ""profile"": 10043413591317300293, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__internal_bench\"", \""alloc\"", \""arbitrary\"", \""clock\"", \""core-error\"", \""default\"", \""defmt\"", \""iana-time-zone\"", \""js-sys\"", \""libc\"", \""now\"", \""oldtime\"", \""pure-rust-locales\"", \""rkyv\"", \""rkyv-16\"", \""rkyv-32\"", \""rkyv-64\"", \""rkyv-validation\"", \""serde\"", \""std\"", \""unstable-locales\"", \""wasm-bindgen\"", \""wasmbind\"", \""winapi\"", \""windows-link\""]"", ""features"": ""[\""alloc\"", \""clock\"", \""default\"", \""iana-time-zone\"", \""js-sys\"", \""now\"", \""oldtime\"", \""serde\"", \""std\"", \""wasm-bindgen\"", \""wasmbind\"", \""winapi\"", \""windows-link\""]"", ""name"": ""chrono"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__internal_happy_eyeballs_tests\"", \""client\"", \""client-legacy\"", \""client-pool\"", \""client-proxy\"", \""client-proxy-system\"", \""default\"", \""full\"", \""http1\"", \""http2\"", \""server\"", \""server-auto\"", \""server-graceful\"", \""service\"", \""tokio\"", \""tracing\""]"", ""features"": ""[\""client\"", \""client-legacy\"", \""client-proxy\"", \""default\"", \""http1\"", \""http2\"", \""server\"", \""service\"", \""tokio\""]"", ""name"": ""hyper_util"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__internal_use_only_features_that_work_on_stable\"", \""alloc\"", \""derive\"", \""float-nightly\"", \""simd\"", \""simd-nightly\"", \""std\"", \""zerocopy-derive\""]"", ""features"": ""[\""derive\"", \""simd\"", \""zerocopy-derive\""]"", ""name"": ""zerocopy"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__private\"", \""__private_docs\"", \""default\"", \""form\"", \""http1\"", \""http2\"", \""json\"", \""macros\"", \""matched-path\"", \""multipart\"", \""original-uri\"", \""query\"", \""tokio\"", \""tower-log\"", \""tracing\"", \""ws\""]"", ""features"": ""[\""http1\"", \""http2\"", \""json\"", \""tokio\""]"", ""name"": ""axum"", ""profile"": 7938159781367629850, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__private_docs\"", \""tracing\""]"", ""features"": ""[]"", ""name"": ""axum_core"", ""profile"": 9498297305443351059, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__rustls\"", \""__rustls-ring\"", \""__tls\"", \""blocking\"", \""brotli\"", \""charset\"", \""cookies\"", \""default\"", \""default-tls\"", \""deflate\"", \""gzip\"", \""h2\"", \""hickory-dns\"", \""http2\"", \""http3\"", \""json\"", \""macos-system-configuration\"", \""multipart\"", \""native-tls\"", \""native-tls-alpn\"", \""native-tls-vendored\"", \""rustls-tls\"", \""rustls-tls-manual-roots\"", \""rustls-tls-manual-roots-no-provider\"", \""rustls-tls-native-roots\"", \""rustls-tls-native-roots-no-provider\"", \""rustls-tls-no-provider\"", \""rustls-tls-webpki-roots\"", \""rustls-tls-webpki-roots-no-provider\"", \""socks\"", \""stream\"", \""system-proxy\"", \""trust-dns\"", \""zstd\""]"", ""features"": ""[\""__rustls\"", \""__rustls-ring\"", \""__tls\"", \""blocking\"", \""deflate\"", \""gzip\"", \""h2\"", \""http2\"", \""json\"", \""rustls-tls\"", \""rustls-tls-native-roots\"", \""rustls-tls-native-roots-no-provider\"", \""rustls-tls-webpki-roots\"", \""rustls-tls-webpki-roots-no-provider\"", \""stream\""]"", ""name"": ""reqwest"", ""profile"": 17166782826556458903, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__test_helpers\"", \""default\""]"", ""features"": ""[\""default\""]"", ""name"": ""matchit"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""_netsim\"", \""_proto-fragmentation\"", \""alloc\"", \""assembler-max-segment-count-1\"", \""assembler-max-segment-count-16\"", \""assembler-max-segment-count-2\"", \""assembler-max-segment-count-3\"", \""assembler-max-segment-count-32\"", \""assembler-max-segment-count-4\"", \""assembler-max-segment-count-8\"", \""async\"", \""auto-icmp-echo-reply\"", \""default\"", \""defmt\"", \""dns-max-name-size-128\"", \""dns-max-name-size-255\"", \""dns-max-name-size-64\"", \""dns-max-result-count-1\"", \""dns-max-result-count-16\"", \""dns-max-result-count-2\"", \""dns-max-result-count-3\"", \""dns-max-result-count-32\"", \""dns-max-result-count-4\"", \""dns-max-result-count-8\"", \""dns-max-server-count-1\"", \""dns-max-server-count-16\"", \""dns-max-server-count-2\"", \""dns-max-server-count-3\"", \""dns-max-server-count-32\"", \""dns-max-server-count-4\"", \""dns-max-server-count-8\"", \""fragmentation-buffer-size-1024\"", \""fragmentation-buffer-size-1500\"", \""fragmentation-buffer-size-16384\"", \""fragmentation-buffer-size-2048\"", \""fragmentation-buffer-size-256\"", \""fragmentation-buffer-size-32768\"", \""fragmentation-buffer-size-4096\"", \""fragmentation-buffer-size-512\"", \""fragmentation-buffer-size-65536\"", \""fragmentation-buffer-size-8192\"", \""iface-max-addr-count-1\"", \""iface-max-addr-count-2\"", \""iface-max-addr-count-3\"", \""iface-max-addr-count-4\"", \""iface-max-addr-count-5\"", \""iface-max-addr-count-6\"", \""iface-max-addr-count-7\"", \""iface-max-addr-count-8\"", \""iface-max-multicast-group-count-1\"", \""iface-max-multicast-group-count-1024\"", \""iface-max-multicast-group-count-128\"", \""iface-max-multicast-group-count-16\"", \""iface-max-multicast-group-count-2\"", \""iface-max-multicast-group-count-256\"", \""iface-max-multicast-group-count-3\"", \""iface-max-multicast-group-count-32\"", \""iface-max-multicast-group-count-4\"", \""iface-max-multicast-group-count-5\"", \""iface-max-multicast-group-count-512\"", \""iface-max-multicast-group-count-6\"", \""iface-max-multicast-group-count-64\"", \""iface-max-multicast-group-count-7\"", \""iface-max-multicast-group-count-8\"", \""iface-max-prefix-count-1\"", \""iface-max-prefix-count-2\"", \""iface-max-prefix-count-3\"", \""iface-max-prefix-count-4\"", \""iface-max-prefix-count-5\"", \""iface-max-prefix-count-6\"", \""iface-max-prefix-count-7\"", \""iface-max-prefix-count-8\"", \""iface-max-route-count-0\"", \""iface-max-route-count-1\"", \""iface-max-route-count-1024\"", \""iface-max-route-count-128\"", \""iface-max-route-count-16\"", \""iface-max-route-count-2\"", \""iface-max-route-count-256\"", \""iface-max-route-count-3\"", \""iface-max-route-count-32\"", \""iface-max-route-count-4\"", \""iface-max-route-count-5\"", \""iface-max-route-count-512\"", \""iface-max-route-count-6\"", \""iface-max-route-count-64\"", \""iface-max-route-count-7\"", \""iface-max-route-count-8\"", \""iface-max-sixlowpan-address-context-count-1\"", \""iface-max-sixlowpan-address-context-count-1024\"", \""iface-max-sixlowpan-address-context-count-128\"", \""iface-max-sixlowpan-address-context-count-16\"", \""iface-max-sixlowpan-address-context-count-2\"", \""iface-max-sixlowpan-address-context-count-256\"", \""iface-max-sixlowpan-address-context-count-3\"", \""iface-max-sixlowpan-address-context-count-32\"", \""iface-max-sixlowpan-address-context-count-4\"", \""iface-max-sixlowpan-address-context-count-5\"", \""iface-max-sixlowpan-address-context-count-512\"", \""iface-max-sixlowpan-address-context-count-6\"", \""iface-max-sixlowpan-address-context-count-64\"", \""iface-max-sixlowpan-address-context-count-7\"", \""iface-max-sixlowpan-address-context-count-8\"", \""iface-neighbor-cache-count-1\"", \""iface-neighbor-cache-count-1024\"", \""iface-neighbor-cache-count-128\"", \""iface-neighbor-cache-count-16\"", \""iface-neighbor-cache-count-2\"", \""iface-neighbor-cache-count-256\"", \""iface-neighbor-cache-count-3\"", \""iface-neighbor-cache-count-32\"", \""iface-neighbor-cache-count-4\"", \""iface-neighbor-cache-count-5\"", \""iface-neighbor-cache-count-512\"", \""iface-neighbor-cache-count-6\"", \""iface-neighbor-cache-count-64\"", \""iface-neighbor-cache-count-7\"", \""iface-neighbor-cache-count-8\"", \""ipv6-hbh-max-options-1\"", \""ipv6-hbh-max-options-16\"", \""ipv6-hbh-max-options-2\"", \""ipv6-hbh-max-options-3\"", \""ipv6-hbh-max-options-32\"", \""ipv6-hbh-max-options-4\"", \""ipv6-hbh-max-options-8\"", \""libc\"", \""log\"", \""medium-ethernet\"", \""medium-ieee802154\"", \""medium-ip\"", \""multicast\"", \""packetmeta-id\"", \""phy-raw_socket\"", \""phy-tuntap_interface\"", \""proto-dhcpv4\"", \""proto-dns\"", \""proto-ipsec\"", \""proto-ipsec-ah\"", \""proto-ipsec-esp\"", \""proto-ipv4\"", \""proto-ipv4-fragmentation\"", \""proto-ipv6\"", \""proto-ipv6-fragmentation\"", \""proto-ipv6-hbh\"", \""proto-ipv6-routing\"", \""proto-ipv6-slaac\"", \""proto-rpl\"", \""proto-sixlowpan\"", \""proto-sixlowpan-fragmentation\"", \""reassembly-buffer-count-1\"", \""reassembly-buffer-count-16\"", \""reassembly-buffer-count-2\"", \""reassembly-buffer-count-3\"", \""reassembly-buffer-count-32\"", \""reassembly-buffer-count-4\"", \""reassembly-buffer-count-8\"", \""reassembly-buffer-size-1024\"", \""reassembly-buffer-size-1500\"", \""reassembly-buffer-size-16384\"", \""reassembly-buffer-size-2048\"", \""reassembly-buffer-size-256\"", \""reassembly-buffer-size-32768\"", \""reassembly-buffer-size-4096\"", \""reassembly-buffer-size-512\"", \""reassembly-buffer-size-65536\"", \""reassembly-buffer-size-8192\"", \""rpl-parents-buffer-count-16\"", \""rpl-parents-buffer-count-2\"", \""rpl-parents-buffer-count-32\"", \""rpl-parents-buffer-count-4\"", \""rpl-parents-buffer-count-8\"", \""rpl-relations-buffer-count-1\"", \""rpl-relations-buffer-count-128\"", \""rpl-relations-buffer-count-16\"", \""rpl-relations-buffer-count-2\"", \""rpl-relations-buffer-count-32\"", \""rpl-relations-buffer-count-4\"", \""rpl-relations-buffer-count-64\"", \""rpl-relations-buffer-count-8\"", \""socket\"", \""socket-dhcpv4\"", \""socket-dns\"", \""socket-icmp\"", \""socket-mdns\"", \""socket-raw\"", \""socket-tcp\"", \""socket-tcp-cubic\"", \""socket-tcp-pause-synack\"", \""socket-tcp-reno\"", \""socket-udp\"", \""std\"", \""verbose\""]"", ""features"": ""[\""alloc\"", \""log\"", \""medium-ethernet\"", \""proto-dhcpv4\"", \""proto-dns\"", \""proto-ipv4\"", \""socket\"", \""socket-tcp\"", \""socket-tcp-pause-synack\"", \""socket-udp\"", \""std\""]"", ""name"": ""smoltcp"", ""profile"": 13333777962520875826, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""aarch64\"", \""alloc\"", \""default\"", \""derive\"", \""serde\"", \""simd\"", \""std\"", \""zeroize_derive\""]"", ""features"": ""[\""alloc\"", \""default\""]"", ""name"": ""zeroize"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""abi-7-10\"", \""abi-7-11\"", \""abi-7-12\"", \""abi-7-13\"", \""abi-7-14\"", \""abi-7-15\"", \""abi-7-16\"", \""abi-7-17\"", \""abi-7-18\"", \""abi-7-19\"", \""abi-7-20\"", \""abi-7-21\"", \""abi-7-22\"", \""abi-7-23\"", \""abi-7-24\"", \""abi-7-25\"", \""abi-7-26\"", \""abi-7-27\"", \""abi-7-28\"", \""abi-7-29\"", \""abi-7-30\"", \""abi-7-31\"", \""abi-7-9\"", \""default\"", \""libfuse\"", \""macfuse-4-compat\"", \""macfuse-5\"", \""pkg-config\"", \""serde\"", \""serializable\""]"", ""features"": ""[\""abi-7-10\"", \""abi-7-11\"", \""abi-7-12\"", \""abi-7-13\"", \""abi-7-14\"", \""abi-7-15\"", \""abi-7-16\"", \""abi-7-17\"", \""abi-7-18\"", \""abi-7-19\"", \""abi-7-20\"", \""abi-7-21\"", \""abi-7-22\"", \""abi-7-23\"", \""abi-7-24\"", \""abi-7-25\"", \""abi-7-26\"", \""abi-7-27\"", \""abi-7-28\"", \""abi-7-29\"", \""abi-7-30\"", \""abi-7-31\"", \""abi-7-9\""]"", ""name"": ""fuser"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""acct\"", \""aio\"", \""default\"", \""dir\"", \""env\"", \""event\"", \""fanotify\"", \""feature\"", \""fs\"", \""hostname\"", \""inotify\"", \""ioctl\"", \""kmod\"", \""memoffset\"", \""mman\"", \""mount\"", \""mqueue\"", \""net\"", \""personality\"", \""pin-utils\"", \""poll\"", \""process\"", \""pthread\"", \""ptrace\"", \""quota\"", \""reboot\"", \""resource\"", \""sched\"", \""signal\"", \""socket\"", \""syslog\"", \""term\"", \""time\"", \""ucontext\"", \""uio\"", \""user\"", \""zerocopy\""]"", ""features"": ""[\""default\"", \""fs\"", \""ioctl\"", \""memoffset\"", \""net\"", \""poll\"", \""socket\"", \""term\""]"", ""name"": ""nix"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""acct\"", \""aio\"", \""default\"", \""dir\"", \""env\"", \""event\"", \""fanotify\"", \""feature\"", \""fs\"", \""hostname\"", \""inotify\"", \""ioctl\"", \""kmod\"", \""memoffset\"", \""mman\"", \""mount\"", \""mqueue\"", \""net\"", \""personality\"", \""pin-utils\"", \""poll\"", \""process\"", \""pthread\"", \""ptrace\"", \""quota\"", \""reboot\"", \""resource\"", \""sched\"", \""signal\"", \""socket\"", \""term\"", \""time\"", \""ucontext\"", \""uio\"", \""user\"", \""zerocopy\""]"", ""features"": ""[\""default\"", \""feature\"", \""fs\"", \""user\""]"", ""name"": ""nix"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""add-extension\"", \""async-compression\"", \""auth\"", \""base64\"", \""catch-panic\"", \""compression-br\"", \""compression-deflate\"", \""compression-full\"", \""compression-gzip\"", \""compression-zstd\"", \""cors\"", \""decompression-br\"", \""decompression-deflate\"", \""decompression-full\"", \""decompression-gzip\"", \""decompression-zstd\"", \""default\"", \""follow-redirect\"", \""fs\"", \""full\"", \""futures-core\"", \""futures-util\"", \""httpdate\"", \""iri-string\"", \""limit\"", \""map-request-body\"", \""map-response-body\"", \""metrics\"", \""mime\"", \""mime_guess\"", \""normalize-path\"", \""percent-encoding\"", \""propagate-header\"", \""redirect\"", \""request-id\"", \""sensitive-headers\"", \""set-header\"", \""set-status\"", \""timeout\"", \""tokio\"", \""tokio-util\"", \""tower\"", \""trace\"", \""tracing\"", \""util\"", \""uuid\"", \""validate-request\""]"", ""features"": ""[\""async-compression\"", \""decompression-deflate\"", \""decompression-gzip\"", \""follow-redirect\"", \""futures-core\"", \""futures-util\"", \""iri-string\"", \""tokio\"", \""tokio-util\"", \""tower\""]"", ""name"": ""tower_http"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""ahash\"", \""alloc\"", \""allocator-api2\"", \""compiler_builtins\"", \""core\"", \""default\"", \""equivalent\"", \""inline-more\"", \""nightly\"", \""raw\"", \""rayon\"", \""rkyv\"", \""rustc-dep-of-std\"", \""rustc-internal-api\"", \""serde\""]"", ""features"": ""[\""raw\""]"", ""name"": ""hashbrown"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""align\"", \""const-extern-fn\"", \""default\"", \""extra_traits\"", \""rustc-dep-of-std\"", \""rustc-std-workspace-core\"", \""std\"", \""use_std\""]"", ""features"": ""[\""default\"", \""extra_traits\"", \""std\""]"", ""name"": ""libc"", ""profile"": 4248016837003788142, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""align\"", \""const-extern-fn\"", \""default\"", \""extra_traits\"", \""rustc-dep-of-std\"", \""rustc-std-workspace-core\"", \""std\"", \""use_std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""libc"", ""profile"": 4793633013790561351, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""all-algorithms\"", \""brotli\"", \""bzip2\"", \""deflate\"", \""deflate64\"", \""flate2\"", \""gzip\"", \""libzstd\"", \""lz4\"", \""lzma\"", \""memchr\"", \""xz\"", \""xz-parallel\"", \""xz2\"", \""zlib\"", \""zstd\"", \""zstd-safe\"", \""zstdmt\""]"", ""features"": ""[\""flate2\"", \""gzip\"", \""memchr\"", \""zlib\""]"", ""name"": ""compression_codecs"", ""profile"": 4107725171516170900, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""all-apis\"", \""alloc\"", \""core\"", \""default\"", \""event\"", \""fs\"", \""io_uring\"", \""libc\"", \""libc_errno\"", \""linux_4_11\"", \""linux_5_1\"", \""linux_5_11\"", \""linux_latest\"", \""mm\"", \""mount\"", \""net\"", \""param\"", \""pipe\"", \""process\"", \""pty\"", \""rand\"", \""runtime\"", \""rustc-dep-of-std\"", \""rustc-std-workspace-alloc\"", \""shm\"", \""std\"", \""stdio\"", \""system\"", \""termios\"", \""thread\"", \""time\"", \""try_close\"", \""use-explicitly-provided-auxv\"", \""use-libc\"", \""use-libc-auxv\""]"", ""features"": ""[\""alloc\"", \""default\"", \""fs\"", \""std\""]"", ""name"": ""rustix"", ""profile"": 4272393398390303732, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""all\"", \""all-algorithms\"", \""all-implementations\"", \""brotli\"", \""bzip2\"", \""deflate\"", \""deflate64\"", \""futures-io\"", \""gzip\"", \""lz4\"", \""lzma\"", \""tokio\"", \""xz\"", \""xz-parallel\"", \""xz2\"", \""zlib\"", \""zstd\"", \""zstdmt\""]"", ""features"": ""[\""gzip\"", \""tokio\"", \""zlib\""]"", ""name"": ""async_compression"", ""profile"": 4107725171516170900, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""all\""]"", ""features"": ""[\""all\""]"", ""name"": ""socket2"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""allocator-api2\"", \""core\"", \""default\"", \""default-hasher\"", \""equivalent\"", \""inline-more\"", \""nightly\"", \""raw-entry\"", \""rayon\"", \""rustc-dep-of-std\"", \""rustc-internal-api\"", \""serde\""]"", ""features"": ""[\""allocator-api2\"", \""default\"", \""default-hasher\"", \""equivalent\"", \""inline-more\"", \""raw-entry\""]"", ""name"": ""hashbrown"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""allocator-api2\"", \""core\"", \""default\"", \""default-hasher\"", \""equivalent\"", \""inline-more\"", \""nightly\"", \""raw-entry\"", \""rayon\"", \""rustc-dep-of-std\"", \""rustc-internal-api\"", \""serde\""]"", ""features"": ""[]"", ""name"": ""hashbrown"", ""profile"": 10067188661882979502, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""arbitrary\"", \""bytemuck\"", \""ctutils\"", \""extra-sizes\"", \""serde\"", \""subtle\"", \""zerocopy\"", \""zeroize\""]"", ""features"": ""[]"", ""name"": ""hybrid_array"", ""profile"": 7230693119043292679, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""arbitrary_precision\"", \""default\"", \""float_roundtrip\"", \""indexmap\"", \""preserve_order\"", \""raw_value\"", \""std\"", \""unbounded_depth\""]"", ""features"": ""[\""default\"", \""raw_value\"", \""std\""]"", ""name"": ""serde_json"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""async-await\"", \""async-await-macro\"", \""bilock\"", \""cfg-target-has-atomic\"", \""channel\"", \""compat\"", \""default\"", \""futures-channel\"", \""futures-io\"", \""futures-macro\"", \""futures-sink\"", \""futures_01\"", \""io\"", \""io-compat\"", \""libc\"", \""memchr\"", \""portable-atomic\"", \""sink\"", \""slab\"", \""spin\"", \""std\"", \""tokio-io\"", \""unstable\"", \""write-all-vectored\""]"", ""features"": ""[\""alloc\"", \""async-await\"", \""async-await-macro\"", \""default\"", \""futures-io\"", \""futures-macro\"", \""futures-sink\"", \""io\"", \""memchr\"", \""sink\"", \""slab\"", \""std\""]"", ""name"": ""futures_util"", ""profile"": 8157235479358073570, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""async-await\"", \""bilock\"", \""cfg-target-has-atomic\"", \""compat\"", \""default\"", \""executor\"", \""futures-executor\"", \""io-compat\"", \""spin\"", \""std\"", \""thread-pool\"", \""unstable\"", \""write-all-vectored\""]"", ""features"": ""[\""alloc\""]"", ""name"": ""futures"", ""profile"": 8157235479358073570, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""atomic-polyfill\"", \""critical-section\"", \""default\"", \""parking_lot\"", \""portable-atomic\"", \""race\"", \""std\"", \""unstable\""]"", ""features"": ""[\""alloc\"", \""default\"", \""race\"", \""std\""]"", ""name"": ""once_cell"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""aws-lc-rs\"", \""aws-lc-rs-fips\"", \""aws-lc-rs-unstable\"", \""default\"", \""ring\"", \""std\""]"", ""features"": ""[\""alloc\"", \""ring\"", \""std\""]"", ""name"": ""webpki"", ""profile"": 13987763031497049980, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""baked\"", \""deserialize_bincode_1\"", \""deserialize_json\"", \""deserialize_postcard_1\"", \""export\"", \""logging\"", \""serde\"", \""std\"", \""sync\"", \""zerotrie\""]"", ""features"": ""[\""baked\""]"", ""name"": ""icu_provider"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""bincode_derive\"", \""default\"", \""derive\"", \""serde\"", \""std\""]"", ""features"": ""[\""bincode_derive\"", \""derive\""]"", ""name"": ""bincode"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""blobby\"", \""block-api\"", \""default\"", \""dev\"", \""getrandom\"", \""mac\"", \""oid\"", \""rand_core\"", \""zeroize\""]"", ""features"": ""[\""alloc\"", \""block-api\"", \""default\"", \""oid\""]"", ""name"": ""digest"", ""profile"": 2606296542320589278, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""blobby\"", \""block-buffer\"", \""const-oid\"", \""core-api\"", \""default\"", \""dev\"", \""mac\"", \""oid\"", \""rand_core\"", \""std\"", \""subtle\""]"", ""features"": ""[\""block-buffer\"", \""core-api\"", \""default\""]"", ""name"": ""digest"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""block-boundary\"", \""core\"", \""default\"", \""rustc-dep-of-std\"", \""serde\"", \""simd\"", \""simd-adler32\"", \""std\"", \""with-alloc\""]"", ""features"": ""[\""default\"", \""simd\"", \""simd-adler32\"", \""std\"", \""with-alloc\""]"", ""name"": ""miniz_oxide"", ""profile"": 13333777962520875826, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""bytes\"", \""defmt\"", \""embedded-io-v0.7\"", \""mpmc_large\"", \""nightly\"", \""portable-atomic\"", \""portable-atomic-critical-section\"", \""portable-atomic-unsafe-assume-single-core\"", \""serde\"", \""ufmt\"", \""zeroize\""]"", ""features"": ""[]"", ""name"": ""heapless"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""cfg-target-has-atomic\"", \""default\"", \""futures-sink\"", \""sink\"", \""std\"", \""unstable\""]"", ""features"": ""[\""alloc\"", \""default\"", \""futures-sink\"", \""sink\"", \""std\""]"", ""name"": ""futures_channel"", ""profile"": 8157235479358073570, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""cfg-target-has-atomic\"", \""default\"", \""portable-atomic\"", \""std\"", \""unstable\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""futures_core"", ""profile"": 8157235479358073570, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""cfg-target-has-atomic\"", \""default\"", \""std\"", \""unstable\""]"", ""features"": ""[\""alloc\"", \""std\""]"", ""name"": ""futures_task"", ""profile"": 8157235479358073570, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""chacha\"", \""default\"", \""log\"", \""serde\"", \""simd_support\"", \""std\"", \""std_rng\"", \""sys_rng\"", \""thread_rng\"", \""unbiased\""]"", ""features"": ""[\""alloc\"", \""std\"", \""std_rng\"", \""sys_rng\"", \""thread_rng\""]"", ""name"": ""rand"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""compiled_data\"", \""datagen\"", \""default\"", \""harfbuzz_traits\"", \""serde\"", \""unicode_bidi\""]"", ""features"": ""[\""compiled_data\""]"", ""name"": ""icu_properties"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""compiled_data\"", \""default\"", \""std\""]"", ""features"": ""[\""alloc\"", \""compiled_data\"", \""std\""]"", ""name"": ""idna"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""core\"", \""default\"", \""libc\"", \""logging\"", \""rustc-dep-of-std\"", \""std\"", \""use_std\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""memchr"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""databake\"", \""default\"", \""dense\"", \""litemap\"", \""serde\"", \""yoke\"", \""zerofrom\"", \""zerovec\""]"", ""features"": ""[\""yoke\"", \""zerofrom\""]"", ""name"": ""zerotrie"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""databake\"", \""default\"", \""serde\"", \""std\"", \""zerovec\""]"", ""features"": ""[\""zerovec\""]"", ""name"": ""tinystr"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""databake\"", \""default\"", \""serde\"", \""testing\"", \""yoke\""]"", ""features"": ""[]"", ""name"": ""litemap"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""databake\"", \""default\"", \""serde\"", \""writeable\"", \""zerovec\""]"", ""features"": ""[\""zerovec\""]"", ""name"": ""potential_utf"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""databake\"", \""derive\"", \""hashmap\"", \""schemars\"", \""serde\"", \""std\"", \""yoke\""]"", ""features"": ""[\""derive\"", \""yoke\""]"", ""name"": ""zerovec"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""databake\"", \""serde\"", \""zerovec\""]"", ""features"": ""[\""zerovec\""]"", ""name"": ""icu_locale_core"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""databake\"", \""serde\""]"", ""features"": ""[]"", ""name"": ""icu_collections"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""debug\"", \""default\"", \""simd\"", \""std\"", \""unstable-doc\"", \""unstable-recover\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""winnow"", ""profile"": 16551665274674009540, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""defmt\"", \""js\"", \""logging\"", \""perf-inline\"", \""serde\"", \""static\"", \""static-tz\"", \""std\"", \""tz-fat\"", \""tz-system\"", \""tzdb-bundle-always\"", \""tzdb-bundle-platform\"", \""tzdb-concatenated\"", \""tzdb-zoneinfo\""]"", ""features"": ""[\""alloc\"", \""std\""]"", ""name"": ""jiff"", ""profile"": 7947306171983741744, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""defmt\"", \""logging\"", \""std\"", \""tz-fat\""]"", ""features"": ""[\""alloc\"", \""std\""]"", ""name"": ""jiff_core"", ""profile"": 7947306171983741744, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""derive\"", \""rc\"", \""serde_derive\"", \""std\"", \""unstable\""]"", ""features"": ""[\""default\"", \""derive\"", \""rc\"", \""serde_derive\"", \""std\""]"", ""name"": ""serde"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""derive\"", \""serde\"", \""zerofrom\""]"", ""features"": ""[\""derive\"", \""zerofrom\""]"", ""name"": ""yoke"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""derive\""]"", ""features"": ""[\""derive\""]"", ""name"": ""zerofrom"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""dev_urandom_fallback\"", \""less-safe-getrandom-custom-or-rdrand\"", \""less-safe-getrandom-espidf\"", \""slow_tests\"", \""std\"", \""test_logging\"", \""unstable-testing-arm-no-hw\"", \""unstable-testing-arm-no-neon\"", \""wasm32_unknown_unknown_js\""]"", ""features"": ""[\""alloc\"", \""default\"", \""dev_urandom_fallback\"", \""std\""]"", ""name"": ""ring"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""dfa\"", \""dfa-build\"", \""dfa-onepass\"", \""dfa-search\"", \""hybrid\"", \""internal-instrument\"", \""internal-instrument-pikevm\"", \""logging\"", \""meta\"", \""nfa\"", \""nfa-backtrack\"", \""nfa-pikevm\"", \""nfa-thompson\"", \""perf\"", \""perf-inline\"", \""perf-literal\"", \""perf-literal-multisubstring\"", \""perf-literal-substring\"", \""std\"", \""syntax\"", \""unicode\"", \""unicode-age\"", \""unicode-bool\"", \""unicode-case\"", \""unicode-gencat\"", \""unicode-perl\"", \""unicode-script\"", \""unicode-segment\"", \""unicode-word-boundary\""]"", ""features"": ""[\""alloc\"", \""dfa-onepass\"", \""hybrid\"", \""meta\"", \""nfa\"", \""nfa-backtrack\"", \""nfa-pikevm\"", \""nfa-thompson\"", \""perf\"", \""perf-inline\"", \""perf-literal\"", \""perf-literal-multisubstring\"", \""perf-literal-substring\"", \""std\"", \""syntax\"", \""unicode\"", \""unicode-age\"", \""unicode-bool\"", \""unicode-case\"", \""unicode-gencat\"", \""unicode-perl\"", \""unicode-script\"", \""unicode-segment\"", \""unicode-word-boundary\""]"", ""name"": ""regex_automata"", ""profile"": 2547513045437502204, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""either\""]"", ""features"": ""[]"", ""name"": ""writeable"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""fresh-rust\"", \""nightly\"", \""serde\"", \""std\""]"", ""features"": ""[\""alloc\""]"", ""name"": ""allocator_api2"", ""profile"": 3361788016818567686, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""getrandom\"", \""js\"", \""std\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""fastrand"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""log\"", \""nightly\"", \""os_rng\"", \""serde\"", \""simd_support\"", \""small_rng\"", \""std\"", \""std_rng\"", \""thread_rng\"", \""unbiased\""]"", ""features"": ""[\""alloc\"", \""default\"", \""os_rng\"", \""small_rng\"", \""std\"", \""std_rng\"", \""thread_rng\""]"", ""name"": ""rand"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""map\"", \""std\""]"", ""features"": ""[\""alloc\"", \""map\"", \""std\""]"", ""name"": ""managed"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""memchr\"", \""serde\"", \""std\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""iri_string"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""oid\"", \""zeroize\""]"", ""features"": ""[\""alloc\"", \""default\"", \""oid\""]"", ""name"": ""sha2"", ""profile"": 8048078827458334496, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""rc\"", \""result\"", \""std\"", \""unstable\""]"", ""features"": ""[\""alloc\"", \""default\"", \""rc\"", \""result\"", \""std\""]"", ""name"": ""serde_core"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""serde\"", \""std\"", \""unicode\""]"", ""features"": ""[\""alloc\"", \""std\""]"", ""name"": ""bstr"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""std\"", \""web\"", \""web-time\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""rustls_pki_types"", ""profile"": 8134619797147437084, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""base64"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""form_urlencoded"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""futures_sink"", ""profile"": 8157235479358073570, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""percent_encoding"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""toml_write"", ""profile"": 11768715044625735406, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""features"": ""[]"", ""name"": ""stable_deref_trait"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""kv\"", \""kv_serde\"", \""kv_std\"", \""kv_sval\"", \""kv_unstable\"", \""kv_unstable_serde\"", \""kv_unstable_std\"", \""kv_unstable_sval\"", \""max_level_debug\"", \""max_level_error\"", \""max_level_info\"", \""max_level_off\"", \""max_level_trace\"", \""max_level_warn\"", \""release_max_level_debug\"", \""release_max_level_error\"", \""release_max_level_info\"", \""release_max_level_off\"", \""release_max_level_trace\"", \""release_max_level_warn\"", \""serde\"", \""serde_core\"", \""std\"", \""sval\"", \""sval_ref\"", \""value-bag\""]"", ""features"": ""[\""alloc\"", \""std\""]"", ""name"": ""log"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""amd-sev\"", \""aws-nitro\"", \""bitfield\"", \""blk\"", \""default\"", \""efi\"", \""gpu\"", \""init-blob\"", \""input\"", \""iocuddle\"", \""kbs-types\"", \""krun_display\"", \""krun_input\"", \""net\"", \""pw\"", \""rutabaga_gfx\"", \""serde\"", \""snd\"", \""tdx\"", \""tee\"", \""test_utils\"", \""thiserror\"", \""virgl_resource_map2\"", \""zerocopy\""]"", ""features"": ""[\""default\"", \""init-blob\"", \""net\""]"", ""name"": ""pvisor_vm"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""ansi\"", \""bitflags\"", \""cursor-icon\"", \""default\"", \""log\"", \""serde\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""vte"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""any_c_zlib\"", \""any_impl\"", \""any_zlib\"", \""cloudflare-zlib-sys\"", \""cloudflare_zlib\"", \""default\"", \""document-features\"", \""libz-ng-sys\"", \""libz-sys\"", \""miniz-sys\"", \""miniz_oxide\"", \""rust_backend\"", \""zlib\"", \""zlib-default\"", \""zlib-ng\"", \""zlib-ng-compat\"", \""zlib-rs\""]"", ""features"": ""[\""any_impl\"", \""default\"", \""miniz_oxide\"", \""rust_backend\""]"", ""name"": ""flate2"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""async-tokio\"", \""default\"", \""document-features\"", \""encoding\"", \""encoding_rs\"", \""escape-html\"", \""overlapped-lists\"", \""serde\"", \""serde-types\"", \""serialize\"", \""tokio\""]"", ""features"": ""[\""default\"", \""overlapped-lists\"", \""serde\"", \""serialize\""]"", ""name"": ""quick_xml"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""atomic\"", \""borsh\"", \""bytemuck\"", \""default\"", \""fast-rng\"", \""js\"", \""macro-diagnostics\"", \""md5\"", \""rng\"", \""rng-getrandom\"", \""rng-rand\"", \""serde\"", \""sha1\"", \""slog\"", \""std\"", \""uuid-rng-internal-lib\"", \""v1\"", \""v3\"", \""v4\"", \""v5\"", \""v6\"", \""v7\"", \""v8\"", \""zerocopy\""]"", ""features"": ""[\""default\"", \""rng\"", \""std\"", \""v4\""]"", ""name"": ""uuid"", ""profile"": 5623565614784616014, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""bincode\"", \""const_generics\"", \""const_new\"", \""debugger_visualizer\"", \""drain_filter\"", \""drain_keep_rest\"", \""impl_bincode\"", \""malloc_size_of\"", \""may_dangle\"", \""serde\"", \""specialization\"", \""union\"", \""unty\"", \""write\""]"", ""features"": ""[\""const_generics\"", \""const_new\""]"", ""name"": ""smallvec"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""borsh\"", \""default\"", \""quickcheck\"", \""rayon\"", \""serde\"", \""std\"", \""sval\"", \""test_debug\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""indexmap"", ""profile"": 10937888587037481829, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""bytemuck\"", \""example_generated\"", \""serde\"", \""serde_core\"", \""std\""]"", ""features"": ""[\""std\""]"", ""name"": ""bitflags"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""db\""]"", ""features"": ""[]"", ""name"": ""const_oid"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""default\"", \""log\"", \""serde\"", \""serde1\"", \""simd-accel\""]"", ""features"": ""[\""default\"", \""log\""]"", ""name"": ""globset"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""default\"", \""std\"", \""unicode\"", \""unicode-age\"", \""unicode-bool\"", \""unicode-case\"", \""unicode-gencat\"", \""unicode-perl\"", \""unicode-script\"", \""unicode-segment\""]"", ""features"": ""[\""default\"", \""std\"", \""unicode\"", \""unicode-age\"", \""unicode-bool\"", \""unicode-case\"", \""unicode-gencat\"", \""unicode-perl\"", \""unicode-script\"", \""unicode-segment\""]"", ""name"": ""regex_syntax"", ""profile"": 2547513045437502204, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""inline\"", \""raw-api\"", \""rayon\"", \""serde\"", \""typesize\""]"", ""features"": ""[]"", ""name"": ""dashmap"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arc-swap\"", \""backend-atomic\"", \""backend-bitmap\"", \""backend-mmap\"", \""bitflags\"", \""default\"", \""rawfd\"", \""vmm-sys-util\"", \""xen\""]"", ""features"": ""[\""backend-mmap\"", \""default\"", \""rawfd\""]"", ""name"": ""vm_memory"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arc_lock\"", \""atomic_usize\"", \""default\"", \""nightly\"", \""owning_ref\"", \""serde\""]"", ""features"": ""[\""atomic_usize\"", \""default\""]"", ""name"": ""lock_api"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arc_lock\"", \""deadlock_detection\"", \""default\"", \""hardware-lock-elision\"", \""nightly\"", \""owning_ref\"", \""send_guard\"", \""serde\""]"", ""features"": ""[\""default\""]"", ""name"": ""parking_lot"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arrays\"", \""bindgen\"", \""debug\"", \""default\"", \""doc-cfg\"", \""experimental\"", \""fat-lto\"", \""legacy\"", \""no_asm\"", \""pkg-config\"", \""seekable\"", \""std\"", \""thin\"", \""thin-lto\"", \""zdict_builder\"", \""zstdmt\""]"", ""features"": ""[\""arrays\"", \""legacy\"", \""std\"", \""zdict_builder\""]"", ""name"": ""zstd_safe"", ""profile"": 10592522859712577474, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arrays\"", \""bindgen\"", \""debug\"", \""default\"", \""doc-cfg\"", \""experimental\"", \""fat-lto\"", \""legacy\"", \""no_asm\"", \""pkg-config\"", \""thin\"", \""thin-lto\"", \""wasm\"", \""zdict_builder\"", \""zstdmt\""]"", ""features"": ""[\""arrays\"", \""default\"", \""legacy\"", \""zdict_builder\""]"", ""name"": ""zstd"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""asm\"", \""default\"", \""force-soft\"", \""loongarch64_asm\"", \""md5-asm\"", \""oid\"", \""std\""]"", ""features"": ""[]"", ""name"": ""md5"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""async-await\"", \""attributes\"", \""default\"", \""log\"", \""log-always\"", \""max_level_debug\"", \""max_level_error\"", \""max_level_info\"", \""max_level_off\"", \""max_level_trace\"", \""max_level_warn\"", \""release_max_level_debug\"", \""release_max_level_error\"", \""release_max_level_info\"", \""release_max_level_off\"", \""release_max_level_trace\"", \""release_max_level_warn\"", \""std\"", \""tracing-attributes\"", \""valuable\""]"", ""features"": ""[\""attributes\"", \""default\"", \""std\"", \""tracing-attributes\""]"", ""name"": ""tracing"", ""profile"": 1647937577980832904, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""async-await\""]"", ""features"": ""[]"", ""name"": ""tracing_attributes"", ""profile"": 6207960371462447473, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""async\"", \""default\"", \""sync\"", \""sync-wrappers\"", \""vm-memory\""]"", ""features"": ""[\""async\"", \""default\"", \""sync-wrappers\""]"", ""name"": ""imago"", ""profile"": 15916455528278280289, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""auto-color\"", \""color\"", \""default\"", \""humantime\"", \""kv\"", \""regex\"", \""unstable-kv\""]"", ""features"": ""[\""auto-color\"", \""color\"", \""default\"", \""humantime\"", \""regex\""]"", ""name"": ""env_logger"", ""profile"": 10632806285984276979, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""auto\"", \""default\"", \""test\"", \""wincon\""]"", ""features"": ""[\""auto\"", \""default\"", \""wincon\""]"", ""name"": ""anstream"", ""profile"": 4227688284201523262, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""auto\"", \""default\"", \""test\"", \""wincon\""]"", ""features"": ""[\""auto\"", \""wincon\""]"", ""name"": ""anstream"", ""profile"": 10632806285984276979, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""auxvec\"", \""bootparam\"", \""btrfs\"", \""core\"", \""default\"", \""elf\"", \""elf_uapi\"", \""errno\"", \""general\"", \""if_arp\"", \""if_ether\"", \""if_packet\"", \""if_tun\"", \""image\"", \""io_uring\"", \""ioctl\"", \""landlock\"", \""loop_device\"", \""mempolicy\"", \""net\"", \""netlink\"", \""no_std\"", \""prctl\"", \""ptrace\"", \""rustc-dep-of-std\"", \""std\"", \""system\"", \""vm_sockets\"", \""xdp\""]"", ""features"": ""[\""auxvec\"", \""default\"", \""elf\"", \""errno\"", \""general\"", \""ioctl\"", \""no_std\"", \""std\""]"", ""name"": ""linux_raw_sys"", ""profile"": 14036798396267011297, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""aws-lc-rs\"", \""aws_lc_rs\"", \""brotli\"", \""custom-provider\"", \""default\"", \""fips\"", \""hashbrown\"", \""log\"", \""logging\"", \""prefer-post-quantum\"", \""read_buf\"", \""ring\"", \""rustversion\"", \""std\"", \""tls12\"", \""zlib\""]"", ""features"": ""[\""ring\"", \""std\"", \""tls12\""]"", ""name"": ""rustls"", ""profile"": 16187626775398413875, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""aws-lc-rs\"", \""aws_lc_rs\"", \""brotli\"", \""default\"", \""early-data\"", \""fips\"", \""logging\"", \""ring\"", \""tls12\"", \""zlib\""]"", ""features"": ""[\""ring\"", \""tls12\""]"", ""name"": ""tokio_rustls"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""aws-lc-rs\"", \""default\"", \""fips\"", \""http1\"", \""http2\"", \""log\"", \""logging\"", \""native-tokio\"", \""ring\"", \""rustls-native-certs\"", \""rustls-platform-verifier\"", \""tls12\"", \""webpki-roots\"", \""webpki-tokio\""]"", ""features"": ""[\""http1\"", \""http2\"", \""native-tokio\"", \""ring\"", \""rustls-native-certs\"", \""tls12\"", \""webpki-roots\"", \""webpki-tokio\""]"", ""name"": ""hyper_rustls"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""aws\"", \""azure\"", \""base64\"", \""cloud\"", \""default\"", \""form_urlencoded\"", \""fs\"", \""gcp\"", \""http\"", \""http-body-util\"", \""httparse\"", \""hyper\"", \""integration\"", \""md-5\"", \""quick-xml\"", \""rand\"", \""reqwest\"", \""ring\"", \""rustls-pki-types\"", \""serde\"", \""serde_json\"", \""serde_urlencoded\"", \""tls-webpki-roots\"", \""tokio\"", \""walkdir\""]"", ""features"": ""[\""aws\"", \""base64\"", \""cloud\"", \""form_urlencoded\"", \""http-body-util\"", \""hyper\"", \""md-5\"", \""quick-xml\"", \""rand\"", \""reqwest\"", \""ring\"", \""serde\"", \""serde_json\"", \""serde_urlencoded\"", \""tls-webpki-roots\"", \""tokio\""]"", ""name"": ""object_store"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""backtrace\"", \""deadlock_detection\"", \""nightly\"", \""petgraph\""]"", ""features"": ""[]"", ""name"": ""parking_lot_core"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""backtrace\"", \""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""anyhow"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""balance\"", \""buffer\"", \""discover\"", \""filter\"", \""full\"", \""futures-core\"", \""futures-util\"", \""hdrhistogram\"", \""hedge\"", \""indexmap\"", \""limit\"", \""load\"", \""load-shed\"", \""log\"", \""make\"", \""pin-project-lite\"", \""ready-cache\"", \""reconnect\"", \""retry\"", \""slab\"", \""spawn-ready\"", \""steer\"", \""sync_wrapper\"", \""timeout\"", \""tokio\"", \""tokio-stream\"", \""tokio-util\"", \""tracing\"", \""util\""]"", ""features"": ""[\""futures-core\"", \""futures-util\"", \""make\"", \""pin-project-lite\"", \""retry\"", \""sync_wrapper\"", \""timeout\"", \""tokio\"", \""util\""]"", ""name"": ""tower"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""bindgen\"", \""debug\"", \""default\"", \""experimental\"", \""fat-lto\"", \""legacy\"", \""no_asm\"", \""no_wasm_shim\"", \""non-cargo\"", \""pkg-config\"", \""seekable\"", \""std\"", \""thin\"", \""thin-lto\"", \""zdict_builder\"", \""zstdmt\""]"", ""features"": ""[\""legacy\"", \""std\"", \""zdict_builder\""]"", ""name"": ""zstd_sys"", ""profile"": 10592522859712577474, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""borsh\"", \""default\"", \""serde\"", \""std\"", \""zeroize\""]"", ""features"": ""[]"", ""name"": ""arrayvec"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""bytes\"", \""default\"", \""fs\"", \""full\"", \""io-std\"", \""io-uring\"", \""io-util\"", \""libc\"", \""macros\"", \""mio\"", \""net\"", \""parking_lot\"", \""process\"", \""rt\"", \""rt-multi-thread\"", \""signal\"", \""signal-hook-registry\"", \""socket2\"", \""sync\"", \""taskdump\"", \""test-util\"", \""time\"", \""tokio-macros\"", \""tracing\"", \""windows-sys\""]"", ""features"": ""[\""bytes\"", \""default\"", \""fs\"", \""io-util\"", \""libc\"", \""macros\"", \""mio\"", \""net\"", \""process\"", \""rt\"", \""rt-multi-thread\"", \""signal\"", \""signal-hook-registry\"", \""socket2\"", \""sync\"", \""time\"", \""tokio-macros\""]"", ""name"": ""tokio"", ""profile"": 10043413591317300293, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""bzimage\"", \""default\"", \""elf\"", \""pe\""]"", ""features"": ""[\""bzimage\"", \""default\"", \""elf\"", \""pe\""]"", ""name"": ""linux_loader"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""capi\"", \""client\"", \""default\"", \""ffi\"", \""full\"", \""http1\"", \""http2\"", \""nightly\"", \""server\"", \""tracing\""]"", ""features"": ""[\""client\"", \""default\"", \""http1\"", \""http2\"", \""server\""]"", ""name"": ""hyper"", ""profile"": 5907227947356068632, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""cargo\"", \""color\"", \""debug\"", \""default\"", \""deprecated\"", \""derive\"", \""env\"", \""error-context\"", \""help\"", \""std\"", \""string\"", \""suggestions\"", \""unicode\"", \""unstable-derive-ui-tests\"", \""unstable-doc\"", \""unstable-ext\"", \""unstable-markdown\"", \""unstable-styles\"", \""unstable-v5\"", \""usage\"", \""wrap_help\""]"", ""features"": ""[\""color\"", \""default\"", \""derive\"", \""env\"", \""error-context\"", \""help\"", \""std\"", \""string\"", \""suggestions\"", \""usage\""]"", ""name"": ""clap"", ""profile"": 4628411354643344498, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""cargo\"", \""color\"", \""debug\"", \""default\"", \""deprecated\"", \""env\"", \""error-context\"", \""help\"", \""std\"", \""string\"", \""suggestions\"", \""unicode\"", \""unstable-doc\"", \""unstable-ext\"", \""unstable-styles\"", \""unstable-v5\"", \""usage\"", \""wrap_help\""]"", ""features"": ""[\""color\"", \""env\"", \""error-context\"", \""help\"", \""std\"", \""string\"", \""suggestions\"", \""usage\""]"", ""name"": ""clap_builder"", ""profile"": 4628411354643344498, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""channel\"", \""default\"", \""full\""]"", ""features"": ""[\""default\""]"", ""name"": ""http_body_util"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""cipher\"", \""default\"", \""legacy\"", \""rng\"", \""xchacha\"", \""zeroize\""]"", ""features"": ""[\""rng\""]"", ""name"": ""chacha20"", ""profile"": 4285365006624603084, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""cjk\"", \""core\"", \""default\"", \""no_std\"", \""rustc-dep-of-std\"", \""std\""]"", ""features"": ""[\""cjk\"", \""default\""]"", ""name"": ""unicode_width"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""cli\"", \""default\""]"", ""features"": ""[]"", ""name"": ""pvisor_overlayfs"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""clone-impls\"", \""default\"", \""derive\"", \""extra-traits\"", \""fold\"", \""full\"", \""parsing\"", \""printing\"", \""proc-macro\"", \""test\"", \""visit\"", \""visit-mut\""]"", ""features"": ""[\""clone-impls\"", \""default\"", \""derive\"", \""extra-traits\"", \""fold\"", \""full\"", \""parsing\"", \""printing\"", \""proc-macro\"", \""visit\"", \""visit-mut\""]"", ""name"": ""syn"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""compiled_data\"", \""datagen\"", \""default\"", \""harfbuzz_traits\"", \""icu_properties\"", \""serde\"", \""utf16_iter\"", \""utf8_iter\"", \""write16\""]"", ""features"": ""[\""compiled_data\""]"", ""name"": ""icu_normalizer"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""compiled_data\""]"", ""features"": ""[\""compiled_data\""]"", ""name"": ""idna_adapter"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""compiler_builtins\"", \""core\"", \""custom\"", \""js\"", \""js-sys\"", \""linux_disable_fallback\"", \""rdrand\"", \""rustc-dep-of-std\"", \""std\"", \""test-in-browser\"", \""wasm-bindgen\""]"", ""features"": ""[]"", ""name"": ""getrandom"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""compiler_builtins\"", \""core\"", \""default\"", \""example_generated\"", \""rustc-dep-of-std\""]"", ""features"": ""[\""default\""]"", ""name"": ""bitflags"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""const-generics\"", \""core_hint_black_box\"", \""default\"", \""i128\"", \""nightly\"", \""std\""]"", ""features"": ""[]"", ""name"": ""subtle"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""const-generics\"", \""default\"", \""nightly\"", \""std\""]"", ""features"": ""[]"", ""name"": ""simd_adler32"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""const-generics\"", \""i128\"", \""scale-info\"", \""scale_info\"", \""strict\""]"", ""features"": ""[\""const-generics\""]"", ""name"": ""typenum"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""core\"", \""default\"", \""rustc-dep-of-std\"", \""std\""]"", ""features"": ""[]"", ""name"": ""adler2"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""core\"", \""default\"", \""utf8\""]"", ""features"": ""[\""default\"", \""utf8\""]"", ""name"": ""anstyle_parse"", ""profile"": 10632806285984276979, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""core\"", \""default\"", \""utf8\""]"", ""features"": ""[\""default\"", \""utf8\""]"", ""name"": ""anstyle_parse"", ""profile"": 4227688284201523262, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""core\"", \""rustc-dep-of-std\""]"", ""features"": ""[]"", ""name"": ""cfg_if"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""core\"", \""rustc-dep-of-std\""]"", ""features"": ""[]"", ""name"": ""cfg_if"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""count_instructions_test\"", \""default\"", \""std\""]"", ""features"": ""[\""std\""]"", ""name"": ""constant_time_eq"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""debug\"", \""default\"", \""deprecated\"", \""raw-deprecated\"", \""unstable-markdown\"", \""unstable-v5\""]"", ""features"": ""[\""default\""]"", ""name"": ""clap_derive"", ""profile"": 5346920246396443047, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""debugger_visualizer\"", \""default\"", \""expose_internals\"", \""serde\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""url"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""deserialize_in_place\""]"", ""features"": ""[\""default\""]"", ""name"": ""serde_derive"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""digest\"", \""mmap\"", \""neon\"", \""no_avx2\"", \""no_avx512\"", \""no_neon\"", \""no_sse2\"", \""no_sse41\"", \""prefer_intrinsics\"", \""pure\"", \""rayon\"", \""serde\"", \""std\"", \""traits-preview\"", \""wasm32_simd\"", \""zeroize\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""blake3"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""display\"", \""indexmap\"", \""parse\"", \""preserve_order\"", \""unbounded\""]"", ""features"": ""[\""default\"", \""display\"", \""parse\""]"", ""name"": ""toml"", ""profile"": 11768715044625735406, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""display\"", \""parse\"", \""perf\"", \""serde\"", \""unbounded\"", \""unstable-debug\""]"", ""features"": ""[\""default\"", \""display\"", \""parse\"", \""serde\""]"", ""name"": ""toml_edit"", ""profile"": 11768715044625735406, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""echo-server\""]"", ""features"": ""[]"", ""name"": ""pvisor_gateway"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""extra-platforms\"", \""serde\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""bytes"", ""profile"": 11003502350373712158, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""fs\"", \""full\"", \""io-util\"", \""net\"", \""signal\"", \""sync\"", \""time\"", \""tokio-util\""]"", ""features"": ""[\""default\"", \""time\""]"", ""name"": ""tokio_stream"", ""profile"": 10043413591317300293, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""gateway\""]"", ""features"": ""[\""default\"", \""gateway\""]"", ""name"": ""pvisor_cli"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""gateway\""]"", ""features"": ""[\""gateway\""]"", ""name"": ""pvisor"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""getrandom\"", \""nightly\""]"", ""features"": ""[\""default\"", \""getrandom\""]"", ""name"": ""tempfile"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""hashbrown\"", \""nightly\""]"", ""features"": ""[\""default\"", \""hashbrown\""]"", ""name"": ""lru"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""heapless\"", \""json\"", \""schemars\"", \""schemars08\"", \""schemars1\"", \""ser_as_str\"", \""serde\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""ipnet"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""i128\"", \""libm\"", \""std\""]"", ""features"": ""[]"", ""name"": ""num_traits"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""i128\"", \""std\""]"", ""features"": ""[]"", ""name"": ""byteorder"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""is_sync\""]"", ""features"": ""[\""default\""]"", ""name"": ""maybe_async"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""libbz2-rs-sys\"", \""static\""]"", ""features"": ""[\""default\""]"", ""name"": ""bzip2"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""log\"", \""net\"", \""os-ext\"", \""os-poll\""]"", ""features"": ""[\""net\"", \""os-ext\"", \""os-poll\""]"", ""name"": ""mio"", ""profile"": 16626080924472169626, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""logging\"", \""pattern\"", \""perf\"", \""perf-backtrack\"", \""perf-cache\"", \""perf-dfa\"", \""perf-dfa-full\"", \""perf-inline\"", \""perf-literal\"", \""perf-onepass\"", \""std\"", \""unicode\"", \""unicode-age\"", \""unicode-bool\"", \""unicode-case\"", \""unicode-gencat\"", \""unicode-perl\"", \""unicode-script\"", \""unicode-segment\"", \""unstable\"", \""use_std\""]"", ""features"": ""[\""default\"", \""perf\"", \""perf-backtrack\"", \""perf-cache\"", \""perf-dfa\"", \""perf-inline\"", \""perf-literal\"", \""perf-onepass\"", \""std\"", \""unicode\"", \""unicode-age\"", \""unicode-bool\"", \""unicode-case\"", \""unicode-gencat\"", \""unicode-perl\"", \""unicode-script\"", \""unicode-segment\""]"", ""name"": ""regex"", ""profile"": 2547513045437502204, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""logging\"", \""perf-literal\"", \""std\""]"", ""features"": ""[\""default\"", \""perf-literal\"", \""std\""]"", ""name"": ""aho_corasick"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""loom\"", \""nightly\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""crossbeam_utils"", ""profile"": 10433883304768107316, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""nightly\"", \""proc-macro\"", \""span-locations\""]"", ""features"": ""[\""default\"", \""proc-macro\""]"", ""name"": ""proc_macro2"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""nightly\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""crc32fast"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""nightly\""]"", ""features"": ""[\""default\""]"", ""name"": ""utf8parse"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""no_simd\"", \""simd\"", \""std\""]"", ""features"": ""[\""simd\"", \""std\""]"", ""name"": ""ppv_lite86"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""once_cell\"", \""std\"", \""valuable\""]"", ""features"": ""[\""once_cell\"", \""std\""]"", ""name"": ""tracing_core"", ""profile"": 1647937577980832904, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""os_rng\"", \""serde\"", \""std\""]"", ""features"": ""[\""std\""]"", ""name"": ""rand_chacha"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""proc-macro\""]"", ""features"": ""[\""default\"", \""proc-macro\""]"", ""name"": ""quote"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""proc-macro\""]"", ""features"": ""[\""default\"", \""proc-macro\""]"", ""name"": ""synstructure"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""regex\"", \""std\""]"", ""features"": ""[\""regex\"", \""std\""]"", ""name"": ""env_filter"", ""profile"": 10632806285984276979, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""serde\"", \""std\"", \""use_std\""]"", ""features"": ""[\""std\"", \""use_std\""]"", ""name"": ""either"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""serde\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""semver"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""serde\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""slab"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""set\""]"", ""features"": ""[\""default\""]"", ""name"": ""hostname"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\"", \""unstable\""]"", ""features"": ""[\""std\""]"", ""name"": ""futures_io"", ""profile"": 8157235479358073570, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""anstyle"", ""profile"": 10632806285984276979, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""crossbeam_channel"", ""profile"": 10433883304768107316, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""errno"", ""profile"": 14231071289633644017, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""fnv"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""http"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""httparse"", ""profile"": 1675385711784277470, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""shlex"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""thiserror"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[]"", ""name"": ""displaydoc"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[]"", ""name"": ""foldhash"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""unstable_const\"", \""unstable_offset_of\""]"", ""features"": ""[\""default\""]"", ""name"": ""memoffset"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""unsupported\""]"", ""features"": ""[\""default\"", \""unsupported\""]"", ""name"": ""xattr"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""use_alloc\"", \""use_std\""]"", ""features"": ""[\""default\"", \""use_alloc\"", \""use_std\""]"", ""name"": ""itertools"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""use_std\""]"", ""features"": ""[]"", ""name"": ""scopeguard"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""xattr\""]"", ""features"": ""[\""default\"", \""xattr\""]"", ""name"": ""tar"", ""profile"": 4339154717131654023, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\""]"", ""features"": ""[\""default\""]"", ""name"": ""is_terminal_polyfill"", ""profile"": 5812794364236305338, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""fallback\""]"", ""features"": ""[\""fallback\""]"", ""name"": ""iana_time_zone"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""fam-wrappers\"", \""serde\"", \""vmm-sys-util\""]"", ""features"": ""[\""fam-wrappers\"", \""serde\"", \""vmm-sys-util\""]"", ""name"": ""kvm_bindings"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""futures\"", \""futures-core\""]"", ""features"": ""[\""futures\"", \""futures-core\""]"", ""name"": ""sync_wrapper"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""getrandom\"", \""rand_core\"", \""std\""]"", ""features"": ""[]"", ""name"": ""crypto_common"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""getrandom\"", \""rand_core\"", \""zeroize\""]"", ""features"": ""[]"", ""name"": ""crypto_common"", ""profile"": 2606296542320589278, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""jobserver\"", \""parallel\""]"", ""features"": ""[\""parallel\""]"", ""name"": ""cc"", ""profile"": 12458195255332598410, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""more_lengths\"", \""serde\"", \""zeroize\""]"", ""features"": ""[\""more_lengths\""]"", ""name"": ""generic_array"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""mu\""]"", ""features"": ""[]"", ""name"": ""humantime"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""no-panic\"", \""small\""]"", ""features"": ""[]"", ""name"": ""ryu"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""no-panic\""]"", ""features"": ""[]"", ""name"": ""itoa"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""no-panic\""]"", ""features"": ""[]"", ""name"": ""zmij"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""no_std\"", \""spin\""]"", ""features"": ""[]"", ""name"": ""page_size"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""os_rng\"", \""serde\"", \""std\""]"", ""features"": ""[\""os_rng\"", \""std\""]"", ""name"": ""rand_core"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""portable-atomic\""]"", ""features"": ""[]"", ""name"": ""atomic_waker"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""proc-macro2\""]"", ""features"": ""[]"", ""name"": ""virtue"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""serde\"", \""serde_derive\"", \""with-serde\""]"", ""features"": ""[\""serde\"", \""serde_derive\"", \""with-serde\""]"", ""name"": ""vmm_sys_util"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""serde\"", \""serde_support\""]"", ""features"": ""[]"", ""name"": ""caps"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""serde\""]"", ""features"": ""[\""serde\""]"", ""name"": ""serde_spanned"", ""profile"": 11768715044625735406, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""serde\""]"", ""features"": ""[\""serde\""]"", ""name"": ""toml_datetime"", ""profile"": 11768715044625735406, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""std\"", \""sys_rng\"", \""wasm_js\""]"", ""features"": ""[\""std\"", \""sys_rng\""]"", ""name"": ""getrandom"", ""profile"": 7847730558161019686, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""std\"", \""wasm_js\""]"", ""features"": ""[\""std\""]"", ""name"": ""getrandom"", ""profile"": 3240661740686181567, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""stream\"", \""unstable\""]"", ""features"": ""[]"", ""name"": ""h2"", ""profile"": 13333777962520875826, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""zeroize\""]"", ""features"": ""[]"", ""name"": ""block_buffer"", ""profile"": 2548229083023945596, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""anstyle_query"", ""profile"": 8192080853972711267, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""arrayref"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""async_trait"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""autocfg"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""bincode_derive"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""block_buffer"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""cfg_aliases"", ""profile"": 11980061553486606077, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""clap_lex"", ""profile"": 4628411354643344498, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""colorchoice"", ""profile"": 10632806285984276979, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""compression_core"", ""profile"": 4107725171516170900, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""cpufeatures"", ""profile"": 2548229083023945596, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""dirs"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""dirs_sys"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""equivalent"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""filetime"", ""profile"": 4796329815077315148, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""find_msvc_tools"", ""profile"": 12458195255332598410, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""fs2"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""futures_macro"", ""profile"": 1218413102171684145, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""hash32"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""heck"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""http_body"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""httpdate"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""icu_normalizer_data"", ""profile"": 18083946540466509289, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""icu_properties_data"", ""profile"": 18083946540466509289, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""jobserver"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""kvm_ioctls"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""libloading"", ""profile"": 12556093047751754406, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""libloading"", ""profile"": 6679834303860230680, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""mime"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""openssl_probe"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""option_ext"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""pin_project_lite"", ""profile"": 14034241328553317513, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""pkg_config"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""pvisor_core"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""pvisor_guest"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""pvisor_journal"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""pvisor_overlay_core"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""pvisor_overlaynet"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""pvisor_replay"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""rand_core"", ""profile"": 11804347602484681322, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""rustc_version"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""rustls_native_certs"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""serde_path_to_error"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""serde_urlencoded"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""serde_yaml"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""signal_hook_registry"", ""profile"": 8111324945914507537, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""strsim"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""thiserror_impl"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""tokio_macros"", ""profile"": 5029862450655259090, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""tower_layer"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""tower_service"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""try_lock"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""unicode_ident"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""unsafe_libyaml"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""untrusted"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""unty"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""utf8_iter"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""version_check"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""virtio_bindings"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""vt100"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""want"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""webpki_roots"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""yoke_derive"", ""profile"": 4981139003136920394, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""zerocopy_derive"", ""profile"": 16810066265625103578, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""zerofrom_derive"", ""profile"": 4981139003136920394, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""zerovec_derive"", ""profile"": 4981139003136920394, ""rustc"": 1625334936438085654, ""rustflags"": []}]}, ""environment"": {""CARGO_BUILD_JOBS"": ""2"", ""CARGO_NET_OFFLINE"": ""true""}, ""firmware_sha256"": ""b61f68dac3ef20a88e1ee387733e4baed2f7c02edac940c8882e0b549dac95e4"", ""pvisor_sha256"": ""8840b16cdc0610e381eec66b0f75f1e86efba76feb358c9eae5b3d923854b592"", ""rustc"": ""rustc 1.98.1 (48a229cea 2026-09-01)\nbinary: rustc\ncommit-hash: 48a229ceaefd4985c50990b14116b6d856af0985\ncommit-date: 2026-09-01\nhost: x86_64-unknown-linux-gnu\nrelease: 1.98.1\nLLVM version: 22.1.8\n"", ""source_identity"": {""head"": ""b3e2f03509396b8ebe840a9ccdefa63424bc1afe"", ""manifest_sha256"": ""5311e7c05ca1fc114c60416920c204866e5a6079f509343a91b95f6bf0d37b24"", ""recorded_at"": ""2026-10-11T06:42:21.492933+00:00"", ""scope"": ""complete current tracked/unignored files excluding generated target and raw .data"", ""status"": "" M .github/workflows/ci.yml\n M .github/workflows/docs.yml\n M .github/workflows/pvisor-benchmark.yml\n M README.md\n M benchmark/README.md\n M crates/pvisor-cli/README.md\n M crates/pvisor-cli/src/cli/host_process.rs\n M crates/pvisor-cli/src/cli/host_service.rs\n M crates/pvisor-cli/src/cli/runtime.rs\n M crates/pvisor-overlay-core/src/core.rs\n M crates/pvisor-overlayfs/README.md\n M crates/pvisor-overlayfs/src/api.rs\n M crates/pvisor-overlayfs/src/dispatch.rs\n M crates/pvisor-overlayfs/src/fs.rs\n M crates/pvisor-vm/Cargo.toml\n M crates/pvisor-vm/README.md\n M crates/pvisor-vm/src/api.rs\n M crates/pvisor-vm/src/builder.rs\n M crates/pvisor-vm/src/contract_tests/vm_snapshot_fs.rs\n M crates/pvisor-vm/src/devices/virtio/descriptor_utils.rs\n M crates/pvisor-vm/src/devices/virtio/device.rs\n M crates/pvisor-vm/src/devices/virtio/fs/augment_fs.rs\n M crates/pvisor-vm/src/devices/virtio/fs/device.rs\n M crates/pvisor-vm/src/devices/virtio/fs/filesystem.rs\n M crates/pvisor-vm/src/devices/virtio/fs/fuse.rs\n M crates/pvisor-vm/src/devices/virtio/fs/linux/passthrough.rs\n M crates/pvisor-vm/src/devices/virtio/fs/linux/passthrough_snapshot.rs\n M crates/pvisor-vm/src/devices/virtio/fs/macos/passthrough.rs\n M crates/pvisor-vm/src/devices/virtio/fs/mod.rs\n M crates/pvisor-vm/src/devices/virtio/fs/overlay.rs\n M crates/pvisor-vm/src/devices/virtio/fs/read_only.rs\n M crates/pvisor-vm/src/devices/virtio/fs/server.rs\n M crates/pvisor-vm/src/devices/virtio/fs/snapshot.rs\n M crates/pvisor-vm/src/devices/virtio/fs/worker.rs\n M crates/pvisor-vm/src/devices/virtio/gpu/device.rs\n M crates/pvisor-vm/src/devices/virtio/mmio.rs\n M crates/pvisor-vm/src/portable.rs\n M crates/pvisor-vm/src/vmm/builder.rs\n M crates/pvisor-vm/src/vmm/linux/vstate.rs\n M crates/pvisor-vm/tests/api_contract.rs\n M docs/README.md\n M docs/src/en/benchmarks/agent-tasks.md\n M docs/src/en/benchmarks/cluster-scalability.md\n M docs/src/en/benchmarks/compare-rl-infra.md\n M docs/src/en/benchmarks/density.md\n M docs/src/en/benchmarks/evidence-sources.csv\n M docs/src/en/benchmarks/filesystem.csv\n M docs/src/en/benchmarks/filesystem.md\n M docs/src/en/benchmarks/index.csv\n M docs/src/en/benchmarks/index.md\n M docs/src/en/benchmarks/isolation-tests.md\n M docs/src/en/benchmarks/lazy-image-startup.md\n M docs/src/en/benchmarks/methodology.md\n M docs/src/en/benchmarks/network.md\n M docs/src/en/benchmarks/replay-fidelity.md\n M docs/src/en/benchmarks/startup.md\n M docs/src/en/benchmarks/supervision-cost.md\n M docs/src/en/benchmarks/vm-memory/index.md\n M docs/src/en/design/filesystem-performance-analysis.md\n M docs/src/en/design/index.md\n M docs/src/en/design/vm-startup-performance-analysis.md\n M docs/src/zh/benchmarks/agent-tasks.md\n M docs/src/zh/benchmarks/cluster-scalability.md\n M docs/src/zh/benchmarks/compare-rl-infra.md\n M docs/src/zh/benchmarks/density.md\n M docs/src/zh/benchmarks/evidence-sources.csv\n M docs/src/zh/benchmarks/filesystem.csv\n M docs/src/zh/benchmarks/filesystem.md\n M docs/src/zh/benchmarks/index.csv\n M docs/src/zh/benchmarks/index.md\n M docs/src/zh/benchmarks/isolation-tests.md\n M docs/src/zh/benchmarks/lazy-image-startup.md\n M docs/src/zh/benchmarks/methodology.md\n M docs/src/zh/benchmarks/network.md\n M docs/src/zh/benchmarks/replay-fidelity.md\n M docs/src/zh/benchmarks/startup.md\n M docs/src/zh/benchmarks/supervision-cost.md\n M docs/src/zh/benchmarks/vm-memory/index.md\n M docs/src/zh/design/filesystem-performance-analysis.md\n M docs/src/zh/design/index.md\n M docs/src/zh/design/vm-startup-performance-analysis.md\n M docs/translations.json\n M docs/zensical.en.toml\n M docs/zensical.zh.toml\n D experimental/README.md\n D experimental/vcpu-idle-offload/README.md\n D experimental/vcpu-idle-offload/design.md\n D experimental/vcpu-idle-offload/implementation-plan.md\n D experimental/vcpu-idle-offload/validation.md\n M justfile\n M tests/justfile\n M tests/test_docs.py\n M tools/semspec/Cargo.lock\n M tools/semspec/Cargo.toml\n M tools/semspec/DESIGN.md\n M tools/semspec/README.md\n M tools/semspec/src/helpers.rs\n M tools/semspec/src/lib.rs\n M tools/semspec/src/main.rs\n?? benchmark.new/\n?? crates/pvisor-vm/src/devices/virtio/fs/dax.rs\n?? crates/pvisor-vm/src/devices/virtio/fs/worker/\n?? crates/pvisor-vm/src/probes/readonly_dax_case.rs\n?? docs/semspec_tables.py\n?? docs/src/en/benchmarks/filesystem-cache-policy.csv\n?? docs/src/en/benchmarks/filesystem-comparisons.csv\n?? docs/src/en/benchmarks/filesystem-provenance.csv\n?? docs/src/en/benchmarks/filesystem-semspec.csv\n?? docs/src/en/benchmarks/filesystem-semspec.render.provenance.json\n?? docs/src/en/benchmarks/measurement-coverage.csv\n?? docs/src/en/benchmarks/measurement-coverage.render.json\n?? docs/src/en/benchmarks/replay-semspec.csv\n?? docs/src/en/benchmarks/replay-semspec.render.json\n?? docs/src/en/benchmarks/replay-semspec.render.provenance.json\n?? docs/src/en/benchmarks/startup-hyperfine.csv\n?? docs/src/en/benchmarks/startup-ready-exit.csv\n?? docs/src/en/benchmarks/startup-ready-exit.provenance.json\n?? docs/src/en/benchmarks/startup-ready.csv\n?? docs/src/en/benchmarks/startup-ready.provenance.json\n?? docs/src/en/benchmarks/startup-reboot-comparison.csv\n?? docs/src/en/benchmarks/startup-semspec.csv\n?? docs/src/en/benchmarks/startup-semspec.provenance.json\n?? docs/src/en/benchmarks/startup-timing.csv\n?? docs/src/en/benchmarks/startup-timing.exit.render.json\n?? docs/src/en/benchmarks/startup-timing.provenance.json\n?? docs/src/en/benchmarks/startup-timing.ready.render.json\n?? docs/src/en/benchmarks/startup-timing.render.json\n?? docs/src/en/design/concurrency-filesystem-summary.csv\n?? docs/src/en/design/concurrency-retest-provenance.csv\n?? docs/src/en/design/concurrency-startup-summary.csv\n?? docs/src/en/design/direct-queue-filesystem-summary.csv\n?? docs/src/en/design/direct-queue-provenance.csv\n?? docs/src/en/design/experimental/\n?? docs/src/en/design/filesystem-evidence-overview.csv\n?? docs/src/en/design/filesystem-measurement-status.csv\n?? docs/src/en/design/host-copy-up-summary.csv\n?? docs/src/en/design/host-idle-dispatch-provenance.csv\n?? docs/src/en/design/host-idle-dispatch-summary.csv\n?? docs/src/en/design/host-read-summary.csv\n?? docs/src/en/design/journal-lock-summary.csv\n?? docs/src/en/design/readonly-dax-provenance.csv\n?? docs/src/en/design/readonly-dax-summary.csv\n?? docs/src/en/design/vm-completion-dispatch-provenance.csv\n?? docs/src/en/design/vm-completion-dispatch-summary.csv\n?? docs/src/en/design/writeback-filesystem-summary.csv\n?? docs/src/en/design/writeback-provenance.csv\n?? docs/src/zh/benchmarks/filesystem-cache-policy.csv\n?? docs/src/zh/benchmarks/filesystem-comparisons.csv\n?? docs/src/zh/benchmarks/filesystem-provenance.csv\n?? docs/src/zh/benchmarks/filesystem-semspec.csv\n?? docs/src/zh/benchmarks/filesystem-semspec.render.provenance.json\n?? docs/src/zh/benchmarks/measurement-coverage.csv\n?? docs/src/zh/benchmarks/measurement-coverage.render.json\n?? docs/src/zh/benchmarks/replay-semspec.csv\n?? docs/src/zh/benchmarks/replay-semspec.render.json\n?? docs/src/zh/benchmarks/replay-semspec.render.provenance.json\n?? docs/src/zh/benchmarks/startup-hyperfine.csv\n?? docs/src/zh/benchmarks/startup-ready-exit.csv\n?? docs/src/zh/benchmarks/startup-ready-exit.provenance.json\n?? docs/src/zh/benchmarks/startup-ready.csv\n?? docs/src/zh/benchmarks/startup-ready.provenance.json\n?? docs/src/zh/benchmarks/startup-reboot-comparison.csv\n?? docs/src/zh/benchmarks/startup-semspec.csv\n?? docs/src/zh/benchmarks/startup-semspec.provenance.json\n?? docs/src/zh/benchmarks/startup-timing.csv\n?? docs/src/zh/benchmarks/startup-timing.exit.render.json\n?? docs/src/zh/benchmarks/startup-timing.provenance.json\n?? docs/src/zh/benchmarks/startup-timing.ready.render.json\n?? docs/src/zh/benchmarks/startup-timing.render.json\n?? docs/src/zh/design/concurrency-filesystem-summary.csv\n?? docs/src/zh/design/concurrency-retest-provenance.csv\n?? docs/src/zh/design/concurrency-startup-summary.csv\n?? docs/src/zh/design/direct-queue-filesystem-summary.csv\n?? docs/src/zh/design/direct-queue-provenance.csv\n?? docs/src/zh/design/experimental/\n?? docs/src/zh/design/filesystem-evidence-overview.csv\n?? docs/src/zh/design/filesystem-measurement-status.csv\n?? docs/src/zh/design/host-copy-up-summary.csv\n?? docs/src/zh/design/host-idle-dispatch-provenance.csv\n?? docs/src/zh/design/host-idle-dispatch-summary.csv\n?? docs/src/zh/design/host-read-summary.csv\n?? docs/src/zh/design/journal-lock-summary.csv\n?? docs/src/zh/design/readonly-dax-provenance.csv\n?? docs/src/zh/design/readonly-dax-summary.csv\n?? docs/src/zh/design/vm-completion-dispatch-provenance.csv\n?? docs/src/zh/design/vm-completion-dispatch-summary.csv\n?? docs/src/zh/design/writeback-filesystem-summary.csv\n?? docs/src/zh/design/writeback-provenance.csv\n?? tests/test_semspec_tables.py\n?? tools/semspec/src/table.rs\n?? tools/semspec/tests/tables.rs\n""}, ""source_manifest_sha256"": ""5311e7c05ca1fc114c60416920c204866e5a6079f509343a91b95f6bf0d37b24""}}" +B-FS-ENG,fof1,environment_overrides,"{""baseline"": {}, ""selected"": {}}" +B-FS-ENG,fof1,harness_manifest_sha256,08cf17c064b561e88d02ad8686479cb64663c98205c0edb2527f34ac5e95d622 +B-FS-ENG,fof1,helper_sha256,"{""child.py"": ""e000fec596b843255ffd06963e981f4a338bba11393cfc51ec308d5351fa33f5"", ""common.py"": ""1f284d8ead6ece72c3247d8742c41564e504da0d2de8f03c4c5dede62f8906cd"", ""launch.py"": ""099103de2d2d1b61b1eceed82855a1dc87003315b427ca470545d0b231f0bac4"", ""paired.py"": ""597773aff5c9383c6f7c4f900e4d30627e5565e722ece91c74b0880118bcea9e"", ""protocol.json"": ""c4ce1b9ee4b7d3fb634d44872e02ad630bbf7e3677e9ddb7699808af3122040b"", ""summarize.py"": ""19be5a05a3d1774a55b6409425efa0505ad0e915274403aea6c52a731cc59b58""}" +B-FS-ENG,fof1,protocol_sha256,c4ce1b9ee4b7d3fb634d44872e02ad630bbf7e3677e9ddb7699808af3122040b +B-FS-ENG,fof1,order_sha256,d56bb3783c17241a3d15e54d5431c80b361d86384370a52f4341728cf9516d49 +B-FS-ENG,fof1,inputs,"{""bytes"": 13455954066, ""directories"": 3256, ""directory_metadata_coverage"": ""complete"", ""files"": 75309, ""input_manifest_sha256"": ""730df0172d73d3c260c741eebf7ac0926dea0add1f05a176c6a39176e9469518"", ""scope"": ""complete rootfs inventory including directory modes; declared asset bytes/modes/symlinks; OCI image and host interpreter verification are separate"", ""symlinks"": 14}" diff --git a/docs/src/en/design/filesystem-optimization-n30-summary.csv b/docs/src/en/design/filesystem-optimization-n30-summary.csv new file mode 100644 index 000000000..3869728d1 --- /dev/null +++ b/docs/src/en/design/filesystem-optimization-n30-summary.csv @@ -0,0 +1,12 @@ +benchmark_id,batch,role,backend,metric,unit,reference,candidate,n,failures,reference_p50_ms,candidate_p50_ms,median_difference_ms,ci95_low_ms,ci95_high_ms,change_percent,ci95_low_percent,ci95_high_percent,reference_distribution,candidate_distribution,interpretation,distribution_limitation +B-FS-ENG,fof1,formal engineering A/B,pvisor-vm,ready_ms,ms,baseline,selected,30,0,693.899161,692.7548735,-1.1442875000000186,-5.2979636499999545,2.705507987499978,-0.16490688623270744,-0.7612537138123532,0.3902048022017106,"{""n"": 30, ""p50"": 693.899161, ""p95_reference"": 704.77595625, ""minimum"": 685.063406, ""maximum"": 759.587939, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 692.7548735, ""p95_reference"": 703.0278088, ""minimum"": 684.449459, ""maximum"": 748.874626, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",no detected difference,predeclared descriptive split rule +B-FS-ENG,fof1,formal engineering A/B,pvisor-vm,result_ms,ms,baseline,selected,30,0,4333.5223785,4257.057506,-76.46487249999973,-285.78103699999974,-7.64772748749933,-1.7644970031622909,-6.290803300512538,-0.17697442920818976,"{""n"": 30, ""p50"": 4333.5223785, ""p95_reference"": 4765.5494103500005, ""minimum"": 4098.158195, ""maximum"": 4909.774685, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 4257.057506, ""p95_reference"": 4832.62272865, ""minimum"": 4091.989833, ""maximum"": 5047.659524, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",faster,predeclared descriptive split rule +B-FS-ENG,fof1,formal engineering A/B,pvisor-vm,completion_ms,ms,baseline,selected,30,0,4546.352988,4467.5308425,-78.82214549999935,-269.8794343625004,-9.921920700000758,-1.7337445136365037,-5.698320121926485,-0.22006512668154682,"{""n"": 30, ""p50"": 4546.352988, ""p95_reference"": 4973.5086638, ""minimum"": 4306.462009, ""maximum"": 5129.24075, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 4467.5308425, ""p95_reference"": 5088.49077675, ""minimum"": 4283.208342, ""maximum"": 5274.443229, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",faster,predeclared descriptive split rule +B-FS-ENG,fof1,formal engineering A/B,pvisor-vm,worker_ms,ms,baseline,selected,30,0,3641.731702,3562.537183,-79.19451900000013,-284.4672740000001,-9.523004500000297,-2.1746390311100483,-7.391955081182977,-0.2652750212475352,"{""n"": 30, ""p50"": 3641.731702, ""p95_reference"": 4037.1929185500003, ""minimum"": 3409.246215, ""maximum"": 4216.111067, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 3562.537183, ""p95_reference"": 4138.1722642, ""minimum"": 3400.59452, ""maximum"": 4349.952972, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",faster,predeclared descriptive split rule +B-FS-ENG,fof1,formal engineering A/B,pvisor-vm,metadata,ms,baseline,selected,30,0,149.126784,132.075482,-17.051301999999993,-36.769790887500015,9.156760999999989,-11.434097579680923,-23.736129395694633,6.361913246535744,"{""n"": 30, ""p50"": 149.126784, ""p95_reference"": 184.22898425, ""minimum"": 108.754779, ""maximum"": 190.56574, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 132.075482, ""p95_reference"": 176.371968, ""minimum"": 107.316462, ""maximum"": 186.0651, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",no detected difference,predeclared descriptive split rule +B-FS-ENG,fof1,formal engineering A/B,pvisor-vm,read,ms,baseline,selected,30,0,114.58174600000001,115.6647925,1.0830464999999947,-0.43468500000000176,4.999490499999993,0.9452173123631669,-0.37351130052453163,4.376549075174441,"{""n"": 30, ""p50"": 114.58174600000001, ""p95_reference"": 121.67214895000001, ""minimum"": 111.850294, ""maximum"": 121.963128, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 115.6647925, ""p95_reference"": 122.11053675000001, ""minimum"": 112.518417, ""maximum"": 127.715376, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",no detected difference,predeclared descriptive split rule +B-FS-ENG,fof1,formal engineering A/B,pvisor-vm,write,ms,baseline,selected,30,0,159.77246300000002,137.107249,-22.66521400000002,-28.724122962499994,-14.978764500000011,-14.18593265348862,-17.580605675377935,-9.820266917526087,"{""n"": 30, ""p50"": 159.77246300000002, ""p95_reference"": 187.98593385, ""minimum"": 130.825608, ""maximum"": 260.216677, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 137.107249, ""p95_reference"": 146.59808115, ""minimum"": 96.838472, ""maximum"": 150.630048, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",faster,predeclared descriptive split rule +B-FS-ENG,fof1,formal engineering A/B,pvisor-vm,git,ms,baseline,selected,30,0,,,,,,,,,"{""n"": 30, ""p50"": """", ""p95_reference"": 776.07380215, ""minimum"": 366.416615, ""maximum"": 778.366539, ""distribution"": ""separated-clusters"", ""low_n"": 22, ""low_p50"": 381.60562849999997, ""high_n"": 8, ""high_p50"": 770.0179430000001}","{""n"": 30, ""p50"": """", ""p95_reference"": 745.14214455, ""minimum"": 347.109328, ""maximum"": 749.268411, ""distribution"": ""separated-clusters"", ""low_n"": 24, ""low_p50"": 369.18431999999996, ""high_n"": 6, ""high_p50"": 736.4385605}",separated clusters; no overall ranking,predeclared descriptive split rule +B-FS-ENG,fof1,formal engineering A/B,pvisor-vm,rg,ms,baseline,selected,30,0,422.94244849999995,424.78182749999996,1.839379000000008,-5.798639499999979,9.07188750000006,0.4349005417932261,-1.3610608883080078,2.1765528699795755,"{""n"": 30, ""p50"": 422.94244849999995, ""p95_reference"": 435.3985634, ""minimum"": 383.878402, ""maximum"": 436.144075, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 424.78182749999996, ""p95_reference"": 436.33608189999995, ""minimum"": 394.482648, ""maximum"": 440.127836, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",no detected difference,predeclared descriptive split rule +B-FS-ENG,fof1,formal engineering A/B,pvisor-vm,cargo,ms,baseline,selected,30,0,484.6475145,482.3360645,-2.3114499999999794,-6.5772249999999985,4.432882387499992,-0.4769342523884945,-1.3474574103963688,0.915387684540544,"{""n"": 30, ""p50"": 484.6475145, ""p95_reference"": 498.9358284, ""minimum"": 468.659131, ""maximum"": 500.977308, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 482.3360645, ""p95_reference"": 500.41343274999997, ""minimum"": 470.665895, ""maximum"": 518.905058, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",no detected difference,predeclared descriptive split rule +B-FS-ENG,fof1,formal engineering A/B,pvisor-vm,npm,ms,baseline,selected,30,0,937.0268659999999,920.087084,-16.939781999999923,-34.37900493749996,-0.9755494999999428,-1.8078224450823743,-3.6317278925731222,-0.10490188738011552,"{""n"": 30, ""p50"": 937.0268659999999, ""p95_reference"": 988.38562445, ""minimum"": 767.385957, ""maximum"": 1457.152233, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 920.087084, ""p95_reference"": 1431.9912869500001, ""minimum"": 758.941401, ""maximum"": 1646.893086, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",faster,predeclared descriptive split rule diff --git a/docs/src/en/design/filesystem-performance-analysis.md b/docs/src/en/design/filesystem-performance-analysis.md index 69e4c72c3..cd527c698 100644 --- a/docs/src/en/design/filesystem-performance-analysis.md +++ b/docs/src/en/design/filesystem-performance-analysis.md @@ -1,5 +1,421 @@ # Filesystem performance: technical analysis and experiment records +## Current conclusions and measurement coverage {#filesystem-status} + +**Latest independent N30 paired comparison: Selected C3 (new-VM writeback off by default): Launch through normal exit changes by -1.73%, 95% CI [-5.70, -0.22]%; Write 256 files changes by -14.19%, 95% CI [-17.58, -9.82]%. Results apply to the measured combined code/default configuration; complete recovery of historical performance is not established.** + +Independent cohorts: [Linux: paired N30 comparison of filesystem optimizations with default-off writeback](#filesystem-optimization-n30). The existing overview retains each historical cohort's conclusions. + +Historical engineering conclusions: + +**The filesystem regression remains unresolved overall.** The small direct-queue retest supports improvements in some tools, with no detected overall task difference. Disabling writeback reduces write cost but has no detected speedup for the complete seven-tool task. DAX retains explicit opt-in and is disabled by default; first mmap and repeated small-file reads regress clearly. + +Each row below uses its own same-condition cohort. Percentages cannot be added, and absolute times cannot be compared across cohorts. Change is candidate relative to that cohort's baseline; negative means less time. Intervals are paired-bootstrap 95% percentage intervals. Direct queues have N=10 per arm; other rows have N=30 per arm. All rows are engineering results. + +| Comparison / metric | N per arm | Baseline ms | Candidate ms | Change | 95% CI % | Conclusion | +| --- | --- | --- | --- | --- | --- | --- | +| [Staged idle dispatch: completion](#host-idle-dispatch) | 30 | 1826.42 | 1774.15 | -2.86% | [-3.57, -1.22] | faster | +| [VM worker fix: completion](#vm-completion-dispatch) | 30 | 4741.87 | 4592.36 | -3.15% | [-6.14, -1.23] | faster | +| [VM concurrency refactor: internal total](#concurrency-retest) | 30 | 3578.18 | 3749.07 | +4.78% | [+2.96, +5.92] | slower | +| [VM direct queues: internal total](#direct-queue-fast) | 10 | 3707.29 | 3606.71 | -2.71% | [-5.28, +7.25] | no detected difference | +| [Writeback disabled: write](#writeback-ab) | 30 | 162.10 | 131.61 | -18.81% | [-25.06, -12.87] | faster | +| [DAX enabled: repeated sequential read](#readonly-dax-ab) | 30 | 5.54 | 4.58 | -17.38% | [-21.40, -10.10] | faster | +| [DAX enabled: first parallel read](#readonly-dax-ab) | 30 | 54.43 | 38.27 | -29.69% | [-32.16, -28.25] | faster | +| [DAX enabled: first mmap page traversal](#readonly-dax-ab) | 30 | 23.83 | 104.75 | +339.59% | [+328.77, +352.40] | slower | +| [DAX enabled: repeated small-file read](#readonly-dax-ab) | 30 | 25.37 | 36.63 | +44.37% | [+36.47, +47.98] | slower | + +Ordinary rootfs/workspace attachments allocate no DAX window by default; immutable shares retain explicit DAX configuration. New VM writeback defaults to disabled, with explicit `PVISOR_VM_FS_WRITEBACK=1` / `0` configuration. By default, restoring a negotiated snapshot retains its mode; explicit `PVISOR_VM_FS_WRITEBACK=0` rejects a snapshot with writeback enabled. Stage synchronization defaults to serial. Historical engineering artifacts and conditions remain intact; their percentages are not summed or used to rescale current user results. + +The selected frozen source/defaults completed the cross-runtime seven-tool cohort on 2026-10-12: thirty trials for each of eight backends, totaling 240 valid tasks, with DAX disabled and new VM writeback disabled by default; see [tool waiting](../benchmarks/filesystem.md#reference-fs). These user data come from independent cohort `fo5a4e7d2b-f`, without pooling history or deriving cross-cohort causal speedups. Earlier accepted and rejected attempts remain in the [acceptance CSV](filesystem-measurement-status.csv). + +macOS [responsiveness during copying and journal locks](#host-concurrency) and Linux HOST [immutable-lower caching](#immutable-lower-cache) have separate valid experiments. [Extended kernel caching](#host-cache-evidence) has passing correctness and request-count evidence, with no accepted formal timing. Cold disks, large repositories and concurrent throughput lack matching new measurements. The [engineering evidence CSV](filesystem-evidence-overview.csv) retains each table row's cohort, metric, source and full precision. + +## Linux: paired N30 comparison of filesystem optimizations with default-off writeback {#filesystem-optimization-n30} + +Independent B-FS-ENG cohort `fof1`, N=30 with three warmups per arm. These tables attribute the measured combined variant/default configuration; screening cohorts, historical switch experiments and cross-runtime user cohorts remain separate. + +The control is the frozen SHA939 build; selected C3 is the frozen SHA884 build containing queue hot-path, handle-lease and Linux regular-file metadata changes, plus default-off writeback for new VMs. Neither arm supplies WRITEBACK/WORKERS environment overrides: the baseline uses its enabled default and C3 its disabled default. Attribution therefore applies only to the combined code/default configuration. Frozen source, artifact and firmware identities are in the provenance download. Linux/KVM/FUSE/Btrfs, warm host caches, a new VM, private user/mount/PID namespace, owned pivot_root, workspace and tool caches per trial; whole parent CPUs 0,1, quota two, 16 GiB and zero swap, guest 2 vCPU/16 GiB. Fresh Host-listener startup is included in completion and cannot be subtracted from the warm-listener cross-runtime cohort. Three warmups and thirty seed-20261011 randomized paired rounds per arm; all sixty formal tasks and six warmups pass seven-tool outputs, full 256×64KiB bytes, Run Bundle, host isolation, input/artifact/budget and external interference gates. Environment observation proves visible exec-time variable inheritance, not precise FUSE INIT negotiation; short, inaccessible or unrecognized interference may be missed. Profiling is disabled with no speed-based exclusions. Earlier N5/N10 screens selected C3 and contribute no samples or uncertainty to this cohort. + +**Selected C3 (new-VM writeback off by default)** + +| Metric | N per arm | Baseline ms | Candidate ms | Change | 95% CI ms | 95% CI % | Conclusion | +| --- | --- | --- | --- | --- | --- | --- | --- | +| First valid output Ready | 30 | 693.90 | 692.75 | -0.16% | [-5.30, +2.71] | [-0.76, +0.39] | No detected difference | +| Launch through complete result | 30 | 4333.52 | 4257.06 | -1.76% | [-285.78, -7.65] | [-6.29, -0.18] | Faster | +| Launch through normal exit | 30 | 4546.35 | 4467.53 | -1.73% | [-269.88, -9.92] | [-5.70, -0.22] | Faster | +| Internal seven-tool total | 30 | 3641.73 | 3562.54 | -2.17% | [-284.47, -9.52] | [-7.39, -0.27] | Faster | +| Traverse 2,048 files | 30 | 149.13 | 132.08 | -11.43% | [-36.77, +9.16] | [-23.74, +6.36] | No detected difference | +| Read and verify 64 MiB | 30 | 114.58 | 115.66 | +0.95% | [-0.43, +5.00] | [-0.37, +4.38] | No detected difference | +| Write 256 files | 30 | 159.77 | 137.11 | -14.19% | [-28.72, -14.98] | [-17.58, -9.82] | Faster | +| git status | 30 | 381.61 (n=22); 770.02 (n=8) | 369.18 (n=24); 736.44 (n=6) | — | — | — | Separated clusters; no overall ranking | +| Ripgrep search | 30 | 422.94 | 424.78 | +0.43% | [-5.80, +9.07] | [-1.36, +2.18] | No detected difference | +| Offline Cargo build | 30 | 484.65 | 482.34 | -0.48% | [-6.58, +4.43] | [-1.35, +0.92] | No detected difference | +| Offline npm install | 30 | 937.03 | 920.09 | -1.81% | [-34.38, -0.98] | [-3.63, -0.10] | Faster | + +Milliseconds; negative changes indicate lower latency. Intervals use 5,000 same-cohort paired bootstrap resamples. Valid slow samples remain; separated clusters receive no overall P50, percentage or ranking. No P95/P99 is reported. + +[Same-cohort full precision CSV](filesystem-optimization-n30-summary.csv) · [Artifact, configuration and provenance](filesystem-optimization-n30-provenance.csv). Raw evidence is local at `benchmark/.data/fof1/report.json`; acceptance is `benchmark/.data/fof1/derived/audit.json`. + +## Linux: paired comparison with guest writeback disabled {#writeback-ab} + +**Disabling writeback reduces 256 × 64 KiB write time from 162.10 ms to 131.61 ms, a decrease of 18.81%, with 95% CI [-25.06%, -12.87%].** Neither completion nor internal seven-tool time has a detected difference. rg and Cargo increase by 1.82% and 1.08%; Git and disabled-condition npm have separated clusters. The detected benefit is confined to this write workload. + +### Control and timing boundaries {#writeback-protocol} + +B-FS-ENG uses one frozen GNU release/gateway artifact, SHA256 `ae221676e4bb04231c88f2ce17c2570824f4064f8a3a63d6ef32817b0e2afa98`, including direct queue service, heavy-operation stealing and scoped I/O fences. Conditions only set host-runner `PVISOR_VM_FS_WRITEBACK` to `1` / `0`; a dedicated worker restores and verifies the originating environment. Firmware, seven-tool workload, inputs, workspaces and tool-cache policy are identical. Rootfs/workspace attachments have no DAX window. + +Linux/KVM/FUSE/Btrfs, warm host caches, fresh VMs, private user/mount/PID namespaces and owned pivot_root per trial. The whole parent uses CPUs 0,1, a two-core quota, 16 GiB and zero swap; guests have 2 vCPU/16 GiB. Input/namespace preparation is excluded. Fresh Host-listener startup, VM launch and exit enter completion; operations use the original workload's internal timer and include output validation. Profiling is disabled. + +Each arm has 3 warmups and 30 seeded shuffled paired rounds, totaling 60 measured tasks. All 66 runs pass output, normal exit, Run Bundle, host isolation and full-byte validation of the 256 upper files. Input/artifact digests match, with no OOM, 0 failures and 0 exclusions. External build/test polling every 0.5 s detects no interference but can miss brief or inaccessible activity. There are 5,000 paired bootstrap resamples; slow valid samples remain, distributions are checked first, and P95/P99 are not reported. + +### Seven operations and complete waiting {#writeback-results} + +Units are ms, N=30 per arm. Change is disabled relative to enabled; the 95% CI is for the median-time difference. Separated distributions show each cluster's median and count, with no overall P50, percentage or ranking. + +| Metric | Writeback on ms | Writeback off ms | Change | 95% CI ms | Conclusion | +| --- | --- | --- | --- | --- | --- | +| First valid output (Ready) | 696.95 | 694.99 | -0.28% | [-4.96, +2.39] | no detected difference | +| Launch to full result | 4409.19 | 4363.77 | -1.03% | [-247.19, +262.05] | no detected difference | +| Launch to normal exit | 4616.37 | 4564.87 | -1.12% | [-250.56, +271.96] | no detected difference | +| Internal seven-tool total | 3699.55 | 3660.70 | -1.05% | [-259.00, +263.60] | no detected difference | +| Traverse 2,048 files | 148.93 | 145.89 | -2.04% | [-26.36, +19.23] | no detected difference | +| Read and verify 64 MiB | 114.83 | 116.22 | +1.21% | [-1.53, +2.47] | no detected difference | +| Write 256 × 64 KiB | 162.10 | 131.61 | -18.81% | [-41.05, -20.17] | faster | +| git status | 391.26 (n=23); 759.78 (n=7) | 375.22 (n=21); 751.37 (n=9) | — | — | Separated clusters; no overall ranking | +| rg | 420.05 | 427.67 | +1.82% | [+1.09, +14.25] | slower | +| Offline Cargo compile | 485.05 | 490.29 | +1.08% | [+0.35, +12.54] | slower | +| Offline npm install | 941.86 | 937.78 (n=22); 1661.33 (n=8) | — | — | Separated clusters; no overall ranking | + +The completion median difference is -51.50 ms, with 95% CI [-250.56, +271.96] ms. Internal task time differs by -38.86 ms, interval [-259.00, +263.60] ms. The write improvement does not yield a detected overall benefit; these timings do not establish READ request counts or specific lock costs. Both Git conditions and disabled-condition npm have separated clusters; all samples are retained and displayed separately. + +[Complete statistics CSV](writeback-filesystem-summary.csv) · [Artifact, switch, budget and provenance](writeback-provenance.csv). Raw evidence is local under `benchmark/.data/writeback-quick-20261011/`; frozen harness, execution and acceptance records are under `benchmark/pvisor/.data/writeback-ab-20261011/`. This cohort has N=30 per arm despite its directory name and is independent of the N=10 direct-queue retest and read-only DAX experiment. The historical enabled/disabled conditions remain intact; the current new-VM default follows the selected frozen source. + +## Linux: paired DAX comparison for an immutable read-only share {#readonly-dax-ab} + +**DAX has substantial tradeoffs in this workload and does not justify default enablement.** The first two-thread 64 MiB read is 29.69% faster and repeated sequential reads are 17.38% faster. The first sequential read is 8.72% slower, repeated small-file reads are 44.37% slower, and the first page-stride access to a 64 MiB mmap is 339.59% slower. Metadata has no detected difference. The measured scope is an explicit immutable share; writable CLI workspaces and startup/exit regressions were not retested by this experiment. + +### Control and timing boundaries {#readonly-dax-protocol} + +B-FS-ENG uses one frozen `performance` artifact (opt-level 3, Thin LTO). Both conditions create a read-only device and a 128 MiB DAX window through the public `immutable_filesystem` API. Only the guest mount option changes, from `dax=never` to `dax=inode`. Every trial verifies the expected statx DAX attribute; the disabled condition retains the same device and window configuration. The host is a Ryzen 7 9700X running Linux 7.2.8; the guest runs Linux 6.12.109. Both use the same Btrfs backing and firmware, warm host content caches, and a fresh VM with private rootfs upper/work directories. + +The complete campaign and VMs inherit CPU 0,1 affinity and share a two-core quota, 2 GiB memory.max, zero swap and a 65,536 FD limit; guests have 2 vCPU/512 MiB. The user cgroup lacks a cpuset controller, so actual affinity/quota is verified before and after. Profiling is disabled. Each condition has 3 warmups followed by 30 pairs in a predetermined balanced random order, totaling 60 measured VMs. All 6 warmups and 60 measured runs passed correctness and exited normally. Input/artifact digests match before/after, with no OOM, 0 failures and 0 exclusions. An observer outside the group scanned for external builds/tests every 0.5 seconds, 123 times, and detected none; brief or other host activity may escape detection. + +Each guest runs metadata → small files → sequential read → mmap → two-thread read. Data classes use independent files while sharing the window and its reclamation state. Metadata stats 2,048 paths. Small-file reads open/read/close 128×64 KiB files, whose 2 MiB mapping-slot demand exceeds the window. Large files are 64 MiB each: read uses 64 KiB pread calls on an already-open FD; the first mmap phase includes mapping and one byte read per 4 KiB page, while repeats retain the mapping; parallel includes creating and joining two reader threads. Full-byte validation is outside each phase timer. First file-data access does not mean cold storage or an empty DAX window. + +Guest `CLOCK_MONOTONIC_RAW` records phases. Repeated read/parallel values are the per-pass average of 4 passes, mmap averages 16 passes, and other phases use one pass. The table reports medians of these 30 samples. CIs use 5,000 paired bootstrap resamples, calculating the difference and percentage between each resample's condition medians; intervals crossing zero are labeled no detected difference. The preregistered adjacent-gap/cluster-size rule detected no separated clusters. All slow valid samples remain; P95/P99 are not reported. + +### Same-batch results {#readonly-dax-results} + +Units are ms. Change is enabled relative to disabled; positive values mean slower. The 95% CI is for the percentage change. + +| Operation | DAX off ms | DAX on ms | Change | 95% CI | Conclusion | +| --- | --- | --- | --- | --- | --- | +| 2,048-path stat: first | 129.0957 | 130.0621 | +0.75% | [-8.33%, +11.32%] | no detected difference | +| 2,048-path stat: repeated | 0.9369 | 0.9690 | +3.43% | [-5.26%, +11.44%] | no detected difference | +| 128×64 KiB open/read/close: first | 40.2088 | 41.4687 | +3.13% | [-0.68%, +5.74%] | no detected difference | +| 128×64 KiB open/read/close: repeated | 25.3731 | 36.6306 | +44.37% | [+36.47%, +47.98%] | slower | +| 64 MiB sequential pread: first | 60.6072 | 65.8917 | +8.72% | [+7.11%, +9.84%] | slower | +| 64 MiB sequential pread: repeated | 5.5443 | 4.5805 | -17.38% | [-21.40%, -10.10%] | faster | +| 64 MiB mmap page-stride access: first | 23.8292 | 104.7507 | +339.59% | [+328.77%, +352.40%] | slower | +| 64 MiB mmap page-stride access: repeated | 0.0964 | 0.1252 | +29.90% | [+27.00%, +33.71%] | slower | +| 64 MiB two-thread pread: first | 54.4338 | 38.2736 | -29.69% | [-32.16%, -28.25%] | faster | +| 64 MiB two-thread pread: repeated | 4.7336 | 4.1625 | -12.07% | [-16.90%, +0.74%] | no detected difference | + +The first mmap adds about 80.92 ms, while each warm sequential read saves about 0.96 ms. Reducing data READ requests is insufficient to offset the current first-access cost. Repeated small-file open/read/close also regresses consistently. Initial mapping, page faults, window reclamation and host mapping contention require separate diagnosis; these paired phase times do not attribute the difference to one lock or step. DAX is disabled by default with explicit opt-in retained. First mmap and window pressure need investigation before integrating read-only tool/image paths. The disabled experimental condition still reserves a window, so its cost does not exactly represent an ordinary device without one. + +[Complete statistics CSV](readonly-dax-summary.csv) · [Provenance and validation summary](readonly-dax-provenance.csv). The entry and reproduction instructions are in repository `benchmark.new/filesystem/README.md`. Raw logs, frozen source and artifacts are local under `benchmark.new/filesystem/.data/dax-20261011-final/`. This engineering batch is independent of the seven-tool, cross-runtime and historical startup datasets. + +## Linux: direct queue service and scoped fences, 60-sample quick retest {#direct-queue-fast} + +**Scheduling fixes show an improvement trend; the write regression remains.** Against the previous concurrency artifact, VM rg, Cargo and npm are about 7.54%, 4.10% and 4.64% faster in this batch. Internal task time falls about 2.71%, but its interval crosses zero. Write is still about 32.40% slower than the earlier optimized artifact. Each cell has only N=10: this is an exploratory direction check, not formal 30-round performance acceptance. + +### Changes and experiment design {#direct-queue-fast-protocol} + +Each queue thread directly waits for and consumes its own kick, admits requests, executes and publishes completion. Ordinary requests no longer use a coordinator-to-owner wake handoff. Stealing only considers a heavyweight operation running for at least 2 ms with actual ring backlog; admission rechecks eligibility under the ring lock. Short metadata operations cannot be stolen merely because their owner is busy. The 2 ms threshold is an elapsed-time heuristic, not proof of kernel sleep. The control thread checks rescue every 2 ms; idle queue threads wait indefinitely. + +Ordinary I/O obtains a native Arc lease under short handle-map/admission locks without a namespace operation guard. Namespace mutations remain serialized, but close admission and drain I/O only for affected files, directory subtrees and known object aliases. Release waits only for its handle. Scoped fences remain through mutation and reopen on failure; new I/O on unrelated handles can enter while a writer waits. Snapshot/restore/destroy retain a global admission fence and drain all I/O. Writeback and host FUSE dispatch are unchanged. + +The frozen GNU release/gateway candidate has SHA256 `ae221676e4bb04231c88f2ce17c2570824f4064f8a3a63d6ef32817b0e2afa98`. Controls in the same cohort are previous concurrency `build2` and earlier `candidate6`; the latter is a recovery reference, not the literal parent of `cf8bfa5`. The original seven-operation workload, firmware, complete tool inputs and timing boundaries are retained. Linux/KVM/FUSE/Btrfs; whole parent CPUs 0,1, two-core quota, 16 GiB, zero swap; VM 2 vCPU/16 GiB. Each trial owns a fresh namespace, pivot_root, workspace and tool caches. Host caches are warm. Fresh Host-listener/VM startup and exit enter completion; input/namespace preparation is excluded. Profiling is off. + +At the user's request, the 180-sample attempt was cancelled and retained separately. A new 60-sample batch runs three versions and two backends, each with 3 warmups and 10 shuffled paired rounds, seed 20261010. All 60 timed tasks and 18 warmups passed the original workload, Run Bundle, isolation and full-byte checks for 256 × 64 KiB upper files. Inputs/artifacts match before/after; no external build/test interference was detected. Polling every 0.5 s may miss brief or inaccessible activity. All slow valid samples remain; no P95/P99 is reported. Intervals from 5,000 paired bootstrap resamples are exploratory only. + +### Quick results and remaining issues {#direct-queue-fast-results} + +Units are ms, N=10 per arm. The previous condition is the same-cohort concurrency artifact; the candidate uses direct queues. The 95% CI is an exploratory median-difference interval. All seven operations, internal total, Ready/Result and normal exit are listed separately. + +| Backend | Metric | Previous ms | Candidate ms | Change | 95% CI ms | Conclusion | +| --- | --- | --- | --- | --- | --- | --- | +| staged | First valid output (Ready) | 218.77 | 217.92 | -0.39% | [-2.07, +2.81] | no detected difference | +| staged | Launch to full result | 1432.62 | 1431.14 | -0.10% | [-9.49, +15.92] | no detected difference | +| staged | Launch to normal exit | 1668.79 | 1667.37 | -0.09% | [-20.76, +28.91] | no detected difference | +| staged | Internal seven-tool total | 1213.94 | 1213.16 | -0.06% | [-7.08, +16.04] | no detected difference | +| staged | Traverse 2,048 files | 66.31 | 65.26 | -1.58% | [-1.32, -0.31] | faster | +| staged | Read and verify 64 MiB | 68.04 | 68.75 | +1.03% | [-1.30, +4.92] | no detected difference | +| staged | Write 256 × 64 KiB | 27.67 | 27.12 | -1.99% | [-1.64, +0.56] | no detected difference | +| staged | git status | 143.37 | 141.31 | -1.43% | [-5.07, +1.35] | no detected difference | +| staged | rg | 106.64 | 106.09 | -0.52% | [-1.78, +0.69] | no detected difference | +| staged | Offline Cargo compile | 77.50 | 76.54 | -1.24% | [-1.90, +9.94] | no detected difference | +| staged | Offline npm install | 226.99 | 230.30 | +1.46% | [+0.62, +6.67] | slower | +| VM | First valid output (Ready) | 691.96 | 693.36 | +0.20% | [-7.61, +5.32] | no detected difference | +| VM | Launch to full result | 4408.21 | 4303.32 | -2.38% | [-198.58, +264.50] | no detected difference | +| VM | Launch to normal exit | 4595.45 | 4484.43 | -2.42% | [-222.17, +248.70] | no detected difference | +| VM | Internal seven-tool total | 3707.29 | 3606.71 | -2.71% | [-200.10, +264.86] | no detected difference | +| VM | Traverse 2,048 files | 148.97 | 142.88 | -4.09% | [-34.00, +57.34] | no detected difference | +| VM | Read and verify 64 MiB | 116.58 | 116.24 | -0.29% | [-5.80, +2.48] | no detected difference | +| VM | Write 256 × 64 KiB | 153.00 | 157.08 | +2.67% | [-21.79, +28.41] | no detected difference | +| VM | git status | 404.51 | 387.07 | -4.31% | [-43.16, +362.83] | no detected difference | +| VM | rg | 453.05 | 418.87 | -7.54% | [-52.35, -19.30] | faster | +| VM | Offline Cargo compile | 508.94 | 488.07 | -4.10% | [-30.01, -11.13] | faster | +| VM | Offline npm install | 975.21 | 929.92 | -4.64% | [-142.85, -34.51] | faster | + +Against previous concurrency, VM internal task P50 is 3707.29 → 3606.71 ms (-2.71%), difference interval [-200.10, 264.86] ms. Completion is 4595.45 → 4484.43 ms (-2.42%), interval [-222.17, 248.70] ms. Both cross zero, so overall recovery is not established. + +- rg is 453.05 → 418.87 ms (-7.54%), exploratory difference interval [-52.35, -19.30] ms. +- Cargo is 508.94 → 488.07 ms (-4.10%), interval [-30.01, -11.13] ms. +- npm is 975.21 → 929.92 ms (-4.64%), interval [-142.85, -34.51] ms. +- Write is 153.00 → 157.08 ms (+2.67%), interval [-21.79, 28.41] ms, with no detected improvement. Metadata/read/Git also have no detected difference. + +Against same-cohort `candidate6`, VM internal task is 3621.29 → 3606.71 ms (-0.40%), with an interval crossing zero; rg/npm also have no detected difference. Write is 118.64 → 157.08 ms (+32.40%), exploratory difference interval [9.88, 63.74] ms, and remains the main unresolved item. Staged internal work is 1213.94 → 1213.16 ms and completion 1668.79 → 1667.37 ms, both without a detected difference. Its small-sample metadata/npm changes are not attributed to unused VM execution changes. Comparisons use only this cohort, without pooling previous medians or attributing separate contributions to the three fixes. + +VM library tests pass 363 with 8 skipped; all-target Clippy and warnings-denied rustdoc pass. Regressions cover direct kick/stop, non-stealing of short requests, long-request backlog completion on its original ring, same-object waits, independent namespace mutation and new unrelated I/O admission during a wait. Snapshot/lifecycle tests are retained. After the measurement build, existing `cfg(test)` device tests were only moved to the end of their file for lint; production implementation matches. + +[Quick statistics and exploratory intervals](direct-queue-filesystem-summary.csv) · [Artifacts, cancellation and provenance](direct-queue-provenance.csv). Raw evidence is local under `benchmark/.data/direct-queue-quick-20261011/`; frozen source, build, preflight, cancellation, validation and static checks are under `benchmark/pvisor/.data/direct-queue-retest-20261011/`. This batch is not expanded or substituted into cross-runtime user tables. Write still needs separate diagnosis; cold storage, concurrency throughput and macOS are not covered. + +## Linux: concurrency refactor retest, 2026-10-11 {#concurrency-retest} + +**This batch does not resolve all filesystem regressions.** Against the pre-refactor artifact, staged completion improves by about 5.00%, while internal task time has no detected difference. VM internal task time increases by about 4.78%, with detected regressions in write, rg and npm. Implementing multiple queues, dedicated workers and handle leases is not performance acceptance. + +### Motivation and experiment design {#concurrency-retest-protocol} + +This experiment checks dedicated hiprio service, multiple normal queues and queue threads, direct execution and pending-descriptor stealing, staged guest writeback, leased I/O, and startup/exit cleanup changes. The candidate is `build2`, a frozen GNU release/gateway build of the current uncommitted source, SHA256 `87ebb0658f2df6a84f9e4eca29c8d3d8bc01923817f04432780272c8b1b30309`. The other arms are the earlier `candidate6` and a control reversing only `65e65c93` and `09bd1d59`. That control retains other changes and default stage synchronization; it is not the literal parent of `cf8bfa5`. This is a version comparison, not attribution of every difference to one optimization. + +Linux/KVM/FUSE/Btrfs, identical firmware and complete tool inputs. The whole parent group uses CPUs 0,1, a two-core quota, 16 GiB and zero swap; the VM has 2 vCPUs and 16 GiB. Each arm/backend has 3 warmups and 30 shuffled paired rounds, seed 20261010, warm host caches, fresh workspaces/tool caches, and a private user/mount/PID namespace with owned pivot_root per trial. Namespace/input preparation is excluded; a fresh Host listener, VM startup and exit are included in completion. The original seven-operation reference workload and timer are used, without profiling; this is not hyperfine. + +### Data and analysis {#concurrency-retest-results} + +Units are ms, N=30 per arm. The previous condition is the same-cohort optimized artifact before the refactor; the candidate is the concurrency-refactor artifact. The 95% CI is for the median difference; separated distributions retain cluster medians and counts. + +| Backend | Metric | Previous ms | Candidate ms | Change | 95% CI ms | Conclusion | +| --- | --- | --- | --- | --- | --- | --- | +| staged | First valid output (Ready) | 229.99 | 219.25 | -4.67% | [-11.62, -9.23] | faster | +| staged | Launch to full result | 1452.13 | 1424.04 | -1.93% | [-38.42, -8.24] | faster | +| staged | Launch to normal exit | 1755.90 | 1668.16 | -5.00% | [-99.79, -66.71] | faster | +| staged | Internal seven-tool total | 1221.64 | 1204.69 | -1.39% | [-21.77, +0.03] | no detected difference | +| staged | Traverse 2,048 files | 66.75 | 66.35 | -0.60% | [-0.71, +0.03] | no detected difference | +| staged | Read and verify 64 MiB | 67.20 | 67.62 | +0.62% | [-0.82, +1.47] | no detected difference | +| staged | Write 256 × 64 KiB | 27.76 | 26.67 | -3.92% | [-1.84, -0.68] | faster | +| staged | git status | 142.65 | 141.87 | -0.54% | [-2.02, +1.26] | no detected difference | +| staged | rg | 107.21 | 107.58 | +0.34% | [-0.41, +1.26] | no detected difference | +| staged | Offline Cargo compile | 71.79 | 70.61 | -1.64% | [-2.77, +0.59] | no detected difference | +| staged | Offline npm install | 230.26 | 230.26 | -0.00% | [-2.28, +2.13] | no detected difference | +| VM | First valid output (Ready) | 701.29 | 693.92 | -1.05% | [-15.10, +0.22] | no detected difference | +| VM | Launch to full result | 4278.45 | 4446.60 | +3.93% | [+65.69, +210.06] | slower | +| VM | Launch to normal exit | 4535.76 | 4635.26 | +2.19% | [-1.90, +148.13] | no detected difference | +| VM | Internal seven-tool total | 3578.18 | 3749.07 | +4.78% | [+107.48, +210.48] | slower | +| VM | Traverse 2,048 files | 157.80 | 138.86 | -12.00% | [-35.21, +1.45] | no detected difference | +| VM | Read and verify 64 MiB | 117.67 | 116.86 | -0.69% | [-3.52, +0.19] | no detected difference | +| VM | Write 256 × 64 KiB | 131.05 | 163.68 | +24.90% | [+15.38, +42.35] | slower | +| VM | git status | 375.05 (n=24); 749.92 (n=6) | 399.04 (n=25); 798.60 (n=5) | — | — | Separated clusters; no overall ranking | +| VM | rg | 426.74 | 450.55 | +5.58% | [+16.56, +36.92] | slower | +| VM | Offline Cargo compile | 501.38 | 502.77 | +0.28% | [-6.34, +10.58] | no detected difference | +| VM | Offline npm install | 923.22 | 973.34 | +5.43% | [+31.98, +62.59] | slower | + +All 180 formal tasks and 18 warmups passed the original task, Run Bundle, isolation and full byte checks for all 256 × 64 KiB upper files. Complete input inventories match before/after. Intervals use 5,000 paired-round bootstrap resamples; all slow valid samples are retained, with no P99. Against `candidate6`: + +- Staged completion P50 is 1755.90 → 1668.16 ms: -87.74 ms, CI95 [-99.79, -66.71]. Internal task time is 1221.64 → 1204.69 ms, with difference interval [-21.77, 0.03] crossing zero. Write improves by about 3.92%; other operations have no detected difference. +- VM internal task time is 3578.18 → 3749.07 ms: +170.89 ms, CI95 [107.48, 210.48]. Write is 131.05 → 163.68 ms (+24.90%), rg 426.74 → 450.55 ms (+5.58%), and npm 923.22 → 973.34 ms (+5.43%). All three difference intervals exclude zero; metadata, read and cargo have no detected difference. +- VM completion is 4535.76 → 4635.26 ms, with difference interval [-1.90, 148.13] crossing zero. Better exit handling cannot conceal slower internal work. Git has separated clusters in both arms: baseline cluster medians 375.05/749.92 ms with 24/6 samples, candidate 399.04/798.60 ms with 25/5. No whole-distribution median ranking is made. +- Against the same-cohort rollback control, VM internal work is still 4.45% slower, write 24.59%, rg 6.17%, and npm 4.86%, with all these difference intervals excluding zero. Staged internal task time has no detected difference. + +The first formal attempt was rejected for external Cargo activity; its 27 samples are retained separately. Only the new batch is used above, with no interference detected by 0.5 s external build/test polling, which may miss brief, inaccessible or unrecognized activity. Initial VM preflight also exposed a race between an empty-ring check and guest descriptor publication. The fix was rebuilt from newly frozen source and passed preflight; a new 5,000-publication concurrency regression test passed. Related library tests passed 633 with 19 skipped; after the fix, VM library tests passed 361 with 8 skipped. Full CLI integration testing had Host-listener/namespace environment failures and is not reported as a complete pass. + +[Full statistics and paired intervals](concurrency-filesystem-summary.csv) · [Artifacts, control scope and provenance](concurrency-retest-provenance.csv) · [Startup/exit retest](vm-startup-performance-analysis.md#concurrency-retest). Raw report and per-trial evidence are local under `benchmark/.data/concurrency-formal2-20261011/`; builds, rejected attempts and validation scripts are under `benchmark/pvisor/.data/concurrency-retest-20261011/`. These engineering results do not replace historical cross-runtime user tables or establish cold-disk, concurrency-throughput or macOS performance. VM write/tool costs and staged startup variability require separate diagnosis. + +## Idle request dispatch in Linux staged execution {#host-idle-dispatch} + +**A controlled engineering A/B finds that idle dispatch reduces staged write time by 19.27%, Git by 12.78%, search by 12.88%, and the complete seven-tool task by 2.86%; no difference is detected for reading or startup.** This covers Linux staged only. It does not rescale the cross-runtime user table or establish VM, macOS, cold-disk or concurrent-throughput results. + +### Motivation, implementation and experiment design {#host-idle-protocol} + +Responsiveness during copying and everyday tool cost must be evaluated together. The previous scheduler hands even idle short requests to a worker, adding thread switches. The fix executes mutations requiring no copy preparation directly when the mutation lane is idle; reads execute directly only when both lanes are idle. READ requests of at least 64 KiB retain worker dispatch for bulk-read pipelining. Accepted requests or active copy preparation keep reads on their independent bounded queue; inode pins, FIFO, capacity limits and shutdown draining remain enforced. Baseline fingerprints, journals, durability and isolation retain their existing semantics. + +Measured 2026-10-10 on Linux x86_64 / Fedora 7.2.8-200.fc44.x86_64 / Ryzen 7 9700X / Btrfs. Both release artifacts use one frozen source; the candidate changes only host dispatch, private tests and API/README documentation. The whole parent uses CPUs 0,1, a two-core quota, 16 GiB memory and zero swap. Host caches are warm; every task has a fresh workspace and private tool caches, Python bypasses bytecode and Node caching is enabled. The seven workloads match [B-FS-TOOLS](../benchmarks/filesystem.md#reference-fs), with default serial stage synchronization. + +Every sample uses independent user/mount/PID namespaces and an owned pivot_root, binding workspace and stage to the same Btrfs storage. Namespace creation, binary copies and input preparation are untimed; a fresh Host listener is included in complete-task timing. Absolute times therefore are not pooled with the user page's cross-runtime configuration. Each arm has three warmups and 30 seeded randomly paired rounds, seed 20261010, without profiling, using the original reference task timer rather than hyperfine. All 60 formal tasks and six warmups pass output, Run Bundle, host-isolation and full-byte checks of all 256 upper files; input inventories match before and after. An external observer checks same-user builds/tests every 0.5 seconds and rejects the entire cohort on detection. None is detected; short, inaccessible or unrecognized activity can be missed. + +### Engineering results and correctness boundaries {#host-idle-results} + +Milliseconds, P50, N=30 per arm. Change is candidate relative to baseline; intervals use 5,000 paired-round bootstrap resamples. No distribution triggers the separated-cluster rule, and no P99 is reported. + +| Metric | Baseline ms | Fixed ms | Change and 95% CI | +| --- | ---: | ---: | --- | +| Tool Ready | 231.34 | 231.15 | -0.08% [-0.87%, +0.41%]; no detected difference | +| Complete seven-tool task and exit | 1826.42 | 1774.15 | -2.86% [-3.57%, -1.22%] | +| Traverse 2,048 files | 67.69 | 66.88 | -1.20% [-1.99%, -0.81%] | +| Read and verify 64 MiB | 72.05 | 70.12 | -2.68% [-6.31%, +4.80%]; no detected difference | +| Write 256 files | 35.09 | 28.33 | -19.27% [-20.37%, -18.47%] | +| git status | 162.31 | 141.56 | -12.78% [-13.35%, -12.15%] | +| Ripgrep search | 122.74 | 106.93 | -12.88% [-13.50%, -12.17%] | +| Offline Cargo build | 79.37 | 77.06 | -2.92% [-4.21%, -1.63%] | +| Offline npm install | 231.70 | 229.50 | -0.95% [-1.74%, -0.06%] | + +The complete-task median difference is -52.27 ms, 95% CI [-65.35, -22.30] ms. The read interval includes zero, so no read-speed gain is claimed. All 68 ordinary Rust tests, seven real Linux FUSE mount tests, targeted Clippy and warnings-denied rustdoc pass. Coverage retains metadata/readonly access during copying, saturation, FIFO, worker and inline shutdown draining, cache notifications and parallel mount stress. This fix has no macOS performance acceptance yet. + +[Idle-dispatch summary CSV](host-idle-dispatch-summary.csv) · [Source, artifact and experiment provenance](host-idle-dispatch-provenance.csv). Raw reports, stdout, budget and namespace receipts remain locally in `benchmark/.data/fr10fixf3/`; frozen sources, build receipts, publication checks and external interference records remain in `benchmark/pvisor/.data/filesystem-regression-20261010/`. Raw and intermediate diagnostic samples are retained and excluded from formal statistics. + +## Linux VM worker scheduling and completion wakes {#vm-completion-dispatch} + +**In the controlled Linux VM engineering comparison, the fix reduces complete-task latency by 3.15%, 64 MiB read/verify latency by 3.06%, and offline Cargo build latency by 3.14%. Other metrics show no detected difference or separated clusters.** Against the control reverting only the two recent changes, Traverse 2,048 files, Read and verify 64 MiB, Offline Cargo build still show detectable costs; complete recovery of single-request costs is not established. This is B-FS-ENG evidence, kept separate from the staged engineering cohort and cross-runtime user tables. It does not establish macOS, cold-disk or concurrent-throughput results. + +### Motivation, implementation and experiment design {#vm-completion-protocol} + +Offloading large reads and directory requests leaves metadata/hiprio admission responsive, but worker migration, receiver selectors and completion wakes add tool latency for uncontended requests. The fix reuses the first worker whenever all in-flight requests have cleared, reducing migration for uncontended requests. Requests arriving with work in flight retain the original shared FIFO and parallel receivers for bursts. The dedicated idle inlet has capacity one and counts against the same total bound covering queued, executing and unpublished completions. The owner retains RAM lease management. Before waiting, the owner atomically arms and checks the completion channel; after sending completion, workers atomically disarm and signal only an armed owner. Ready completions use a zero-timeout poll that still checks stop first. Large READ/directory requests remain asynchronous, the owner alone publishes the used ring, and shutdown drains before joining all workers. + +Measured on 2026-10-11: Linux x86_64 / Fedora 7.2.8-200.fc44.x86_64 / Ryzen 7 9700X / Btrfs. Three release artifacts share the same frozen October 10 source: the reverted control reverses only `65e65c93` and `09bd1d59`; the current baseline retains those changes and the host idle-dispatch fix; the candidate changes only the VM worker implementation, private tests and crate README relative to that baseline. The reverted control retains all other source and serial stage-sync defaults. It is unrelated to the October 6 historical cohort. + +The whole parent has CPUs 0,1, a two-core quota, 16 GiB and no swap; VMs have 2 vCPUs and 16 GiB guest RAM with the same firmware, tools and inputs. Host caches are warm; every trial has a fresh workspace and separate tool caches, Python reads/writes no bytecode, and Node caching is enabled. Each trial owns a private user/mount/PID namespace, pivot_root, fresh VM and Host listener. Namespace/input preparation is outside the timer; launch and exit are included in completion. Workspace/stage remain Btrfs binds. Each artifact has 3 warmups and 30 randomly paired rounds, seed 20261010. Timing uses the original reference task timer, not hyperfine, with profiling disabled. + +All 90 formal tasks and 9 warmups passed output, Run Bundle, host isolation, complete bytes in all 256 upper files and before/after input-inventory checks. Same-user build/test polling every 0.5 s detected no interference; short, inaccessible or unrecognized activity may be missed. All valid slow samples remain. This cohort is retained separately and does not fill other cohorts. + +### Controlled results and remaining costs {#vm-completion-results} + +Units ms, N=30 per artifact. Values are normally P50; separated clusters show their medians and counts without an overall ranking. Intervals use 5,000 paired-round bootstrap resamples. No P99 is reported. + +| Metric | Current baseline ms | Fixed ms | Change and 95% CI | +| --- | ---: | ---: | --- | +| Tool Ready | 702.39 | 701.45 | -0.13% [-0.39%, +0.41%]; no detected difference | +| Complete seven-tool task and exit | 4741.87 | 4592.36 | -3.15% [-6.14%, -1.23%] | +| Traverse 2,048 files | 154.58 | 159.74 | +3.34% [-11.45%, +10.03%]; no detected difference | +| Read and verify 64 MiB | 120.47 | 116.78 | -3.06% [-3.89%, -0.96%] | +| Write 256 files | 130.89 | 130.24 | -0.50% [-9.19%, +9.89%]; no detected difference | +| git status | 436.55 | 371.64 (25/30); 617.08 (5/30) | Separated clusters; no overall ranking | +| Ripgrep search | 417.92 | 424.90 | +1.67% [-1.01%, +5.40%]; no detected difference | +| Offline Cargo build | 523.22 | 506.79 | -3.14% [-4.46%, -0.53%] | +| Offline npm install | 926.99 | 923.10 | -0.42% [-1.88%, +0.63%]; no detected difference | + + +Recovery uses the reverted control from this same cohort, rather than historical page percentages: + +| Metric | Reverted control ms | Fixed ms | Change and 95% CI | +| --- | ---: | ---: | --- | +| Complete seven-tool task and exit | 4553.30 | 4592.36 | +0.86% [-1.41%, +2.43%]; no detected difference | +| Traverse 2,048 files | 132.99 | 159.74 | +20.12% [+1.50%, +35.87%] | +| Read and verify 64 MiB | 114.36 | 116.78 | +2.12% [+0.80%, +3.71%] | +| Offline Cargo build | 488.23 | 506.79 | +3.80% [+2.84%, +5.71%] | + + +Against the control reverting only the two recent changes, Traverse 2,048 files, Read and verify 64 MiB, Offline Cargo build still show detectable costs; complete recovery of single-request costs is not established. The three artifacts include recent scheduling and journal semantic changes, so remaining gaps cannot all be attributed to one worker handoff. Metrics with separated clusters have no single overall ranking. All 359 VM tests, targeted Clippy, warnings-denied rustdoc and real CLI/guest execution passed. Coverage includes idle-worker reuse, metadata behind long requests, out-of-order completion, retained RAM leases, nonparking ready completions and freeze drain. macOS performance has not been accepted. + +[VM scheduling statistics CSV](vm-completion-dispatch-summary.csv) · [Source, artifact and experiment provenance](vm-completion-dispatch-provenance.csv). Raw reports and trial evidence remain locally in `benchmark/.data/fr10vmfixf/`; frozen source, builds and publication checks are in `benchmark/pvisor/.data/filesystem-regression-20261010/`. These absolute times are not joined to the configuration on the cross-runtime user page. + +## Optimization candidates after the fixes {#optimization-candidates} + +**Prioritize repeated physical checks during path resolution, then the cache contract for immutable rootfs inputs, directory requests and completion notifications.** Three instrumented diagnostics support this investigation order; speedups for these candidates remain unmeasured. + +### Motivation and experiment design {#optimization-candidates-protocol} + +On 2026-10-11, B-FS-DIAG runs three independent VM seven-tool tasks using the final frozen `candidate5` release above (SHA-256 `a4cfdbc6e2657f9ff6a43a4c640cd472c1c94c965c9d388924e58727dc55556d`). Inputs, firmware, private namespaces/owned pivot_root, whole-parent CPUs 0,1 / two-core quota / 16 GiB / zero swap and tool-cache policy match the earlier experiment. Filesystem profiling and startup timing are enabled; stderr goes to regular files. There are no warmups, version controls or external build/test interference monitoring. These diagnostics do not enter user tables or performance acceptance. + +All three tasks pass tool output, Run Bundle, host isolation and full-byte checks of 256 upper files; input inventories match before and after. Each has 13/13 final profile instances, retaining the last cumulative record by PID/component/instance and separating equal-numbered supervisor and VMM instances. The table aggregates the two active VM instances. Cumulative spans for a label can overlap across workers; they cannot establish task-time fractions, predicted speedups or attribution to individual tools. + +### Request costs and candidate priorities {#optimization-candidates-results} + +| Observation | Range across three tasks | Implication | +| --- | ---: | --- | +| Core path `resolve`, cumulative inclusive span | 368.6–378.8 ms | Investigate repeated path validation first | +| Core physical parent stat count | 186,401–187,722 | Deep paths still repeat many checks per entry | +| Core parent identity statx count | 109,564–110,238 | Preserve identity validation; evaluate reuse within one request | +| Worker queue and completion wait, sum of cumulative spans | 84.0–93.5 ms | Dispatch costs remain; preserve responsiveness during long I/O | +| Used interrupt count across both VM instances | 37,572–37,660 | Investigate batching and notification opportunities | + +| Priority | Candidate | Required contract checks and experiment | +| --- | --- | --- | +| 1 | Reuse validated parent/leaf information between Core and VM backing | Core resolves metadata before VM native lookup performs openat/statx again. Evaluate passing validated identities and owned backing handles within one request; preserve host replacements, mount identity, hard-link denials and reference release after errors. Measure deep-path metadata, READDIRPLUS and Cargo separately. | +| 2 | Metadata/data caches for explicitly immutable rootfs inputs | A guest read-only mount can still be modified by its host; input lifetime or invalidation must establish immutability. Reduce Core physical queries before evaluating guest request counts; retain external-change visibility for live workspaces. Measure first access, warm caches and TTL expiry separately. | +| 3 | Separate ordinary READDIR and READDIRPLUS service paths | Ordinary READDIR usually consumes OPENDIR name/type snapshots; PLUS resolves fresh attributes per entry. Evaluate bounded snapshot handling and parent reuse in batches; preserve old-handle fallbacks, lock waits, large directories, deletion, permissions and cookies/restoration. Moving every directory request inline is not justified. | +| 4 | Worker dispatch and completion/interrupt batching | Preserve shared load balancing, long-read offload, owner-only virtqueue publication, RAM leases and freeze draining. Three earlier timing attempts for a per-worker queue candidate were rejected for external build/test interference; it remains unmerged. Add mixed long/short request and saturation correctness coverage before an independent 30-round A/B. | +| 5 | Repeated fingerprint and persistence work | Each workspace performs 2,157 content fingerprints, with 46.3–51.9 ms cumulative spans. Investigate repeated observations/batching while retaining preimages before first mutation, journal order, durability and recovery; delaying records is not an equivalent low-cost substitute. | + +Linux local reads in this frozen artifact use `preadv` directly into guest descriptor buffers; the diagnostic does not establish a benefit from eliminating another intermediate copy. Independent DAX and writeback switch results are now available in the [read-only share comparison](#readonly-dax-ab) and [writeback comparison](#writeback-ab); guest-local/block rootfs lacks corresponding acceptance. These three diagnostic trials have no per-tool checkpoints, so overlapping spans cannot establish the timing share of metadata, read or Cargo. + +Raw evidence: `benchmark/.data/fr11explore/report.json`, frozen `runner.py`/`child.py`/`harness/`, artifact/source receipts, per-task stderr/stdout, `diagnostic-summary.json` and `counter-summary.csv`. Formal candidates continue through B-FS-ENG acceptance: same-source randomized pairing, three warmups/30 rounds, correctness and interference gates. A passing candidate can then justify repeating the cross-runtime user matrix. + +## Concurrent host access during copying and fingerprinting {#host-concurrency} + +**Independent engineering A/B on Apple M4 / APFS / macFUSE FSKit shows that host scheduling and journal-lock changes allow unrelated short file requests to overlap a 1 GiB copy or hash.** The gain is responsiveness during blocking work; complete seven-tool tasks, Linux FUSE, VM and cold disks have no corresponding new performance conclusions. These three independent B-FS-ENG cohorts are not pooled or used to rescale the [cross-runtime tool baseline](../benchmarks/filesystem.md). + +### Motivation and experiment design {#host-concurrency-protocol} + +A first write to a large file triggers copy-up, and a first read observation triggers content fingerprinting. Evaluate unrelated access waiting, idle request overhead and long-operation completion together to judge the scheduling benefits and costs. + +Measured 2026-10-10 on Apple M4, 24 GiB RAM, Darwin 27.0.0 / arm64, APFS, macFUSE 5.4.0 / FSKit, Rust 1.98.0 release, without fixed CPU affinity or whole-host resource limits. Each A/B freezes the same source and dependencies, restoring/retaining only its selected changes. Copy-up scheduling baseline files come from `392c9d99`; read-dispatch/journal-lock baseline files come from `f109dd25`, with candidate source `09bd1d59`. Other changes remain identical within each comparison; the two comparisons do not share one candidate artifact. + +Each cell has three warmups and 30 formal paired rounds, with seed 4207 shuffling conditions and A/B order. Inputs are warm-cache allocated deterministic 1 GiB files, fresh upper/work and independent paths. Copy experiments use real host mounts; journal experiments use one shared native Core without FUSE. Probes require an actually incomplete temporary copy, or evidence that the first content read occurred while hashing remains active, without artificial delay. Read probes release OPEN, READ and enumeration simultaneously and independently check that copying is still active, preventing an earlier blocking probe from turning later probes into idle measurements. + +Untimed checks cover complete lower/upper bytes, identity, hardlinks, held descriptors, rename, temporary-copy cleanup, journal fingerprints and normal unmount; native journal trials verify all 129 observations. Visible build/test interference is checked before, every 250 ms during and after each trial. Errors, missed windows or detected interference fail the whole cohort. Reports retain zero formal failures and zero slow-sample exclusions; every valid slow sample remains. Visible-process sampling cannot exclude short or inaccessible activity. + +Tables use milliseconds, N=30 per baseline/candidate cell, normally P50. Separated distributions list cluster medians and counts without a whole-distribution ranking. Intervals use 5,000 paired-round bootstrap resamples; P95 is descriptive only, with no P99. Formal timing has no profiling instrumentation. + +### Copy-up scheduling: metadata responsiveness {#host-copy-up} + +| Journal condition | Operation | Baseline ms | Candidate ms | Median change and 95% CI | +| --- | --- | ---: | ---: | --- | +| nojournal | First stat during copying | 802.858 | 0.681 | -99.915% [-99.919%, -99.911%] | +| nojournal | 64-path stat during copying | 836.938 | 20.999 | -97.491% [-97.545%, -97.422%] | +| nojournal | 1 GiB writable-open | 803.733 | 819.464 | +1.957% [-0.896%, +4.095%]; no detected difference | +| nojournal | Idle 64-path stat | 34.558 | 34.596 | +0.108% [-8.278%, +7.639%]; no detected difference | +| compact | First stat during copying | 790.321 | 1.655 | -99.791% [-99.816%, -99.782%] | +| compact | 64-path stat during copying | 827.025 | 34.640 | -95.811% [-95.854%, -95.739%] | +| compact | 1 GiB writable-open | 1232.667 | 1231.293 | -0.111% [-1.280%, +1.470%]; no detected difference | +| compact | Idle 64-path stat | 32.165 | 34.069 | +5.921% [+2.299%, +10.745%] | + +Candidate 64-path stat completes before copying in 30/30 samples in both conditions; baseline first stat waits for copying to finish. The trigger occurs after byte copying begins, excluding earlier baseline-fingerprint waiting. Writable-open includes fingerprinting, copying, publication and protocol return; neither condition detects a median difference. Compact idle batches increase by 1.90 ms, absolute 95% CI [+0.76, +3.36] ms, precluding a general claim of faster everyday metadata operations. + +### Independent read dispatch and journal fingerprint locks {#host-read-journal} + +The mounted-read cohort is independent of the metadata cohort above. Every candidate read probe in every condition finishes before copying in 30/30 samples, versus 0/30 for baseline. + +| Journal condition | Operation | Baseline ms | Candidate ms | Median change and 95% CI | +| --- | --- | ---: | ---: | --- | +| nojournal | Read-only OPEN during copying | 784.602 | 1.577 | -99.799% [-99.817%, -99.782%] | +| nojournal | Held-fd READ during copying, 64 KiB | 784.597 | 0.521 | -99.934% [-99.936%, -99.927%] | +| nojournal | OPENDIR and 64-entry enumeration during copying | 791.558 | 6.919 | -99.126% [-99.190%, -99.074%] | +| legacy | Read-only OPEN during copying | 797.214 | 3.172 | -99.602% [-99.612%, -99.579%] | +| legacy | Held-fd READ during copying, 64 KiB | 797.424 | 0.960 | -99.880% [-99.909%, -99.860%] | +| legacy | OPENDIR and 64-entry enumeration during copying | 804.033 | 10.292 | -98.720% [-98.758%, -98.695%] | +| compact | Read-only OPEN during copying | 800.275 (25/30); 2340.599 (5/30) | 2.219 | Separated; no single ranking | +| compact | Held-fd READ during copying, 64 KiB | 800.486 (25/30); 2340.717 (5/30) | 0.760 | Separated; no single ranking | +| compact | OPENDIR and 64-entry enumeration during copying | 807.137 (25/30); 2347.782 (5/30) | 9.737 | Separated; no single ranking | + +Compact baseline reads separate into 25/30 ordinary and 5/30 slow samples; both clusters remain without a single speedup percentage. OPEN measures opening only; complete open/read/close is separate in the CSV and includes FSKit parent xattr queries and read-only close callbacks. + +The native journal cohort measures `observe_read` for 64 unrelated paths, also recording hash completion. Read observations retain their non-durable-per-read policy, with final `sync_preimages` outside timing. + +| Journal condition | Operation | Baseline ms | Candidate ms | Median change and 95% CI | +| --- | --- | ---: | ---: | --- | +| legacy | First unrelated read observation | 462.276 | 0.184 | -99.960% [-99.964%, -99.956%] | +| legacy | 64-path read observations | 473.262 | 12.011 | -97.462% [-97.521%, -97.384%] | +| legacy | Complete 1 GiB fingerprint | 462.230 | 469.558 | +1.585% [+0.621%, +1.964%] | +| compact | First unrelated read observation | 450.714 | 0.045 (25/30); 0.071 (5/30) | Separated; no single ranking | +| compact | 64-path read observations | 453.158 | 2.385 | -99.474% [-99.477%, -99.464%] | +| compact | Complete 1 GiB fingerprint | 450.739 | 452.237 | +0.332% [-0.398%, +0.783%]; no detected difference | + +Legacy's 64-path observation waiting falls substantially, while its full hash increases by 7.329 ms, absolute 95% CI [+2.894, +9.066] ms. Compact hashing detects no difference. All idle probes in the read cohort and idle observations in the journal cohort detect no difference, which does not prove equal overhead. Compact writable-open median falls by 1.89%, 95% CI [−3.13%, −0.45%]; other conditions detect no difference. This measures open completion under concurrent probes, not independent storage bandwidth. + +Long-operation tails remain uncertain: the read cohort's no-journal writable-open reference P95 moves from 952.6 to 3166.9 ms, with maxima 1469.6/4636.8 ms; all slow samples remain. Normal read/write I/O, directory snapshots and publication can still occupy the shared state lock. Independent queues do not guarantee arrival order for overlapping reads and mutations, and concurrent first observations can duplicate fingerprint work. Short-request improvements do not establish that complete Agent tasks or long operations never regress. + +### Parallel stage synchronization: measurement status {#parallel-stage-sync} + +`PVISOR_STAGE_SYNC_WORKERS=2|4|8` opts into a bounded regular-file synchronization pool during stage sealing; unset or `1` retains serial execution. Journal validation and persistence finish first. Every file synchronization must succeed and every worker join before bottom-up directory synchronization, parent persistence and atomic seal publication; failure publishes no new seal. Explicit workload fsync and journal synchronization do not use this pool. + +**No independent serial/parallel timing data is available.** The A/B above does not change worker counts and cannot quantify this option's gains. Evaluation requires one frozen binary, fresh stages and otherwise identical configurations, timing through complete persistence and verifying unchanged lowers, complete upper bytes and completion markers. Implementation availability does not establish a speedup magnitude. + +### Derived data, sources and reproduction {#host-concurrency-evidence} + +[Copy-up statistics CSV](host-copy-up-summary.csv) · [Read-dispatch statistics CSV](host-read-summary.csv) · [Journal-lock statistics CSV](journal-lock-summary.csv). These directly copy committed derived results, retaining cohorts, sample counts, distributions, relative/absolute intervals and raw-report digests; artifact digests and source receipts are in the source reports. Complete idle controls, reference tails and extrema remain in the CSVs. + +Sources: [copy-up engineering report](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/pvisor/HOST_COPY_UP_REPORT.md), [read-dispatch and journal-lock engineering report](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/pvisor/HOST_READ_JOURNAL_REPORT.md). Raw logs, sources and artifacts remain in the measurement workspace's `benchmark/pvisor/.data/host-copy-up-*`, `.data/read-*` and `.data/journal-*`, without site downloads. Reproduce using the [host copy-up manual](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/pvisor/README.md#host-copy-up-scheduling-ab-on-macos) and [read-dispatch/journal-lock manual](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/pvisor/README.md#read-dispatch-and-journal-fingerprint-locks-on-macos). macOS/macFUSE is required; Linux harness tests cannot replace mounted measurements. + ## Current Linux HOST kernel caching and evidence {#host-kernel-cache} **Under an explicit ownership contract, the Linux HOST API supports writable `Metadata` and read-only `MetadataAndData`; current-source correctness tests and independent request counters are valid, while formal performance acceptance remains blocked.** Kernel caching and the service-side immutable-lower physical metadata cache below are separate mechanisms. Neither automatically changes product executor defaults. diff --git a/docs/src/en/design/host-copy-up-summary.csv b/docs/src/en/design/host-copy-up-summary.csv new file mode 100644 index 000000000..37941900e --- /dev/null +++ b/docs/src/en/design/host-copy-up-summary.csv @@ -0,0 +1,9 @@ +benchmark,batch,condition,metric,unit,samples_per_arm,formal_failures,excluded_slow_samples,baseline_distribution,baseline_high_n,baseline_high_p50,baseline_low_n,baseline_low_p50,baseline_maximum,baseline_minimum,baseline_n,baseline_p50,baseline_p95_reference,candidate_distribution,candidate_high_n,candidate_high_p50,candidate_low_n,candidate_low_p50,candidate_maximum,candidate_minimum,candidate_n,candidate_p50,candidate_p95_reference,percent_change,ci95_percent_low,ci95_percent_high,median_difference_ms,ci95_difference_low_ms,ci95_difference_high_ms,report_sha256,bootstrap +B-FS-ENG,host-copy-up-formal-20261010,nojournal,first_stat_ms,ms,30,0,0,unsplit,,,,,3634.056499984581,773.0186250410043,30,802.857500500977,867.7273062290624,unsplit,,,,,1.1204580077901483,0.6125419749878347,30,0.6813124928157777,0.8123080275254324,-99.91513905115283,-99.9189855369621,-99.91109010023358,-802.1761880081613,-816.8258755176794,-794.943229004275,68c475e116a752f2029cdcb77798a747aff1e1da9147d1222ce869b1d5db15bb,5000 paired-round resamples; seed=4207 +B-FS-ENG,host-copy-up-formal-20261010,nojournal,copying_metadata_ms,ms,30,0,0,unsplit,,,,,3667.125958018005,808.33191704005,30,836.937708518235,903.5226732521551,unsplit,,,,,29.996583005413413,19.96750000398606,30,20.99912500125356,24.43401061464101,-97.4909572376143,-97.54533893196262,-97.42224995407759,-815.9385835169815,-830.5969170178287,-810.2244584879372,68c475e116a752f2029cdcb77798a747aff1e1da9147d1222ce869b1d5db15bb,5000 paired-round resamples; seed=4207 +B-FS-ENG,host-copy-up-formal-20261010,nojournal,open_ms,ms,30,0,0,unsplit,,,,,3634.9424170330167,773.8944999873638,30,803.7328124919441,868.3261270605726,unsplit,,,,,3249.2663330049254,772.2236250410788,30,819.4636040134355,2668.3761774387676,1.9572165372617745,-0.8961738589858936,4.09467614666541,15.730791521491483,-7.3310419684275985,32.76160449604504,68c475e116a752f2029cdcb77798a747aff1e1da9147d1222ce869b1d5db15bb,5000 paired-round resamples; seed=4207 +B-FS-ENG,host-copy-up-formal-20261010,nojournal,idle_metadata_ms,ms,30,0,0,unsplit,,,,,43.7037919764407,26.64250001544133,30,34.55812548054382,41.39485379564576,unsplit,,,,,44.494583969935775,27.319041022565216,30,34.59558301256038,40.166706405580044,0.10838994157147397,-8.27768514563212,7.638638248704233,0.037457532016560435,-2.914375509135425,2.479542017681524,68c475e116a752f2029cdcb77798a747aff1e1da9147d1222ce869b1d5db15bb,5000 paired-round resamples; seed=4207 +B-FS-ENG,host-copy-up-formal-20261010,compact,first_stat_ms,ms,30,0,0,unsplit,,,,,2496.950750006363,750.2186250058003,30,790.3214584803209,1632.986518763938,unsplit,,,,,2.092125010676682,1.0929999989457428,30,1.6546040133107454,1.954728623968549,-99.79064164390876,-99.81594063187839,-99.7815962367874,-788.6668544670101,-796.6828960052226,-779.6370619907975,68c475e116a752f2029cdcb77798a747aff1e1da9147d1222ce869b1d5db15bb,5000 paired-round resamples; seed=4207 +B-FS-ENG,host-copy-up-formal-20261010,compact,copying_metadata_ms,ms,30,0,0,unsplit,,,,,2532.46862499509,784.1280410066247,30,827.0250624918845,1667.264447780327,unsplit,,,,,37.64983295695856,32.7152909594588,30,34.64014548808336,37.418529522255994,-95.81147572678024,-95.85382866371097,-95.73939479771849,-792.3849170038011,-798.2175000070129,-781.1195210088044,68c475e116a752f2029cdcb77798a747aff1e1da9147d1222ce869b1d5db15bb,5000 paired-round resamples; seed=4207 +B-FS-ENG,host-copy-up-formal-20261010,compact,open_ms,ms,30,0,0,unsplit,,,,,2939.6734590409324,1189.8464169935323,30,1232.6674994837958,2075.7134746876554,unsplit,,,,,2999.5553749613464,1193.1392920087092,30,1231.2933119828813,2251.845103499364,-0.11148079279205136,-1.279514241934676,1.4696008595677323,-1.3741875009145588,-15.877063036896288,17.951396468561143,68c475e116a752f2029cdcb77798a747aff1e1da9147d1222ce869b1d5db15bb,5000 paired-round resamples; seed=4207 +B-FS-ENG,host-copy-up-formal-20261010,compact,idle_metadata_ms,ms,30,0,0,unsplit,,,,,43.17408398492262,27.599584020208567,30,32.16456301743165,39.65559376520105,unsplit,,,,,42.17404103837907,28.907125000841916,30,34.068979002768174,40.57833549159113,5.92085141745724,2.299077085046819,10.744671092543845,1.9044159853365272,0.7628744933754206,3.363728988915682,68c475e116a752f2029cdcb77798a747aff1e1da9147d1222ce869b1d5db15bb,5000 paired-round resamples; seed=4207 diff --git a/docs/src/en/design/host-idle-dispatch-provenance.csv b/docs/src/en/design/host-idle-dispatch-provenance.csv new file mode 100644 index 000000000..f6b087197 --- /dev/null +++ b/docs/src/en/design/host-idle-dispatch-provenance.csv @@ -0,0 +1,22 @@ +field,value +benchmark_id,B-FS-ENG +batch,fr10fixf3 +report_sha256,3291766797d87996fcc8aa956fd90b4adbf7009c7f931a8f5eff286f346b9715 +host_kernel,7.2.8-200.fc44.x86_64 +baseline_binary_sha256,c6965986ac119544f0886e06806fcf41b814f54b54ad655978046a2449797c42 +candidate_binary_sha256,6d08e35b2f5ea9cdf1a01718d3303f7c6e06bb91216037bd48f4a175d5ae3e1d +baseline_source_manifest_sha256,a52c826c8d6c70632f4f3d8e36fda672fe8029b7f87cb5f7138a72824b6b5578 +candidate_source_manifest_sha256,e2ed6dda5e67800d803c6f2e178dfc0b5937bb5c9331b2f8ed7d0e2d31dedce9 +runner_sha256,fc06cadfb3b5dd34462ea6b1b12578f358a98bdfe410dd8cf17bf7134dbbe4cb +child_sha256,80418c8bad57bfcbd15ce01139e83de7bb2eb649a4d0785ab5680a4dded95489 +host_guard_sha256,66bfa5309994598d2f1cfc32ca1c132f02e1d48efa784b4fa60e3f11dd856e4c +launch_sha256,88b29f7000bf0bc3524547cd6213c326c63bc8abc91d363da5928ba262c705c4 +budget,"whole private parent: CPU 0,1; CPU quota 2; RAM 16 GiB; swap 0; resource observation off" +protocol,30 seeded randomly paired rounds; 3 warmups per arm; fresh workspaces and tool caches; no profiling; independent private user/mount/PID namespace and owned pivot_root per trial; native Btrfs workspace/stage bind; namespace and binary preparation outside timer; fresh Host listener included; full upper bytes and host isolation checked +path_alias,retained row paths normalize private /tmp/sample to the arm evidence directory; stdout bytes are preserved unchanged +scope,Linux staged only; not pooled with cross-runtime user cohort; no VM/macOS/cold/concurrent throughput claim +raw_location,benchmark/.data/fr10fixf3/report.json +changed_files,crates/pvisor-overlayfs/README.md;crates/pvisor-overlayfs/src/api.rs;crates/pvisor-overlayfs/src/dispatch.rs +statistics,5000 paired-round bootstrap resamples of median difference and median ratio; separated clusters have no whole-distribution ranking; no P99 +input_final_verification,passed +interference,none detected; same-UID build/test polling every 0.5 s; short/inaccessible/unrecognized activity may be missed diff --git a/docs/src/en/design/host-idle-dispatch-summary.csv b/docs/src/en/design/host-idle-dispatch-summary.csv new file mode 100644 index 000000000..e93377e27 --- /dev/null +++ b/docs/src/en/design/host-idle-dispatch-summary.csv @@ -0,0 +1,10 @@ +benchmark_id,batch,backend,metric,unit,n,failures,baseline_p50_ms,candidate_p50_ms,baseline_distribution,candidate_distribution,baseline_clusters,candidate_clusters,median_difference_ms,ci95_low_ms,ci95_high_ms,change_percent,ci95_low_percent,ci95_high_percent,interpretation,report_sha256 +B-FS-ENG,fr10fixf3,pvisor-staged,ready_ms,ms,30,0,231.3429895,231.1496125,unsplit,unsplit,"{""n"": 30, ""p50"": 231.3429895, ""p95_reference"": 258.24731945, ""minimum"": 228.156094, ""maximum"": 721.567696, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 231.1496125, ""p95_reference"": 269.66952440000006, ""minimum"": 226.086159, ""maximum"": 334.906686, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.19337699999999813,-2.009503262500016,0.9543538749999989,-0.08358887399957382,-0.8668534654312147,0.41287355946471843,no detected difference,3291766797d87996fcc8aa956fd90b4adbf7009c7f931a8f5eff286f346b9715 +B-FS-ENG,fr10fixf3,pvisor-staged,completion_ms,ms,30,0,1826.4212615000001,1774.147928,unsplit,unsplit,"{""n"": 30, ""p50"": 1826.4212615000001, ""p95_reference"": 1886.6145353000002, ""minimum"": 1798.35404, ""maximum"": 2616.115118, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 1774.147928, ""p95_reference"": 2983.1745506000007, ""minimum"": 1753.962962, ""maximum"": 3944.151722, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-52.273333500000035,-65.34960165000021,-22.30310499999996,-2.862063347700472,-3.5741408369402943,-1.2217776782787793,faster,3291766797d87996fcc8aa956fd90b4adbf7009c7f931a8f5eff286f346b9715 +B-FS-ENG,fr10fixf3,pvisor-staged,metadata,ms,30,0,67.688012,66.8750695,unsplit,unsplit,"{""n"": 30, ""p50"": 67.688012, ""p95_reference"": 70.7873342, ""minimum"": 65.844761, ""maximum"": 74.339872, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 66.8750695, ""p95_reference"": 68.75847935, ""minimum"": 66.02054, ""maximum"": 70.891403, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.8129425000000055,-1.3601690000000133,-0.5477080000000001,-1.2010139993474889,-1.9937533501155458,-0.8103561838894572,faster,3291766797d87996fcc8aa956fd90b4adbf7009c7f931a8f5eff286f346b9715 +B-FS-ENG,fr10fixf3,pvisor-staged,read,ms,30,0,72.0473415,70.11834999999999,unsplit,unsplit,"{""n"": 30, ""p50"": 72.0473415, ""p95_reference"": 98.7668031, ""minimum"": 65.695082, ""maximum"": 114.985235, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 70.11834999999999, ""p95_reference"": 126.29148915000002, ""minimum"": 66.140483, ""maximum"": 179.850969, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-1.9289915000000093,-4.661307500000007,3.2695784999999944,-2.6773944185019105,-6.311997208869792,4.796779722316624,no detected difference,3291766797d87996fcc8aa956fd90b4adbf7009c7f931a8f5eff286f346b9715 +B-FS-ENG,fr10fixf3,pvisor-staged,write,ms,30,0,35.0920515,28.329621,unsplit,unsplit,"{""n"": 30, ""p50"": 35.0920515, ""p95_reference"": 36.1894698, ""minimum"": 34.582365, ""maximum"": 36.824261, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 28.329621, ""p95_reference"": 29.88571505, ""minimum"": 26.699338, ""maximum"": 31.289888, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-6.762430499999997,-7.179589499999999,-6.482372999999999,-19.270547633842373,-20.373777665160333,-18.472713563606924,faster,3291766797d87996fcc8aa956fd90b4adbf7009c7f931a8f5eff286f346b9715 +B-FS-ENG,fr10fixf3,pvisor-staged,git,ms,30,0,162.3064585,141.557841,unsplit,unsplit,"{""n"": 30, ""p50"": 162.3064585, ""p95_reference"": 177.79855020000002, ""minimum"": 160.367624, ""maximum"": 187.201031, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 141.557841, ""p95_reference"": 156.44679190000002, ""minimum"": 137.689894, ""maximum"": 163.478031, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-20.748617499999995,-21.786389050000015,-19.63463250000001,-12.783605588929781,-13.350054808993184,-12.149787988728688,faster,3291766797d87996fcc8aa956fd90b4adbf7009c7f931a8f5eff286f346b9715 +B-FS-ENG,fr10fixf3,pvisor-staged,rg,ms,30,0,122.74300450000001,106.9286245,unsplit,unsplit,"{""n"": 30, ""p50"": 122.74300450000001, ""p95_reference"": 124.06584275, ""minimum"": 120.99182, ""maximum"": 125.752858, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 106.9286245, ""p95_reference"": 109.06011240000001, ""minimum"": 105.246052, ""maximum"": 109.688326, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-15.814380000000014,-16.619792949999994,-14.843722499999998,-12.884139560067565,-13.501847285401436,-12.173678366971384,faster,3291766797d87996fcc8aa956fd90b4adbf7009c7f931a8f5eff286f346b9715 +B-FS-ENG,fr10fixf3,pvisor-staged,cargo,ms,30,0,79.374105,77.058392,unsplit,unsplit,"{""n"": 30, ""p50"": 79.374105, ""p95_reference"": 82.93333115, ""minimum"": 76.296244, ""maximum"": 86.754449, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 77.058392, ""p95_reference"": 569.1450376, ""minimum"": 74.912911, ""maximum"": 842.661286, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-2.3157130000000024,-3.378038699999999,-1.2891612874999974,-2.9174665969461944,-4.214848097245516,-1.6295569723169845,faster,3291766797d87996fcc8aa956fd90b4adbf7009c7f931a8f5eff286f346b9715 +B-FS-ENG,fr10fixf3,pvisor-staged,npm,ms,30,0,231.69645400000002,229.5019615,unsplit,unsplit,"{""n"": 30, ""p50"": 231.69645400000002, ""p95_reference"": 236.11639, ""minimum"": 226.46159, ""maximum"": 236.332118, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 229.5019615, ""p95_reference"": 235.8596702, ""minimum"": 222.832165, ""maximum"": 252.974519, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-2.194492500000024,-4.048117249999992,-0.13122049999998353,-0.9471411677280273,-1.739780613468427,-0.05722562147116861,faster,3291766797d87996fcc8aa956fd90b4adbf7009c7f931a8f5eff286f346b9715 diff --git a/docs/src/en/design/host-read-summary.csv b/docs/src/en/design/host-read-summary.csv new file mode 100644 index 000000000..7567ad0f1 --- /dev/null +++ b/docs/src/en/design/host-read-summary.csv @@ -0,0 +1,28 @@ +benchmark,batch,mode,condition,metric,unit,samples_per_arm,failures,excluded_slow_samples,baseline_distribution,baseline_high_n,baseline_high_p50,baseline_low_n,baseline_low_p50,baseline_maximum,baseline_minimum,baseline_n,baseline_p50,baseline_p95_reference,candidate_distribution,candidate_high_n,candidate_high_p50,candidate_low_n,candidate_low_p50,candidate_maximum,candidate_minimum,candidate_n,candidate_p50,candidate_p95_reference,percent_change,ci95_percent_low,ci95_percent_high,median_difference_ms,ci95_difference_low_ms,ci95_difference_high_ms,report_sha256,bootstrap +B-FS-ENG,read-formal-20261010,reads,nojournal,open_ms,ms,30,0,0,unsplit,,,,,1467.6001670304686,727.9673750163056,30,784.6023124875501,950.3094731189776,unsplit,,,,,2.0554999937303364,1.167292008176446,30,1.576833485160023,1.9665085535962135,-99.79902767809074,-99.81675234968277,-99.78187322877787,-783.0254790023901,-793.8365415029693,-772.1069794788491,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,nojournal,open_complete_ms,ms,30,0,0,unsplit,,,,,1468.5537079931237,728.8003340363503,30,785.6761250004638,951.3767916854704,unsplit,,,,,3.0296669574454427,1.6788339708000422,30,2.2468749957624823,2.679954335326329,-99.71402020192976,-99.73612596711502,-99.69670926052945,-783.4292500047013,-794.0954170189798,-772.2922500106506,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,nojournal,read_ms,ms,30,0,0,unsplit,,,,,1467.5941249588504,728.0374999972992,30,784.5969375048298,950.3548499749741,unsplit,,,,,0.7355830166488886,0.3607079852372408,30,0.5213750118855387,0.72110828186851,-99.93354868124472,-99.93638603700258,-99.92703108163721,-784.0755624929443,-794.8402499896474,-770.0029794941656,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,nojournal,opendir_ms,ms,30,0,0,unsplit,,,,,1475.5269159795716,734.0120829758234,30,791.558166500181,957.3525714979041,unsplit,,,,,8.624417008832097,5.774791003204882,30,6.91893749171868,7.8491248248610646,-99.12590915177968,-99.19034255283533,-99.07428796449891,-784.6392290084623,-796.1403124791104,-769.7398550517391,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,nojournal,idle_open_ms,ms,30,0,0,unsplit,,,,,8.858042012434453,1.3563329703174531,30,1.9793754909187555,3.313037345651537,unsplit,,,,,3.896458016242832,1.2366669834591448,30,2.2158960055094212,2.803768770536408,11.949249431237586,-2.2766008540052396,20.597797709589962,0.2365205145906657,-0.0468534417450428,0.401749974116683,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,nojournal,idle_open_complete_ms,ms,30,0,0,unsplit,,,,,9.896000032313168,1.9259999971836805,30,3.064186981646344,4.434341986780055,unsplit,,,,,5.184125038795173,1.7114590154960752,30,3.363395982887596,4.02716686076019,9.764710934203213,-4.422795895504739,20.066967555659332,0.2992090012412518,-0.15222898218780756,0.5729374825023115,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,nojournal,idle_read_ms,ms,30,0,0,unsplit,,,,,0.9218340273946524,0.42150000808760524,30,0.6724790146108717,0.8842267328873277,unsplit,,,,,1.0244580334983766,0.43766701128333807,30,0.7467709947377443,0.9546792134642601,11.047479328386412,-9.681196863024189,23.15627781216896,0.07429198012687266,-0.06581255001947284,0.14406247646547854,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,nojournal,idle_opendir_ms,ms,30,0,0,unsplit,,,,,14.119667001068592,5.843000020831823,30,8.688895497471094,13.98289187636692,unsplit,,,,,14.713207958266139,5.559374985750765,30,10.010457976022735,13.021998229669407,15.209786778265233,-7.5136188921661855,26.71938430162628,1.321562478551641,-0.7691250357311219,2.1843129652552307,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,nojournal,writable_open_ms,ms,30,0,0,unsplit,,,,,1469.6194999851286,730.0400409731083,30,788.512979488587,952.5587270618416,unsplit,,,,,4636.813291988801,710.722749994602,30,789.5954585110303,3166.8974856351274,0.13728106582917565,-1.7803175611326383,2.7758275132282684,1.0824790224432945,-14.134833501884714,21.44658350152895,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,legacy,open_ms,ms,30,0,0,unsplit,,,,,4079.3276660260744,740.7681670156308,30,797.213895508321,3205.285191591247,unsplit,,,,,3.9670830010436475,2.7496250113472342,30,3.171958029270172,3.6743526259670034,-99.60211957579494,-99.6123422023328,-99.57909025695338,-794.0419374790508,-806.7137919424567,-781.1695204873104,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,legacy,open_complete_ms,ms,30,0,0,unsplit,,,,,4080.268750025425,741.74700002186,30,798.079416010296,3206.24652254628,unsplit,,,,,4.941958002746105,3.4635409829206765,30,4.113770497497171,4.700891155516729,-99.48454121043963,-99.50463951139422,-99.4540087594658,-793.9656455127988,-806.8653954542242,-781.2600409670267,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,legacy,read_ms,ms,30,0,0,unsplit,,,,,4079.449167009443,740.9073750022799,30,797.4235625006258,3205.3679455042584,unsplit,,,,,1.3484999653883278,0.6014580139890313,30,0.9599165059626102,1.2668725394178182,-99.87962275619842,-99.90923663145915,-99.86022054204186,-796.4636459946632,-809.3296039733104,-783.6247090017423,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,legacy,opendir_ms,ms,30,0,0,unsplit,,,,,4086.193875002209,748.3972500194795,30,804.0333125100005,3211.8898859334877,unsplit,,,,,11.42004202120006,9.477083978708833,30,10.292229504557326,11.231884008157067,-98.7199249901192,-98.75821953502975,-98.69517080959629,-793.7410830054432,-806.5722915052902,-780.7135004841257,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,legacy,idle_open_ms,ms,30,0,0,unsplit,,,,,5.049208994023502,1.8661249778233469,30,3.0317084747366607,4.967633690102957,unsplit,,,,,4.629125003702939,1.9340830040164292,30,2.9050830053165555,4.360628966242075,-4.176703349787092,-21.485888746623083,10.281873806672138,-0.12662546942010522,-0.7467505347449332,0.2952289942186326,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,legacy,idle_open_complete_ms,ms,30,0,0,unsplit,,,,,6.605250004213303,2.7859589899890125,30,4.162375000305474,6.209821350057609,unsplit,,,,,6.180915981531143,2.647208049893379,30,3.8770829851273447,6.136547855567187,-6.85406805386809,-20.864950837115526,8.549011515896442,-0.2852920151781291,-0.9798755054362118,0.34118699841201305,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,legacy,idle_read_ms,ms,30,0,0,unsplit,,,,,1.1060829856432974,0.5411250167526305,30,0.7092915184330195,0.9133918560110033,unsplit,,,,,1.4371249708347023,0.5126659525558352,30,0.6742085097357631,0.898106096428819,-4.946204456915337,-15.832598681641096,12.948118756594805,-0.035083008697256446,-0.11649902444332838,0.09027001215144992,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,legacy,idle_opendir_ms,ms,30,0,0,unsplit,,,,,16.912708000745624,8.036916027776897,30,11.7271039634943,14.228056548745371,unsplit,,,,,17.391290981322527,7.9338340437971056,30,10.882042028242722,15.272968751378357,-7.206058186933451,-20.18965769559886,13.147177778689656,-0.8450619352515787,-2.5633335462771356,1.3787500502076,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,legacy,writable_open_ms,ms,30,0,0,unsplit,,,,,4563.512124994304,1199.5800000149757,30,1267.0146664895583,3683.37038923637,unsplit,,,,,4630.5608749971725,1225.7922500139102,30,1267.3787495004945,1427.1339498605812,0.028735500903476208,-1.830411000342702,0.9592807960373539,0.3640830109361559,-23.540854483144358,12.134188000345603,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,compact,open_ms,ms,30,0,0,separated-clusters,5,2340.5992499901913,25,800.2748750150204,3229.997333022766,747.9514590231702,30,,2563.812739399146,unsplit,,,,,3.2600420527160168,1.7667919746600091,30,2.2190835152287036,2.848921046825126,,,,,,,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,compact,open_complete_ms,ms,30,0,0,separated-clusters,5,2341.4492500014603,25,801.4073329977691,3230.9647499932908,749.0443339920603,30,,2564.807000002475,unsplit,,,,,5.438917025458068,2.40358297014609,30,3.0509585049003363,3.6029980692546815,,,,,,,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,compact,read_ms,ms,30,0,0,separated-clusters,5,2340.716542035807,25,800.485708983615,3230.1703749690205,748.2522919890471,30,,2563.9766208012593,unsplit,,,,,1.1305000516586006,0.5438329535536468,30,0.7603749982081354,1.079644073615782,,,,,,,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,compact,opendir_ms,ms,30,0,0,separated-clusters,5,2347.781958000269,25,807.1369589888491,3236.994833976496,755.115499952808,30,,2570.192906109151,unsplit,,,,,11.764166003558785,8.47029103897512,30,9.737082989886403,10.700504359556362,,,,,,,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,compact,idle_open_ms,ms,30,0,0,unsplit,,,,,4.535374988336116,1.463791006244719,30,2.207520476076752,3.1118975195568055,unsplit,,,,,10.741165955550969,1.6459159669466317,30,2.254374965559691,3.077929187566042,2.122493992273622,-12.739470765315774,18.717609309564942,0.04685448948293924,-0.31593802850693464,0.39616605499759316,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,compact,idle_open_complete_ms,ms,30,0,0,unsplit,,,,,6.500458985101432,2.3755410220474005,30,3.3054789819289,5.548160921898671,unsplit,,,,,11.475832958240062,2.276624960359186,30,3.5406039969529957,5.324424992431887,7.113190442581163,-6.380163039835507,20.300573018660995,0.23512501502409577,-0.22566696861758828,0.6431675574276596,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,compact,idle_read_ms,ms,30,0,0,unsplit,,,,,1.8350000027567148,0.484999967738986,30,0.70477050030604,1.1332250287523493,unsplit,,,,,1.599457988049835,0.5460829706862569,30,0.6993120186962187,1.0646562412148342,-0.7745048363192009,-11.945557375284055,16.193849762775624,-0.00545848160982132,-0.09214604506269097,0.10735500836744905,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,compact,idle_opendir_ms,ms,30,0,0,unsplit,,,,,13.834165991283953,7.464417023584247,30,10.807042010128498,13.161814393242821,unsplit,,,,,13.926875020843,7.543832994997501,30,10.611874982714653,12.461610906757414,-1.805924574281581,-15.271794499301706,12.826513400154372,-0.19516702741384506,-1.6715420060791075,1.2616039894055575,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,compact,writable_open_ms,ms,30,0,0,unsplit,,,,,3701.6902919858694,1206.1046250164509,30,1275.5105630203616,3025.0988392363074,unsplit,,,,,1450.9638330200687,1200.4248749581166,30,1251.4398540079128,1362.5303499953588,-1.8871430555189028,-3.126423601885342,-0.4510966465714472,-24.070709012448788,-40.43987503973767,-5.648437014315277,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 diff --git a/docs/src/en/design/index.md b/docs/src/en/design/index.md index 07a4c616d..26ca2e43d 100644 --- a/docs/src/en/design/index.md +++ b/docs/src/en/design/index.md @@ -218,6 +218,8 @@ Concrete counterexamples guide experiments. Repeated, read-heavy VMs can expose Pin tasks, inputs, output checks, versions and total budgets, then measure correct completions, CPU, memory peaks, memory-time and restoration latency separately. Supervision cost additionally needs actual review, rejection and rework data; machine runtime cannot stand in for human attention. [Research directions](research/index.md) retain these hypotheses and integration boundaries. [Benchmarks](../benchmarks/index.md) retain each dataset's measurement scope. +The [Experimental features](experimental/index.md) registry records platform, profile and implementation stage. vCPU idle observation currently provides an M0 observation prototype; automatic offload still needs validation. The guest filesystem cache remains an RFC proposing local metadata operations and ordinary read-only open while preserving host stage ownership and access boundaries. Both routes require complete lifecycle and resource measurements to establish net benefit. + ## 11. Returning to the whole system {#results} The example now forms a complete path. Admission fixes policy; the Session prepares VM, files and networking; the guest reads a lower through virtio-fs and opens HTTPS through controlled egress; writes copy up into the upper; completion synchronizes the stage, reclaims resources and retains facts. Finally, the developer uses preimage checks to publish selected candidate files into the target. diff --git a/docs/src/en/design/journal-lock-summary.csv b/docs/src/en/design/journal-lock-summary.csv new file mode 100644 index 000000000..5c2503dc8 --- /dev/null +++ b/docs/src/en/design/journal-lock-summary.csv @@ -0,0 +1,9 @@ +benchmark,batch,mode,condition,metric,unit,samples_per_arm,failures,excluded_slow_samples,baseline_distribution,baseline_high_n,baseline_high_p50,baseline_low_n,baseline_low_p50,baseline_maximum,baseline_minimum,baseline_n,baseline_p50,baseline_p95_reference,candidate_distribution,candidate_high_n,candidate_high_p50,candidate_low_n,candidate_low_p50,candidate_maximum,candidate_minimum,candidate_n,candidate_p50,candidate_p95_reference,percent_change,ci95_percent_low,ci95_percent_high,median_difference_ms,ci95_difference_low_ms,ci95_difference_high_ms,report_sha256,bootstrap +B-FS-ENG,journal-formal-20261010,journal,legacy,first_journal_ms,ms,30,0,0,unsplit,,,,,568.324916,458.69720800000005,30,462.2756875,495.60153545000003,unsplit,,,,,0.38699999999999996,0.124583,30,0.18412499999999998,0.35779360000000004,-99.96016987157691,-99.96382274198461,-99.95586138813522,-462.0915625,-466.22510399999993,-461.00487499999997,912a04d76e0dcf79f5d5800837ee913cebe4d8fc0fdffc7794fa6d555f34b7a0,5000 paired-round resamples; seed=4207 +B-FS-ENG,journal-formal-20261010,journal,legacy,journal_batch_ms,ms,30,0,0,unsplit,,,,,582.0345,468.937416,30,473.2618545,506.18555,unsplit,,,,,15.090667,11.177792,30,12.0108955,13.70676875,-97.46210361435331,-97.52085173909593,-97.3835427004595,-461.250959,-465.0704995,-459.908458,912a04d76e0dcf79f5d5800837ee913cebe4d8fc0fdffc7794fa6d555f34b7a0,5000 paired-round resamples; seed=4207 +B-FS-ENG,journal-formal-20261010,journal,legacy,idle_journal_ms,ms,30,0,0,unsplit,,,,,14.546417,10.37175,30,11.5576875,13.209233300000001,unsplit,,,,,14.919208,10.267833000000001,30,11.575124500000001,13.05685405,0.15086928072767236,-1.5641657096678085,1.7679201005688094,0.017437000000001035,-0.1838754999999992,0.2026249999999994,912a04d76e0dcf79f5d5800837ee913cebe4d8fc0fdffc7794fa6d555f34b7a0,5000 paired-round resamples; seed=4207 +B-FS-ENG,journal-formal-20261010,journal,legacy,hash_ms,ms,30,0,0,unsplit,,,,,568.166333,458.621959,30,462.22968749999995,495.5203,unsplit,,,,,477.01720800000004,465.995583,30,469.558313,473.19524605,1.5854943328364346,0.621484196309452,1.9643508230387408,7.328625500000044,2.8939379999999915,9.066229999999962,912a04d76e0dcf79f5d5800837ee913cebe4d8fc0fdffc7794fa6d555f34b7a0,5000 paired-round resamples; seed=4207 +B-FS-ENG,journal-formal-20261010,journal,compact,first_journal_ms,ms,30,0,0,unsplit,,,,,464.882458,447.15683299999995,30,450.7143125,460.11643925,separated-clusters,5,0.070541,25,0.045292,0.16804100000000002,0.043125,30,,0.07129725,,,,,,,912a04d76e0dcf79f5d5800837ee913cebe4d8fc0fdffc7794fa6d555f34b7a0,5000 paired-round resamples; seed=4207 +B-FS-ENG,journal-formal-20261010,journal,compact,journal_batch_ms,ms,30,0,0,unsplit,,,,,467.385042,449.662916,30,453.1578335,462.5730788,unsplit,,,,,2.736958,2.331709,30,2.3846875,2.71766265,-99.47376226918959,-99.47651529707625,-99.46397417187268,-450.773146,-452.92120900000003,-449.49152050000004,912a04d76e0dcf79f5d5800837ee913cebe4d8fc0fdffc7794fa6d555f34b7a0,5000 paired-round resamples; seed=4207 +B-FS-ENG,journal-formal-20261010,journal,compact,idle_journal_ms,ms,30,0,0,unsplit,,,,,2.917375,2.3904579999999997,30,2.4861875,2.742283,unsplit,,,,,3.22125,2.359,30,2.504521,2.9772981,0.7374142135297523,-1.5067998043199138,5.203211239337668,0.01833349999999978,-0.03820900000000016,0.1288545000000001,912a04d76e0dcf79f5d5800837ee913cebe4d8fc0fdffc7794fa6d555f34b7a0,5000 paired-round resamples; seed=4207 +B-FS-ENG,journal-formal-20261010,journal,compact,hash_ms,ms,30,0,0,unsplit,,,,,464.912333,447.199083,30,450.73900000000003,460.14889755,unsplit,,,,,466.439833,447.136333,30,452.237021,462.23250625,0.3323477666676178,-0.39781265752761863,0.7834892479944067,1.4980209999999943,-1.7978335000000243,3.518750000000068,912a04d76e0dcf79f5d5800837ee913cebe4d8fc0fdffc7794fa6d555f34b7a0,5000 paired-round resamples; seed=4207 diff --git a/docs/src/en/design/readonly-dax-provenance.csv b/docs/src/en/design/readonly-dax-provenance.csv new file mode 100644 index 000000000..b56fd8098 --- /dev/null +++ b/docs/src/en/design/readonly-dax-provenance.csv @@ -0,0 +1,51 @@ +field,value +benchmark_id,B-FS-ENG +role,engineering A/B +batch,dax-20261011-final +raw_evidence,benchmark.new/filesystem/.data/dax-20261011-final +clock,guest CLOCK_MONOTONIC_RAW; phase only; verification excluded +cache,host-hot Btrfs; new guest; same-guest repeats +host,Linux-7.2.8-200.fc44.x86_64-x86_64-with-glibc2.43 +host_cpu,AMD Ryzen 7 9700X +guest_kernel,6.12.109 +subject_profile,performance; opt-level=3; Thin LTO +affinity,"0,1; inherited by full VM group" +cpu_quota,200000 100000 +cpuset_controller,not delegated; affinity and quota verified +memory_max_bytes,2147483648 +memory_swap_max_bytes,0 +guest_cpus,2 +guest_memory_mib,512 +nofile_limit,65536 +dax_window_bytes,134217728 +only_variable,guest mount dax=never vs dax=inode; same read-only DAX-capable subject +order,"metadata,small,sequential,mmap,parallel; independent backing files; shared window/reclamation" +warmups_per_arm,3 +formal_pairs,30 +formal_vm_runs,60 +failures,0 +excluded,0 +interference_scan_ticks,123 +detected_build_test_interference,0 +bootstrap_samples,5000 +bootstrap_estimator,paired resampling; median(on)-median(off) and median(on)/median(off)-1 +seed,20261011 +distribution_rule,"sorted gap >=1.5x, >=3 samples on each side, cluster medians >=1.7x => separated; no overall ranking" +read_parallel_repeated_passes,4; per-pass mean +mmap_repeated_passes,16; per-pass mean +correctness,"all 66 runs: statx selection, full bytes, namespace, normal zero exit; input/artifact inventories match; no OOM; stages reaped" +coverage_limit,immutable share only; no CLI writable workspace/startup/exit or cold storage result +bin/subject_sha256,dcf9d8cc6e52b032fa7d0fdc20616489c7bd3b34ae8ff957bf6a95183d750657 +bin/libkrunfw.so_sha256,b61f68dac3ef20a88e1ee387733e4baed2f7c02edac940c8882e0b549dac95e4 +immutable/workload_sha256,04f484ba1f035acd2484f6d0ca76dc504505279433b1f8e4dfa706f75e468da4 +harness/dax_ab.py_sha256,281960fb18a29c8d41fca6f588c12cd3930d5b1b64f8c47a5cc85d957b797044 +harness/dax_driver.rs_sha256,f24dc1b9c89e2f0d10a5e6df9ff5845219c6f6c07ae8b2b59920e1ab5aec6c1e +harness/dax_workload.c_sha256,fd712782fee61404746544a1c51ea6a2aa8b0cd025ec75b4ae036b27367fc158 +driver/Cargo.toml_sha256,f3d57fbe2d7027335a5b78926d8ac79b60fc6b16feefb77f85f3b10ea4fa6020 +driver/Cargo.lock_sha256,5aef3f760fb491e621434fb44160e986cd2ea1d327567288ff9b8e6e63a9d41f +protocol.json_sha256,de2c025a793ba54eb6b6a770b8062649431f1cf9c57258e996ed0446c934ad9d +records.json_sha256,6e530f00b59f17dd84c896c74de8da1d54a6d85aaf75aa63be63ba233ccf6e1d +summary.json_sha256,1b486c1a2048ad4cf9827b20dd241c15a7ef9de2f0024cf2e17b74adf9c00b4b +in.bench.json_sha256,240215cc199e49e707406b6ca2ffa505d8864991eeacbe54db3a25d2fb3dd9b2 +audit.json_sha256,acaa4792f8807d777410d3aec726045845133d97eaf113424174be4a197df8d9 +source_inventory_entries,1126 diff --git a/docs/src/en/design/readonly-dax-summary.csv b/docs/src/en/design/readonly-dax-summary.csv new file mode 100644 index 000000000..29a0daf50 --- /dev/null +++ b/docs/src/en/design/readonly-dax-summary.csv @@ -0,0 +1,11 @@ +batch,metric,pairs,off_ms,on_ms,delta_percent,ci95_low_percent,ci95_high_percent,verdict +dax-20261011-final,metadata_first,30,129.09571449999999,130.062067,0.7485550575732081,-8.331332908565948,11.320616416738938,not detected +dax-20261011-final,metadata_repeat,30,0.9368765,0.968997,3.4284668256701956,-5.263369735224099,11.443024177112582,not detected +dax-20261011-final,small_first,30,40.208754,41.468747,3.133628562576196,-0.6843687800041409,5.740488924683174,not detected +dax-20261011-final,small_repeat,30,25.373077000000002,36.6305735,44.36788056884071,36.46610040529619,47.98132060479314,slower +dax-20261011-final,read_first,30,60.607162,65.8917175,8.719358118104914,7.1137647656107195,9.84490311050028,slower +dax-20261011-final,read_repeat,30,5.544313,4.580477,-17.384227766361672,-21.395220652225067,-10.101683557067531,faster +dax-20261011-final,mmap_first,30,23.829177,104.75073,339.5902132918816,328.766260843115,352.398630591051,slower +dax-20261011-final,mmap_repeat,30,0.0964175,0.1252455,29.899136567531826,27.003940158837647,33.7086975848274,slower +dax-20261011-final,parallel_first,30,54.43383,38.2735545,-29.687926607405725,-32.155415115930666,-28.250685731462653,faster +dax-20261011-final,parallel_repeat,30,4.733574,4.162454,-12.065302031826263,-16.90299974114886,0.744292090068277,not detected diff --git a/docs/src/en/design/vm-completion-dispatch-provenance.csv b/docs/src/en/design/vm-completion-dispatch-provenance.csv new file mode 100644 index 000000000..5fa253d52 --- /dev/null +++ b/docs/src/en/design/vm-completion-dispatch-provenance.csv @@ -0,0 +1,25 @@ +field,value +benchmark_id,B-FS-ENG +batch,fr10vmfixf +report_sha256,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +host_kernel,7.2.8-200.fc44.x86_64 +before_binary_sha256,b074594368e5655c0e6949dcd45e6eb958816f3cdfca53011911f6d059fcbfe8 +baseline_binary_sha256,6d08e35b2f5ea9cdf1a01718d3303f7c6e06bb91216037bd48f4a175d5ae3e1d +candidate_binary_sha256,a4cfdbc6e2657f9ff6a43a4c640cd472c1c94c965c9d388924e58727dc55556d +before_source_manifest_sha256,1068608e3b4e611871e0e00a755b8ddb4e9d9c1a5e48b4b46597d353937b603e +before_scope,same frozen source; reverses only 65e65c93 copy-up/dispatch and 09bd1d59 read-dispatch/journal changes; stage sync default unchanged +baseline_source_manifest_sha256,e2ed6dda5e67800d803c6f2e178dfc0b5937bb5c9331b2f8ed7d0e2d31dedce9 +candidate_source_manifest_sha256,b4c43ac9ddc7a12ff644f4dc940efba3e963a37fbce252f27b9a099164066f03 +runner_sha256,fc06cadfb3b5dd34462ea6b1b12578f358a98bdfe410dd8cf17bf7134dbbe4cb +child_sha256,80418c8bad57bfcbd15ce01139e83de7bb2eb649a4d0785ab5680a4dded95489 +host_guard_sha256,55ea8ee64ccd792d6f203da38cfe516f4e383ba9caf91bb06fe46576f2fc3701 +launch_sha256,cd3b3459e4bcdbbf137200054e284c8dc85017e1ce59944d284ab06b3b7aa05d +budget,"whole private parent: CPU 0,1; CPU quota 2; RAM 16 GiB; swap 0; resource observation off" +protocol,30 seeded randomly paired rounds; 3 warmups per arm; fresh workspaces and tool caches; no profiling; independent private user/mount/PID namespace and owned pivot_root per trial; native Btrfs workspace/stage bind; namespace and binary preparation outside timer; fresh VM launch and Host listener included; full upper bytes and host isolation checked +path_alias,retained row paths normalize private /tmp/sample to the arm evidence directory; stdout bytes are preserved unchanged +scope,Linux VM only; three-arm same-cohort comparisons; not pooled with staged or cross-runtime user cohorts; no macOS/cold/concurrent throughput claim +raw_location,benchmark/.data/fr10vmfixf/report.json +changed_files,crates/pvisor-vm/README.md;crates/pvisor-vm/src/devices/virtio/fs/worker.rs +statistics,5000 paired-round bootstrap resamples of median difference and median ratio; separated clusters have no whole-distribution ranking; no P99 +input_final_verification,passed +interference,none detected; same-UID build/test polling every 0.5 s; short/inaccessible/unrecognized activity may be missed diff --git a/docs/src/en/design/vm-completion-dispatch-summary.csv b/docs/src/en/design/vm-completion-dispatch-summary.csv new file mode 100644 index 000000000..62f363d2d --- /dev/null +++ b/docs/src/en/design/vm-completion-dispatch-summary.csv @@ -0,0 +1,19 @@ +benchmark_id,reference,batch,backend,metric,unit,n,failures,baseline_p50_ms,candidate_p50_ms,baseline_distribution,candidate_distribution,baseline_clusters,candidate_clusters,median_difference_ms,ci95_low_ms,ci95_high_ms,change_percent,ci95_low_percent,ci95_high_percent,interpretation,report_sha256 +B-FS-ENG,baseline,fr10vmfixf,pvisor-vm,ready_ms,ms,30,0,702.3939865,701.4516415,unsplit,unsplit,"{""n"": 30, ""p50"": 702.3939865, ""p95_reference"": 808.14949285, ""minimum"": 687.619476, ""maximum"": 868.72758, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 701.4516415, ""p95_reference"": 804.2475959000001, ""minimum"": 691.019323, ""maximum"": 997.421687, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.942344999999932,-2.752516987500073,2.8529320625001042,-0.13416188323245315,-0.391114148694679,0.40816561589901446,no detected difference,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +B-FS-ENG,baseline,fr10vmfixf,pvisor-vm,completion_ms,ms,30,0,4741.868579,4592.3552565,unsplit,unsplit,"{""n"": 30, ""p50"": 4741.868579, ""p95_reference"": 5820.2319456000005, ""minimum"": 4497.116143, ""maximum"": 5981.512409, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 4592.3552565, ""p95_reference"": 5326.98575385, ""minimum"": 4377.759236, ""maximum"": 6562.385439, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-149.51332249999996,-297.75259800000094,-57.810381499999494,-3.153046526049663,-6.139649356853594,-1.2345936261486057,faster,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +B-FS-ENG,baseline,fr10vmfixf,pvisor-vm,metadata,ms,30,0,154.5752905,159.74082900000002,unsplit,unsplit,"{""n"": 30, ""p50"": 154.5752905, ""p95_reference"": 196.7367547, ""minimum"": 111.713649, ""maximum"": 197.391937, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 159.74082900000002, ""p95_reference"": 197.9649697, ""minimum"": 111.054683, ""maximum"": 229.822146, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",5.165538500000025,-18.704709500000007,14.823612712499976,3.3417621168889466,-11.451575593893605,10.026152135336996,no detected difference,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +B-FS-ENG,baseline,fr10vmfixf,pvisor-vm,read,ms,30,0,120.4666895,116.776761,unsplit,unsplit,"{""n"": 30, ""p50"": 120.4666895, ""p95_reference"": 137.1536455, ""minimum"": 115.152226, ""maximum"": 449.899657, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 116.776761, ""p95_reference"": 121.74813105, ""minimum"": 113.592056, ""maximum"": 140.507933, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-3.6899285000000077,-4.713184999999996,-1.1335625000000107,-3.0630280580591585,-3.8940370007429097,-0.9621710154964713,faster,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +B-FS-ENG,baseline,fr10vmfixf,pvisor-vm,write,ms,30,0,130.8912965,130.237845,unsplit,unsplit,"{""n"": 30, ""p50"": 130.8912965, ""p95_reference"": 509.23759750000005, ""minimum"": 96.253082, ""maximum"": 549.579803, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 130.237845, ""p95_reference"": 147.1340695, ""minimum"": 96.222624, ""maximum"": 151.850029, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.653451500000017,-12.501612999999992,11.757977000000011,-0.49923220066814267,-9.186748271715182,9.887533079905088,no detected difference,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +B-FS-ENG,baseline,fr10vmfixf,pvisor-vm,git,ms,30,0,436.545769,,unsplit,separated-clusters,"{""n"": 30, ""p50"": 436.545769, ""p95_reference"": 757.9528677, ""minimum"": 362.876494, ""maximum"": 781.502844, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": """", ""p95_reference"": 662.80974025, ""minimum"": 348.549234, ""maximum"": 730.702844, ""distribution"": ""separated-clusters"", ""low_n"": 25, ""low_p50"": 371.637144, ""high_n"": 5, ""high_p50"": 617.078816}",,,,,,,separated clusters; no whole-distribution ranking,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +B-FS-ENG,baseline,fr10vmfixf,pvisor-vm,rg,ms,30,0,417.9235195,424.8981695,unsplit,unsplit,"{""n"": 30, ""p50"": 417.9235195, ""p95_reference"": 438.58633055, ""minimum"": 372.132895, ""maximum"": 446.147432, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 424.8981695, ""p95_reference"": 442.5985308, ""minimum"": 391.163356, ""maximum"": 445.670106, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",6.974649999999997,-4.3014853499999735,21.828084637500062,1.6688819065135085,-1.0065899682415047,5.403448458678617,no detected difference,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +B-FS-ENG,baseline,fr10vmfixf,pvisor-vm,cargo,ms,30,0,523.217155,506.78623500000003,unsplit,unsplit,"{""n"": 30, ""p50"": 523.217155, ""p95_reference"": 558.5827725500001, ""minimum"": 505.388571, ""maximum"": 639.279189, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 506.78623500000003, ""p95_reference"": 546.5293922, ""minimum"": 489.259377, ""maximum"": 588.752572, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-16.430920000000015,-23.509490500000084,-2.757717174999903,-3.1403633927102437,-4.455254371495688,-0.5327595802016086,faster,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +B-FS-ENG,baseline,fr10vmfixf,pvisor-vm,npm,ms,30,0,926.9887335000001,923.0999919999999,unsplit,unsplit,"{""n"": 30, ""p50"": 926.9887335000001, ""p95_reference"": 994.1152894, ""minimum"": 891.468702, ""maximum"": 997.64239, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 923.0999919999999, ""p95_reference"": 957.64733845, ""minimum"": 847.70355, ""maximum"": 2157.706799, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-3.888741500000151,-17.679189000000065,5.820267000000058,-0.4195025634580851,-1.882554003609349,0.6313804659402678,no detected difference,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +B-FS-ENG,before,fr10vmfixf,pvisor-vm,ready_ms,ms,30,0,702.549837,701.4516415,unsplit,unsplit,"{""n"": 30, ""p50"": 702.549837, ""p95_reference"": 725.27700655, ""minimum"": 690.48279, ""maximum"": 745.307488, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 701.4516415, ""p95_reference"": 804.2475959000001, ""minimum"": 691.019323, ""maximum"": 997.421687, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-1.0981954999999743,-4.383062999999993,3.034041012500024,-0.15631567216489728,-0.6212667717275133,0.4344802511269897,no detected difference,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +B-FS-ENG,before,fr10vmfixf,pvisor-vm,completion_ms,ms,30,0,4553.2998769999995,4592.3552565,unsplit,unsplit,"{""n"": 30, ""p50"": 4553.2998769999995, ""p95_reference"": 5171.8396289, ""minimum"": 4375.251084, ""maximum"": 6064.830518, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 4592.3552565, ""p95_reference"": 5326.98575385, ""minimum"": 4377.759236, ""maximum"": 6562.385439, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",39.05537950000053,-65.00957449999987,109.30449549999958,0.8577379165664034,-1.4145246877866557,2.425865142059336,no detected difference,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +B-FS-ENG,before,fr10vmfixf,pvisor-vm,metadata,ms,30,0,132.986561,159.74082900000002,unsplit,unsplit,"{""n"": 30, ""p50"": 132.986561, ""p95_reference"": 194.86099745, ""minimum"": 109.04524, ""maximum"": 197.681179, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 159.74082900000002, ""p95_reference"": 197.9649697, ""minimum"": 111.054683, ""maximum"": 229.822146, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",26.754268000000025,2.2465518750000006,42.72792774999997,20.118023805428,1.5034244846752791,35.86653826558702,slower,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +B-FS-ENG,before,fr10vmfixf,pvisor-vm,read,ms,30,0,114.356567,116.776761,unsplit,unsplit,"{""n"": 30, ""p50"": 114.356567, ""p95_reference"": 121.2560414, ""minimum"": 110.975274, ""maximum"": 128.126917, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 116.776761, ""p95_reference"": 121.74813105, ""minimum"": 113.592056, ""maximum"": 140.507933, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",2.420193999999995,0.9247463875000029,4.215384499999999,2.1163576902409,0.8010390682132118,3.7146177573702754,slower,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +B-FS-ENG,before,fr10vmfixf,pvisor-vm,write,ms,30,0,128.7334605,130.237845,unsplit,unsplit,"{""n"": 30, ""p50"": 128.7334605, ""p95_reference"": 141.3955217, ""minimum"": 95.923001, ""maximum"": 158.291904, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 130.237845, ""p95_reference"": 147.1340695, ""minimum"": 96.222624, ""maximum"": 151.850029, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",1.5043844999999862,-8.28170449999999,8.014729000000017,1.1686041019614901,-6.226101820650198,6.475990285338895,no detected difference,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +B-FS-ENG,before,fr10vmfixf,pvisor-vm,git,ms,30,0,,,separated-clusters,separated-clusters,"{""n"": 30, ""p50"": """", ""p95_reference"": 743.2506656, ""minimum"": 347.876071, ""maximum"": 757.800732, ""distribution"": ""separated-clusters"", ""low_n"": 24, ""low_p50"": 371.36215749999997, ""high_n"": 6, ""high_p50"": 730.0854245}","{""n"": 30, ""p50"": """", ""p95_reference"": 662.80974025, ""minimum"": 348.549234, ""maximum"": 730.702844, ""distribution"": ""separated-clusters"", ""low_n"": 25, ""low_p50"": 371.637144, ""high_n"": 5, ""high_p50"": 617.078816}",,,,,,,separated clusters; no whole-distribution ranking,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +B-FS-ENG,before,fr10vmfixf,pvisor-vm,rg,ms,30,0,426.5778535,424.8981695,unsplit,unsplit,"{""n"": 30, ""p50"": 426.5778535, ""p95_reference"": 442.32714699999997, ""minimum"": 396.98717, ""maximum"": 445.517949, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 424.8981695, ""p95_reference"": 442.5985308, ""minimum"": 391.163356, ""maximum"": 445.670106, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-1.6796840000000088,-6.513258175000037,3.5822351250000133,-0.3937579005141689,-1.521132228535439,0.8491631897326141,no detected difference,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +B-FS-ENG,before,fr10vmfixf,pvisor-vm,cargo,ms,30,0,488.230866,506.78623500000003,unsplit,unsplit,"{""n"": 30, ""p50"": 488.230866, ""p95_reference"": 508.37289334999997, ""minimum"": 470.29451, ""maximum"": 535.109574, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 506.78623500000003, ""p95_reference"": 546.5293922, ""minimum"": 489.259377, ""maximum"": 588.752572, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",18.55536900000004,13.895840999999962,27.86026868750004,3.8005317345094047,2.8358804935130766,5.707160165494329,slower,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +B-FS-ENG,before,fr10vmfixf,pvisor-vm,npm,ms,30,0,923.1478515,923.0999919999999,unsplit,unsplit,"{""n"": 30, ""p50"": 923.1478515, ""p95_reference"": 1368.2353354000004, ""minimum"": 761.534452, ""maximum"": 2025.01419, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 923.0999919999999, ""p95_reference"": 957.64733845, ""minimum"": 847.70355, ""maximum"": 2157.706799, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.04785950000007233,-17.101800000000026,12.256040999999982,-0.005184380803391342,-1.8228309470598059,1.3448264683470823,no detected difference,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 diff --git a/docs/src/en/design/vm-startup-performance-analysis.md b/docs/src/en/design/vm-startup-performance-analysis.md index 726d3003d..415f1072d 100644 --- a/docs/src/en/design/vm-startup-performance-analysis.md +++ b/docs/src/en/design/vm-startup-performance-analysis.md @@ -1,5 +1,27 @@ # VM startup: technical analysis and experiment records +## Linux: startup and exit fixes retest, 2026-10-11 {#concurrency-retest} + +**Exit cleanup has a measured benefit, but startup regressions are not fully resolved.** Against the `cf8bfa5` GNU artifact, host Ready improves by about 9.11 ms and Exit by 75.00 ms. VM Exit improves by about 48.26 ms, with no detected Ready improvement. New staged Ready has two separated clusters and cannot be declared restored. + +### Motivation and experiment design {#concurrency-retest-protocol} + +This experiment checks fixes for repeated binary reads/hashes, repeated process-tree cleanup and fixed waits, together with the new filesystem execution model. The three arms are the historical `cfe36981` GNU release/gateway artifact, the `cf8bfa5fe8ad3438a096a2293bcc411176d388c8` GNU release/gateway artifact, and current frozen `build2`. All VMs use the same dynamic firmware, without mixing GNU and musl. Candidate SHA256 is `87ebb0658f2df6a84f9e4eca29c8d3d8bc01923817f04432780272c8b1b30309`. Other source changes intervene between historical and current versions, so differences cannot be attributed solely to hashing or cleanup. + +B-STARTUP-REVISION-ENG, Linux/KVM/FUSE: 3 warmups and 60 shuffled paired rounds per backend/version, seed 20261011. Host, staged and VM run sequentially, without overlapping filesystem sampling. The whole parent uses CPUs 0,1, a two-core quota, 16 GiB and zero swap; VMs have 2 vCPUs and 128 MiB. Each arm owns a private user/mount/PID namespace, pivot_root and warmed Host listener. Input/namespace preparation and initial service startup are outside timing. The original startup workload measures Popen to exact Ready and normal zero exit, checking output, Run Bundle, isolation and complete before/after input inventories. Formal timing uses the original Python perf_counter_ns observer without profiling, not hyperfine. + +### Data and analysis {#concurrency-retest-results} + +All 540 formal runs and 27 warmups passed, with unchanged artifact hashes and no external build/test interference detected. Intervals use 5,000 paired-round bootstrap resamples. All slow valid samples are retained; separated clusters have no whole-distribution median ranking, and no P99 is reported. Against `cf8bfa5`: + +- Host Ready is 29.14 → 20.03 ms: -9.11 ms, CI95 [-9.51, -8.72]. Exit is 99.16 → 24.16 ms: -75.00 ms, CI [-75.24, -74.59]. Against historical `cfe36981`, Ready still adds 6.82 ms, CI [6.16, 7.27], and Exit 0.25 ms, CI [0.02, 0.63]. The major exit regression is removed, but not every metric is restored. +- Staged baseline Ready is 41.23 ms. Candidate Ready has 19/41 samples in two clusters, with medians 32.17/121.95 ms; no overall improvement percentage is assigned. Exit is 118.41 → 118.67 ms, with difference interval [-31.54, 16.60] crossing zero. Historical-control Exit also has separated clusters: 53/7 samples at 34.12/74.43 ms. +- VM Ready is 119.41 → 115.25 ms: -4.16 ms, CI [-7.08, 0.77] crossing zero. Exit is 218.71 → 170.45 ms: -48.26 ms, CI [-67.43, -33.36]. Historical `cfe36981` Ready/Exit in this cohort are 174.06/211.83 ms, slower than current. The versions differ in several ways, so this does not isolate a single optimization. + +**Overall performance acceptance still fails:** staged startup is variable, and the [same-artifact filesystem retest](filesystem-performance-analysis.md#concurrency-retest) shows remaining VM write/tool regressions. These require separate diagnosis; host exit gains cannot stand in for other backends or internal task performance. + +[Full statistics, distributions and paired intervals](concurrency-startup-summary.csv) · [Artifact and experiment provenance](concurrency-retest-provenance.csv). Raw samples, per-trial stdout/timing, Run Bundles, inputs and build receipts are local under `benchmark.new/startup/.data/concurrency-retest2-20261011/`. These engineering version comparisons do not overwrite historical cross-runtime user tables or cover cold caches, concurrent workloads, macOS or initial service startup. Polling every 0.5 s may miss brief, inaccessible or unrecognized interference. + ## Capability checks for current generic and trimmed kernels {#kernel-capabilities} **The trimmed configuration passes these staged-workspace, development-tool, IPv4 HTTP and execution-snapshot restoration checks.** Tests use the current CLI and two firmware variants rebuilt from identical Linux 6.12.109 sources, patches and compact packaging implementation. Complete digests verify all 79 frozen source inputs and both actual build input trees before and after; the CLI's 1,063 product source/dependency files match the current worktree. Measured on 2026-10-06, Linux/KVM, host CPUs 0,1 and 2 configured vCPUs. Tool preflights sample resources; these capability checks do not provide formal timings. diff --git a/docs/src/en/design/writeback-filesystem-summary.csv b/docs/src/en/design/writeback-filesystem-summary.csv new file mode 100644 index 000000000..6ea87bca7 --- /dev/null +++ b/docs/src/en/design/writeback-filesystem-summary.csv @@ -0,0 +1,12 @@ +benchmark_id,batch,backend,metric,reference,condition,unit,n,failures,reference_p50_ms,candidate_p50_ms,reference_distribution,candidate_distribution,reference_clusters,candidate_clusters,median_difference_ms,ci95_low_ms,ci95_high_ms,change_percent,ci95_low_percent,ci95_high_percent,interpretation +B-FS-ENG,writeback-quick-20261011,pvisor-vm,ready_ms,enabled,disabled,ms,30,0,696.9494565,694.986362,unsplit,unsplit,"{""n"": 30, ""p50"": 696.9494565, ""p95_reference"": 712.4009224, ""minimum"": 683.565333, ""maximum"": 717.745967, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 694.986362, ""p95_reference"": 716.7755949499999, ""minimum"": 686.484271, ""maximum"": 719.396438, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-1.963094500000011,-4.96355296250002,2.394365699999976,-0.28166956465658366,-0.711456305702351,0.3442330024193646,no detected difference +B-FS-ENG,writeback-quick-20261011,pvisor-vm,result_ms,enabled,disabled,ms,30,0,4409.188655,4363.77111,unsplit,unsplit,"{""n"": 30, ""p50"": 4409.188655, ""p95_reference"": 4940.9638421, ""minimum"": 4029.662268, ""maximum"": 5265.56947, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 4363.77111, ""p95_reference"": 5159.0962155, ""minimum"": 4091.938518, ""maximum"": 5461.139328, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-45.417545000000246,-247.19498550000026,262.0467350125003,-1.030065813774994,-5.437974504970356,6.046697594907724,no detected difference +B-FS-ENG,writeback-quick-20261011,pvisor-vm,completion_ms,enabled,disabled,ms,30,0,4616.3745155,4564.8741415,unsplit,unsplit,"{""n"": 30, ""p50"": 4616.3745155, ""p95_reference"": 5171.99739015, ""minimum"": 4222.747388, ""maximum"": 5491.126034, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 4564.8741415, ""p95_reference"": 5374.2518042, ""minimum"": 4282.715599, ""maximum"": 5611.732468, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-51.50037399999928,-250.55795301249947,271.9566279999999,-1.1156021641459346,-5.257892084083037,6.0241089839853545,no detected difference +B-FS-ENG,writeback-quick-20261011,pvisor-vm,worker_ms,enabled,disabled,ms,30,0,3699.5548980000003,3660.6983525,unsplit,unsplit,"{""n"": 30, ""p50"": 3699.5548980000003, ""p95_reference"": 4242.751085, ""minimum"": 3334.736781, ""maximum"": 4550.259803, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 3660.6983525, ""p95_reference"": 4449.1540682, ""minimum"": 3398.779741, ""maximum"": 4768.843474, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-38.856545500000266,-258.99918149999985,263.6009412499999,-1.050303254616014,-6.6853570373825555,7.235655063411996,no detected difference +B-FS-ENG,writeback-quick-20261011,pvisor-vm,metadata,enabled,disabled,ms,30,0,148.926601,145.8935565,unsplit,unsplit,"{""n"": 30, ""p50"": 148.926601, ""p95_reference"": 183.48406035000002, ""minimum"": 108.516341, ""maximum"": 191.798087, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 145.8935565, ""p95_reference"": 190.69478625, ""minimum"": 108.840289, ""maximum"": 190.742598, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-3.033044500000017,-26.35526191249999,19.229239000000007,-2.0366035883676803,-17.44780390399727,13.625263081824613,no detected difference +B-FS-ENG,writeback-quick-20261011,pvisor-vm,read,enabled,disabled,ms,30,0,114.829478,116.2222105,unsplit,unsplit,"{""n"": 30, ""p50"": 114.829478, ""p95_reference"": 136.8899074, ""minimum"": 111.745819, ""maximum"": 143.110932, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 116.2222105, ""p95_reference"": 122.09985255000001, ""minimum"": 111.780323, ""maximum"": 133.880558, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",1.3927325000000081,-1.525919800000009,2.470849000000001,1.2128701830378397,-1.3180313620600361,2.161548355282683,no detected difference +B-FS-ENG,writeback-quick-20261011,pvisor-vm,write,enabled,disabled,ms,30,0,162.10058700000002,131.6145675,unsplit,unsplit,"{""n"": 30, ""p50"": 162.10058700000002, ""p95_reference"": 176.42817875, ""minimum"": 100.124991, ""maximum"": 193.086273, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 131.6145675, ""p95_reference"": 150.63688915, ""minimum"": 96.603164, ""maximum"": 155.468492, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-30.486019500000026,-41.04980449999998,-20.17293277499999,-18.80685324106817,-25.058474230843046,-12.86551705502134,faster +B-FS-ENG,writeback-quick-20261011,pvisor-vm,git,enabled,disabled,ms,30,0,,,separated-clusters,separated-clusters,"{""n"": 30, ""p50"": """", ""p95_reference"": 770.411194, ""minimum"": 356.547428, ""maximum"": 772.120794, ""distribution"": ""separated-clusters"", ""low_n"": 23, ""low_p50"": 391.260942, ""high_n"": 7, ""high_p50"": 759.777391}","{""n"": 30, ""p50"": """", ""p95_reference"": 763.62356215, ""minimum"": 351.748717, ""maximum"": 768.235727, ""distribution"": ""separated-clusters"", ""low_n"": 21, ""low_p50"": 375.215455, ""high_n"": 9, ""high_p50"": 751.366224}",,,,,,,separated clusters; no whole-distribution ranking +B-FS-ENG,writeback-quick-20261011,pvisor-vm,rg,enabled,disabled,ms,30,0,420.04630199999997,427.67075850000003,unsplit,unsplit,"{""n"": 30, ""p50"": 420.04630199999997, ""p95_reference"": 441.54148365000003, ""minimum"": 399.870682, ""maximum"": 445.790778, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 427.67075850000003, ""p95_reference"": 445.77900589999996, ""minimum"": 406.710051, ""maximum"": 449.623597, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",7.624456500000065,1.0899594999999636,14.249818000000005,1.815146678758306,0.26007782187420503,3.4109237718902357,slower +B-FS-ENG,writeback-quick-20261011,pvisor-vm,cargo,enabled,disabled,ms,30,0,485.0502085,490.2938245,unsplit,unsplit,"{""n"": 30, ""p50"": 485.0502085, ""p95_reference"": 564.250258, ""minimum"": 468.755493, ""maximum"": 811.936019, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 490.2938245, ""p95_reference"": 504.8452994, ""minimum"": 466.780379, ""maximum"": 506.569854, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",5.243616000000031,0.3492803000000435,12.541900412500015,1.0810460253621423,0.07130699507785941,2.613647008284152,slower +B-FS-ENG,writeback-quick-20261011,pvisor-vm,npm,enabled,disabled,ms,30,0,941.861598,,unsplit,separated-clusters,"{""n"": 30, ""p50"": 941.861598, ""p95_reference"": 1450.4631543000005, ""minimum"": 759.464465, ""maximum"": 1787.042376, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": """", ""p95_reference"": 1746.6509477500001, ""minimum"": 779.404457, ""maximum"": 1791.564318, ""distribution"": ""separated-clusters"", ""low_n"": 22, ""low_p50"": 937.7803074999999, ""high_n"": 8, ""high_p50"": 1661.332023}",,,,,,,separated clusters; no whole-distribution ranking diff --git a/docs/src/en/design/writeback-provenance.csv b/docs/src/en/design/writeback-provenance.csv new file mode 100644 index 000000000..10b66c013 --- /dev/null +++ b/docs/src/en/design/writeback-provenance.csv @@ -0,0 +1,15 @@ +field,value +report_sha256,44a7e0ba8fd7e36f66bfd749db4a29c10f722230a589a5cc6750895cf2d109ea +raw,benchmark/.data/writeback-quick-20261011/report.json +launch_sha256,2026de69ba8814fa6cd5cb77a86fe61a56b367ea91d90208e05704f690d58e4a +guard_sha256,462b4a5ce08df697628c1acd0e5cee14b914cfc7ec8f981cd58245d6d721e0ff +protocol,"30 seeded shuffled paired VM rounds; 3 warmups per arm; same frozen binary, inputs, seven-operation workload and private namespace per trial; full written-byte and Run Bundle checks; profiles off" +binary_sha256,ae221676e4bb04231c88f2ce17c2570824f4064f8a3a63d6ef32817b0e2afa98 +source_manifest_sha256,f5d1759332f5393bf9ff56569018ccb443d889f4a028ee6df4563569329a5590 +runner_sha256,95df22d6c110834d3e84abb459bbb2b02a141bbbf92ea0f42412dbe68518c6de +child_sha256,1bbde1e30507e8d9e4038d0ccdb3d24997c9183915e3be4929a9d72988f90b79 +switch,PVISOR_VM_FS_WRITEBACK=1 versus 0; originating environment restored in dedicated execution worker; only disabled arm changes the flag; production defaults unchanged +budget,"whole owned parent CPUs 0,1; CPUQuota 200%; RAM 16 GiB; swap 0; VM 2 vCPU/16 GiB" +statistics,5000 paired-round bootstrap resamples; difference and ratio of group medians CI95; separated clusters prevent global ranking; no P95/P99 +scope,"B-FS-ENG Linux KVM/Btrfs warm caches, sequential task workload; no cold-cache, macOS or concurrent-throughput claim" +publication_script_sha256,7831bc3559630703ae23a2ae2f9d421da8d68fa01493513357bccba75e5b6f58 diff --git a/docs/src/zh/benchmarks/agent-tasks.md b/docs/src/zh/benchmarks/agent-tasks.md index bd30cbe85..2d3f42c68 100644 --- a/docs/src/zh/benchmarks/agent-tasks.md +++ b/docs/src/zh/benchmarks/agent-tasks.md @@ -11,6 +11,12 @@ | 独立 guest 内核 | 选择 VM 前验证 CLI 兼容性和完整工具成本 | | 远端 API 与托管弹性 | 按仓库同步、部署和运维需求评估云端;同条件性能与账单未测 | + + +本页数值和结论仍属于原始历史批次;新框架尚无匹配的复测数据,见[复测覆盖](index.md#measurement-coverage)。 + + + ## Motivation {#motivation} 一次任务包括环境准备、模型与工具交互、测试验证、审查合入和清理。只看启动会漏掉工具与结果处理;只看工具速度,又会漏掉反复创建工作区的成本。固定工具计划排除推理和公网波动,独立的完整审查流程衡量同样改动如何进入原工作区。 diff --git a/docs/src/zh/benchmarks/cluster-scalability.md b/docs/src/zh/benchmarks/cluster-scalability.md index 9c210fbc9..e55d27a93 100644 --- a/docs/src/zh/benchmarks/cluster-scalability.md +++ b/docs/src/zh/benchmarks/cluster-scalability.md @@ -10,6 +10,12 @@ | 规划本机执行或停驻状态 | 使用[容量](density.md)和[VM 内存](vm-memory/index.md),不使用 Worker 曲线 | | 规划 daemon 或比较调度系统 | 未测;不能沿用旧吞吐或历史成本数字 | + + +本页数值和结论仍属于原始历史批次;新框架尚无匹配的复测数据,见[复测覆盖](index.md#measurement-coverage)。 + + + ## Motivation {#motivation} 容量决策需要与实际部署实现相匹配的证据。Controller/Worker 退役后,实验对象已不再存在,把旧测量改名会掩盖这一边界。就绪、有效任务完成和可恢复停驻状态回答不同问题。 diff --git a/docs/src/zh/benchmarks/compare-rl-infra.md b/docs/src/zh/benchmarks/compare-rl-infra.md index fd81615a9..62bf5a173 100644 --- a/docs/src/zh/benchmarks/compare-rl-infra.md +++ b/docs/src/zh/benchmarks/compare-rl-infra.md @@ -11,6 +11,12 @@ | 历史上下文或未缓存镜像 | 在已测范围内评估前缀准备或按需读取 | | 已有 RL 栈 | 保留任务、模型和训练层;专用集成仍未验证 | + + +本页数值和结论仍属于原始历史批次;新框架尚无匹配的复测数据,见[复测覆盖](index.md#measurement-coverage)。 + + + ## Motivation {#motivation} 一次 rollout 创建环境,交替进行模型推理与工具调用,执行验证、赋予 reward,再保留或拒绝样本。失败尝试可能需要准备上下文、恢复环境并重新执行。选择 executor 时,需要分别预算这些阶段,并保留任务要求的执行边界。 diff --git a/docs/src/zh/benchmarks/density.md b/docs/src/zh/benchmarks/density.md index 6f841c720..b839629e6 100644 --- a/docs/src/zh/benchmarks/density.md +++ b/docs/src/zh/benchmarks/density.md @@ -10,6 +10,12 @@ | 大量空闲环境 | stage 达到更高的已测并发度;它使用宿主进程边界 | | 每个任务需要 VM 边界 | 为 VM 留出更多预算;不要按空闲数规划活跃任务 | + + +本页数值和结论仍属于原始历史批次;新框架尚无匹配的复测数据,见[复测覆盖](index.md#measurement-coverage)。 + + + ## Motivation {#motivation} 多个 Agent 同时执行时,成功完成多少任务比创建多少环境更有用。容量规划还要计入运行时、辅助进程、页缓存和 VM backing,不能用单个进程 RSS 或配置内存推算。 diff --git a/docs/src/zh/benchmarks/evidence-sources.csv b/docs/src/zh/benchmarks/evidence-sources.csv index 9e4d4aa10..4fee18183 100644 --- a/docs/src/zh/benchmarks/evidence-sources.csv +++ b/docs/src/zh/benchmarks/evidence-sources.csv @@ -1,7 +1,7 @@ source_documents,cohort,local_raw_record,raw_sha256,identity retired CLI comparison (historical record only),reference-env-20261004,docs/src/assets/benchmarks/.data/reference-env-20261004/report.tsv,090abcab9593a0c52f8e0b27186db51f6d684f4501221045098e239b80e92804,"{""driver_sha256"": ""404a61ee30ae4228b1c6f7d4493c5f1c3297bf953bcabc03ef6e06a3e765bc7c"", ""host_kernel"": ""7.2.8-200.fc44.x86_64"", ""kernel_sha256"": ""aa55fa2dbe2762a55020182560fabd16562d565392505b1986fc51326c741f3d"", ""pvisor_sha256"": ""1a2db5ad015c5ace40b3c96c7dd0dc94a08b8893de35150bd909554286e2cd3c"", ""recorded_at"": ""2026-10-04T06:07:14.020510+08:00"", ""scope"": ""pinned Claude/Codex; N=30 successful cells; no real inference"", ""source_commit"": ""944e32fcf30e71c37c67848ccf2ac4428cdfb053"", ""workload_sha256"": ""bb56a7727316cb566dfc7fca7223a5e0e1be591e6f90376d1b3c599a8fe4764e""}" startup.md;agent-tasks.md;compare-rl-infra.md;index.md,6r,benchmark/.data/6r/report.json,aa2ab1f5a4938365895cc7528263f1949e2178fc9f4e13f9c9a7332509b960c1,"{""binary_sha256"": ""04d73057a0042bd965a4fc49555e6d24c30126ae59fc349153d4466286cfb66b"", ""failures"": 0, ""samples"": 480, ""scope"": ""independent registered workload; no pooled samples"", ""source_manifest_sha256"": ""cad1cedb1c7b7745b53ad730a83bf5a3b69e35072f19969b0074dd66eace051f""}" -filesystem.md;agent-tasks.md;index.md,6f,benchmark/.data/6f/report.json,199a0a0a5ac4a4d0863c9ae4c02807bf57e6a560aa4edc176a20ae83ae969d7e,"{""binary_sha256"": ""04d73057a0042bd965a4fc49555e6d24c30126ae59fc349153d4466286cfb66b"", ""failures"": 0, ""samples"": 480, ""scope"": ""independent registered workload; no pooled samples"", ""source_manifest_sha256"": ""cad1cedb1c7b7745b53ad730a83bf5a3b69e35072f19969b0074dd66eace051f""}" +filesystem.md (historical 2026-10-06);agent-tasks.md;index.md,6f,benchmark/.data/6f/report.json,199a0a0a5ac4a4d0863c9ae4c02807bf57e6a560aa4edc176a20ae83ae969d7e,"{""binary_sha256"": ""04d73057a0042bd965a4fc49555e6d24c30126ae59fc349153d4466286cfb66b"", ""failures"": 0, ""samples"": 480, ""scope"": ""independent registered workload; no pooled samples"", ""source_manifest_sha256"": ""cad1cedb1c7b7745b53ad730a83bf5a3b69e35072f19969b0074dd66eace051f""}" agent-tasks.md;compare-rl-infra.md;index.md,6t,benchmark/.data/6t/report.json,176a8508dd2fc7bb358f95d54f1e25e8c0f1d18c521775240213bec8fdeaad77,"{""binary_sha256"": ""04d73057a0042bd965a4fc49555e6d24c30126ae59fc349153d4466286cfb66b"", ""failures"": 0, ""samples"": 480, ""scope"": ""independent registered workload; no pooled samples"", ""source_manifest_sha256"": ""cad1cedb1c7b7745b53ad730a83bf5a3b69e35072f19969b0074dd66eace051f""}" supervision-cost.md;agent-tasks.md;index.md,6w,benchmark/.data/6w/report.json,52521abbb0c6245dc8af1ab1b70cf23b750b09f605dbde4cad4a7f60850c5569,"{""binary_sha256"": ""04d73057a0042bd965a4fc49555e6d24c30126ae59fc349153d4466286cfb66b"", ""failures"": 0, ""samples"": 360, ""scope"": ""independent registered workload; no pooled samples"", ""source_manifest_sha256"": ""cad1cedb1c7b7745b53ad730a83bf5a3b69e35072f19969b0074dd66eace051f""}" network.md,6net,benchmark/.data/6net/report.json,a694f5930f7b49138d8e53b82e4e1a8c0ec0e494d489aee304220c8d50d6f9bc,"{""benchmark_id"": ""B-NETWORK"", ""binary_sha256"": ""f2cc7afd57efc7c029545744ad2d70c18c542bc29291e35f514cca75f364548a"", ""processed_provenance"": ""network-provenance.csv"", ""recorded_at"": ""2026-10-06T07:30:31.595026+08:00"", ""scope"": {""caches"": ""warm; no host cache eviction"", ""correctness_required"": true, ""git_configuration"": ""global/system config disabled; isolated fixture repository config; C locale"", ""percentile"": ""linear interpolation"", ""samples"": 30, ""startup_logging"": false, ""timing"": ""wall includes process launch and teardown; worker_ms is workload only"", ""vm_memory"": ""1GiB"", ""warmups"": 3}, ""source_manifest_sha256"": ""9ef6773efe0c4ec03a18c2da8824194fb5c2f517e787ad08b437d0594a7a3cd3""}" @@ -16,3 +16,11 @@ isolation-tests.md,6iso,benchmark/.data/6iso/report.json,6f2679977acc7e94d68c7fe isolation-tests.md,6iso,benchmark/.data/6iso/isolation-publication-audit.json,6ca02a14fbc2a6e4546292c89ea5eec99b81d2d9ac104e28dd9e58ecf3b0c1c5,"{""binary_sha256"": ""1a7144081fdbf55047ae57df6dcc13a9e57bb7107f568a17f2b5f209aa2d6f17"", ""recorded_at"": ""2026-10-06T16:01:52.975982+08:00"", ""scope"": ""21 retained command/Bundle/output/host-stage-byte checks; not an escape audit"", ""source_manifest_sha256"": ""7ef73174e02879122a410fe291867e3c8c08f4a1c944c69eb4aded7f521e1b7f""}" supervision-cost.md,6sup2,benchmark/.data/6sup2/report.json,62ddd9de8f70df8c10e8b1c0f5f6a316673094404f22460c6906331620a8b749,"{""binary_sha256"": ""1a7144081fdbf55047ae57df6dcc13a9e57bb7107f568a17f2b5f209aa2d6f17"", ""recorded_at"": ""2026-10-06T16:12:43.375659+08:00"", ""scope"": ""60 paired prepared-view review/select/apply/disposal samples; private-view creation and task execution excluded"", ""source_manifest_sha256"": ""7ef73174e02879122a410fe291867e3c8c08f4a1c944c69eb4aded7f521e1b7f""}" supervision-cost.md,6sup2,benchmark/.data/6sup2/supervision-publication-audit.json,1b4ba2d3c9661a6fcfd31cef360c12e4d0eb9c4577fba93f9163c8dde7b7cf17,"{""binary_sha256"": ""1a7144081fdbf55047ae57df6dcc13a9e57bb7107f568a17f2b5f209aa2d6f17"", ""recorded_at"": ""2026-10-06T16:12:43.375659+08:00"", ""scope"": ""60 formal and six warmup command/diff/final-byte/Run-boundary checks"", ""source_manifest_sha256"": ""7ef73174e02879122a410fe291867e3c8c08f4a1c944c69eb4aded7f521e1b7f""}" +filesystem.md (historical 2026-10-10),f10f2,benchmark/.data/f10f2/report.json,c2f544b79356db3fcda8f4ed34d944861a64f2190c18747c30441201271502ff,"{""binary_source_commit"": ""b3e2f03509396b8ebe840a9ccdefa63424bc1afe"", ""binary_source_manifest_sha256"": ""a52c826c8d6c70632f4f3d8e36fda672fe8029b7f87cb5f7138a72824b6b5578"", ""host_kernel"": ""7.2.8-200.fc44.x86_64"", ""input_manifest_sha256"": ""59d0389ac29bf0a6d216bfcb5fcf1ff48e143162b453e4e16f195f562f6f4670"", ""kernel_sha256"": ""aa55fa2dbe2762a55020182560fabd16562d565392505b1986fc51326c741f3d"", ""pvisor_sha256"": ""c6965986ac119544f0886e06806fcf41b814f54b54ad655978046a2449797c42"", ""recorded_at"": ""2026-10-10T21:33:50.026904+08:00""}" +filesystem.md;index.md,fs11f4,benchmark/.data/fs11f4/report.json,99eb2ee32bd9e595694c39434f33bc4a914b949e88d3c2b345733932e6884369,"{""binary_source_commit"": ""b3e2f03509396b8ebe840a9ccdefa63424bc1afe"", ""binary_source_manifest_sha256"": ""07b27144366d96dbac053bd18288a50c7468b231d0e8570b3f400b38c28d0f8f"", ""host_kernel"": ""7.2.8-200.fc44.x86_64"", ""input_manifest_sha256"": ""730df0172d73d3c260c741eebf7ac0926dea0add1f05a176c6a39176e9469518"", ""kernel_sha256"": ""aa55fa2dbe2762a55020182560fabd16562d565392505b1986fc51326c741f3d"", ""pvisor_sha256"": ""939f72289a72f73ba64d645c891842ed4a9408c7eaef4c54d5fa04aed0371e85"", ""recorded_at"": ""2026-10-11T13:39:23.065270+08:00""}" +filesystem.md;index.md;design/filesystem-performance-analysis.md,fo8e2d57ff-f,benchmark/.data/fo8e2d57ff-f/report.json,d37b961370f9d8ba3487fd1a6a7a85cf5e85e5de6b32ae91285eb69b9dbfdb92,"{""benchmark_id"": ""B-FS-TOOLS"", ""binary_source_commit"": ""b3e2f03509396b8ebe840a9ccdefa63424bc1afe"", ""binary_source_manifest_sha256"": ""5311e7c05ca1fc114c60416920c204866e5a6079f509343a91b95f6bf0d37b24"", ""failures"": 0, ""host_kernel"": ""7.2.8-200.fc44.x86_64"", ""input_manifest_sha256"": ""c3f1b6f957c19a403baf6c7299901fd92f41a2cf758cf7edbbab9a4a8108852b"", ""kernel_sha256"": ""aa55fa2dbe2762a55020182560fabd16562d565392505b1986fc51326c741f3d"", ""processed_provenance"": ""filesystem-provenance.csv"", ""pvisor_sha256"": ""8840b16cdc0610e381eec66b0f75f1e86efba76feb358c9eae5b3d923854b592"", ""record_scope"": ""accepted raw report"", ""recorded_at"": ""2026-10-11T14:57:50.329605+08:00"", ""role"": ""accepted full user-facing cohort"", ""samples"": 240, ""scope"": ""independent full cohort; no pooled samples; phase timings distinct from whole-command probes"", ""selected_build_defaults"": {""new_vm_writeback"": false, ""ordinary_dax"": false, ""reviewed_from_frozen_source"": true, ""stage_sync_workers"": 1}}" +filesystem.md;index.md;design/filesystem-performance-analysis.md,fo8e2d57ff-f,benchmark.new/filesystem/.data/fs-opt-report-20261011/publication-audit.json,ec38a6bc0311c07e45a756ed6ac0099270c680e432a3aa02252e78ea07f66528,"{""benchmark_id"": ""B-FS-TOOLS"", ""binary_source_commit"": ""b3e2f03509396b8ebe840a9ccdefa63424bc1afe"", ""binary_source_manifest_sha256"": ""5311e7c05ca1fc114c60416920c204866e5a6079f509343a91b95f6bf0d37b24"", ""failures"": 0, ""host_kernel"": ""7.2.8-200.fc44.x86_64"", ""input_manifest_sha256"": ""c3f1b6f957c19a403baf6c7299901fd92f41a2cf758cf7edbbab9a4a8108852b"", ""kernel_sha256"": ""aa55fa2dbe2762a55020182560fabd16562d565392505b1986fc51326c741f3d"", ""processed_provenance"": ""filesystem-provenance.csv"", ""pvisor_sha256"": ""8840b16cdc0610e381eec66b0f75f1e86efba76feb358c9eae5b3d923854b592"", ""record_scope"": ""accepted publication audit"", ""recorded_at"": ""2026-10-11T14:57:50.329605+08:00"", ""role"": ""accepted full user-facing cohort"", ""samples"": 240, ""scope"": ""independent full cohort; no pooled samples; phase timings distinct from whole-command probes"", ""selected_build_defaults"": {""new_vm_writeback"": false, ""ordinary_dax"": false, ""reviewed_from_frozen_source"": true, ""stage_sync_workers"": 1}}" +design/filesystem-performance-analysis.md,fof1,benchmark/.data/fof1/report.json,df8ddd01aa86ef0a61a04d4157dc8e02a6c935ebacecf3ccfb448ab624ded7b1,"{""benchmark_id"": ""B-FS-ENG"", ""processed_summary"": ""filesystem-optimization-n30-summary.csv"", ""processed_summary_sha256"": ""65b0287892724c266ebc39db20adcc138ac180aedb6b455cc0f713517d6b25dd"", ""record_scope"": ""raw engineering report"", ""role"": ""independent formal engineering A/B"", ""samples_per_arm"": 30, ""scope"": ""not pooled with user full cohort or screening/history"", ""warmups_per_arm"": 3}" +design/filesystem-performance-analysis.md,fof1,benchmark/.data/fof1/derived/audit.json,3a599448f7a7737f38e52b7d2943555d0a8cc4d74e030ebabbea5461d25fca29,"{""benchmark_id"": ""B-FS-ENG"", ""processed_summary"": ""filesystem-optimization-n30-summary.csv"", ""processed_summary_sha256"": ""65b0287892724c266ebc39db20adcc138ac180aedb6b455cc0f713517d6b25dd"", ""record_scope"": ""engineering acceptance"", ""role"": ""independent formal engineering A/B"", ""samples_per_arm"": 30, ""scope"": ""not pooled with user full cohort or screening/history"", ""warmups_per_arm"": 3}" +filesystem.md;index.md;design/filesystem-performance-analysis.md,fo5a4e7d2b-f,benchmark/.data/fo5a4e7d2b-f/report.json,0127dbdd8a04317be06a0235e5b1cec75dc298b3c6e312f22f1382b8d19f7262,"{""benchmark_id"": ""B-FS-TOOLS"", ""binary_source_commit"": ""3c5c647ee6890b7a310f838f5b80f6751c718575"", ""binary_source_manifest_sha256"": ""3a8356cfa2370ebfaa4716cbb407d50cf90b172ae1943749bb3efff8f2db4642"", ""failures"": 0, ""host_kernel"": ""7.2.8-200.fc44.x86_64"", ""input_manifest_sha256"": ""f33275693e1d765be1901e7ba856ef91a397d622abd29308d57699993ef4e49d"", ""kernel_sha256"": ""aa55fa2dbe2762a55020182560fabd16562d565392505b1986fc51326c741f3d"", ""processed_provenance"": ""filesystem-provenance.csv"", ""pvisor_sha256"": ""de4889d932ed8bb5ab376cb09ae43327084a2d017f66db5c54946b78b50446ac"", ""record_scope"": ""accepted raw report"", ""recorded_at"": ""2026-10-12T01:50:38.436534+08:00"", ""role"": ""accepted full user-facing cohort"", ""samples"": 240, ""scope"": ""independent full cohort; no pooled samples; phase timings distinct from whole-command probes"", ""selected_build_defaults"": {""new_vm_writeback"": false, ""ordinary_dax"": false, ""reviewed_from_frozen_source"": true, ""stage_sync_workers"": 1}}" +filesystem.md;index.md;design/filesystem-performance-analysis.md,fo5a4e7d2b-f,benchmark.new/filesystem/.data/fs-docs-20261012/publication-audit.json,45f479ab7709723e781bfd6b78d8ace3dfa2a8914a687173babfd9ee23daf24e,"{""benchmark_id"": ""B-FS-TOOLS"", ""binary_source_commit"": ""3c5c647ee6890b7a310f838f5b80f6751c718575"", ""binary_source_manifest_sha256"": ""3a8356cfa2370ebfaa4716cbb407d50cf90b172ae1943749bb3efff8f2db4642"", ""failures"": 0, ""host_kernel"": ""7.2.8-200.fc44.x86_64"", ""input_manifest_sha256"": ""f33275693e1d765be1901e7ba856ef91a397d622abd29308d57699993ef4e49d"", ""kernel_sha256"": ""aa55fa2dbe2762a55020182560fabd16562d565392505b1986fc51326c741f3d"", ""processed_provenance"": ""filesystem-provenance.csv"", ""pvisor_sha256"": ""de4889d932ed8bb5ab376cb09ae43327084a2d017f66db5c54946b78b50446ac"", ""record_scope"": ""accepted publication audit"", ""recorded_at"": ""2026-10-12T01:50:38.436534+08:00"", ""role"": ""accepted full user-facing cohort"", ""samples"": 240, ""scope"": ""independent full cohort; no pooled samples; phase timings distinct from whole-command probes"", ""selected_build_defaults"": {""new_vm_writeback"": false, ""ordinary_dax"": false, ""reviewed_from_frozen_source"": true, ""stage_sync_workers"": 1}}" diff --git a/docs/src/zh/benchmarks/filesystem-cache-policy.csv b/docs/src/zh/benchmarks/filesystem-cache-policy.csv new file mode 100644 index 000000000..b4dac69c2 --- /dev/null +++ b/docs/src/zh/benchmarks/filesystem-cache-policy.csv @@ -0,0 +1,9 @@ +benchmark_id,role,batch,backend,validation_state,formal_valid,preflight_failed,cache_status,cache_filesystem,filesystem_magic,cache_directory,failure_reason,report_sha256,stdout_sha256 +B-AGENT-TASK,environment capability observation; no timing publication,f10e,native,available,1,0,ALREADY_ENABLED,tmpfs,0x1021994,/tmp/.data/pvisor-reference-cmlac1bu/node-compile-cache/v24.18.0-x64-cf738c9d-1000,,c94b5d3748122f49b9148530ce90f3922d196232d55f208dbd7499d5ea68733a,3c4e4e0ecf0ad75581624d294800e44b73b0c89a53545917c7f7b619ae72f6d9 +B-AGENT-TASK,environment capability observation; no timing publication,f10e,pvisor-host,available,1,0,ALREADY_ENABLED,tmpfs,0x1021994,/tmp/.data/pvisor-reference-9lzu3o5e/node-compile-cache/v24.18.0-x64-cf738c9d-1000,,c94b5d3748122f49b9148530ce90f3922d196232d55f208dbd7499d5ea68733a,b6a17b4ea92cc10d1490c1817a06d16a9d5121316c22c7fabfa8e0fea0313ac8 +B-AGENT-TASK,environment capability observation; no timing publication,f10e,pvisor-staged,available,1,0,ALREADY_ENABLED,tmpfs,0x1021994,/tmp/.data/pvisor-reference-ch2i5d0c/node-compile-cache/v24.18.0-x64-cf738c9d-1000,,c94b5d3748122f49b9148530ce90f3922d196232d55f208dbd7499d5ea68733a,527433a05c20885895cd39b3e15013bde21c85a98c3f1e6ac427a76794a89b78 +B-AGENT-TASK,environment capability observation; no timing publication,f10e,pvisor-vm,available,1,0,ALREADY_ENABLED,tmpfs,0x1021994,/.pvisor-tmp-run-c924a108-6c68-4e4f-81cd-14da4ae9ee84/.data/pvisor-reference-ywed6kwb/node-compile-cache/v24.18.0-x64-cf738c9d-0,,c94b5d3748122f49b9148530ce90f3922d196232d55f208dbd7499d5ea68733a,d62500b13045ada1b2fe7d90c02c017e93e94ba4ecb94d7f3cbe26e0e443eba5 +B-AGENT-TASK,environment capability observation; no timing publication,f10e,docker,available,1,0,ALREADY_ENABLED,overlayfs,0x794c7630,/tmp/.data/pvisor-reference-yd09ot2_/node-compile-cache/v24.18.0-x64-cf738c9d-0,,c94b5d3748122f49b9148530ce90f3922d196232d55f208dbd7499d5ea68733a,9b38b12fe39bbb929dfa6b8cc28d5d7bcdabc0584d0d01a94acae80d523f8a56 +B-AGENT-TASK,environment capability observation; no timing publication,f10e,firecracker,failed-preflight,0,1,ALREADY_ENABLED,ext4,0xef53,/tmp/.data/pvisor-reference-ymw5tlm5/node-compile-cache/v24.18.0-x64-cf738c9d-0,guest must emit exactly one Ready marker,c94b5d3748122f49b9148530ce90f3922d196232d55f208dbd7499d5ea68733a,a58fd162425fdb0dfd13eb1da9e3a310f1d19f64d025cea3099d70b6747c3e18 +B-AGENT-TASK,environment capability observation; no timing publication,f10e,qemu,failed-preflight,0,1,ALREADY_ENABLED,ext4,0xef53,/tmp/.data/pvisor-reference-lrewstqa/node-compile-cache/v24.18.0-x64-cf738c9d-0,guest must emit exactly one Ready marker,c94b5d3748122f49b9148530ce90f3922d196232d55f208dbd7499d5ea68733a,f6a45fc7aaa63c2d26080a4c50019ffc32d800b22c7f9f1b4e1bf6c36446c1f5 +B-AGENT-TASK,environment capability observation; no timing publication,f10e,qemu-microvm,failed-preflight,0,1,ALREADY_ENABLED,ext4,0xef53,/tmp/.data/pvisor-reference-92jbr460/node-compile-cache/v24.18.0-x64-cf738c9d-0,guest must emit exactly one Ready marker,c94b5d3748122f49b9148530ce90f3922d196232d55f208dbd7499d5ea68733a,d85e10eab369cb15a899a31034ef9bcce89e295eba24f14550e3c8c9a68756a3 diff --git a/docs/src/zh/benchmarks/filesystem-comparisons.csv b/docs/src/zh/benchmarks/filesystem-comparisons.csv new file mode 100644 index 000000000..7a745ee56 --- /dev/null +++ b/docs/src/zh/benchmarks/filesystem-comparisons.csv @@ -0,0 +1,151 @@ +batch,benchmark_id,mode,candidate,control,metric,unit,n,difference_ms,ci95_low_ms,ci95_high_ms,conclusion,confidence_method +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,native,ready_ms,ms,30,21.354811499999983,20.717782675000013,23.427103624999987,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,native,result_ms,ms,30,52.800583000000074,48.103993025000044,62.20575908749991,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,native,completion_ms,ms,30,69.6681044999998,65.13375883750004,79.66904299999999,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,native,metadata_worker_ms,ms,30,-0.004884999999999806,-0.1306995000000004,0.16186400000000045,no detected difference,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,native,read_worker_ms,ms,30,0.6513659999999959,0.244121550000005,1.673229487499995,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,native,write_worker_ms,ms,30,0.07752499999999962,-0.13989657499999997,0.1782782874999999,no detected difference,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,native,git_worker_ms,ms,30,1.1633475000000004,0.08171393750000074,1.832967,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,native,rg_worker_ms,ms,30,0.018464999999999065,-0.2702340000000003,0.8803643124999997,no detected difference,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,native,cargo_worker_ms,ms,30,5.970192499999996,3.6305704000000003,8.054308924999997,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,native,npm_worker_ms,ms,30,13.438230500000031,11.142320762500013,15.550342387499997,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,docker,ready_ms,ms,30,-192.18906400000003,-238.00896250000005,-169.72389694999998,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,docker,result_ms,ms,30,-157.04292799999985,-200.36289886249986,-132.82160399999998,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,docker,completion_ms,ms,30,-200.42969400000015,-241.39521500000023,-167.35366419999994,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,docker,metadata_worker_ms,ms,30,-0.08691349999999964,-0.1813180000000001,0.04570827500000045,no detected difference,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,docker,read_worker_ms,ms,30,0.32456849999999804,0.07155249999999569,1.3483590624999908,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,docker,write_worker_ms,ms,30,-0.08147150000000014,-0.2650030000000001,0.05023117500000042,no detected difference,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,docker,git_worker_ms,ms,30,0.9607700000000001,-0.06168631250000115,1.675598024999998,no detected difference,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,docker,rg_worker_ms,ms,30,-0.47471250000000076,-0.7639528625000008,0.44008392499999843,no detected difference,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,docker,cargo_worker_ms,ms,30,10.127195,7.408730575,11.773046137500002,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,docker,npm_worker_ms,ms,30,9.155480000000011,7.278795000000002,11.67338657500002,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,firecracker,ready_ms,ms,30,-642.8175115,-648.5643,-640.7428865375,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,firecracker,result_ms,ms,30,-1584.6247370000003,-1593.5329044624998,-1565.5314213,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,firecracker,completion_ms,ms,30,-1607.1344020000001,-1617.5947384499998,-1595.17350005,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,firecracker,metadata_worker_ms,ms,30,-17.239526,-19.149071575,-17.003035050000005,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,firecracker,read_worker_ms,ms,30,-47.387362499999995,-47.7189269875,-46.22877482500001,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,firecracker,write_worker_ms,ms,30,-39.216832,-39.34645075,-39.122198000000004,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,firecracker,git_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,firecracker,rg_worker_ms,ms,30,-8.195251500000001,-8.6260351125,-7.213457,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,firecracker,cargo_worker_ms,ms,30,-327.178037,-334.33065750000003,-319.7967431375,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,firecracker,npm_worker_ms,ms,30,-330.77358649999996,-335.69834168750003,-321.427424,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu,ready_ms,ms,30,-280.10003400000005,-336.7426709999999,-275.017773,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu,result_ms,ms,30,-1109.1334920000002,-1240.0404424999997,-1026.4512989999996,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu,completion_ms,ms,30,-1125.8445930000003,-1278.7792020000002,-1045.7348415000001,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu,metadata_worker_ms,ms,30,-12.012132999999999,-14.212173950000002,-11.543457,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu,read_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu,write_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu,git_worker_ms,ms,30,-72.4847685,-75.97099,-68.3695655,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu,rg_worker_ms,ms,30,-6.818395500000001,-7.775931,-5.677642750000002,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu,cargo_worker_ms,ms,30,-314.82165399999997,-333.0714988625,-307.134967875,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu,npm_worker_ms,ms,30,-308.200423,-327.0849611,-240.9299808875,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu-microvm,ready_ms,ms,30,-211.32980099999997,-270.38579200000004,-164.125515,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu-microvm,result_ms,ms,30,-1247.1282525,-1305.98371,-1159.386878,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu-microvm,completion_ms,ms,30,-1289.3285475000002,-1326.194048425,-1186.7533285,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu-microvm,metadata_worker_ms,ms,30,-15.249888499999997,-16.5170625,-14.8280532375,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu-microvm,read_worker_ms,ms,30,-41.39920950000001,-55.87539199999999,-23.30983700000001,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu-microvm,write_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu-microvm,git_worker_ms,ms,30,-151.464851,-158.2403045,-145.42583,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu-microvm,rg_worker_ms,ms,30,-8.281779499999999,-9.007349587500002,-6.970837762500001,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu-microvm,cargo_worker_ms,ms,30,-371.4054985,-390.55829556250006,-339.63162848750017,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-host,qemu-microvm,npm_worker_ms,ms,30,-355.344023,-370.229405,-318.6296337,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,native,ready_ms,ms,30,33.94435249999998,32.63888800000001,35.534462687500024,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,native,result_ms,ms,30,540.3684264999998,533.9711397000001,549.2702949875,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,native,completion_ms,ms,30,808.447336,793.5487008,816.4551753249999,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,native,metadata_worker_ms,ms,30,78.17458900000001,74.7027016375,79.0251345,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,native,read_worker_ms,ms,30,45.3585295,42.67462863749999,46.770893,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,native,write_worker_ms,ms,30,28.294047999999997,26.8714861,29.557854000000003,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,native,git_worker_ms,ms,30,147.95046100000002,138.37097541250003,156.8120554375,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,native,rg_worker_ms,ms,30,99.264704,97.42978045,102.072395,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,native,cargo_worker_ms,ms,30,37.0588795,34.918250574999995,38.4174845,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,native,npm_worker_ms,ms,30,32.37371999999999,29.259301550000014,35.9872963,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,docker,ready_ms,ms,30,-179.59952300000003,-225.62106475000004,-157.46620099999998,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,docker,result_ms,ms,30,330.5249154999999,287.3952519499999,355.58885750000013,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,docker,completion_ms,ms,30,538.3495375,497.98129399999993,570.6188934124999,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,docker,metadata_worker_ms,ms,30,78.09256050000002,74.62362001250001,78.962167,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,docker,read_worker_ms,ms,30,45.031732000000005,42.383445512499996,46.5158808625,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,docker,write_worker_ms,ms,30,28.1350515,26.697902137499998,29.387190500000003,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,docker,git_worker_ms,ms,30,147.7478835,138.2521645,156.5467665,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,docker,rg_worker_ms,ms,30,98.7715265,97.03198950000001,101.57483,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,docker,cargo_worker_ms,ms,30,41.21588200000001,38.604738999999995,42.3511745,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,docker,npm_worker_ms,ms,30,28.09096949999997,25.46852100000001,32.3563627625,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,firecracker,ready_ms,ms,30,-630.2279705000001,-635.6626974375,-628.522135,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,firecracker,result_ms,ms,30,-1097.0568935000006,-1108.9951634375,-1077.2782639999998,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,firecracker,completion_ms,ms,30,-868.3551705,-888.1790590999999,-855.6087235,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,firecracker,metadata_worker_ms,ms,30,60.939948000000015,57.203875999999994,61.75334599999999,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,firecracker,read_worker_ms,ms,30,-2.6801989999999876,-5.281012000000004,-1.245096999999987,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,firecracker,write_worker_ms,ms,30,-11.000309000000001,-12.401892500000002,-9.755308499999998,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,firecracker,git_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,firecracker,rg_worker_ms,ms,30,91.05098749999999,89.110830425,94.00118675,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,firecracker,cargo_worker_ms,ms,30,-296.08935,-303.490479075,-287.935798175,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,firecracker,npm_worker_ms,ms,30,-311.838097,-317.62806650000005,-301.456369,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu,ready_ms,ms,30,-267.51049300000005,-324.2623488499999,-262.8967688125,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu,result_ms,ms,30,-621.5656485000004,-754.2959272624998,-538.2588335124998,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu,completion_ms,ms,30,-387.0653615000001,-542.8254525000002,-306.6929869999999,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu,metadata_worker_ms,ms,30,66.16734100000002,61.9000293,67.237834,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu,read_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu,write_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu,git_worker_ms,ms,30,74.302345,65.117742,85.32994350000001,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu,rg_worker_ms,ms,30,92.4278435,90.606458,95.217265,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu,cargo_worker_ms,ms,30,-283.732967,-301.68573841250003,-275.3837045125,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu,npm_worker_ms,ms,30,-289.26493350000004,-309.15471318749996,-220.97989466250007,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu-microvm,ready_ms,ms,30,-198.74025999999998,-257.80681703749997,-151.31966550000004,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu-microvm,result_ms,ms,30,-759.5604090000002,-818.8753958375002,-670.6451519625,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu-microvm,completion_ms,ms,30,-550.5493160000001,-591.0010197375002,-450.1275515624999,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu-microvm,metadata_worker_ms,ms,30,62.929585500000016,59.17957199999999,63.9421089375,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu-microvm,read_worker_ms,ms,30,3.307953999999995,-12.026521499999987,21.577668774999996,no detected difference,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu-microvm,write_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu-microvm,git_worker_ms,ms,30,-4.677737500000006,-17.807794462499995,7.438843624999973,no detected difference,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu-microvm,rg_worker_ms,ms,30,90.9644595,89.125186,93.72936425,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu-microvm,cargo_worker_ms,ms,30,-340.31681150000003,-358.8761985,-307.98643649999997,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-staged,qemu-microvm,npm_worker_ms,ms,30,-336.40853350000003,-350.74429549999996,-298.74167303749994,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,native,ready_ms,ms,30,570.2632964999999,562.2344208125,579.9940357874998,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,native,result_ms,ms,30,4174.5818865,3980.3865591250005,4417.520103475,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,native,completion_ms,ms,30,4463.837227,4191.473749,4639.6729338000005,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,native,metadata_worker_ms,ms,30,172.54763100000002,158.8435415,182.67950011249997,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,native,read_worker_ms,ms,30,103.419885,101.56575838750001,107.19638047500001,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,native,write_worker_ms,ms,30,151.848376,148.17121107499997,158.20359850000003,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,native,git_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,native,rg_worker_ms,ms,30,419.492317,407.7423475,431.519171,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,native,cargo_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,native,npm_worker_ms,ms,30,903.2197145,875.8506089125002,957.7747454999999,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,docker,ready_ms,ms,30,356.7194209999999,311.06533828749997,380.0942947874999,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,docker,result_ms,ms,30,3964.7383755,3748.5483914875,4206.4075014,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,docker,completion_ms,ms,30,4193.7394285,3918.3813497124997,4376.5615969875,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,docker,metadata_worker_ms,ms,30,172.46560250000002,158.774123,182.6062945,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,docker,read_worker_ms,ms,30,103.0930875,101.273033975,106.80225150000001,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,docker,write_worker_ms,ms,30,151.6893795,147.95817799999998,158.08465427500002,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,docker,git_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,docker,rg_worker_ms,ms,30,418.9991395,407.4594651,431.08609261249995,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,docker,cargo_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,docker,npm_worker_ms,ms,30,898.936964,872.2733834750002,953.2581134999998,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,firecracker,ready_ms,ms,30,-93.9090265000001,-103.86495615,-85.38884900000005,candidate faster,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,firecracker,result_ms,ms,30,2537.1565665,2345.4487751124993,2784.6814974999998,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,firecracker,completion_ms,ms,30,2787.0347205000003,2516.8881205625003,2963.2176690000006,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,firecracker,metadata_worker_ms,ms,30,155.31299,141.31960164999998,165.32276119999997,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,firecracker,read_worker_ms,ms,30,55.3811565,53.44678000000002,59.151491500000006,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,firecracker,write_worker_ms,ms,30,112.55401899999998,108.80335499999998,118.93815100000002,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,firecracker,git_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,firecracker,rg_worker_ms,ms,30,411.2786005,399.5964279375,423.4593125,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,firecracker,cargo_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,firecracker,npm_worker_ms,ms,30,559.0078975,531.8477048374999,615.6954783874996,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu,ready_ms,ms,30,268.8084509999999,210.11025737499997,276.99498551249997,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu,result_ms,ms,30,3012.6478115,2829.1194989249993,3237.1212299999997,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu,completion_ms,ms,30,3268.3245295,3025.0442515,3409.2922661625,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu,metadata_worker_ms,ms,30,160.54038300000002,146.5920661875,170.57778726249998,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu,read_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu,write_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu,git_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu,rg_worker_ms,ms,30,412.6554565,400.9053585125,424.8616591625,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu,cargo_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu,npm_worker_ms,ms,30,581.581061,553.3811230000002,654.9121011625,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu-microvm,ready_ms,ms,30,337.57868399999995,275.98321250000004,384.5440325,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu-microvm,result_ms,ms,30,2874.6530510000002,2660.2120335,3136.7555492999995,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu-microvm,completion_ms,ms,30,3104.840575,2836.525256,3317.196449,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu-microvm,metadata_worker_ms,ms,30,157.30262750000003,143.2223885,167.27193947499998,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu-microvm,read_worker_ms,ms,30,61.369309499999986,46.62113199999999,80.64720150000001,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu-microvm,write_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu-microvm,git_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu-microvm,rg_worker_ms,ms,30,411.1920725,399.75469315,422.941422,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu-microvm,cargo_worker_ms,ms,30,,,,separated distribution; inspect clusters,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" +fo5a4e7d2b-f,B-FS-TOOLS,filesystem,pvisor-vm,qemu-microvm,npm_worker_ms,ms,30,534.437461,501.8885435000001,598.0208625,candidate slower,"5000 paired-round bootstrap resamples, seed 20261006; percentile 95% CI" diff --git a/docs/src/zh/benchmarks/filesystem-provenance.csv b/docs/src/zh/benchmarks/filesystem-provenance.csv new file mode 100644 index 000000000..669ad62fb --- /dev/null +++ b/docs/src/zh/benchmarks/filesystem-provenance.csv @@ -0,0 +1,43 @@ +batch,benchmark_id,field,value +fo5a4e7d2b-f,B-FS-TOOLS,recorded_at,2026-10-12T01:50:38.436534+08:00 +fo5a4e7d2b-f,B-FS-TOOLS,source_commit,b86d521403c1f04cb08cfbde04576365f7e40580 +fo5a4e7d2b-f,B-FS-TOOLS,pvisor_sha256,de4889d932ed8bb5ab376cb09ae43327084a2d017f66db5c54946b78b50446ac +fo5a4e7d2b-f,B-FS-TOOLS,kernel_sha256,aa55fa2dbe2762a55020182560fabd16562d565392505b1986fc51326c741f3d +fo5a4e7d2b-f,B-FS-TOOLS,bzimage_sha256,99de4727b274d92232bd67f9e62b9744e67e99ca89d1bdb09a55e2deb7c80619 +fo5a4e7d2b-f,B-FS-TOOLS,driver_sha256,ac76b9a0a848cd76cda22bec0ccad468297c31a0fb365ef4cccc9d2a8eb73a81 +fo5a4e7d2b-f,B-FS-TOOLS,workload_sha256,e7625cb3d73f09b5f42faa2e64999301a076d37fa66e6477163b8a9a50ec6b6a +fo5a4e7d2b-f,B-FS-TOOLS,host_kernel,7.2.8-200.fc44.x86_64 +fo5a4e7d2b-f,B-FS-TOOLS,host_cpu_model,AMD Ryzen 7 9700X 8-Core Processor +fo5a4e7d2b-f,B-FS-TOOLS,docker_storage_driver,overlay2 +fo5a4e7d2b-f,B-FS-TOOLS,docker_version,"Docker version 29.7.2, build 1.fc44" +fo5a4e7d2b-f,B-FS-TOOLS,firecracker_version,"Firecracker v1.13.1 + +2026-10-12T01:50:38.542183835 [anonymous-instance:main] Firecracker exiting successfully. exit_code=0" +fo5a4e7d2b-f,B-FS-TOOLS,qemu-system-x86_64_version,"QEMU emulator version 10.2.2 (qemu-10.2.2-1.fc44) +Copyright (c) 2003-2025 Fabrice Bellard and the QEMU Project developers" +fo5a4e7d2b-f,B-FS-TOOLS,report_sha256,0127dbdd8a04317be06a0235e5b1cec75dc298b3c6e312f22f1382b8d19f7262 +fo5a4e7d2b-f,B-FS-TOOLS,raw_location,benchmark/.data/fo5a4e7d2b-f/report.json +fo5a4e7d2b-f,B-FS-TOOLS,arguments,"{""backends"": ""native,pvisor-host,pvisor-staged,pvisor-vm,docker,firecracker,qemu,qemu-microvm"", ""cpu_affinity"": ""0,1"", ""host_isolation"": ""rootless_process"", ""memory_mib"": ""16384"", ""modes"": ""filesystem"", ""samples"": ""30"", ""seed"": ""20261012"", ""staged_isolation"": ""rootless_process"", ""warmups"": ""3""}" +fo5a4e7d2b-f,B-FS-TOOLS,statistics,batches separate; P95 descriptive at N>=30; no P99; separated clusters replace P50 +fo5a4e7d2b-f,B-FS-TOOLS,split_rule,"both clusters >=max(5,10% N); largest gap >=20% median and >3x median adjacent gap; cluster medians >=1.5x" +fo5a4e7d2b-f,B-FS-TOOLS,reference_kernels,"{""firecracker"": null, ""qemu"": null, ""qemu-microvm"": null}" +fo5a4e7d2b-f,B-FS-TOOLS,fc_ready_policy,normal +fo5a4e7d2b-f,B-FS-TOOLS,binary_source_commit,3c5c647ee6890b7a310f838f5b80f6751c718575 +fo5a4e7d2b-f,B-FS-TOOLS,binary_source_manifest_sha256,3a8356cfa2370ebfaa4716cbb407d50cf90b172ae1943749bb3efff8f2db4642 +fo5a4e7d2b-f,B-FS-TOOLS,input_manifest_sha256,f33275693e1d765be1901e7ba856ef91a397d622abd29308d57699993ef4e49d +fo5a4e7d2b-f,B-FS-TOOLS,source_commit_scope,runner worktree HEAD; measured binary source is recorded separately +fo5a4e7d2b-f,B-FS-TOOLS,host_guard_sha256,280559af658f371595ea9306a62db8cdc834a2a9e208dd227c83fc992ecfd039 +fo5a4e7d2b-f,B-FS-TOOLS,host_guard_scope,0.5 s scans of visible recognized same-UID builds/tests and foreign KVM FDs outside owned parent; 0 detections; inaccessible/short/unrecognized activity may be missed +fo5a4e7d2b-f,B-FS-TOOLS,namespace_helper_sha256,870d3e91d4ad8596b4b397443c2c2bbd356a3727dc6ba439ec9d77bbd18077e6 +fo5a4e7d2b-f,B-FS-TOOLS,namespace_path_mapping,Private /tmp/private-out aliases canonical raw root; Docker receives host canonical paths for the same physical workspaces +fo5a4e7d2b-f,B-FS-TOOLS,assets_equivalence_sha256,3f3e79bcd19fd000c9040a381598cd9ced030f2cd341194e3085224885cc278a +fo5a4e7d2b-f,B-FS-TOOLS,formal_source_before_sha256,c31e839f68c304b459eb35670d4ba13a3ea4208acb31e0181f6b3bda593f7048 +fo5a4e7d2b-f,B-FS-TOOLS,formal_source_after_sha256,c31e839f68c304b459eb35670d4ba13a3ea4208acb31e0181f6b3bda593f7048 +fo5a4e7d2b-f,B-FS-TOOLS,protocol_sha256,cec2792884d478d88c9c7ae0c7265163ae63c712507419aeab81501efb7b6175 +fo5a4e7d2b-f,B-FS-TOOLS,source_harness,/home/reiase/workspace/pvisor/benchmark.new/filesystem/.data/cross-runtime-20261011T052947Z/build/source/benchmark/pvisor +fo5a4e7d2b-f,B-FS-TOOLS,preflight_batch,/home/reiase/workspace/pvisor/benchmark/.data/fo5a4e7d2b-p (independent; not pooled) +fo5a4e7d2b-f,B-FS-TOOLS,cacheprobe_gate,independent diagnostic only; failures/unknown do not gate B-FS-TOOLS +fo5a4e7d2b-f,B-FS-TOOLS,per_trial_cache_validation,actual parent/child Python flags and absent bytecode prefix; fresh TMPDIR/HOME/Cargo/Node variable inheritance; Node API enable status and backing filesystem type not measured per filesystem trial +fo5a4e7d2b-f,B-FS-TOOLS,node_actual_probe,"{""backends"": {""docker"": {""node_api"": ""unknown"", ""node_filesystem_type"": ""unknown"", ""state"": ""unknown""}, ""firecracker"": {""node_api"": ""unknown"", ""node_filesystem_type"": ""unknown"", ""state"": ""unknown""}, ""native"": {""node_api"": ""unknown"", ""node_filesystem_type"": ""unknown"", ""state"": ""unknown""}, ""pvisor-host"": {""node_api"": ""unknown"", ""node_filesystem_type"": ""unknown"", ""state"": ""unknown""}, ""pvisor-staged"": {""node_api"": ""unknown"", ""node_filesystem_type"": ""unknown"", ""state"": ""unknown""}, ""pvisor-vm"": {""node_api"": ""unknown"", ""node_filesystem_type"": ""unknown"", ""state"": ""unknown""}, ""qemu"": {""node_api"": ""unknown"", ""node_filesystem_type"": ""unknown"", ""state"": ""unknown""}, ""qemu-microvm"": {""node_api"": ""unknown"", ""node_filesystem_type"": ""unknown"", ""state"": ""unknown""}}, ""reason"": ""independent env/cacheprobe not run"", ""scope"": ""independent env only; not a formal acceptance gate or per-filesystem-trial API verification"", ""state"": ""unknown""}" +fo5a4e7d2b-f,B-FS-TOOLS,resource_budget,"{""cpu_period"": 100000, ""cpu_placement"": ""affinity"", ""cpu_quota"": 200000, ""declared_cpu_set"": [0, 1], ""memory_max_bytes"": 17179869184, ""scope"": ""whole selected cgroup and descendants; precharged shared cache outside this parent is excluded"", ""swap_max_bytes"": 0}" +fo5a4e7d2b-f,B-FS-TOOLS,product_defaults,No PVISOR_* overrides; ordinary rootfs/workspace DAX off; selected build writeback/stage sync defaults diff --git a/docs/src/zh/benchmarks/filesystem.csv b/docs/src/zh/benchmarks/filesystem.csv index f91359c12..f36e9ff1a 100644 --- a/docs/src/zh/benchmarks/filesystem.csv +++ b/docs/src/zh/benchmarks/filesystem.csv @@ -1,81 +1,81 @@ benchmark_id,batch,mode,backend,metric,unit,planned,failed,capability,n,p50,p95_reference,minimum,maximum,distribution,low_n,low_p50,high_n,high_p50 -B-FS-TOOLS,6f,filesystem,native,ready_ms,ms,60,0,available,60,38.718969,40.673907,37.40886,46.106537,unsplit,,,, -B-FS-TOOLS,6f,filesystem,native,result_ms,ms,60,0,available,60,438.558489,447.14285685,422.181008,461.528705,unsplit,,,, -B-FS-TOOLS,6f,filesystem,native,completion_ms,ms,60,0,available,60,446.302805,455.78432404999995,429.385781,469.382669,unsplit,,,, -B-FS-TOOLS,6f,filesystem,native,metadata_worker_ms,ms,60,0,available,60,4.641109500000001,5.17389515,4.449222,5.362606,unsplit,,,, -B-FS-TOOLS,6f,filesystem,native,read_worker_ms,ms,60,0,available,60,32.6151435,35.157436749999995,31.506636,53.177359,unsplit,,,, -B-FS-TOOLS,6f,filesystem,native,write_worker_ms,ms,60,0,available,60,3.620475,4.1908692,3.472649,4.591477,unsplit,,,, -B-FS-TOOLS,6f,filesystem,native,git_worker_ms,ms,60,0,available,60,14.503667,15.87029235,13.365548,16.220334,unsplit,,,, -B-FS-TOOLS,6f,filesystem,native,rg_worker_ms,ms,60,0,available,60,7.35332,9.12002745,6.210087,10.598316,unsplit,,,, -B-FS-TOOLS,6f,filesystem,native,cargo_worker_ms,ms,60,0,available,60,51.001313999999994,53.7769879,46.616801,55.215895,unsplit,,,, -B-FS-TOOLS,6f,filesystem,native,npm_worker_ms,ms,60,0,available,60,170.5554055,174.53612955,160.418952,176.741996,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-host,ready_ms,ms,60,0,available,60,50.3116885,51.86005975,48.723523,53.31195,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-host,result_ms,ms,60,0,available,60,446.934048,451.42736494999997,431.481291,463.831691,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-host,completion_ms,ms,60,0,available,60,466.55926999999997,477.7210124,446.443268,489.674762,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-host,metadata_worker_ms,ms,60,0,available,60,4.629473,5.19579745,4.51267,5.306872,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-host,read_worker_ms,ms,60,0,available,60,31.726145,32.139979849999996,30.671117,32.341854,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-host,write_worker_ms,ms,60,0,available,60,3.6607655,4.23614365,3.434579,4.300063,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-host,git_worker_ms,ms,60,0,available,60,14.3027315,15.4673852,13.406535,15.856385,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-host,rg_worker_ms,ms,60,0,available,60,7.2227815,9.18533375,6.311987,9.701521,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-host,cargo_worker_ms,ms,60,0,available,60,50.814181000000005,53.590199600000005,45.446097,55.271089,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-host,npm_worker_ms,ms,60,0,available,60,170.220099,173.9290419,165.183504,176.402917,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-staged,ready_ms,ms,60,0,available,60,62.7551585,64.71813474999999,61.03481,78.609021,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-staged,result_ms,ms,60,0,available,60,855.4340265000001,879.9626576,823.212071,901.514323,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-staged,completion_ms,ms,60,0,available,60,1087.9090965,1133.47125295,1059.776843,1168.342319,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-staged,metadata_worker_ms,ms,60,0,available,60,73.5262865,75.310744,72.107391,76.874656,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-staged,read_worker_ms,ms,60,0,available,60,66.97873,73.16035419999997,64.76068,90.741488,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-staged,write_worker_ms,ms,60,0,available,60,26.2285335,28.0284303,25.085366,28.483614,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-staged,git_worker_ms,ms,60,0,available,60,120.013295,123.4570139,116.284012,125.390948,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-staged,rg_worker_ms,ms,60,0,available,60,84.812746,87.0158754,81.899975,88.733742,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-staged,cargo_worker_ms,ms,60,0,available,60,71.7562705,90.997613,66.586693,97.508324,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-staged,npm_worker_ms,ms,60,0,available,60,226.6667185,235.35751175000001,218.374569,236.520729,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-vm,ready_ms,ms,60,0,available,60,645.5001139999999,666.3695806,627.65156,673.798754,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-vm,result_ms,ms,60,0,available,60,4080.6987475,4389.8365378,3707.093396,4415.390039,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-vm,completion_ms,ms,60,0,available,60,4274.0440905,4597.3515632,3908.572492,4615.848642,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-vm,metadata_worker_ms,ms,60,0,available,60,152.966238,192.58089089999999,113.383179,207.592094,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-vm,read_worker_ms,ms,60,0,available,60,116.44990200000001,126.5072706,113.621026,136.267095,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-vm,write_worker_ms,ms,60,0,available,60,135.157989,160.57990535,95.251008,169.256275,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-vm,git_worker_ms,ms,60,0,available,60,,670.2830111,339.313361,687.730789,separated-clusters,43,352.112209,17,665.459033 -B-FS-TOOLS,6f,filesystem,pvisor-vm,rg_worker_ms,ms,60,0,available,60,434.070974,452.5059095,417.14269,453.968307,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-vm,cargo_worker_ms,ms,60,0,available,60,482.69275600000003,496.9210567,449.864108,498.039945,unsplit,,,, -B-FS-TOOLS,6f,filesystem,pvisor-vm,npm_worker_ms,ms,60,0,available,60,1320.161248,1341.16265765,982.658604,1347.666952,unsplit,,,, -B-FS-TOOLS,6f,filesystem,docker,ready_ms,ms,60,0,available,60,304.26727200000005,318.89538605,289.097849,322.063816,unsplit,,,, -B-FS-TOOLS,6f,filesystem,docker,result_ms,ms,60,0,available,60,743.5170740000001,763.6446859499999,712.560459,773.706595,unsplit,,,, -B-FS-TOOLS,6f,filesystem,docker,completion_ms,ms,60,0,available,60,819.097779,848.1057026000001,785.880926,885.639052,unsplit,,,, -B-FS-TOOLS,6f,filesystem,docker,metadata_worker_ms,ms,60,0,available,60,4.6162475,5.2411318,4.503383,5.286664,unsplit,,,, -B-FS-TOOLS,6f,filesystem,docker,read_worker_ms,ms,60,0,available,60,32.639399,48.5405815,31.293198,58.949029,unsplit,,,, -B-FS-TOOLS,6f,filesystem,docker,write_worker_ms,ms,60,0,available,60,3.8166659999999997,4.389662749999999,3.61746,4.851903,unsplit,,,, -B-FS-TOOLS,6f,filesystem,docker,git_worker_ms,ms,60,0,available,60,14.42278,15.43605755,13.110323,15.679054,unsplit,,,, -B-FS-TOOLS,6f,filesystem,docker,rg_worker_ms,ms,60,0,available,60,6.864732999999999,8.571483899999999,5.467331,9.356478,unsplit,,,, -B-FS-TOOLS,6f,filesystem,docker,cargo_worker_ms,ms,60,0,available,60,48.141624,50.7564907,43.133882,51.876843,unsplit,,,, -B-FS-TOOLS,6f,filesystem,docker,npm_worker_ms,ms,60,0,available,60,215.771378,220.7936918,207.96636,227.922485,unsplit,,,, -B-FS-TOOLS,6f,filesystem,firecracker,ready_ms,ms,60,0,available,60,892.2389115000001,910.39147955,880.805193,971.501006,unsplit,,,, -B-FS-TOOLS,6f,filesystem,firecracker,result_ms,ms,60,0,available,60,2241.676295,2269.6311304,2168.287977,2322.53717,unsplit,,,, -B-FS-TOOLS,6f,filesystem,firecracker,completion_ms,ms,60,0,available,60,2294.594474,2330.73949115,2224.284116,2379.330947,unsplit,,,, -B-FS-TOOLS,6f,filesystem,firecracker,metadata_worker_ms,ms,60,0,available,60,23.4311765,25.91937505,22.497981,26.574197,unsplit,,,, -B-FS-TOOLS,6f,filesystem,firecracker,read_worker_ms,ms,60,0,available,60,79.7937295,80.76123965,78.436668,81.201274,unsplit,,,, -B-FS-TOOLS,6f,filesystem,firecracker,write_worker_ms,ms,60,0,available,60,43.1106805,43.4686728,42.745951,43.627144,unsplit,,,, -B-FS-TOOLS,6f,filesystem,firecracker,git_worker_ms,ms,60,0,available,60,129.974651,131.7618373,81.099614,133.869808,unsplit,,,, -B-FS-TOOLS,6f,filesystem,firecracker,rg_worker_ms,ms,60,0,available,60,15.8222085,17.270146,13.78636,18.510551,unsplit,,,, -B-FS-TOOLS,6f,filesystem,firecracker,cargo_worker_ms,ms,60,0,available,60,373.87753,391.22986235,357.174434,396.021742,unsplit,,,, -B-FS-TOOLS,6f,filesystem,firecracker,npm_worker_ms,ms,60,0,available,60,546.5018875000001,557.1935717499999,511.303333,559.989116,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu,ready_ms,ms,60,0,available,60,509.96446949999995,529.9265078499999,500.582695,573.519603,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu,result_ms,ms,60,0,available,60,1448.6987795,1464.8185786,1399.031148,1519.407691,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu,completion_ms,ms,60,0,available,60,1493.774382,1512.9859668000001,1445.566768,1557.982624,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu,metadata_worker_ms,ms,60,0,available,60,15.9239355,17.25538915,14.682698,18.692158,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu,read_worker_ms,ms,60,0,available,60,37.431223,39.95221694999999,35.397689,41.541492,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu,write_worker_ms,ms,60,0,available,60,5.0229405,5.4307787,4.78764,5.591216,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu,git_worker_ms,ms,60,0,available,60,80.27290400000001,88.06932905,70.208037,89.033935,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu,rg_worker_ms,ms,60,0,available,60,12.572064000000001,14.233104999999998,11.012685,16.505808,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu,cargo_worker_ms,ms,60,0,available,60,260.3383265,265.7006174,246.18703,267.048997,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu,npm_worker_ms,ms,60,0,available,60,399.416952,405.5594957,381.613346,408.54634,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu-microvm,ready_ms,ms,60,0,available,60,395.825773,402.77567065,382.520527,403.880963,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu-microvm,result_ms,ms,60,0,available,60,1450.5053165,1494.7084255,1370.234255,1511.26061,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu-microvm,completion_ms,ms,60,0,available,60,1496.0018344999999,1544.1113959999998,1410.115726,1557.370031,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu-microvm,metadata_worker_ms,ms,60,0,available,60,19.9123065,21.01612415,16.159798,21.152792,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu-microvm,read_worker_ms,ms,60,0,available,60,40.1853485,42.9384862,35.101444,43.804887,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu-microvm,write_worker_ms,ms,60,0,available,60,4.9870885000000005,5.52697975,4.741052,5.674583,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu-microvm,git_worker_ms,ms,60,0,available,60,155.025384,166.07573555,96.507041,174.913846,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu-microvm,rg_worker_ms,ms,60,0,available,60,13.029406999999999,14.6376077,11.522019,15.511221,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu-microvm,cargo_worker_ms,ms,60,0,available,60,296.9366325,307.6363157,275.839424,309.551662,unsplit,,,, -B-FS-TOOLS,6f,filesystem,qemu-microvm,npm_worker_ms,ms,60,0,available,60,421.368042,438.87622930000003,399.675662,440.234561,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,native,ready_ms,ms,30,0,available,30,182.465548,184.66115815,180.50758,185.578294,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,native,result_ms,ms,30,0,available,30,987.9138695,1001.8607265,977.11348,1044.324118,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,native,completion_ms,ms,30,0,available,30,995.803697,1010.01738555,985.049088,1053.914362,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,native,metadata_worker_ms,ms,30,0,available,30,4.7363165,5.1789204,4.50331,5.361775,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,native,read_worker_ms,ms,30,0,available,30,32.178629,34.753323900000005,30.518614,71.186124,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,native,write_worker_ms,ms,30,0,available,30,3.710751,4.9750842,3.545145,12.282307,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,native,git_worker_ms,ms,30,0,available,30,15.5089435,18.981597100000002,14.618774,23.83834,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,native,rg_worker_ms,ms,30,0,available,30,7.832204000000001,9.151287649999999,6.211545,9.758558,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,native,cargo_worker_ms,ms,30,0,available,30,52.961877,59.60838480000001,49.294797,71.030684,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,native,npm_worker_ms,ms,30,0,available,30,212.5488825,219.6670915,207.729456,220.383747,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-host,ready_ms,ms,30,0,available,30,203.8203595,214.81943165,199.809798,223.144231,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-host,result_ms,ms,30,0,available,30,1040.7144525,1093.4281973500001,1027.069683,1112.758687,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-host,completion_ms,ms,30,0,available,30,1065.4718014999999,1128.24778055,1051.502181,1156.259654,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-host,metadata_worker_ms,ms,30,0,available,30,4.7314315,5.080784850000001,4.51399,5.209561,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-host,read_worker_ms,ms,30,0,available,30,32.829995,52.8599144,31.833638,61.459125,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-host,write_worker_ms,ms,30,0,available,30,3.7882759999999998,11.342525250000003,3.590529,41.182301,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-host,git_worker_ms,ms,30,0,available,30,16.672291,20.84634455,14.790798,38.526144,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-host,rg_worker_ms,ms,30,0,available,30,7.850669,9.7832336,6.093914,10.048358,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-host,cargo_worker_ms,ms,30,0,available,30,58.9320695,74.15180705,51.194873,76.845696,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-host,npm_worker_ms,ms,30,0,available,30,225.98711300000002,232.34957545,217.264237,236.371774,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-staged,ready_ms,ms,30,0,available,30,216.4099005,225.30323005,212.352451,226.313958,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-staged,result_ms,ms,30,0,available,30,1528.2822959999999,1584.4384019499998,1477.329154,1624.337424,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-staged,completion_ms,ms,30,0,available,30,1804.251033,1871.2055623,1744.139498,2003.345921,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-staged,metadata_worker_ms,ms,30,0,available,30,82.91090550000001,85.85953435,67.877186,87.453979,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-staged,read_worker_ms,ms,30,0,available,30,77.5371585,81.17731715000001,70.870722,101.522545,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-staged,write_worker_ms,ms,30,0,available,30,32.004799,41.0214825,27.877215,146.34535,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-staged,git_worker_ms,ms,30,0,available,30,163.4594045,189.6700826,147.495196,195.909548,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-staged,rg_worker_ms,ms,30,0,available,30,107.096908,116.3114228,98.449463,117.464964,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-staged,cargo_worker_ms,ms,30,0,available,30,90.0207565,94.58179555000001,81.224402,110.726564,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-staged,npm_worker_ms,ms,30,0,available,30,244.92260249999998,255.76343315,238.350971,265.638176,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-vm,ready_ms,ms,30,0,available,30,752.7288444999999,1090.86519745,713.56419,1467.695682,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-vm,result_ms,ms,30,0,available,30,5162.495756,6125.701362350001,4668.148745,7363.038623,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-vm,completion_ms,ms,30,0,available,30,5459.640924,6395.156288600001,4894.636097,7626.123884,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-vm,metadata_worker_ms,ms,30,0,available,30,177.2839475,235.70400335000002,130.330329,252.913481,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-vm,read_worker_ms,ms,30,0,available,30,135.598514,161.00462620000002,128.525866,250.076331,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-vm,write_worker_ms,ms,30,0,available,30,155.559127,183.81163030000002,140.975503,200.292732,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-vm,git_worker_ms,ms,30,0,available,30,,880.12217625,458.712227,917.710762,separated-clusters,24,503.527761,6,869.803677 +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-vm,rg_worker_ms,ms,30,0,available,30,427.324521,462.2170549,402.203797,480.548157,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-vm,cargo_worker_ms,ms,30,0,available,30,,918.0943852,548.518191,983.825618,separated-clusters,23,579.03501,7,897.466328 +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,pvisor-vm,npm_worker_ms,ms,30,0,available,30,1115.768597,1595.5712444,936.477456,2778.370618,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,docker,ready_ms,ms,30,0,available,30,396.0094235,646.9344247000001,311.611065,1072.09258,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,docker,result_ms,ms,30,0,available,30,1197.7573805,1452.3970861000003,1114.786491,1894.671694,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,docker,completion_ms,ms,30,0,available,30,1265.9014955,1515.6430474000001,1168.358998,1949.623603,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,docker,metadata_worker_ms,ms,30,0,available,30,4.818345,5.32187935,4.663822,5.453125,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,docker,read_worker_ms,ms,30,0,available,30,32.5054265,34.59822705,31.221144,36.343896,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,docker,write_worker_ms,ms,30,0,available,30,3.8697475,4.45431805,3.624247,4.471353,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,docker,git_worker_ms,ms,30,0,available,30,15.711521000000001,16.7675555,14.958961,17.373886,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,docker,rg_worker_ms,ms,30,0,available,30,8.3253815,9.1626135,5.84598,9.857111,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,docker,cargo_worker_ms,ms,30,0,available,30,48.8048745,54.11412685,45.643443,61.830119,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,docker,npm_worker_ms,ms,30,0,available,30,216.831633,221.65962705,212.204345,224.303499,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,firecracker,ready_ms,ms,30,0,available,30,846.637871,865.51308065,838.879562,883.623234,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,firecracker,result_ms,ms,30,0,available,30,2625.3391895000004,2691.9045444999997,2574.05605,2698.165712,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,firecracker,completion_ms,ms,30,0,available,30,2672.6062035,2744.7993392,2616.419595,2750.43502,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,firecracker,metadata_worker_ms,ms,30,0,available,30,21.9709575,24.2486421,21.414358,24.43863,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,firecracker,read_worker_ms,ms,30,0,available,30,80.21735749999999,81.191374,78.859215,83.965081,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,firecracker,write_worker_ms,ms,30,0,available,30,43.005108,44.146844550000004,42.666974,44.957088,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,firecracker,git_worker_ms,ms,30,0,available,30,,138.15357565000002,80.67993,152.140491,separated-clusters,10,82.33407249999999,20,130.9950965 +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,firecracker,rg_worker_ms,ms,30,0,available,30,16.0459205,18.51288275,11.259585,21.360006,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,firecracker,cargo_worker_ms,ms,30,0,available,30,386.11010650000003,421.28405555,359.364871,450.699631,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,firecracker,npm_worker_ms,ms,30,0,available,30,556.7606995,586.09519605,527.486842,589.740707,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu,ready_ms,ms,30,0,available,30,483.92039350000005,730.3337502,472.093352,820.122658,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu,result_ms,ms,30,0,available,30,2149.8479445000003,2475.0982949,1820.90384,2624.344942,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu,completion_ms,ms,30,0,available,30,2191.3163945,2532.19803595,1870.616908,2687.187849,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu,metadata_worker_ms,ms,30,0,available,30,16.743564499999998,24.5816776,14.681197,34.06964,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu,read_worker_ms,ms,30,0,available,30,,96.6300496,36.733986,100.826339,separated-clusters,19,41.182007,11,92.980909 +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu,write_worker_ms,ms,30,0,available,30,,49.50765065,4.811522,55.025523,separated-clusters,22,8.0437125,8,43.339612 +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu,git_worker_ms,ms,30,0,available,30,89.1570595,108.8193965,75.078953,119.699847,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu,rg_worker_ms,ms,30,0,available,30,14.669064500000001,16.5042948,12.284764,16.946815,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu,cargo_worker_ms,ms,30,0,available,30,373.7537235,447.32145765,257.357305,453.279028,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu,npm_worker_ms,ms,30,0,available,30,534.187536,587.56610565,401.274419,610.731741,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu-microvm,ready_ms,ms,30,0,available,30,415.15016049999997,766.3505122,358.240682,784.439592,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu-microvm,result_ms,ms,30,0,available,30,2287.842705,2739.20379145,1824.908763,2851.011222,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu-microvm,completion_ms,ms,30,0,available,30,2354.800349,2790.853572,1873.416963,2896.791151,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu-microvm,metadata_worker_ms,ms,30,0,available,30,19.981319999999997,27.46570415,16.268874,29.255514,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu-microvm,read_worker_ms,ms,30,0,available,30,74.22920450000001,101.97725285,38.323857,108.963527,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu-microvm,write_worker_ms,ms,30,0,available,30,,45.767617200000004,4.85331,54.743675,separated-clusters,17,9.76511,13,43.537202 +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu-microvm,git_worker_ms,ms,30,0,available,30,168.137142,193.96028055000002,107.810104,202.634229,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu-microvm,rg_worker_ms,ms,30,0,available,30,16.1324485,23.8998906,12.319789,32.315682,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu-microvm,cargo_worker_ms,ms,30,0,available,30,430.33756800000003,502.6895008,288.163707,520.255369,unsplit,,,, +B-FS-TOOLS,fo5a4e7d2b-f,filesystem,qemu-microvm,npm_worker_ms,ms,30,0,available,30,581.331136,668.5946378,442.379572,707.33894,unsplit,,,, diff --git a/docs/src/zh/benchmarks/filesystem.md b/docs/src/zh/benchmarks/filesystem.md index 28e023465..a8373629c 100644 --- a/docs/src/zh/benchmarks/filesystem.md +++ b/docs/src/zh/benchmarks/filesystem.md @@ -2,7 +2,7 @@ ## 主要结论 {#conclusions} -**七项工具到退出,pVisor host 为 0.47 s、staged 为 1.09 s、VM 为 4.27 s;Docker 为 0.82 s,Firecracker 为 2.29 s,QEMU microvm 为 1.50 s。staged 的审查能力有文件访问成本,VM 工具成本仍明显。** +**在两核、热宿主缓存、每任务独立工具缓存下,七项工具到退出:pVisor host 1.07 s,staged 1.80 s,VM 5.46 s;Docker 1.27 s,Firecracker 2.67 s,QEMU microvm 2.35 s。按完整任务与单项工具分别预算等待;分离簇按各簇中位数和数量展示。** | 需求 | 选型含义 | | --- | --- | @@ -10,7 +10,13 @@ | 独立 guest 内核 | 同时预算启动和 VM 工具等待 | | 并发或闲置环境 | 需要固定资源下的吞吐与物理内存实测 | -上述耗时来自 2026-10-06 的跨运行时基线,未计入后续缓存优化。当前缓存实验尚未建立默认 staged、VM 或完整审查任务的新性能结论,选型仍应使用这组基线。 +这是 2026-10-12 使用所选冻结源码和制品、通过完整验收的跨运行时七工具对照。本批普通 rootfs/workspace 不启用 DAX,新 VM 写合并默认关闭,stage 同步默认串行。历史批次分别保留;隔离、缓存、listener 和制品条件不同,不计算跨批次优化加速比例。 + + + +已验收批次 `fo5a4e7d2b-f`:八个后端各 30 次,七工具内部阶段与完整任务分别报告。历史数据和工程 A/B 分别保留,不合并样本。 + + ## Motivation {#motivation} @@ -18,56 +24,74 @@ Agent 的工具循环需要遍历、读写、搜索、编译和安装依赖。 ## 实验设计 {#interpretation} -Linux x86_64,AMD Ryzen 7 9700X,Fedora 7.2.8-200.fc44.x86_64。工具负载与专用 Docker daemon 固定到 CPU 0,1;容器初始化进程全生命周期的 CPU 亲和性未验证。VM 为 2 vCPU,shell 探针配置 128 MiB,工具任务配置 16 GiB。原生/Docker 未限制内存,因此不能推导相同资源预算下的容量。host/staged 使用 rootless_process。 +Linux x86_64,AMD Ryzen 7 9700X,Fedora 7.2.8-200.fc44.x86_64,Btrfs 工作区。协调器、负载与专用 rootless Docker daemon 固定到 CPU 0,1,并在同一独立 cgroup 父组内随机交替执行:总 CPU quota 为两核、内存上限 16 GiB、禁用 swap。各 VM 为 2 vCPU、16 GiB guest RAM;guest 配置容量与整组宿主上限不同。启动该父组前已计费的共享工具/镜像缓存在父组之外;进程与线程检查是可见时刻的观测,未证明所有短生命周期进程的完整 CPU 归属。host/staged 使用 rootless_process。协调器通过私有 user/mount namespace 与 owned pivot_root 隔离宿主已有 listener;namespace 准备不计时,专属 listener 在预检和预热后复用。私有 /tmp 为 2 GiB tmpfs,工作区与 stage 仍为 Btrfs;Docker bind 指向相同物理目录。用户 cgroup 未委托 cpuset,使用继承 affinity 与两核 quota,保留可见进程与线程的观测边界。 -同一套离线工具与固定输入,每次新建工作区;热缓存、3 次预热、每格 60 次正式采样,固定种子随机交替执行。环境准备、构建、镜像导入和输入重置不计时;启动和退出计入完整任务。Docker Engine 29.7.2 使用专用 rootless **overlay2** daemon、经典镜像存储和可写 bind mount;Firecracker 1.13.1 PCI 不使用 jailer,QEMU 10.2.2 分别使用 q35/microvm 与私有 ext4。pVisor VM 使用 virtio-fs 和自己的固件。内核、存储和暂存语义不同,结果是这些配置下的任务成本,不是纯 VMM 或安全排名。 +同一套离线工具与固定输入,每次新建工作区;热宿主缓存、3 次预热、每格 30 次正式采样,seed 20261012 随机交替后端。环境准备、构建、镜像导入和输入重置不计时;启动和退出计入完整任务。保留执行器提供的 TMPDIR,在其中为每任务新建私有 HOME 与 Cargo 缓存,并配置 `NODE_COMPILE_CACHE` 环境变量;Python 不读写 bytecode。七项操作的 Cargo 使用工作区内独立的 `_cargo-home`、`_tmp` 和 `_cargo-target`。临时存储随执行器配置不同,属于任务成本的一部分;目录、策略与子进程继承均校验。 逐次门禁验证缓存环境变量、父/子继承及 Python 的实际缓存行为;本批实际 Node API 启用状态和 Node 缓存目录文件系统类型均未知。10 月 10 日的独立环境检查观测到 native/host/staged/pVisor VM 的 Node 缓存使用 tmpfs,Docker 使用 overlayfs;参考 VM 输出为 ext4,但环境探测的 Ready 门禁未通过。类型与失败状态在[缓存观察 CSV](filesystem-cache-policy.csv)中分开记录,这份历史环境探测不提供本次计时或新的缓存类型证据;本批逐次校验缓存目录与父/子继承。 -负载为 32 个目录中的 2,048 文件遍历、64 MiB 读取和 SHA256 校验、256 × 64 KiB 写入、git status、rg、64 个无外部依赖 Cargo 模块和 32 个离线 npm 包。单项含校验,不含启动与退出;完整任务包括七项和退出。大型仓库、冷磁盘、在线 registry 和并发吞吐未测。 +Docker Engine 29.7.2 使用专用 rootless overlay2 daemon、经典镜像存储和可写 bind mount;Firecracker PCI 使用冻结的独立定制参考内核、不使用 jailer,QEMU 分别使用 q35/microvm、相同参考内核与私有 ext4。pVisor VM 使用 virtio-fs 与冻结的 pVisor 固件。这里的 Firecracker/QEMU 工具数据没有测量 stock 发行版内核,不能用[独立 stock 启动结果](startup.md)替代。内核、存储和暂存语义不同,结果是这些配置下的任务成本,不是纯 VMM 或安全排名。 -这些样本未验证 Node/npm 编译缓存跨后端一致。新工作区不等于空工具缓存;npm 与完整任务的差值包含各配置下的缓存行为,不能全部归因于 FUSE、stage 或 VMM。 +负载为 32 个目录中的 2,048 文件遍历、64 MiB 读取和 SHA256 校验、256 × 64 KiB 写入、git status、rg、64 个无外部依赖 Cargo 模块和 32 个离线 npm 包。单项不含启动与退出,遍历、读取等包含负载内校验;写入后的完整字节验证在写入计时之外,仍计入完整任务。完整任务包括七项、校验和退出。大型仓库、冷磁盘、在线 registry 和并发吞吐未测。 ## 实验数据和分析 {#results} -测于 2026-10-06,每个后端/负载 60/60 有效,正式失败 0。输出、退出和执行器记录必须通过校验;暂存模式还验证宿主原文件不变,以及 upper 中 256 个写入文件的数量和大小,未独立校验全部写入字节。保留所有有效慢样本,没有按耗时剔除。表格通常为 P50;分离分布展示各簇中位数和数量,P95 仅作观察参考。原始报告、二进制、输入与源码摘要保存在忽略的 `.data/`,公开 CSV 保留负载、批次和来源关联。 +测于 2026-10-12,每个后端/负载 30/30 有效,共 240 个正式任务,正式失败 0。另有能力检查与预热,均不计入统计。每个样本必须通过唯一输出、退出和执行器记录校验;staged/VM 还要求宿主工作区没有出现写入结果,upper 中全部 256 个文件的完整内容匹配。共同输入清单在采样前后校验,缓存策略在父/子负载中校验。任何错误或监测到的同用户 Rust 构建/Python 测试干扰拒绝整批;未检出这类干扰,不按耗时剔除有效慢样本。0.5 s 的进程轮询可能漏掉短暂、不可见或未识别的宿主活动。 + +### 启动到退出 {#complete-task} + +单位 s;每个后端 N=30。通常为 P50,分离分布展示各簇中位数与数量;P95 仅作观察参考,不报告 P99。 + +| Runtime | 有效 / 失败 | 完整任务 P50 s | 完整任务 P95 s(参考) | +| --- | --- | --- | --- | +| Native | 30 / 0 | 1.00 | 1.01 | +| pVisor host | 30 / 0 | 1.07 | 1.13 | +| pVisor staged | 30 / 0 | 1.80 | 1.87 | +| pVisor VM | 30 / 0 | 5.46 | 6.40 | +| Docker rootless / overlay2 | 30 / 0 | 1.27 | 1.52 | +| Firecracker PCI | 30 / 0 | 2.67 | 2.74 | +| QEMU q35 | 30 / 0 | 2.19 | 2.53 | +| QEMU microvm | 30 / 0 | 2.35 | 2.79 | + +| 同批完整任务对照 | 中位数差 ms | 95% CI ms | 结论 | +| --- | --- | --- | --- | +| pVisor staged − Docker rootless / overlay2 | +538.35 | [+497.98, +570.62] | 更慢 | +| pVisor VM − QEMU microvm | +3104.84 | [+2836.53, +3317.20] | 更慢 | + +pVisor staged 相对 Docker rootless / overlay2的完整任务中位数差为 +538.35 ms,95% CI [+497.98, +570.62] ms;pVisor VM 相对 QEMU microvm的完整任务中位数差为 +3104.84 ms,95% CI [+2836.53, +3317.20] ms。区间来自同一批 5,000 次按轮次配对的 bootstrap(seed 20261006);有效慢样本保留,分离分布不作单一中位数排名。存储配置与暂存能力不同,总差距不能归因于某一个组件。 ### 七项工具操作 {#reference-fs} -单位 ms;P50 或分离簇的中位数与数量。 +单位 ms,每个后端/操作 N=30;P50 或分离簇的中位数与数量。操作时间排除启动与收尾;不能把七项中位数相加替代完整任务时间。 -| Operation | Native | pVisor host | pVisor staged | pVisor VM | Docker rootless / overlay2 | Firecracker PCI | QEMU q35 | QEMU microvm | +| 操作 | Native | pVisor host | pVisor staged | pVisor VM | Docker rootless / overlay2 | Firecracker PCI | QEMU q35 | QEMU microvm | | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| 遍历 2,048 文件 | 4.64 | 4.63 | 73.53 | 152.97 | 4.62 | 23.43 | 15.92 | 19.91 | -| 读取并校验 64 MiB | 32.62 | 31.73 | 66.98 | 116.45 | 32.64 | 79.79 | 37.43 | 40.19 | -| 写入 256 文件 | 3.62 | 3.66 | 26.23 | 135.16 | 3.82 | 43.11 | 5.02 | 4.99 | -| git status | 14.50 | 14.30 | 120.01 | 352.11 (43/60); 665.46 (17/60) | 14.42 | 129.97 | 80.27 | 155.03 | -| Ripgrep 搜索 | 7.35 | 7.22 | 84.81 | 434.07 | 6.86 | 15.82 | 12.57 | 13.03 | -| 离线 Cargo 编译 | 51.00 | 50.81 | 71.76 | 482.69 | 48.14 | 373.88 | 260.34 | 296.94 | -| 离线 npm 安装 | 170.56 | 170.22 | 226.67 | 1320.16 | 215.77 | 546.50 | 399.42 | 421.37 | - -### 启动到退出 {#complete-task} +| 遍历 2,048 文件 | 4.74 | 4.73 | 82.91 | 177.28 | 4.82 | 21.97 | 16.74 | 19.98 | +| 读取并校验 64 MiB | 32.18 | 32.83 | 77.54 | 135.60 | 32.51 | 80.22 | 41.18 (n=19); 92.98 (n=11) | 74.23 | +| 写入 256 文件 | 3.71 | 3.79 | 32.00 | 155.56 | 3.87 | 43.01 | 8.04 (n=22); 43.34 (n=8) | 9.77 (n=17); 43.54 (n=13) | +| git status | 15.51 | 16.67 | 163.46 | 503.53 (n=24); 869.80 (n=6) | 15.71 | 82.33 (n=10); 131.00 (n=20) | 89.16 | 168.14 | +| Ripgrep 搜索 | 7.83 | 7.85 | 107.10 | 427.32 | 8.33 | 16.05 | 14.67 | 16.13 | +| 离线 Cargo 编译 | 52.96 | 58.93 | 90.02 | 579.04 (n=23); 897.47 (n=7) | 48.80 | 386.11 | 373.75 | 430.34 | +| 离线 npm 安装 | 212.55 | 225.99 | 244.92 | 1115.77 | 216.83 | 556.76 | 534.19 | 581.33 | -| Runtime | Valid / failed | Completion P50 s | Completion P95 s | -| --- | --- | --- | --- | -| Native | 60 / 0 | 0.45 | 0.46 | -| pVisor host | 60 / 0 | 0.47 | 0.48 | -| pVisor staged | 60 / 0 | 1.09 | 1.13 | -| pVisor VM | 60 / 0 | 4.27 | 4.60 | -| Docker rootless / overlay2 | 60 / 0 | 0.82 | 0.85 | -| Firecracker PCI | 60 / 0 | 2.29 | 2.33 | -| QEMU q35 | 60 / 0 | 1.49 | 1.51 | -| QEMU microvm | 60 / 0 | 1.50 | 1.54 | -staged 相对 Docker 的完整任务中位数差为 +268.81 ms,95% 配对 bootstrap 区间 [+264.64, +271.15] ms。VM 相对 QEMU microvm 为 +2778.04 ms,区间 [+2754.17, +2807.74] ms。暂存能力与存储配置均有差异,不能从总差距推断某一组件的成本。 +文件遍历、Git 与搜索需要经过暂存文件视图;读取、编译和离线安装的成本还包含校验、工具运行与各执行器的临时存储行为。按实际工具组合和完整任务等待设置超时,不能仅凭启动速度估计总成本。 -完整 Ubuntu 文件负载在当前制品下尚未复测。 +完整 Ubuntu 文件负载尚未在这组当前构建与资源条件下测量。 -### 缓存优化的适用范围 {#cache-status} +### 文件系统优化的适用范围 {#cache-status} -不可变 lower 的物理元数据缓存已有独立工程 A/B,收益限定于显式声明并保持 lower 稳定的 Linux HOST FUSE 配置。宿主 rootfs、OCI 解包目录与 lazy 镜像的本地投影不自动获得不变性承诺。该实验未覆盖完整审查流程,不能用于折算上表 staged 或 VM 的耗时;实验设计、结果与置信区间见[不可变 lower 缓存分析](../design/filesystem-performance-analysis.md#immutable-lower-cache)。 +表中冻结构建包含宿主 copy-up 调度、读队列、journal 锁、空闲调度与后续 VM 队列/I/O 屏障修复;独立工程 A/B 见[完整证据范围](../design/filesystem-performance-analysis.md#filesystem-status)。跨运行时结果衡量当前默认配置的任务成本,局部工程 A/B 的百分比不用于折算上述数据。 -扩展内核缓存由 Linux HOST API 显式开启:可写长 metadata 缓存要求不可变 lower、独占 upper/work 和稳定 backing 元数据;KEEP_CACHE 仅支持稳定只读普通文件。配置不兼容 journal、读取观察指标或自定义路径策略时会拒绝启用,不会静默省略观察。当前版本的正式计时受并行构建检查干扰,尚无验收通过的加速幅度;未接入默认 `pvisor run` 或 VM。能力边界与验证状态见[内核缓存分析](../design/filesystem-performance-analysis.md#host-kernel-cache)。 +本批已用当前冻结制品重新测量全部八个后端,并完成正确性、输入、预算和干扰门禁。此前未完成验收的尝试继续单独保留,不进入本页统计;这份新跨运行时结果本身不证明每项修改的因果收益。 + +普通 rootfs/workspace 默认关闭 DAX,不创建 DAX 窗口;不可变 share 保留显式启用入口,见[DAX 对照](../design/filesystem-performance-analysis.md#readonly-dax-ab)。新建 VM 的写合并默认关闭,可用 `PVISOR_VM_FS_WRITEBACK=1` / `0` 显式配置;默认恢复已协商快照时保留原模式,显式 `PVISOR_VM_FS_WRITEBACK=0` 会拒绝已开启写合并的快照。独立[写合并对照](../design/filesystem-performance-analysis.md#writeback-ab)使用其自身冻结制品,不能用于折算本批任务时间。 + +不可变 lower 的物理元数据缓存要求调用者显式声明并保持 lower 稳定;宿主 rootfs、OCI 解包目录与 lazy 镜像本地投影不自动获得该承诺。扩展 Linux HOST 内核缓存尚未接入默认 `pvisor run` 或 VM,其正式计时没有通过验收。能力边界见[不可变 lower 缓存分析](../design/filesystem-performance-analysis.md#immutable-lower-cache)与[内核缓存分析](../design/filesystem-performance-analysis.md#host-kernel-cache)。 + +`PVISOR_STAGE_SYNC_WORKERS` 的并行 stage 同步选项未开启,仍没有独立的串行/并行性能对照;不能把该实现的存在解释为上表的加速来源。 ### 数据下载与复现 {#run} -[整理后的统计 CSV](filesystem.csv) · [全部运行时统计](runtime-summary.csv) · [差异与 95% 置信区间](runtime-comparisons.csv) · [源码与制品来源](runtime-provenance.csv) · [比较方法](methodology.md) · [复现手册](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/pvisor/README.md) +[已验收文件系统统计 CSV](filesystem.csv) · [同批差异与 95% 置信区间](filesystem-comparisons.csv) · [测量制品、源码与输入来源](filesystem-provenance.csv) · [验收记录](../assets/benchmarks/filesystem-publication-audit.json) · [比较方法](methodology.md) · [复现手册](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/pvisor/README.md#same-host-runtime-comparison)与[整组资源预算](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/pvisor/README.md#explicit-whole-parent-resource-budget) + +本批原始证据为本地 `benchmark/.data/fo5a4e7d2b-f/report.json`,冻结源码与构建收据在 `benchmark.new/filesystem/.data/fs-next-20261011/trim/`,执行/验收记录在 `benchmark.new/filesystem/.data/fs-docs-20261012/`;原始样本与制品不作为站点下载。此前有效批次和失败尝试分别保留,见[来源清单](evidence-sources.csv);历史[运行时统计](runtime-summary.csv)和[来源](runtime-provenance.csv)不与本批合并。 diff --git a/docs/src/zh/benchmarks/index.csv b/docs/src/zh/benchmarks/index.csv index 62aca573c..d38580dd9 100644 --- a/docs/src/zh/benchmarks/index.csv +++ b/docs/src/zh/benchmarks/index.csv @@ -1,8 +1,8 @@ source_document,section,source_conditions,table_context,row,metric,value -index.md,主要结论 {#conclusions},"复用[启动](startup.md)、[文件系统](filesystem.md)、[修复任务](agent-tasks.md)和[完整审查流程](supervision-cost.md)的独立注册实验。前三类每后端 60 次、3 次预热;审查流程每规模/条件/后端 30 次、3 次预热。同机 CPU 0,1、热缓存、随机交替执行,全部通过正确性校验,没有按速度剔除。各主题的负载、资源和计时边界不同,不合并其分布。Docker 使用 rootless overlay2;完整审查流程对照是原生 Git/reflink,不是容器或 VM 安全排名。","**频繁创建大工作区、只合入少量改动时,pVisor stage 的完整机器流程更快:10,000 文件中修改 20 个、保留 10 个,stage 为 141 ms,Git worktree 为 248 ms,btrfs reflink 为 343 ms。小工作区原生流程更快;pVisor VM 的工具任务仍慢于所测 Firecracker/QEMU。**",大工作区、稀疏改动、用后丢弃,选型含义,stage 的完整流程有实测优势 -index.md,主要结论 {#conclusions},"复用[启动](startup.md)、[文件系统](filesystem.md)、[修复任务](agent-tasks.md)和[完整审查流程](supervision-cost.md)的独立注册实验。前三类每后端 60 次、3 次预热;审查流程每规模/条件/后端 30 次、3 次预热。同机 CPU 0,1、热缓存、随机交替执行,全部通过正确性校验,没有按速度剔除。各主题的负载、资源和计时边界不同,不合并其分布。Docker 使用 rootless overlay2;完整审查流程对照是原生 Git/reflink,不是容器或 VM 安全排名。","**频繁创建大工作区、只合入少量改动时,pVisor stage 的完整机器流程更快:10,000 文件中修改 20 个、保留 10 个,stage 为 141 ms,Git worktree 为 248 ms,btrfs reflink 为 343 ms。小工作区原生流程更快;pVisor VM 的工具任务仍慢于所测 Firecracker/QEMU。**",小工作区或只关心工具运行,选型含义,比较原生流程与容器成本 -index.md,主要结论 {#conclusions},"复用[启动](startup.md)、[文件系统](filesystem.md)、[修复任务](agent-tasks.md)和[完整审查流程](supervision-cost.md)的独立注册实验。前三类每后端 60 次、3 次预热;审查流程每规模/条件/后端 30 次、3 次预热。同机 CPU 0,1、热缓存、随机交替执行,全部通过正确性校验,没有按速度剔除。各主题的负载、资源和计时边界不同,不合并其分布。Docker 使用 rootless overlay2;完整审查流程对照是原生 Git/reflink,不是容器或 VM 安全排名。","**频繁创建大工作区、只合入少量改动时,pVisor stage 的完整机器流程更快:10,000 文件中修改 20 个、保留 10 个,stage 为 141 ms,Git worktree 为 248 ms,btrfs reflink 为 343 ms。小工作区原生流程更快;pVisor VM 的工具任务仍慢于所测 Firecracker/QEMU。**",VM 或高并发容量,选型含义,需要完整工具和资源实测 -index.md,实验数据和分析 {#results},"复用[启动](startup.md)、[文件系统](filesystem.md)、[修复任务](agent-tasks.md)和[完整审查流程](supervision-cost.md)的独立注册实验。前三类每后端 60 次、3 次预热;审查流程每规模/条件/后端 30 次、3 次预热。同机 CPU 0,1、热缓存、随机交替执行,全部通过正确性校验,没有按速度剔除。各主题的负载、资源和计时边界不同,不合并其分布。Docker 使用 rootless overlay2;完整审查流程对照是原生 Git/reflink,不是容器或 VM 安全排名。","2026-10-06; 启动、文件系统和修复共 1,440 个有效样本,完整审查流程 360 个,失败均为 0。中位数差异和 95% 配对 bootstrap 区间见各专题。",[Startup](startup.md),P50 水位,host 12.72 ms; staged 25.13 ms; VM 100.73 ms; Docker 74.48 ms -index.md,实验数据和分析 {#results},"复用[启动](startup.md)、[文件系统](filesystem.md)、[修复任务](agent-tasks.md)和[完整审查流程](supervision-cost.md)的独立注册实验。前三类每后端 60 次、3 次预热;审查流程每规模/条件/后端 30 次、3 次预热。同机 CPU 0,1、热缓存、随机交替执行,全部通过正确性校验,没有按速度剔除。各主题的负载、资源和计时边界不同,不合并其分布。Docker 使用 rootless overlay2;完整审查流程对照是原生 Git/reflink,不是容器或 VM 安全排名。","2026-10-06; 启动、文件系统和修复共 1,440 个有效样本,完整审查流程 360 个,失败均为 0。中位数差异和 95% 配对 bootstrap 区间见各专题。",[Repair completion](agent-tasks.md),P50 水位,staged 0.64 s; Docker 0.81 s; VM 3.25 s; QEMU microvm 1.40 s -index.md,实验数据和分析 {#results},"复用[启动](startup.md)、[文件系统](filesystem.md)、[修复任务](agent-tasks.md)和[完整审查流程](supervision-cost.md)的独立注册实验。前三类每后端 60 次、3 次预热;审查流程每规模/条件/后端 30 次、3 次预热。同机 CPU 0,1、热缓存、随机交替执行,全部通过正确性校验,没有按速度剔除。各主题的负载、资源和计时边界不同,不合并其分布。Docker 使用 rootless overlay2;完整审查流程对照是原生 Git/reflink,不是容器或 VM 安全排名。","2026-10-06; 启动、文件系统和修复共 1,440 个有效样本,完整审查流程 360 个,失败均为 0。中位数差异和 95% 配对 bootstrap 区间见各专题。",[Seven-tool completion](filesystem.md),P50 水位,staged 1.09 s; Docker 0.82 s; VM 4.27 s; Firecracker 2.29 s -index.md,实验数据和分析 {#results},"复用[启动](startup.md)、[文件系统](filesystem.md)、[修复任务](agent-tasks.md)和[完整审查流程](supervision-cost.md)的独立注册实验。前三类每后端 60 次、3 次预热;审查流程每规模/条件/后端 30 次、3 次预热。同机 CPU 0,1、热缓存、随机交替执行,全部通过正确性校验,没有按速度剔除。各主题的负载、资源和计时边界不同,不合并其分布。Docker 使用 rootless overlay2;完整审查流程对照是原生 Git/reflink,不是容器或 VM 安全排名。","2026-10-06; 启动、文件系统和修复共 1,440 个有效样本,完整审查流程 360 个,失败均为 0。中位数差异和 95% 配对 bootstrap 区间见各专题。",[Review workflow](supervision-cost.md),P50 水位,"10,000 files: stage 141 ms; Git 248 ms; reflink 343 ms" +index.md,主要结论 {#conclusions},"复用[启动](startup.md)、[文件系统](filesystem.md)、[修复任务](agent-tasks.md)和[完整审查流程](supervision-cost.md)的独立注册实验。启动与修复每后端 60 次、文件系统每后端 30 次,均为 3 次预热;审查流程每规模/条件/后端 30 次、3 次预热。同机 CPU 0,1、热缓存、随机交替执行,全部通过正确性校验,没有按速度剔除。各主题的负载、资源和计时边界不同,不合并其分布。Docker 使用 rootless overlay2;完整审查流程对照是原生 Git/reflink,不是容器或 VM 安全排名。","2026-10-06 的启动、文件系统和修复共 1,440 个有效样本,完整审查流程 360 个;2026-10-07 的独立 stock 内核启动对照为 240 个有效样本,失败均为 0。2026-10-11 当前源码文件系统对照另有 240 个有效样本、失败 0,使用每任务独立缓存与统一父组资源约束。各批次不合并,中位数差异和 95% 配对 bootstrap 区间见各专题。",大工作区、稀疏改动、用后丢弃,选型含义,stage 的完整流程有实测优势 +index.md,主要结论 {#conclusions},"复用[启动](startup.md)、[文件系统](filesystem.md)、[修复任务](agent-tasks.md)和[完整审查流程](supervision-cost.md)的独立注册实验。启动与修复每后端 60 次、文件系统每后端 30 次,均为 3 次预热;审查流程每规模/条件/后端 30 次、3 次预热。同机 CPU 0,1、热缓存、随机交替执行,全部通过正确性校验,没有按速度剔除。各主题的负载、资源和计时边界不同,不合并其分布。Docker 使用 rootless overlay2;完整审查流程对照是原生 Git/reflink,不是容器或 VM 安全排名。","2026-10-06 的启动、文件系统和修复共 1,440 个有效样本,完整审查流程 360 个;2026-10-07 的独立 stock 内核启动对照为 240 个有效样本,失败均为 0。2026-10-11 当前源码文件系统对照另有 240 个有效样本、失败 0,使用每任务独立缓存与统一父组资源约束。各批次不合并,中位数差异和 95% 配对 bootstrap 区间见各专题。",小工作区或只关心工具运行,选型含义,比较原生流程与容器成本 +index.md,主要结论 {#conclusions},"复用[启动](startup.md)、[文件系统](filesystem.md)、[修复任务](agent-tasks.md)和[完整审查流程](supervision-cost.md)的独立注册实验。启动与修复每后端 60 次、文件系统每后端 30 次,均为 3 次预热;审查流程每规模/条件/后端 30 次、3 次预热。同机 CPU 0,1、热缓存、随机交替执行,全部通过正确性校验,没有按速度剔除。各主题的负载、资源和计时边界不同,不合并其分布。Docker 使用 rootless overlay2;完整审查流程对照是原生 Git/reflink,不是容器或 VM 安全排名。","2026-10-06 的启动、文件系统和修复共 1,440 个有效样本,完整审查流程 360 个;2026-10-07 的独立 stock 内核启动对照为 240 个有效样本,失败均为 0。2026-10-11 当前源码文件系统对照另有 240 个有效样本、失败 0,使用每任务独立缓存与统一父组资源约束。各批次不合并,中位数差异和 95% 配对 bootstrap 区间见各专题。",VM 或高并发容量,选型含义,需要完整工具和资源实测 +index.md,实验数据和分析 {#results},"复用[启动](startup.md)、[文件系统](filesystem.md)、[修复任务](agent-tasks.md)和[完整审查流程](supervision-cost.md)的独立注册实验。启动与修复每后端 60 次、文件系统每后端 30 次,均为 3 次预热;审查流程每规模/条件/后端 30 次、3 次预热。同机 CPU 0,1、热缓存、随机交替执行,全部通过正确性校验,没有按速度剔除。各主题的负载、资源和计时边界不同,不合并其分布。Docker 使用 rootless overlay2;完整审查流程对照是原生 Git/reflink,不是容器或 VM 安全排名。","2026-10-06 的启动、文件系统和修复共 1,440 个有效样本,完整审查流程 360 个;2026-10-07 的独立 stock 内核启动对照为 240 个有效样本,失败均为 0。2026-10-11 当前源码文件系统对照另有 240 个有效样本、失败 0,使用每任务独立缓存与统一父组资源约束。各批次不合并,中位数差异和 95% 配对 bootstrap 区间见各专题。",[Startup](startup.md),P50 水位,host 12.72 ms; staged 25.13 ms; VM 100.73 ms; Docker 74.48 ms +index.md,实验数据和分析 {#results},"复用[启动](startup.md)、[文件系统](filesystem.md)、[修复任务](agent-tasks.md)和[完整审查流程](supervision-cost.md)的独立注册实验。启动与修复每后端 60 次、文件系统每后端 30 次,均为 3 次预热;审查流程每规模/条件/后端 30 次、3 次预热。同机 CPU 0,1、热缓存、随机交替执行,全部通过正确性校验,没有按速度剔除。各主题的负载、资源和计时边界不同,不合并其分布。Docker 使用 rootless overlay2;完整审查流程对照是原生 Git/reflink,不是容器或 VM 安全排名。","2026-10-06 的启动、文件系统和修复共 1,440 个有效样本,完整审查流程 360 个;2026-10-07 的独立 stock 内核启动对照为 240 个有效样本,失败均为 0。2026-10-11 当前源码文件系统对照另有 240 个有效样本、失败 0,使用每任务独立缓存与统一父组资源约束。各批次不合并,中位数差异和 95% 配对 bootstrap 区间见各专题。",[Repair completion](agent-tasks.md),P50 水位,staged 0.64 s; Docker 0.81 s; VM 3.25 s; QEMU microvm 1.40 s +index.md,实验数据和分析 {#results},"复用[启动](startup.md)、[文件系统](filesystem.md)、[修复任务](agent-tasks.md)和[完整审查流程](supervision-cost.md)的独立注册实验。启动与修复每后端 60 次、文件系统每后端 30 次,均为 3 次预热;审查流程每规模/条件/后端 30 次、3 次预热。同机 CPU 0,1、热缓存、随机交替执行,全部通过正确性校验,没有按速度剔除。各主题的负载、资源和计时边界不同,不合并其分布。Docker 使用 rootless overlay2;完整审查流程对照是原生 Git/reflink,不是容器或 VM 安全排名。","2026-10-06 的启动、文件系统和修复共 1,440 个有效样本,完整审查流程 360 个;2026-10-07 的独立 stock 内核启动对照为 240 个有效样本,失败均为 0。2026-10-11 当前源码文件系统对照另有 240 个有效样本、失败 0,使用每任务独立缓存与统一父组资源约束。各批次不合并,中位数差异和 95% 配对 bootstrap 区间见各专题。",[Seven-tool completion](filesystem.md),P50 水位,staged 1.81 s; Docker 1.23 s; VM 5.78 s; Firecracker 2.65 s +index.md,实验数据和分析 {#results},"复用[启动](startup.md)、[文件系统](filesystem.md)、[修复任务](agent-tasks.md)和[完整审查流程](supervision-cost.md)的独立注册实验。启动与修复每后端 60 次、文件系统每后端 30 次,均为 3 次预热;审查流程每规模/条件/后端 30 次、3 次预热。同机 CPU 0,1、热缓存、随机交替执行,全部通过正确性校验,没有按速度剔除。各主题的负载、资源和计时边界不同,不合并其分布。Docker 使用 rootless overlay2;完整审查流程对照是原生 Git/reflink,不是容器或 VM 安全排名。","2026-10-06 的启动、文件系统和修复共 1,440 个有效样本,完整审查流程 360 个;2026-10-07 的独立 stock 内核启动对照为 240 个有效样本,失败均为 0。2026-10-11 当前源码文件系统对照另有 240 个有效样本、失败 0,使用每任务独立缓存与统一父组资源约束。各批次不合并,中位数差异和 95% 配对 bootstrap 区间见各专题。",[Review workflow](supervision-cost.md),P50 水位,"10,000 files: stage 141 ms; Git 248 ms; reflink 343 ms" diff --git a/docs/src/zh/benchmarks/index.md b/docs/src/zh/benchmarks/index.md index a4ab24451..2ad1e5413 100644 --- a/docs/src/zh/benchmarks/index.md +++ b/docs/src/zh/benchmarks/index.md @@ -10,13 +10,19 @@ | 小工作区或只关心工具运行 | 比较原生流程与容器成本 | | VM 或高并发容量 | 需要完整工具和资源实测 | + + +总览的文件系统一行来自 2026-10-12 已验收批次 `fo5a4e7d2b-f`,其他主题继续使用各自批次。各专题的计时边界和制品分别列出,见[复测覆盖](#measurement-coverage)。 + + + ## Motivation {#motivation} 选型需要知道完成同样结果的总成本,而不只是启动或单个命令。审查合入、执行边界与资源占用决定哪类 Agent 工作适合 pVisor。 ## 实验设计 {#interpretation} -复用[启动](startup.md)、[文件系统](filesystem.md)、[修复任务](agent-tasks.md)和[完整审查流程](supervision-cost.md)的独立注册实验。前三类每后端 60 次、3 次预热;审查流程每规模/条件/后端 30 次、3 次预热。同机 CPU 0,1、热缓存、随机交替执行,全部通过正确性校验,没有按速度剔除。各主题的负载、资源和计时边界不同,不合并其分布。Docker 使用 rootless overlay2;完整审查流程对照是原生 Git/reflink,不是容器或 VM 安全排名。 +复用[启动](startup.md)、[文件系统](filesystem.md)、[修复任务](agent-tasks.md)和[完整审查流程](supervision-cost.md)的独立注册实验。启动与修复每后端 60 次、文件系统每后端 30 次,均为 3 次预热;审查流程每规模/条件/后端 30 次、3 次预热。同机 CPU 0,1、热缓存、随机交替执行,全部通过正确性校验,没有按速度剔除。各主题的负载、资源和计时边界不同,不合并其分布。Docker 使用 rootless overlay2;完整审查流程对照是原生 Git/reflink,不是容器或 VM 安全排名。 [网络](network.md)每条件 30 个独立批次、3 次预热;小请求以每批 256 请求的中位数作为一个样本,origin 在负载 CPU 预算之外,内存没有统一限额。[容量](density.md)在共同两核、2 GiB、零 swap 预算下,按并发度和空闲/有效工具负载分别运行五轮,保留所有失败、未知和 OOM。 @@ -24,13 +30,13 @@ ## 实验数据和分析 {#results} -2026-10-06 的启动、文件系统和修复共 1,440 个有效样本,完整审查流程 360 个;2026-10-07 的独立 stock 内核启动对照为 240 个有效样本,失败均为 0。各批次不合并,中位数差异和 95% 配对 bootstrap 区间见各专题。 +2026-10-06 的启动、文件系统和修复共 1,440 个有效样本,完整审查流程 360 个;2026-10-07 的独立 stock 内核启动对照为 240 个有效样本,失败均为 0。2026-10-12 所选冻结源码文件系统对照另有 240 个有效样本、失败 0,使用每任务独立缓存与统一父组资源约束。各批次不合并,中位数差异和 95% 配对 bootstrap 区间见各专题。 | 问题 | 实测水位(耗时为 P50) | |---|---| | [Startup](startup.md) | stock 对照:VM 100.91 ms;Firecracker 285.04 ms;QEMU microvm 321.56 ms;q35 702.98 ms | | [Repair completion](agent-tasks.md) | staged 0.64 s; Docker 0.81 s; VM 3.25 s; QEMU microvm 1.40 s | -| [Seven-tool completion](filesystem.md) | staged 1.09 s; Docker 0.82 s; VM 4.27 s; Firecracker 2.29 s | +| [Seven-tool completion](filesystem.md) | staged 1.80 s; Docker 1.27 s; VM 5.46 s; Firecracker 2.67 s | | [Review workflow](supervision-cost.md) | 10,000 files: stage 141 ms; Git 248 ms; reflink 343 ms | | [网络](network.md) | 八线程小请求:原生 0.69 ms;host proxy 10.09 ms;VM 2.10 ms | | [活跃容量](density.md) | 2 GiB:stage/Podman 32 路、VM 16 路通过全部五轮 | @@ -44,6 +50,14 @@ [端到端任务](agent-tasks.md)结合工具等待、工作区生命周期与 CLI 兼容性分析 Agent 沙箱、容器、VM 和云端的取舍。[强化学习训练](compare-rl-infra.md)把工具成本、活跃容量与历史前缀准备映射到 rollout;完整训练吞吐与 reward 未测。 +### 新框架的复测覆盖 {#measurement-coverage} + +下表区分新测量与历史证据。仅当指标、输入、配置和正确性门禁一致时才更新相应数据;Ready、完整退出、工具内部计时、内存与正确性互不替代。未准备的实验不会产生新数字,历史结论只适用于原批次。 + +{{ table("measurement-coverage.render.json", {"markdown":true}) }} + +[覆盖清单 CSV](measurement-coverage.csv)。 + ### 数据下载与复现 {#run} -[Runtime statistics](runtime-summary.csv) · [Confidence intervals](runtime-comparisons.csv) · [Runtime provenance](runtime-provenance.csv) · [Workflow statistics](workflow-summary.csv) · [Workflow intervals](workflow-comparisons.csv) · [Workflow provenance](workflow-provenance.csv) · [Method](methodology.md) · [Reproduction manual](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/pvisor/README.md) +[当前文件系统统计](filesystem.csv) · [同批文件系统置信区间](filesystem-comparisons.csv) · [当前文件系统来源](filesystem-provenance.csv) · [历史 Runtime 统计](runtime-summary.csv) · [历史 Runtime 置信区间](runtime-comparisons.csv) · [历史 Runtime 来源](runtime-provenance.csv) · [Workflow statistics](workflow-summary.csv) · [Workflow intervals](workflow-comparisons.csv) · [Workflow provenance](workflow-provenance.csv) · [Method](methodology.md) · [Reproduction manual](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/pvisor/README.md) diff --git a/docs/src/zh/benchmarks/isolation-tests.md b/docs/src/zh/benchmarks/isolation-tests.md index b4778e169..8dc13591d 100644 --- a/docs/src/zh/benchmarks/isolation-tests.md +++ b/docs/src/zh/benchmarks/isolation-tests.md @@ -10,6 +10,12 @@ | 使用 OCI 可写工作区挂载 | 写入直接落到宿主,需另行提供审查和回滚机制 | | 允许访问宿主路径 | 所测 host 保持宿主可见,不能视为工作区文件沙箱 | + + +本页数值和结论仍属于原始历史批次;新框架尚无匹配的复测数据,见[复测覆盖](index.md#measurement-coverage)。 + + + ## Motivation {#motivation} 选择执行模式时,需要同时知道允许访问什么,以及写入最终落在哪里。只看命令返回成功,会把暂存写入与宿主修改混为一谈。隔离边界也决定了哪些性能对照具有相同语义。 diff --git a/docs/src/zh/benchmarks/lazy-image-startup.md b/docs/src/zh/benchmarks/lazy-image-startup.md index f884bd911..521c9dae2 100644 --- a/docs/src/zh/benchmarks/lazy-image-startup.md +++ b/docs/src/zh/benchmarks/lazy-image-startup.md @@ -10,6 +10,12 @@ | 已有完整本地镜像 | Docker 更早输出正确标记;lazy VM 仍需 VM/Job 启动和收尾 | | 首次部署镜像服务 | 另计上游下载、解包和索引成本;缓存准备不衡量这些工作 | + + +本页数值和结论仍属于原始历史批次;新框架尚无匹配的复测数据,见[复测覆盖](index.md#measurement-coverage)。 + + + ## Motivation {#motivation} 一次性环境运行短命令时,完整镜像下载、解包可能比任务本身更贵。选择按需读取前,需要区分服务端准备和客户端启动成本,也要判断镜像已缓存后是否仍有优势。 diff --git a/docs/src/zh/benchmarks/measurement-coverage.csv b/docs/src/zh/benchmarks/measurement-coverage.csv new file mode 100644 index 000000000..07d503e56 --- /dev/null +++ b/docs/src/zh/benchmarks/measurement-coverage.csv @@ -0,0 +1,15 @@ +page,available_bindings,missing_metric_adapters,coverage +benchmarks/agent-tasks.md,,agent/agent.json,historical +benchmarks/cluster-scalability.md,,,historical +benchmarks/compare-rl-infra.md,,,historical +benchmarks/density.md,,density/density.json,historical +benchmarks/filesystem.md,B-FS-TOOLS accepted reference cohort fo5a4e7d2b-f,agent/macos.json,full +benchmarks/index.md,,,historical +benchmarks/isolation-tests.md,,isolation/isolation.json,historical +benchmarks/lazy-image-startup.md,,startup/lazy_startup.json,historical +benchmarks/methodology.md,,,historical +benchmarks/network.md,,network/network.json,historical +benchmarks/replay-fidelity.md,replay/replay.json,,partial +benchmarks/startup.md,startup/native_timing.json;startup/pvisor_host_timing.json;startup/pvisor_staged_timing.json;startup/pvisor_vm_timing.json;startup/docker_timing.json;startup/fc_system_timing.json;startup/qemu_timing.json;startup/qemu_microvm_timing.json,agent/macos.json,partial +benchmarks/supervision-cost.md,,workflow/apply.json;workflow/review.json;workflow/workflow.json,historical +benchmarks/vm-memory/index.md,,memory/memory.json,historical diff --git a/docs/src/zh/benchmarks/measurement-coverage.render.json b/docs/src/zh/benchmarks/measurement-coverage.render.json new file mode 100644 index 000000000..cc4a4524f --- /dev/null +++ b/docs/src/zh/benchmarks/measurement-coverage.render.json @@ -0,0 +1,58 @@ +{ + "[agent-tasks](agent-tasks.md)": { + "数据状态": "历史数据", + "实测范围": "保留原始指标与来源;暂无匹配的新测量" + }, + "[cluster-scalability](cluster-scalability.md)": { + "数据状态": "历史数据", + "实测范围": "保留原始指标与来源;暂无匹配的新测量" + }, + "[compare-rl-infra](compare-rl-infra.md)": { + "数据状态": "历史数据", + "实测范围": "保留原始指标与来源;暂无匹配的新测量" + }, + "[density](density.md)": { + "数据状态": "历史数据", + "实测范围": "保留原始指标与来源;暂无匹配的新测量" + }, + "[filesystem](filesystem.md)": { + "数据状态": "完整复测", + "实测范围": "八后端七工具内部阶段与启动至退出;原生完整命令探针另列" + }, + "总览": { + "数据状态": "历史数据", + "实测范围": "保留原始指标与来源;暂无匹配的新测量" + }, + "[isolation-tests](isolation-tests.md)": { + "数据状态": "历史数据", + "实测范围": "保留原始指标与来源;暂无匹配的新测量" + }, + "[lazy-image-startup](lazy-image-startup.md)": { + "数据状态": "历史数据", + "实测范围": "保留原始指标与来源;暂无匹配的新测量" + }, + "[methodology](methodology.md)": { + "数据状态": "历史数据", + "实测范围": "保留原始指标与来源;暂无匹配的新测量" + }, + "[network](network.md)": { + "数据状态": "历史数据", + "实测范围": "保留原始指标与来源;暂无匹配的新测量" + }, + "[replay-fidelity](replay-fidelity.md)": { + "数据状态": "部分复测", + "实测范围": "固定 task 0 的完整 CLI 时间;20 任务历史分布另列" + }, + "[startup](startup.md)": { + "数据状态": "部分复测", + "实测范围": "同次启动 Ready / Exit;完整命令另列" + }, + "[supervision-cost](supervision-cost.md)": { + "数据状态": "历史数据", + "实测范围": "保留原始指标与来源;暂无匹配的新测量" + }, + "[vm-memory](vm-memory/index.md)": { + "数据状态": "历史数据", + "实测范围": "保留原始指标与来源;暂无匹配的新测量" + } +} diff --git a/docs/src/zh/benchmarks/methodology.md b/docs/src/zh/benchmarks/methodology.md index 174738ca6..085686ec7 100644 --- a/docs/src/zh/benchmarks/methodology.md +++ b/docs/src/zh/benchmarks/methodology.md @@ -10,6 +10,12 @@ | 选独立 guest kernel | Firecracker、QEMU 与 pVisor 的启动和工具数据 | | 设置容量或超时 | 成功率、完整任务等待、相同口径的资源占用 | + + +本页数值和结论仍属于原始历史批次;新框架尚无匹配的复测数据,见[复测覆盖](index.md#measurement-coverage)。 + + + ## Motivation {#motivation} 启动更快并不保证编译更快,可写挂载与暂存审查也提供不同工作流。选型需要同时知道速度、执行边界和改动最终如何进入原目录。 @@ -22,6 +28,10 @@ B-STARTUP、B-FS-TOOLS、B-AGENT-TASK 使用相同离线工具和固定输入, 下载、编译 benchmark 制品、镜像导入和输入复制不计入任务等待。计时区分首条有效输出、内部工具与校验、结果返回,以及启动到进程退出。成功样本必须通过输出、执行器和暂存完整性校验;暂存模式还检查宿主原目录未改动。 +### JSON 表格与 hyperfine {#json-tables} + +表格在 Markdown 渲染前读取已完成的“行名 → 列名 → 标量”JSON;文档构建只读取数据。启动与 replay 的直接命令批次采用 hyperfine 统计值,输入准备和 Python hooks 均在计时外。各条件独立采样,不沿用历史 campaign 的随机配对执行声明。历史分布、正确性计数、容量与物理内存读数保留各自的测量口径。复现方式和其余负载的准备要求见 [JSON benchmark 手册](https://github.com/DeepLink-org/pvisor/blob/main/benchmark.new/README.md)。 + ### 分布与失败 不合并批次,不事后剔除慢样本。失败与校验不通过单列,不能计作零耗时。未执行的容量 guard 不能当成完成任务。P95 只是所测样本的观察参考,少于 30 次不展示,公开统计不展示 P99;小样本不给稳定尾延迟结论。 diff --git a/docs/src/zh/benchmarks/network.md b/docs/src/zh/benchmarks/network.md index 680b51d64..31c8473b4 100644 --- a/docs/src/zh/benchmarks/network.md +++ b/docs/src/zh/benchmarks/network.md @@ -10,6 +10,12 @@ | VM 内下载依赖 | 把传输与环境启动分别预算 | | 阻止直接 TCP 连接 | host/VM deny-all 的正负对照已测;延迟不是隔离证明 | + + +本页数值和结论仍属于原始历史批次;新框架尚无匹配的复测数据,见[复测覆盖](index.md#measurement-coverage)。 + + + ## Motivation {#motivation} Agent 会请求模型、下载依赖和读取流式响应,三种负载对代理成本的敏感度不同。请求延迟、首字节、完整传输和启动开销需要分开,才能判断等待来自哪里。 diff --git a/docs/src/zh/benchmarks/replay-fidelity.md b/docs/src/zh/benchmarks/replay-fidelity.md index db4767c0c..090f8c219 100644 --- a/docs/src/zh/benchmarks/replay-fidelity.md +++ b/docs/src/zh/benchmarks/replay-fidelity.md @@ -2,7 +2,7 @@ ## 主要结论 {#conclusions} -**七种固定版本轨迹格式各通过 60/60 次前缀校验,prepare-only 不执行工具、不修改工作区。多数格式出现分离的耗时簇:较低簇中位数约 6–7 ms,较高簇约 11–16 ms。适合把任务绑定到历史工具观测;真实模型继续执行的一致率尚未测量。** +**新框架中,七个适配器的固定 task 0 各通过 60/60 次校验,完整 prepare-only CLI 时间的中位数为 5.56–5.74 ms;不执行工具,不修改工作区。下方 20 条轨迹的历史分簇结果属于不同输入集合,保留原口径;真实模型继续执行的一致率仍未测量。** | 需求 | 选型含义 | |---|---| @@ -10,6 +10,12 @@ | 恢复实际文件状态 | 单独恢复任务环境;前缀准备不重放文件修改 | | 比较原生 resume 或 RL 管线的速度 | 尚无同轨迹计时对照,不能据此排名 | + + +本页新增实验与历史数据分开发布;各表适用的计时范围和输入见实验设计与来源,不合并不同批次。 + + + ## Motivation {#motivation} 训练和复现需要在准确的历史边界继续任务。丢失工具观测或混入下一步动作,会改变模型看到的上下文;准备时重新执行工具,还可能产生额外副作用。这决定了历史轨迹能否作为可靠的任务输入。 @@ -40,6 +46,14 @@ Mini-SWE-Agent 未触发分簇规则,该行列出全体样本的 P50。分簇是描述性判定:两簇各至少占 10%,最大相邻间隔至少为整体中位数的 20%、且超过相邻间隔中位数的三倍,两簇中位数至少相差 1.5 倍;它不证明慢簇的成因。有效慢样本全部保留,因此不宜把准备成本概括为固定的几毫秒。 +### Hyperfine:直接执行 prepare-only 命令 {#hyperfine} + +独立批次为每个适配器固定一条合成轨迹(task 0),采样 60 次,无预热。hyperfine 使用 `--shell=none` 直接启动 `pvisor-replay`;Python 重置和一致性校验在每次计时外执行。所有执行必须保留精确参数、源摘要与历史观测,不执行工具、不改动工作区。数值表示完整命令结束时间,适用范围是这条固定轨迹;上方二十条轨迹的历史分布保留原口径。二进制摘要标识实测制品,与当前源码的构建对应关系未验证。保留宿主缓存;CPU 亲和性和制品标识见来源下载,不据此推导配对改善比例或尾延迟保证。 + +{{ table("replay-semspec.render.json") }} + +[Hyperfine 数据 CSV](replay-semspec.csv) · [批次来源](../assets/benchmarks/replay-semspec.provenance.json) · [JSON benchmark 复现手册](https://github.com/DeepLink-org/pvisor/blob/main/benchmark.new/README.md#replay) + ### 适用边界 {#acceptance} 这些结果支持固定短前缀的结构、参数和观测保真。真实会话、新版 CLI、长前缀、token 成本、macOS、远端连接恢复及后续模型执行未测。需要工具重放或实际环境恢复时,应另行验证其正确性和成本。 diff --git a/docs/src/zh/benchmarks/replay-semspec.csv b/docs/src/zh/benchmarks/replay-semspec.csv new file mode 100644 index 000000000..409ba9ce6 --- /dev/null +++ b/docs/src/zh/benchmarks/replay-semspec.csv @@ -0,0 +1,15 @@ +batch,benchmark_id,role,runs,warmup,hyperfine_version,metric_scope,evidence_sha256,manifest_sha256,condition,command,N,Median ms,Mean ms,SD ms,Min ms,Max ms +replay-20261010T173216Z-gbh7pkr9,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,5556cb33a1e853ac3074fff9dd34c2fdf722abd2fc9f8f0391bfd89131dedac0,808c779fce917fb38c7845481fb2162f7b2e2b11c387dc60611421caf2532f92,claude-code,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/pvisor-replay --agent claude-code --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/claude-code/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/claude-code/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/claude-code/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/claude-code/output,60,5.813657,5.8942094166666665,0.5230506143534632,5.176242,8.040147000000001 +replay-20261010T173216Z-gbh7pkr9,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,5556cb33a1e853ac3074fff9dd34c2fdf722abd2fc9f8f0391bfd89131dedac0,808c779fce917fb38c7845481fb2162f7b2e2b11c387dc60611421caf2532f92,codex,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/pvisor-replay --agent codex --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/codex/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/codex/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/codex/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/codex/output,60,5.668345500000001,5.793974399999999,0.5887070394240217,5.288943,9.142757 +replay-20261010T173216Z-gbh7pkr9,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,5556cb33a1e853ac3074fff9dd34c2fdf722abd2fc9f8f0391bfd89131dedac0,808c779fce917fb38c7845481fb2162f7b2e2b11c387dc60611421caf2532f92,opencode,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/pvisor-replay --agent opencode --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/opencode/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/opencode/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/opencode/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/opencode/output,60,5.6216435,5.662359433333333,0.25105121611453857,5.226376,6.6708620000000005 +replay-20261010T173216Z-gbh7pkr9,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,5556cb33a1e853ac3074fff9dd34c2fdf722abd2fc9f8f0391bfd89131dedac0,808c779fce917fb38c7845481fb2162f7b2e2b11c387dc60611421caf2532f92,mini-swe-agent,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/pvisor-replay --agent mini-swe-agent --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/mini-swe-agent/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/mini-swe-agent/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/mini-swe-agent/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/mini-swe-agent/output,60,5.660796000000001,5.7662035833333345,0.41099182149198304,5.286918000000001,6.981852 +replay-20261010T173216Z-gbh7pkr9,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,5556cb33a1e853ac3074fff9dd34c2fdf722abd2fc9f8f0391bfd89131dedac0,808c779fce917fb38c7845481fb2162f7b2e2b11c387dc60611421caf2532f92,openhands,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/pvisor-replay --agent openhands --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/openhands/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/openhands/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/openhands/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/openhands/output,60,5.6294830000000005,5.856039483333334,1.2306524438359545,5.282350000000001,14.935868999999999 +replay-20261010T173216Z-gbh7pkr9,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,5556cb33a1e853ac3074fff9dd34c2fdf722abd2fc9f8f0391bfd89131dedac0,808c779fce917fb38c7845481fb2162f7b2e2b11c387dc60611421caf2532f92,pi-agent,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/pvisor-replay --agent pi-agent --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/pi-agent/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/pi-agent/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/pi-agent/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/pi-agent/output,60,5.831475000000001,5.8543261,0.3711018192554459,4.919222,7.126382 +replay-20261010T173216Z-gbh7pkr9,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,5556cb33a1e853ac3074fff9dd34c2fdf722abd2fc9f8f0391bfd89131dedac0,808c779fce917fb38c7845481fb2162f7b2e2b11c387dc60611421caf2532f92,swe-agent,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/pvisor-replay --agent swe-agent --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/swe-agent/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/swe-agent/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/swe-agent/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T173216Z-gbh7pkr9/fixtures/swe-agent/output,60,5.645287000000001,5.8619556500000005,0.8858928147291519,5.243638,11.045348 +replay-20261010T194213Z-9zpco2sv,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,34f0c4a59ad1612860c382951158167a4fb3a2f45e47dbf635d0dca793105eca,202c53d97959bbdf76bb10fd5fb071d22eb2c435f194f48e3372800b02dd1c8e,claude-code,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/pvisor-replay --agent claude-code --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/claude-code/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/claude-code/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/claude-code/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/claude-code/output,60,5.7403095,5.8633256833333345,0.8778318015393844,5.114597,12.135782 +replay-20261010T194213Z-9zpco2sv,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,34f0c4a59ad1612860c382951158167a4fb3a2f45e47dbf635d0dca793105eca,202c53d97959bbdf76bb10fd5fb071d22eb2c435f194f48e3372800b02dd1c8e,codex,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/pvisor-replay --agent codex --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/codex/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/codex/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/codex/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/codex/output,60,5.589363,5.668820266666667,0.31781873367499147,5.052701000000001,6.9522770000000005 +replay-20261010T194213Z-9zpco2sv,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,34f0c4a59ad1612860c382951158167a4fb3a2f45e47dbf635d0dca793105eca,202c53d97959bbdf76bb10fd5fb071d22eb2c435f194f48e3372800b02dd1c8e,opencode,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/pvisor-replay --agent opencode --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/opencode/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/opencode/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/opencode/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/opencode/output,60,5.563975500000001,5.6183589833333345,0.2956679622357836,4.985716,6.622702 +replay-20261010T194213Z-9zpco2sv,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,34f0c4a59ad1612860c382951158167a4fb3a2f45e47dbf635d0dca793105eca,202c53d97959bbdf76bb10fd5fb071d22eb2c435f194f48e3372800b02dd1c8e,mini-swe-agent,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/pvisor-replay --agent mini-swe-agent --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/mini-swe-agent/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/mini-swe-agent/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/mini-swe-agent/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/mini-swe-agent/output,60,5.608388000000001,5.723988500000002,0.3963316399933439,5.279214,7.547869 +replay-20261010T194213Z-9zpco2sv,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,34f0c4a59ad1612860c382951158167a4fb3a2f45e47dbf635d0dca793105eca,202c53d97959bbdf76bb10fd5fb071d22eb2c435f194f48e3372800b02dd1c8e,openhands,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/pvisor-replay --agent openhands --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/openhands/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/openhands/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/openhands/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/openhands/output,60,5.675914000000001,5.734144033333331,0.4020236849784177,4.805099,7.400434000000001 +replay-20261010T194213Z-9zpco2sv,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,34f0c4a59ad1612860c382951158167a4fb3a2f45e47dbf635d0dca793105eca,202c53d97959bbdf76bb10fd5fb071d22eb2c435f194f48e3372800b02dd1c8e,pi-agent,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/pvisor-replay --agent pi-agent --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/pi-agent/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/pi-agent/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/pi-agent/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/pi-agent/output,60,5.582430500000001,5.629462633333333,0.33750797150701284,4.734617,6.9348540000000005 +replay-20261010T194213Z-9zpco2sv,B-REPLAY,user-facing,60,0,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,34f0c4a59ad1612860c382951158167a4fb3a2f45e47dbf635d0dca793105eca,202c53d97959bbdf76bb10fd5fb071d22eb2c435f194f48e3372800b02dd1c8e,swe-agent,/home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/pvisor-replay --agent swe-agent --trajectory /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/swe-agent/trajectory.json --after-step 1 --prepare-only --workspace /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/swe-agent/workspace --state-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/swe-agent/state --output-dir /home/reiase/workspace/pvisor/benchmark.new/replay/.data/replay-20261010T194213Z-9zpco2sv/fixtures/swe-agent/output,60,5.732465000000001,5.763901283333333,0.39466482164589084,4.707587,7.244041 diff --git a/docs/src/zh/benchmarks/replay-semspec.render.json b/docs/src/zh/benchmarks/replay-semspec.render.json new file mode 100644 index 000000000..be43ae10f --- /dev/null +++ b/docs/src/zh/benchmarks/replay-semspec.render.json @@ -0,0 +1,58 @@ +{ + "claude-code": { + "N": 60, + "Median ms": "5.74", + "Mean ms": "5.86", + "SD ms": "0.88", + "Min ms": "5.11", + "Max ms": "12.14" + }, + "codex": { + "N": 60, + "Median ms": "5.59", + "Mean ms": "5.67", + "SD ms": "0.32", + "Min ms": "5.05", + "Max ms": "6.95" + }, + "opencode": { + "N": 60, + "Median ms": "5.56", + "Mean ms": "5.62", + "SD ms": "0.30", + "Min ms": "4.99", + "Max ms": "6.62" + }, + "mini-swe-agent": { + "N": 60, + "Median ms": "5.61", + "Mean ms": "5.72", + "SD ms": "0.40", + "Min ms": "5.28", + "Max ms": "7.55" + }, + "openhands": { + "N": 60, + "Median ms": "5.68", + "Mean ms": "5.73", + "SD ms": "0.40", + "Min ms": "4.81", + "Max ms": "7.40" + }, + "pi-agent": { + "N": 60, + "Median ms": "5.58", + "Mean ms": "5.63", + "SD ms": "0.34", + "Min ms": "4.73", + "Max ms": "6.93" + }, + "swe-agent": { + "N": 60, + "Median ms": "5.73", + "Mean ms": "5.76", + "SD ms": "0.39", + "Min ms": "4.71", + "Max ms": "7.24" + } +} diff --git a/docs/src/zh/benchmarks/startup-hyperfine.csv b/docs/src/zh/benchmarks/startup-hyperfine.csv new file mode 100644 index 000000000..a67109fc9 --- /dev/null +++ b/docs/src/zh/benchmarks/startup-hyperfine.csv @@ -0,0 +1,6 @@ +batch,benchmark_id,role,runs,warmup,hyperfine_version,metric_scope,evidence_sha256,manifest_sha256,command,time_wall_clock_unit,time_wall_clock_mean,time_wall_clock_stddev,time_wall_clock_median,time_wall_clock_min,time_wall_clock_max,memory_peak_resident_unit,memory_peak_resident_mean,memory_peak_resident_stddev,memory_peak_resident_median,memory_peak_resident_min,memory_peak_resident_max +startup-20261010T140239Z-pzfiqe2v,B-STARTUP,user-facing,60,3,hyperfine 2.0.0,complete command; hooks excluded,e9b1a5d82840710d8b3dd2aef18cdb6b00912b85f197b44a32bf4e243765b309,2a14d8b62556407f6c0029235198249e21b3e2fd70ea16cb1039424b4ec9a8a2,native,s,0.0021688563499999997,0.0002314098403387833,0.002130323,0.0017548470000000001,0.0027290260000000003,B,3712341.3333333335,82850.7755381737,3713024,3522560,3887104 +startup-20261010T140419Z-aaegem48,B-STARTUP,user-facing,60,3,hyperfine 2.0.0,complete command; hooks excluded,d7e327c6e6df30815d967b9444da8665c4af61eb36fb1bbced2530255deca76e,5cb5b8b0bd9b6ccf8fd7b15bf9545f3d5f600e081480b64628ba1d0aa6f9cc3f,qemu,s,0.8967260577333336,0.3190259188267261,0.748406994,0.705032997,2.298454349,B,193738410.66666666,250678.38946311103,193744896,193261568,194342912 +startup-20261010T152527Z-ohjmaem2,B-STARTUP,user-facing,60,3,hyperfine 2.0.0,complete command; hooks excluded,9d1e406f7ecc1f9b85924119616764eee2ba98d7ebf6774e616d76f6cb4b0215,30ea22bb257542678d0f35a79c5bca1febea6f9ae35efae946f822b006d55a37,pvisor-host,s,0.31584145333333336,0.25974104189753316,0.1651246125,0.128760758,1.162888514,B,12082858.666666666,76142.55881572179,12095488,11915264,12197888 +startup-20261010T152713Z-s6p1jnm8,B-STARTUP,user-facing,60,3,hyperfine 2.0.0,complete command; hooks excluded,78a82548faf0abfb71a4e2338e5dbe3f7c90dd03c54b7a85d5479bc659d1ddd0,77fb703e3be244bca0fd2bababc6b26960f0fbef553b3681ad127caaa74218a7,docker,s,0.3810838176166668,0.5093205021505954,0.1672732575,0.128847399,2.692299138,B,28099993.6,180496.4793822766,28110848,27734016,28483584 +startup-20261010T152907Z-qyz2tj6w,B-STARTUP,user-facing,60,3,hyperfine 2.0.0,complete command; hooks excluded,aff95227580927f520cd06327d838fc2490bdcbf08b61d2cd67af38e8957e184,1e17b7b84a8d91d11d2a9eb1f905d70d452cf2c31cd2e4f00217743238749e5e,qemu-microvm,s,0.45113018473333344,0.21992967847736003,0.3537632235,0.30724522400000004,1.126818259,B,189431057.06666666,191731.97540686635,189409280,189075456,189825024 diff --git a/docs/src/zh/benchmarks/startup-ready-exit.csv b/docs/src/zh/benchmarks/startup-ready-exit.csv new file mode 100644 index 000000000..f9dae1db1 --- /dev/null +++ b/docs/src/zh/benchmarks/startup-ready-exit.csv @@ -0,0 +1,2 @@ +batch,benchmark_id,role,runs,warmup,hyperfine_version,metric_scope,evidence_sha256,manifest_sha256,measurement_engine,python_version,condition,command,N,Ready median ms,Ready mean ms,Ready SD ms,Ready min ms,Ready max ms,Exit median ms,Exit mean ms,Exit SD ms,Exit min ms,Exit max ms +pvisor_vm-20261010T191930Z-edvvabx9,B-STARTUP,user-facing,60,3,,same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded,fd22c3d38fd277171ae9026ecdcfc8386c7e522b4ab79476ee24c4b6956865d7,0aa3161db744936f86be6b9f40451d0803d83e404c7574554442ddc85345c436,python-perf_counter_ns,3.12.11,pvisor-vm,"/home/reiase/workspace/pvisor/target/release/pvisor run --no-agent-defaults --overlaynet off --stdio inherit --timeout 30s --stage /home/reiase/workspace/pvisor/benchmark.new/startup/.data/pvisor_vm-20261010T191930Z-edvvabx9/current/stage --vm --rootfs /home/reiase/workspace/pvisor/benchmark/.data/6ra5/rootfs --cpu 2 --memory 128MiB -- /bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,145.532771,163.5235932,28.941141549256155,132.431972,231.477036,280.354604,287.9636302166667,30.578377727311814,245.776707,365.590256 diff --git a/docs/src/zh/benchmarks/startup-ready.csv b/docs/src/zh/benchmarks/startup-ready.csv new file mode 100644 index 000000000..6d297840a --- /dev/null +++ b/docs/src/zh/benchmarks/startup-ready.csv @@ -0,0 +1,3 @@ +batch,benchmark_id,role,runs,warmup,hyperfine_version,metric_scope,evidence_sha256,manifest_sha256,measurement_engine,python_version,condition,command,N,Ready median ms,Ready mean ms,Ready SD ms,Ready min ms,Ready max ms +pvisor_vm-20261010T190837Z-03dall_r,B-STARTUP,user-facing,60,3,,Popen launch to receipt of exact REFERENCE_READY line; perf_counter_ns; preparation and teardown excluded,f20e900c362a2d0d2203c638c33cb901d8ff3a41f8f476254fceda4077973776,9b65738a486a4309e61f9b1d4a7fcbb814094459ff0c2b9a6a0bfbd6d73899a6,python-perf_counter_ns,3.12.11,pvisor-vm,"/home/reiase/workspace/pvisor/target/release/pvisor run --no-agent-defaults --overlaynet off --stdio inherit --timeout 30s --stage /home/reiase/workspace/pvisor/benchmark.new/startup/.data/pvisor_vm-20261010T190837Z-03dall_r/current/stage --vm --rootfs /home/reiase/workspace/pvisor/benchmark/.data/6ra5/rootfs --cpu 2 --memory 128MiB -- /bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,131.736131,135.89581985,20.838798984702635,122.028212,228.284618 +pvisor_vm-20261010T190923Z-a6pqcxmk,B-STARTUP,user-facing,60,3,,Popen launch to receipt of exact REFERENCE_READY line; perf_counter_ns; preparation and teardown excluded,316cd941068ddf87b6d690ba4fdb124a7b7feb0c8b3c3b50179efdf70a78f7c9,be31c9369cb2024ec98c04ed10771972c31970ce830e980cbc9e91bc846a1992,python-perf_counter_ns,3.12.11,pvisor-vm,"/home/reiase/workspace/pvisor/target/release/pvisor run --no-agent-defaults --overlaynet off --stdio inherit --timeout 30s --stage /home/reiase/workspace/pvisor/benchmark.new/startup/.data/pvisor_vm-20261010T190923Z-a6pqcxmk/current/stage --vm --rootfs /home/reiase/workspace/pvisor/benchmark/.data/6ra5/rootfs --cpu 2 --memory 128MiB -- /bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,132.9515445,132.50528831666668,5.088249720425674,122.99712,147.525951 diff --git a/docs/src/zh/benchmarks/startup-reboot-comparison.csv b/docs/src/zh/benchmarks/startup-reboot-comparison.csv new file mode 100644 index 000000000..5ecafe31f --- /dev/null +++ b/docs/src/zh/benchmarks/startup-reboot-comparison.csv @@ -0,0 +1,9 @@ +condition,label,before_batch,after_batch,before_N,after_N,warmup,after_boot_id,pvisor_binary_sha256,before Ready median ms,after Ready median ms,before Ready mean ms,after Ready mean ms,before Ready SD ms,after Ready SD ms,before Ready min ms,after Ready min ms,before Ready max ms,after Ready max ms,before Exit median ms,after Exit median ms,before Exit mean ms,after Exit mean ms,before Exit SD ms,after Exit SD ms,before Exit min ms,after Exit min ms,before Exit max ms,after Exit max ms,Ready median ms change %,Exit median ms change %,before_evidence_sha256,after_evidence_sha256,before_manifest_sha256,after_manifest_sha256 +native,Native,native_timing-20261010T193144Z-rjxju56s,native_timing-20261011T004342Z-8wjyuy21,60,60,3,5ba46bee-f753-405b-9cc8-ff6cdf810b8b,,0.477416,0.4768065,0.4867091666666667,0.4821639,0.027473098763457073,0.024363187201185323,0.45534,0.453769,0.593298,0.554185,0.5262070000000001,0.5247455000000001,0.5548987,0.5674711833333334,0.14369408916641668,0.20638363771035492,0.504061,0.498852,1.626316,1.721113,-0.1276664376560488,-0.2777424093560188,ba0b0607cfde7cc9d8851a838ca4a71b38c08eac81f1361f7ab68f96e08b0f24,355c05aa8af734b85978052114fc98485d339ec681f1e1cebc5bd5b317ac4948,9d7e1087e6897807884761ed201bfd2b97d71fb2b3600fa161e57792baae65ef,571e859d1e439e764b42696e5280aa02bdeb4a3cb49797164d54116286d797e0 +pvisor-host,pVisor host,pvisor_host_timing-20261010T193144Z-7kp2ohaf,pvisor_host_timing-20261011T004343Z-gdenfkzx,60,60,3,5ba46bee-f753-405b-9cc8-ff6cdf810b8b,d3f09687cca60b617629c215fa1359c441ab6ac19f8c63b68f1dcdf7614e6b30,37.453213500000004,33.5245775,37.39181438333333,50.48170388333333,1.3806339289004357,29.699968167718016,33.823985,30.57654,45.32391,128.654942,129.0226385,123.626168,128.38440935,138.93658936666665,2.8130187011713157,27.73345550008831,116.414786,112.022101,132.229654,215.842663,-10.489449723719979,-4.182576455371434,2a58fe62fb73d6c637241854da6fe0de2c3d1436b3f670c760166fd745d02de6,b5ec9fd9f67ad90cfa3b5aaa77041a2aad2a3426f82a7afd2c63efeccd954adb,e3e38d24a5cfcaecad68f5c2a07b270b9654733b23aca3d8ebf13ce07c8c67a6,768f808263dc663e926b58944b46d1e966d0b700f42a4cdacf91a7e0bd7c27fa +pvisor-staged,pVisor staged,pvisor_staged_timing-20261010T193207Z-14b8yji_,pvisor_staged_timing-20261011T004437Z-0_8_myox,60,60,3,5ba46bee-f753-405b-9cc8-ff6cdf810b8b,d3f09687cca60b617629c215fa1359c441ab6ac19f8c63b68f1dcdf7614e6b30,56.317359499999995,50.5909475,83.63402366666666,52.77206473333333,35.80452248684234,17.86787212840543,51.526137,46.178707,151.770448,188.290473,159.076721,146.34664,182.31478523333334,148.0955406,36.35013478894402,17.210223152340454,139.925129,133.553461,246.257413,274.998173,-10.168111663686929,-8.002478879357833,d9697f4bf482937967e3b7459fd4fc6f56d1fcf785f1e261fd3ff40fbac5e2b6,ecad00dd273bd159cd74112059f1a1a09a0bf0a58cf999f596d5a5d7a2027049,1d4b415be94fcfe71048b625e8eff82765e26b2392869a67351bd087abae8764,8a4d6d031779febc3b156bfe29163e68ba7ccaa3f3294ff810cdd913b094e22f +pvisor-vm,pVisor VM,pvisor_vm-20261010T193231Z-oqxsd107,pvisor_vm-20261011T004527Z-4o2qaxr9,60,60,3,5ba46bee-f753-405b-9cc8-ff6cdf810b8b,d3f09687cca60b617629c215fa1359c441ab6ac19f8c63b68f1dcdf7614e6b30,179.21802150000002,130.707348,171.2950154,130.10342321666667,22.10104697452002,6.156610149010636,137.615608,119.004459,207.20476,149.988327,297.7932135,243.53000400000002,293.0082749,248.7508986,23.8399220189266,17.71092203479732,247.049522,218.450697,340.454659,288.919716,-27.067966208967455,-18.221775057341915,f49d48ad7568a601eadf2b470c0513edeef1c5d1864a11ee53dd9fdc418e3a3f,6f1b2e5eb12b8012bb1f221d94944017c3792625ae27038e489d29808ae43de3,11644c889e7b014109709022e6b4222de3865f15fbf636b0aa9366278d2426cb,a63cb47729c4b6cf1f37e259241a0b5ee1105157e58a1eb5b79f5e8da82db252 +docker,Docker,docker_timing-20261010T193254Z-vfew0_j6,docker_timing-20261011T004624Z-8aq0yusi,60,60,3,5ba46bee-f753-405b-9cc8-ff6cdf810b8b,,111.212921,105.027266,112.61178271666667,105.5709834,8.444143141831258,7.221437055108282,91.518154,90.523694,140.077263,126.038887,136.83700499999998,129.3059355,138.7524654,130.3731021,9.00429822650372,7.663377586154465,118.732902,114.191419,169.009196,152.928737,-5.561993106898067,-5.503678993851091,66772bfad50467e9854931366f9c466936f432ebdb8cf8169f3f3d130f85589a,9df027d8154142787594d86cd7572465957be3513333dd83c734debd50558c10,5583225f3e0382e0208f8d8cbe951fa92b7196dbfe65e5824586aa680a9a601f,aea46ba225d18a1fd202e2460933d809a691636e33b0346319fcd4cc01eeb8d5 +fc-system,Firecracker,fc_system_timing-20261010T194324Z-0ngxz1rw,fc_system_timing-20261011T004712Z-97n8gne7,60,60,3,5ba46bee-f753-405b-9cc8-ff6cdf810b8b,,291.2311145,287.127745,291.5582495333333,287.06792748333334,5.8558511887453095,5.885774302854362,282.256479,269.078709,313.902527,301.728159,726.9635,722.0567149999999,728.0146826166666,722.7726551166667,5.32211127846656,6.025874020298251,717.707895,711.091197,740.754322,741.229639,-1.408973593719498,-0.674969926275526,3928edf8392072a5529d79f3d8230829f23a036a11387291dea6707aac6c265d,b0343f5973991391c32b67b4a8431a5e992c80cb6c403338d3dc4965e7ce2251,aaeceeb1b87fe05a7491a51f539e0a4b9a65cbae60a01181c5a7b6cca05ad885,107c830711205462c88ae27f0de0e1088ffee1176a5f9842d483dacc1e8413f3 +qemu,QEMU,qemu_timing-20261010T193411Z-xgtqdjpb,qemu_timing-20261011T004840Z-vh5l4h7y,60,60,3,5ba46bee-f753-405b-9cc8-ff6cdf810b8b,,711.3557575,663.2477994999999,715.5877327166667,667.5293913666667,28.84870286560417,28.729089815188537,676.150134,631.375716,777.811211,742.132208,738.2912145,691.7226949999999,744.53627425,695.4386896,29.35072221760295,28.6305736872296,698.122196,656.021112,804.280468,766.893319,-6.762854941818619,-6.307608513469598,b4d1229af611f7884471e6988eee61219de68aeb1dd7966321196d4091609193,7d8b028d8df05ff7b3cc881e9d30b998fb55de23b51c29329969ed1b86ce1ae0,f5a176f8e7b58ce1ce366827fe4b3b5aaf2fdac052e225642a5fb9abb7288912,1431f57af3f73fac2331982e186a0d61ba8de88d7f07e1213af23751a1c1fad9 +qemu-microvm,QEMU microvm,qemu_microvm_timing-20261010T193436Z-bvzj9q7t,qemu_microvm_timing-20261011T005005Z-hra4_tyy,60,60,3,5ba46bee-f753-405b-9cc8-ff6cdf810b8b,,298.98298450000004,260.10463400000003,299.9279542,259.5273039,30.521712217844783,9.566460105458674,244.717479,243.283605,359.294324,288.390241,321.4902005,281.32751199999996,323.5255617166667,281.99962423333335,29.485465599627037,11.500231438448678,266.138102,261.017615,380.83449,315.552385,-13.00353281475789,-12.492663365022239,a12206db296e544ad323a3781bc9f96cd3370dab5cfc795faa0371d5d74aed65,eb2c5c6f18620ac4f0f30e1d67aa47aa1fcaa223f6b050ad70ec9982f6a3b457,8e217c76c1d10a542fa2ebc9cded9817d74fc0b96924b658f3a2d88c170d0d8b,d5c74b3517c2b8ee545c4a274a8cea36e93d9f3c6b4b9153d0ea7ab4ce1d974a diff --git a/docs/src/zh/benchmarks/startup-semspec.csv b/docs/src/zh/benchmarks/startup-semspec.csv new file mode 100644 index 000000000..db90d4b46 --- /dev/null +++ b/docs/src/zh/benchmarks/startup-semspec.csv @@ -0,0 +1,12 @@ +batch,benchmark_id,role,runs,warmup,hyperfine_version,metric_scope,evidence_sha256,manifest_sha256,condition,command,N,Median ms,Mean ms,SD ms,Min ms,Max ms +native-20261010T165842Z-456ayych,B-STARTUP,user-facing,60,3,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,8c1a7b4106d512085aad8f96e3d7f93a8433dc650d37bed4e00a860775504bdf,728d89ddd4aa83008194d7eb4101e11adcd59faf8101fc6d94cb07236cb6d71a,native,"/bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,0.5428189999999999,0.5448796166666666,0.07744633470708881,0.437788,0.7812990000000001 +pvisor_host-20261010T170208Z-q8e1k8f2,B-STARTUP,user-facing,60,3,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,e4ff64d634d1909b5e7f8ba33ba594faf204f766a03cbee7aab462a53d8539e3,886d1d2f27a0f6c8f40d4d36cec50fb240e0e368c102a4b7dddadb985f111747,pvisor-host,"/home/reiase/workspace/pvisor/target/release/pvisor run --no-agent-defaults --overlaynet off --stdio inherit --timeout 30s --filesystem host -- /bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,177.2584755,172.6504246166667,27.25835190318585,118.760767,224.36950000000002 +pvisor_staged-20261010T170343Z-kthbiapw,B-STARTUP,user-facing,60,3,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,a9a90bc0bb92f45906bc95c26693d867aa8a08e7a3d05fbab38672c8fc298b4a,985bc6975f3eabbf2673f3bc58f1a38089aa2848f8e44ade7f91897d91990d2e,pvisor-staged,"/home/reiase/workspace/pvisor/target/release/pvisor run --no-agent-defaults --overlaynet off --stdio inherit --timeout 30s --stage /home/reiase/workspace/pvisor/benchmark.new/startup/.data/pvisor_staged-20261010T170343Z-kthbiapw/current/stage -- /bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,153.850434,154.5756163166667,5.8784454336238,139.860151,178.11594700000003 +pvisor_vm-20261010T170522Z-_ucnw1fs,B-STARTUP,user-facing,60,3,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,009193d1751dd1ffc5e4f6de7539881471824019372843385e88911dd3e17abc,b64051c916339609d9c76b8ea402e3e8fdd2fbfe9d72cb4c2590d4d04f1271a9,pvisor-vm,"/home/reiase/workspace/pvisor/target/release/pvisor run --no-agent-defaults --overlaynet off --stdio inherit --timeout 30s --stage /home/reiase/workspace/pvisor/benchmark.new/startup/.data/pvisor_vm-20261010T170522Z-_ucnw1fs/current/stage --vm --rootfs /home/reiase/workspace/pvisor/benchmark/.data/6ra5/rootfs --cpu 2 --memory 128MiB -- /bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,276.826408,284.53924739999997,32.07646019571068,244.55794400000002,360.190688 +docker-20261010T170722Z-t1_bih1h,B-STARTUP,user-facing,60,3,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,830cff142a2e0fc5b2e3048f21be9baab881202d9d14fee6db52d7171bb6529f,dfd4d2226e5135c2b74e528f7ae6bcf2636cf80a244dbe4262ebd497acdc5234,docker,"docker run --rm --pull never --network none --name pvisor-startup-0T170722Z-t1_bih1h --label pvisor.startup.batch=docker-20261010T170722Z-t1_bih1h --mount type=bind,source=/home/reiase/workspace/pvisor/benchmark.new/startup/.data/docker-20261010T170722Z-t1_bih1h/workspace,target=/work --workdir /work --entrypoint /bench/affinity sha256:7e73ccc8f9aa6a0883f8339b66ac0c0fc8284ffd61ea11cba43d131b5d06d689 0,1 /bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,163.9206565,168.86826574999998,21.384606046647722,144.00522300000003,285.47211500000003 +qemu-20261010T171009Z-blrhft4p,B-STARTUP,user-facing,60,3,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,d14ab629a2e5c3bab8c91bf922b8ffb5a4d5d764ce2c6146c9f9897334132ffb,f3929e404d8239b906596b2b86144dbe31a3fe10cb7e1c9f4f2b3bac65065ffa,qemu,"qemu-system-x86_64 -machine q35 -accel kvm -cpu host -smp 2 -m 128 -nodefaults -display none -serial stdio -no-reboot -kernel /home/reiase/workspace/pvisor/benchmark/.data/stock-kernel2-20261007/vmlinuz -append 'console=ttyS0 reboot=k panic=1 root=/dev/vda rw init=/bench/init quiet pvbench.mode=ready pvbench.scratch=executor' -drive file=/home/reiase/workspace/pvisor/benchmark.new/startup/.data/qemu-20261010T171009Z-blrhft4p/current/rootfs.ext4,format=raw,if=none,id=root -device virtio-blk-pci,drive=root -initrd /home/reiase/workspace/pvisor/benchmark/.data/stock-kernel2-20261007/initrd",60,722.0847175,711.2354185833335,26.748688365603908,664.49363,762.300203 +qemu_microvm-20261010T171236Z-uwfhnjiw,B-STARTUP,user-facing,60,3,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,181c6eec7cdb8cf339e6fa94bcc2e34fb7614224cf3b5498a82bf0471ea2a864,a99c52fed88a07152688bb0f3f6334a01aa84b359fe3ba889b8e7ce71755bbab,qemu-microvm,"qemu-system-x86_64 -machine microvm,acpi=off,x-option-roms=off,pit=off,pic=off,rtc=on -accel kvm -cpu host -smp 2 -m 128 -nodefaults -display none -serial stdio -no-reboot -kernel /home/reiase/workspace/pvisor/benchmark/.data/stock-kernel2-20261007/vmlinuz -append 'console=ttyS0 reboot=t panic=1 root=/dev/vda rw init=/bench/init quiet pvbench.mode=ready pvbench.scratch=executor pci=off' -drive file=/home/reiase/workspace/pvisor/benchmark.new/startup/.data/qemu_microvm-20261010T171236Z-uwfhnjiw/current/rootfs.ext4,format=raw,if=none,id=root -device virtio-blk-device,drive=root -initrd /home/reiase/workspace/pvisor/benchmark/.data/stock-kernel2-20261007/initrd",60,419.649605,429.2667642333335,42.448996785664086,358.36805300000003,521.7273570000001 +qemu_microvm-20261010T171456Z-btvcjqlb,B-STARTUP,user-facing,60,3,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,318fc8345a5ad21155e992ccd1f51242845a002df72aee3cefdd6ab2bfc17918,fae8d591de1055660a02087934500efff85366ef6ad87e5ec96922f74f39bc68,qemu-microvm,"qemu-system-x86_64 -machine microvm,acpi=off,x-option-roms=off,pit=off,pic=off,rtc=on -accel kvm -cpu host -smp 2 -m 128 -nodefaults -display none -serial stdio -no-reboot -kernel /home/reiase/workspace/pvisor/benchmark/.data/stock-kernel2-20261007/vmlinuz -append 'console=ttyS0 reboot=t panic=1 root=/dev/vda rw init=/bench/init quiet pvbench.mode=ready pvbench.scratch=executor pci=off' -drive file=/home/reiase/workspace/pvisor/benchmark.new/startup/.data/qemu_microvm-20261010T171456Z-btvcjqlb/current/rootfs.ext4,format=raw,if=none,id=root -device virtio-blk-device,drive=root -initrd /home/reiase/workspace/pvisor/benchmark/.data/stock-kernel2-20261007/initrd",60,339.4707245,355.0767046166667,86.49622716268142,302.64781700000003,928.470456 +native-20261010T184227Z-qg9cqk2u,B-STARTUP,user-facing,60,3,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,1504d0bfb38f01b3e1481d4c58854d4c986ecf5e7fb5b0e4ec1237434b497c4f,d312adb83d9dd7a36a163987bc4b2126351170d545e5fa0f1583d94b603e8462,native,"/bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,0.4220775,0.4235270833333332,0.01758734921421555,0.393404,0.515292 +pvisor_vm-20261010T184321Z-6f0yo0wi,B-STARTUP,user-facing,60,3,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,7b9eb8a585dd63280e902e8aefbf2a4a917a74c528341b7440ee9fb839687e91,ab2327fd977f75438a97e84cbc7899fcf5be0e27deda3445e55f9107e81d74a9,pvisor-vm,"/home/reiase/workspace/pvisor/target/release/pvisor run --no-agent-defaults --overlaynet off --stdio inherit --timeout 30s --stage /home/reiase/workspace/pvisor/benchmark.new/startup/.data/pvisor_vm-20261010T184321Z-6f0yo0wi/current/stage --vm --rootfs /home/reiase/workspace/pvisor/benchmark/.data/6ra5/rootfs --cpu 2 --memory 128MiB -- /bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,278.82669549999997,305.76999966666665,63.45494958797474,230.387054,523.62118 +pvisor_host-20261010T184736Z-3xq4m_wt,B-STARTUP,user-facing,60,3,hyperfine 2.0.0,direct subject command; hyperfine hooks and semspec excluded,01e92e3c7134054451ab05fff2115608a5538654c6a8ff3f08c1fcf09984c161,c14a02b20a9f9794c4158249ff7eb46f8ebda683df147bf055dc2f4d518b9188,pvisor-host,"/home/reiase/workspace/pvisor/target/release/pvisor run --no-agent-defaults --overlaynet off --stdio inherit --timeout 30s --filesystem host -- /bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,208.7871975,217.9388419333333,34.13297007503415,162.078837,335.255299 diff --git a/docs/src/zh/benchmarks/startup-timing.csv b/docs/src/zh/benchmarks/startup-timing.csv new file mode 100644 index 000000000..fdf33ae1f --- /dev/null +++ b/docs/src/zh/benchmarks/startup-timing.csv @@ -0,0 +1,17 @@ +batch,benchmark_id,role,runs,warmup,hyperfine_version,metric_scope,evidence_sha256,manifest_sha256,measurement_engine,python_version,condition,command,N,Ready median ms,Ready mean ms,Ready SD ms,Ready min ms,Ready max ms,Exit median ms,Exit mean ms,Exit SD ms,Exit min ms,Exit max ms +native_timing-20261010T193144Z-rjxju56s,B-STARTUP,user-facing,60,3,,same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded,ba0b0607cfde7cc9d8851a838ca4a71b38c08eac81f1361f7ab68f96e08b0f24,9d7e1087e6897807884761ed201bfd2b97d71fb2b3600fa161e57792baae65ef,python-perf_counter_ns,3.12.11,native,"/bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,0.477416,0.4867091666666667,0.027473098763457073,0.45534,0.593298,0.5262070000000001,0.5548987,0.14369408916641668,0.504061,1.626316 +pvisor_host_timing-20261010T193144Z-7kp2ohaf,B-STARTUP,user-facing,60,3,,same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded,2a58fe62fb73d6c637241854da6fe0de2c3d1436b3f670c760166fd745d02de6,e3e38d24a5cfcaecad68f5c2a07b270b9654733b23aca3d8ebf13ce07c8c67a6,python-perf_counter_ns,3.12.11,pvisor-host,"/home/reiase/workspace/pvisor/target/release/pvisor run --no-agent-defaults --overlaynet off --stdio inherit --timeout 30s --filesystem host -- /bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,37.453213500000004,37.39181438333333,1.3806339289004357,33.823985,45.32391,129.0226385,128.38440935,2.8130187011713157,116.414786,132.229654 +pvisor_staged_timing-20261010T193207Z-14b8yji_,B-STARTUP,user-facing,60,3,,same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded,d9697f4bf482937967e3b7459fd4fc6f56d1fcf785f1e261fd3ff40fbac5e2b6,1d4b415be94fcfe71048b625e8eff82765e26b2392869a67351bd087abae8764,python-perf_counter_ns,3.12.11,pvisor-staged,"/home/reiase/workspace/pvisor/target/release/pvisor run --no-agent-defaults --overlaynet off --stdio inherit --timeout 30s --stage /home/reiase/workspace/pvisor/benchmark.new/startup/.data/pvisor_staged_timing-20261010T193207Z-14b8yji_/current/stage -- /bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,56.317359499999995,83.63402366666666,35.80452248684234,51.526137,151.770448,159.076721,182.31478523333334,36.35013478894402,139.925129,246.257413 +pvisor_vm-20261010T193231Z-oqxsd107,B-STARTUP,user-facing,60,3,,same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded,f49d48ad7568a601eadf2b470c0513edeef1c5d1864a11ee53dd9fdc418e3a3f,11644c889e7b014109709022e6b4222de3865f15fbf636b0aa9366278d2426cb,python-perf_counter_ns,3.12.11,pvisor-vm,"/home/reiase/workspace/pvisor/target/release/pvisor run --no-agent-defaults --overlaynet off --stdio inherit --timeout 30s --stage /home/reiase/workspace/pvisor/benchmark.new/startup/.data/pvisor_vm-20261010T193231Z-oqxsd107/current/stage --vm --rootfs /home/reiase/workspace/pvisor/benchmark/.data/6ra5/rootfs --cpu 2 --memory 128MiB -- /bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,179.21802150000002,171.2950154,22.10104697452002,137.615608,207.20476,297.7932135,293.0082749,23.8399220189266,247.049522,340.454659 +docker_timing-20261010T193254Z-vfew0_j6,B-STARTUP,user-facing,60,3,,same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded,66772bfad50467e9854931366f9c466936f432ebdb8cf8169f3f3d130f85589a,5583225f3e0382e0208f8d8cbe951fa92b7196dbfe65e5824586aa680a9a601f,python-perf_counter_ns,3.12.11,docker,"docker run --rm --pull never --network none --name pvisor-startup-0T193254Z-vfew0_j6 --label pvisor.startup.batch=docker_timing-20261010T193254Z-vfew0_j6 --mount type=bind,source=/home/reiase/workspace/pvisor/benchmark.new/startup/.data/docker_timing-20261010T193254Z-vfew0_j6/workspace,target=/work --workdir /work --entrypoint /bench/affinity sha256:7e73ccc8f9aa6a0883f8339b66ac0c0fc8284ffd61ea11cba43d131b5d06d689 0,1 /bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,111.212921,112.61178271666667,8.444143141831258,91.518154,140.077263,136.83700499999998,138.7524654,9.00429822650372,118.732902,169.009196 +qemu_timing-20261010T193411Z-xgtqdjpb,B-STARTUP,user-facing,60,3,,same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded,b4d1229af611f7884471e6988eee61219de68aeb1dd7966321196d4091609193,f5a176f8e7b58ce1ce366827fe4b3b5aaf2fdac052e225642a5fb9abb7288912,python-perf_counter_ns,3.12.11,qemu,"qemu-system-x86_64 -machine q35 -accel kvm -cpu host -smp 2 -m 128 -nodefaults -display none -serial stdio -no-reboot -kernel /home/reiase/workspace/pvisor/benchmark/.data/stock-kernel2-20261007/vmlinuz -append 'console=ttyS0 reboot=k panic=1 root=/dev/vda rw init=/bench/init quiet pvbench.mode=ready pvbench.scratch=executor' -drive file=/home/reiase/workspace/pvisor/benchmark.new/startup/.data/qemu_timing-20261010T193411Z-xgtqdjpb/current/rootfs.ext4,format=raw,if=none,id=root -device virtio-blk-pci,drive=root -initrd /home/reiase/workspace/pvisor/benchmark/.data/stock-kernel2-20261007/initrd",60,711.3557575,715.5877327166667,28.84870286560417,676.150134,777.811211,738.2912145,744.53627425,29.35072221760295,698.122196,804.280468 +qemu_microvm_timing-20261010T193436Z-bvzj9q7t,B-STARTUP,user-facing,60,3,,same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded,a12206db296e544ad323a3781bc9f96cd3370dab5cfc795faa0371d5d74aed65,8e217c76c1d10a542fa2ebc9cded9817d74fc0b96924b658f3a2d88c170d0d8b,python-perf_counter_ns,3.12.11,qemu-microvm,"qemu-system-x86_64 -machine microvm,acpi=off,x-option-roms=off,pit=off,pic=off,rtc=on -accel kvm -cpu host -smp 2 -m 128 -nodefaults -display none -serial stdio -no-reboot -kernel /home/reiase/workspace/pvisor/benchmark/.data/stock-kernel2-20261007/vmlinuz -append 'console=ttyS0 reboot=t panic=1 root=/dev/vda rw init=/bench/init quiet pvbench.mode=ready pvbench.scratch=executor pci=off' -drive file=/home/reiase/workspace/pvisor/benchmark.new/startup/.data/qemu_microvm_timing-20261010T193436Z-bvzj9q7t/current/rootfs.ext4,format=raw,if=none,id=root -device virtio-blk-device,drive=root -initrd /home/reiase/workspace/pvisor/benchmark/.data/stock-kernel2-20261007/initrd",60,298.98298450000004,299.9279542,30.521712217844783,244.717479,359.294324,321.4902005,323.5255617166667,29.485465599627037,266.138102,380.83449 +fc_system_timing-20261010T194324Z-0ngxz1rw,B-STARTUP,user-facing,60,3,,same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded,3928edf8392072a5529d79f3d8230829f23a036a11387291dea6707aac6c265d,aaeceeb1b87fe05a7491a51f539e0a4b9a65cbae60a01181c5a7b6cca05ad885,python-perf_counter_ns,3.12.11,fc-system,firecracker --no-api --config-file /home/reiase/workspace/pvisor/benchmark.new/startup/.data/fc_system_timing-20261010T194324Z-0ngxz1rw/current/firecracker.json,60,291.2311145,291.5582495333333,5.8558511887453095,282.256479,313.902527,726.9635,728.0146826166666,5.32211127846656,717.707895,740.754322 +native_timing-20261011T004342Z-8wjyuy21,B-STARTUP,user-facing,60,3,,same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded,355c05aa8af734b85978052114fc98485d339ec681f1e1cebc5bd5b317ac4948,571e859d1e439e764b42696e5280aa02bdeb4a3cb49797164d54116286d797e0,python-perf_counter_ns,3.12.11,native,"/bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,0.4768065,0.4821639,0.024363187201185323,0.453769,0.554185,0.5247455000000001,0.5674711833333334,0.20638363771035492,0.498852,1.721113 +pvisor_host_timing-20261011T004343Z-gdenfkzx,B-STARTUP,user-facing,60,3,,same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded,b5ec9fd9f67ad90cfa3b5aaa77041a2aad2a3426f82a7afd2c63efeccd954adb,768f808263dc663e926b58944b46d1e966d0b700f42a4cdacf91a7e0bd7c27fa,python-perf_counter_ns,3.12.11,pvisor-host,"/home/reiase/workspace/pvisor/target/release/pvisor run --no-agent-defaults --overlaynet off --stdio inherit --timeout 30s --filesystem host -- /bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,33.5245775,50.48170388333333,29.699968167718016,30.57654,128.654942,123.626168,138.93658936666665,27.73345550008831,112.022101,215.842663 +pvisor_staged_timing-20261011T004437Z-0_8_myox,B-STARTUP,user-facing,60,3,,same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded,ecad00dd273bd159cd74112059f1a1a09a0bf0a58cf999f596d5a5d7a2027049,8a4d6d031779febc3b156bfe29163e68ba7ccaa3f3294ff810cdd913b094e22f,python-perf_counter_ns,3.12.11,pvisor-staged,"/home/reiase/workspace/pvisor/target/release/pvisor run --no-agent-defaults --overlaynet off --stdio inherit --timeout 30s --stage /home/reiase/workspace/pvisor/benchmark.new/startup/.data/pvisor_staged_timing-20261011T004437Z-0_8_myox/current/stage -- /bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,50.5909475,52.77206473333333,17.86787212840543,46.178707,188.290473,146.34664,148.0955406,17.210223152340454,133.553461,274.998173 +pvisor_vm-20261011T004527Z-4o2qaxr9,B-STARTUP,user-facing,60,3,,same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded,6f1b2e5eb12b8012bb1f221d94944017c3792625ae27038e489d29808ae43de3,a63cb47729c4b6cf1f37e259241a0b5ee1105157e58a1eb5b79f5e8da82db252,python-perf_counter_ns,3.12.11,pvisor-vm,"/home/reiase/workspace/pvisor/target/release/pvisor run --no-agent-defaults --overlaynet off --stdio inherit --timeout 30s --stage /home/reiase/workspace/pvisor/benchmark.new/startup/.data/pvisor_vm-20261011T004527Z-4o2qaxr9/current/stage --vm --rootfs /home/reiase/workspace/pvisor/benchmark/.data/6ra5/rootfs --cpu 2 --memory 128MiB -- /bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,130.707348,130.10342321666667,6.156610149010636,119.004459,149.988327,243.53000400000002,248.7508986,17.71092203479732,218.450697,288.919716 +docker_timing-20261011T004624Z-8aq0yusi,B-STARTUP,user-facing,60,3,,same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded,9df027d8154142787594d86cd7572465957be3513333dd83c734debd50558c10,aea46ba225d18a1fd202e2460933d809a691636e33b0346319fcd4cc01eeb8d5,python-perf_counter_ns,3.12.11,docker,"docker run --rm --pull never --network none --name pvisor-startup-1T004624Z-8aq0yusi --label pvisor.startup.batch=docker_timing-20261011T004624Z-8aq0yusi --mount type=bind,source=/home/reiase/workspace/pvisor/benchmark.new/startup/.data/docker_timing-20261011T004624Z-8aq0yusi/workspace,target=/work --workdir /work --entrypoint /bench/affinity sha256:7e73ccc8f9aa6a0883f8339b66ac0c0fc8284ffd61ea11cba43d131b5d06d689 0,1 /bin/sh -c 'printf '""'""'REFERENCE_READY\nREFERENCE_RESULT {""mode"":""ready"",""correctness"":""passed""}\n'""'""''",60,105.027266,105.5709834,7.221437055108282,90.523694,126.038887,129.3059355,130.3731021,7.663377586154465,114.191419,152.928737 +fc_system_timing-20261011T004712Z-97n8gne7,B-STARTUP,user-facing,60,3,,same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded,b0343f5973991391c32b67b4a8431a5e992c80cb6c403338d3dc4965e7ce2251,107c830711205462c88ae27f0de0e1088ffee1176a5f9842d483dacc1e8413f3,python-perf_counter_ns,3.12.11,fc-system,firecracker --no-api --config-file /home/reiase/workspace/pvisor/benchmark.new/startup/.data/fc_system_timing-20261011T004712Z-97n8gne7/current/firecracker.json,60,287.127745,287.06792748333334,5.885774302854362,269.078709,301.728159,722.0567149999999,722.7726551166667,6.025874020298251,711.091197,741.229639 +qemu_timing-20261011T004840Z-vh5l4h7y,B-STARTUP,user-facing,60,3,,same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded,7d8b028d8df05ff7b3cc881e9d30b998fb55de23b51c29329969ed1b86ce1ae0,1431f57af3f73fac2331982e186a0d61ba8de88d7f07e1213af23751a1c1fad9,python-perf_counter_ns,3.12.11,qemu,"qemu-system-x86_64 -machine q35 -accel kvm -cpu host -smp 2 -m 128 -nodefaults -display none -serial stdio -no-reboot -kernel /home/reiase/workspace/pvisor/benchmark/.data/stock-kernel2-20261007/vmlinuz -append 'console=ttyS0 reboot=k panic=1 root=/dev/vda rw init=/bench/init quiet pvbench.mode=ready pvbench.scratch=executor' -drive file=/home/reiase/workspace/pvisor/benchmark.new/startup/.data/qemu_timing-20261011T004840Z-vh5l4h7y/current/rootfs.ext4,format=raw,if=none,id=root -device virtio-blk-pci,drive=root -initrd /home/reiase/workspace/pvisor/benchmark/.data/stock-kernel2-20261007/initrd",60,663.2477994999999,667.5293913666667,28.729089815188537,631.375716,742.132208,691.7226949999999,695.4386896,28.6305736872296,656.021112,766.893319 +qemu_microvm_timing-20261011T005005Z-hra4_tyy,B-STARTUP,user-facing,60,3,,same Popen launch to exact Ready line receipt and normal process exit; perf_counter_ns; preparation excluded,eb2c5c6f18620ac4f0f30e1d67aa47aa1fcaa223f6b050ad70ec9982f6a3b457,d5c74b3517c2b8ee545c4a274a8cea36e93d9f3c6b4b9153d0ea7ab4ce1d974a,python-perf_counter_ns,3.12.11,qemu-microvm,"qemu-system-x86_64 -machine microvm,acpi=off,x-option-roms=off,pit=off,pic=off,rtc=on -accel kvm -cpu host -smp 2 -m 128 -nodefaults -display none -serial stdio -no-reboot -kernel /home/reiase/workspace/pvisor/benchmark/.data/stock-kernel2-20261007/vmlinuz -append 'console=ttyS0 reboot=t panic=1 root=/dev/vda rw init=/bench/init quiet pvbench.mode=ready pvbench.scratch=executor pci=off' -drive file=/home/reiase/workspace/pvisor/benchmark.new/startup/.data/qemu_microvm_timing-20261011T005005Z-hra4_tyy/current/rootfs.ext4,format=raw,if=none,id=root -device virtio-blk-device,drive=root -initrd /home/reiase/workspace/pvisor/benchmark/.data/stock-kernel2-20261007/initrd",60,260.10463400000003,259.5273039,9.566460105458674,243.283605,288.390241,281.32751199999996,281.99962423333335,11.500231438448678,261.017615,315.552385 diff --git a/docs/src/zh/benchmarks/startup-timing.exit.render.json b/docs/src/zh/benchmarks/startup-timing.exit.render.json new file mode 100644 index 000000000..22bb56543 --- /dev/null +++ b/docs/src/zh/benchmarks/startup-timing.exit.render.json @@ -0,0 +1,66 @@ +{ + "Native": { + "N": 60, + "Exit 中位数 ms": "0.52", + "Exit 均值 ms": "0.57", + "Exit 标准差 ms": "0.21", + "Exit 最小 ms": "0.50", + "Exit 最大 ms": "1.72" + }, + "pVisor host": { + "N": 60, + "Exit 中位数 ms": "123.63", + "Exit 均值 ms": "138.94", + "Exit 标准差 ms": "27.73", + "Exit 最小 ms": "112.02", + "Exit 最大 ms": "215.84" + }, + "pVisor staged": { + "N": 60, + "Exit 中位数 ms": "146.35", + "Exit 均值 ms": "148.10", + "Exit 标准差 ms": "17.21", + "Exit 最小 ms": "133.55", + "Exit 最大 ms": "275.00" + }, + "pVisor VM": { + "N": 60, + "Exit 中位数 ms": "243.53", + "Exit 均值 ms": "248.75", + "Exit 标准差 ms": "17.71", + "Exit 最小 ms": "218.45", + "Exit 最大 ms": "288.92" + }, + "Docker": { + "N": 60, + "Exit 中位数 ms": "129.31", + "Exit 均值 ms": "130.37", + "Exit 标准差 ms": "7.66", + "Exit 最小 ms": "114.19", + "Exit 最大 ms": "152.93" + }, + "Firecracker": { + "N": 60, + "Exit 中位数 ms": "722.06", + "Exit 均值 ms": "722.77", + "Exit 标准差 ms": "6.03", + "Exit 最小 ms": "711.09", + "Exit 最大 ms": "741.23" + }, + "QEMU": { + "N": 60, + "Exit 中位数 ms": "691.72", + "Exit 均值 ms": "695.44", + "Exit 标准差 ms": "28.63", + "Exit 最小 ms": "656.02", + "Exit 最大 ms": "766.89" + }, + "QEMU microvm": { + "N": 60, + "Exit 中位数 ms": "281.33", + "Exit 均值 ms": "282.00", + "Exit 标准差 ms": "11.50", + "Exit 最小 ms": "261.02", + "Exit 最大 ms": "315.55" + } +} diff --git a/docs/src/zh/benchmarks/startup-timing.ready.render.json b/docs/src/zh/benchmarks/startup-timing.ready.render.json new file mode 100644 index 000000000..c485c90e4 --- /dev/null +++ b/docs/src/zh/benchmarks/startup-timing.ready.render.json @@ -0,0 +1,66 @@ +{ + "Native": { + "N": 60, + "Ready 中位数 ms": "0.48", + "Ready 均值 ms": "0.48", + "Ready 标准差 ms": "0.02", + "Ready 最小 ms": "0.45", + "Ready 最大 ms": "0.55" + }, + "pVisor host": { + "N": 60, + "Ready 中位数 ms": "33.52", + "Ready 均值 ms": "50.48", + "Ready 标准差 ms": "29.70", + "Ready 最小 ms": "30.58", + "Ready 最大 ms": "128.65" + }, + "pVisor staged": { + "N": 60, + "Ready 中位数 ms": "50.59", + "Ready 均值 ms": "52.77", + "Ready 标准差 ms": "17.87", + "Ready 最小 ms": "46.18", + "Ready 最大 ms": "188.29" + }, + "pVisor VM": { + "N": 60, + "Ready 中位数 ms": "130.71", + "Ready 均值 ms": "130.10", + "Ready 标准差 ms": "6.16", + "Ready 最小 ms": "119.00", + "Ready 最大 ms": "149.99" + }, + "Docker": { + "N": 60, + "Ready 中位数 ms": "105.03", + "Ready 均值 ms": "105.57", + "Ready 标准差 ms": "7.22", + "Ready 最小 ms": "90.52", + "Ready 最大 ms": "126.04" + }, + "Firecracker": { + "N": 60, + "Ready 中位数 ms": "287.13", + "Ready 均值 ms": "287.07", + "Ready 标准差 ms": "5.89", + "Ready 最小 ms": "269.08", + "Ready 最大 ms": "301.73" + }, + "QEMU": { + "N": 60, + "Ready 中位数 ms": "663.25", + "Ready 均值 ms": "667.53", + "Ready 标准差 ms": "28.73", + "Ready 最小 ms": "631.38", + "Ready 最大 ms": "742.13" + }, + "QEMU microvm": { + "N": 60, + "Ready 中位数 ms": "260.10", + "Ready 均值 ms": "259.53", + "Ready 标准差 ms": "9.57", + "Ready 最小 ms": "243.28", + "Ready 最大 ms": "288.39" + } +} diff --git a/docs/src/zh/benchmarks/startup.md b/docs/src/zh/benchmarks/startup.md index 2e13644fc..d840e3684 100644 --- a/docs/src/zh/benchmarks/startup.md +++ b/docs/src/zh/benchmarks/startup.md @@ -2,7 +2,9 @@ ## 主要结论 {#conclusions} -**已准备环境下,pVisor host 和 staged 的首条输出分别为 12.72 ms 和 25.13 ms;在 Linux/KVM、2 vCPU / 128 MiB 条件下,pVisor VM 为 100.91 ms,快于使用 Fedora 完整内核的 Firecracker(285.04 ms)、QEMU(702.98 ms)和 microvm(321.56 ms)。** +**2026-10-11 重启后的独立条件复测中,Ready 中位数为 pVisor host 33.52 ms、staged 50.59 ms、VM 130.71 ms、Docker 105.03 ms、Firecracker MMIO 287.13 ms、QEMU 663.25 ms 和 microvm 260.10 ms。Ready 与正常 Exit 来自同次启动的 60 个样本;当前制品的数字应按这组表格读取,历史随机配对的排名和区间不沿用到新批次。** + +以下选型摘要保留历史批次的适用范围。 | 需求 | 选型含义 | | --- | --- | @@ -10,28 +12,69 @@ | 频繁创建一次性 VM | pVisor VM 的首条命令等待约 0.1 秒 | | 完整 Agent 工具任务 | 结合工具与修复任务结果选择执行模式 | + + +本页新增实验与历史数据分开发布;各表适用的计时范围和输入见实验设计与来源,不合并不同批次。 + + + ## Motivation {#motivation} 频繁创建一次性环境时,首条命令的等待会累积。选择 host、staged、容器或 VM,需要知道各模式的启动等待,以及隔离需求带来的成本。短任务还要预算退出和工具执行。 ## 实验设计 {#interpretation} -Linux x86_64 / KVM,AMD Ryzen 7 9700X,Fedora 宿主。对照为 native、pVisor host、pVisor staged、pVisor VM、Docker rootless / overlay2、Firecracker PCI、QEMU q35 和 QEMU microvm。进程固定到 CPU 0,1,VM 均为 2 vCPU / 128 MiB;进程和容器不设内存上限。 +历史 Ready / Exit 对照使用 Linux x86_64 / KVM,AMD Ryzen 7 9700X,Fedora 宿主。对照为 native、pVisor host、pVisor staged、pVisor VM、Docker rootless / overlay2、Firecracker PCI、QEMU q35 和 QEMU microvm。进程固定到 CPU 0,1,VM 均为 2 vCPU / 128 MiB;进程和容器不设内存上限。 Firecracker、QEMU 和 microvm 使用 Fedora 的完整发行版内核,Linux 7.2.8-200.fc44.x86_64。pVisor 的虚拟化基于 libkrun,使用该项目配套 libkrunfw 的定制内核,Linux 6.12.109。 使用已准备的用户态环境和相同的 `/bin/sh` 输出探针,每次执行使用新环境与私有工作区,VM 每次重新启动,不使用快照或环境池。热页缓存,每组 3 次预热、60 次正式样本,组内随机交替执行。环境准备和输入复制不计入启动时间。内核、设备和存储路径不同,结果反映各配置的完整启动成本;这个探针不代表完整发行版开机、冷镜像、Agent CLI 初始化、并发容量或快照恢复。 -Ready 截止到校验后的首条 shell 标记;Exit 包含执行和收尾,要求正确输出并正常零退出。Firecracker 在校验标记和结果、确认无 panic 后受控终止,只报告 Ready。失败、panic、输出不匹配或制品改变的样本不计入耗时分布;保留有效慢样本。 +历史 Ready 截止到校验后的首条 shell 标记;Exit 包含执行和收尾,要求正确输出并正常零退出。Firecracker 在校验标记和结果、确认无 panic 后受控终止,只报告 Ready。失败、panic、输出不匹配或制品改变的样本不计入耗时分布;保留有效慢样本。 + +历史 hyperfine 对照使用当时每个条件的独立 JSON 模板,通过 semspec bench 执行,统一 3 次预热和 60 次采样。它测量完整命令到正常退出,不测首条 Ready;每次输入准备与结果校验在计时外。各条件独立采样,不合并批次或推导配对改善比例。该批次校验 Run Bundle 中的实际执行模式:本机 host 和 staged 使用 `rootless_process`,VM 使用 `vm`;制品摘要与批次来源独立记录。 ## 实验数据和分析 {#results} -### 启动与退出耗时 {#reference-startup} +### Python:同次启动的 Ready / Exit {#python-ready-exit} + +同一进程启动中记录开始、收到完整 `REFERENCE_READY` 行和正常退出三个时刻,固定 +3 次预热、60 次正式样本;下面两张表只是同一个命令输出的列投影,不重复采样。 +已准备 rootfs、2 vCPU / 128 MiB、热缓存、CPU 0,1 亲和性。Python `perf_counter_ns` +计时不包含解释器启动和输入准备;Ready 包含进程创建、管道读取和观察者调度, +Exit 从同一开始时刻计至观察到正常退出。全部样本还须通过输出、Run Bundle 和输入校验。 +新 Firecracker 使用 stock 内核、同一 initrd 和 virtio-MMIO;PCI 配置两次预检在成功 guest 标记后无法正常退出,整批拒绝发布。历史 Firecracker PCI 的 Ready 与新 MMIO 是不同配置,不能相减解释为性能改善。 + +#### Ready + +{{ table("startup-timing.ready.render.json") }} + +#### Exit + +{{ table("startup-timing.exit.render.json") }} + +[同次样本统计 CSV](startup-timing.csv) 与 [来源 JSON](../assets/benchmarks/startup-timing.provenance.json) +记录各条件最新成功的独立批次,标准差采用正式样本的样本标准差。这是 Python 事件时钟数据,独立于 +hyperfine 完整命令实验;历史不同制品的数字不与这组样本合并。整表复测使用 +`just benchmark startup/timing`;仅复测 VM 使用 `just benchmark startup/timing "pvisor vm"`。 +[此前 VM 单条件批次](startup-ready-exit.csv) 保留,不混入新样本。 + +2026-10-11 重启后重新测量全部八个条件,[重启前后对照 CSV](startup-reboot-comparison.csv) +保留两批各自的完整精度统计与来源摘要。被测 pVisor 二进制 SHA-256 为 +`d3f09687cca60b617629c215fa1359c441ab6ac19f8c63b68f1dcdf7614e6b30`,重启前后相同; +用户态输入、被测命令、计时和统计函数也相同。框架期间新增失败处理与来源字段,均位于计时外。 +VM Ready 中位数从 179.22 ms 降至 130.71 ms(描述性差值为 -27.07%),Exit 从 +297.79 ms 降至 243.53 ms。Host Ready 中位数从 37.45 ms 降至 33.52 ms,但标准差 +从 1.38 ms 升至 29.70 ms,后 17 个正式样本明显变慢;全部有效慢样本保留。 +两批独立采样,且仍使用热缓存,这组比较不能单独建立重启的因果效果。 +历史约 101 ms 的 VM Ready 使用不同二进制及固件,剩余差距需要固定制品的对照才能定位。 + +### 历史配对:启动与退出耗时 {#reference-startup} -单位为 ms,每组 N=60,正式失败均为 0。进程/容器与 VM 分别采样,各行保留自己的统计值;四种 VM 在同组内随机交替执行。P95 仅供分布观察,不是稳定尾延迟保证。 +历史对照单位为 ms,每组 N=60,正式失败均为 0。进程/容器与 VM 分别采样,各行保留自己的统计值;四种 VM 在同组内随机交替执行。P95 仅供分布观察,不是稳定尾延迟保证。 | Runtime | Valid / failed | Ready P50 ms | Ready P95 ms | Exit P50 ms | Exit P95 ms | | --- | --- | --- | --- | --- | --- | @@ -56,6 +99,61 @@ host 和 staged 的等待为十几到几十毫秒,适合频繁启动的短命 冷镜像的客户端等待与内容下载见独立的[镜像按需启动](lazy-image-startup.md)实验;镜像服务准备成本单列,样本不与这里的预准备环境合并。 + + +### 历史 Python:仅 Ready 数据 {#python-ready} + +这组实验使用已准备好的 rootfs、2 vCPU / 128 MiB、热缓存和 CPU 0,1 亲和性。 +固定 3 次预热、60 次正式样本;Python `perf_counter_ns` 从 Popen 前计到收到完整的 +`REFERENCE_READY` 行,包含进程创建、管道读取和观察者调度开销。解释器启动、输入准备、 +后续退出和清理不计入 Ready。停表后仍要求正确输出、正常零退出和通过 Run Bundle 校验。 + +| | N | Ready 中位数 ms | Ready 均值 ms | Ready 标准差 ms | Ready 最小 ms | Ready 最大 ms | +| --- | --- | --- | --- | --- | --- | --- | +| pVisor VM | 60 | 132.95 | 132.51 | 5.09 | 123.00 | 147.53 | + +[Ready 统计 CSV](startup-ready.csv) 与 [来源 JSON](../assets/benchmarks/startup-ready.provenance.json) 保留该条件 +最新成功批次;标准差是正式样本的样本标准差。这是 Python 时钟数据,不是 hyperfine +完整退出耗时。历史 Ready 使用不同二进制制品,不能只根据计时边界解释两者数值差异。 +当前 Ready / Exit 实验使用 `just benchmark startup/timing "pvisor vm"`;本节保留旧的 Ready-only 批次。 + +### 历史 Hyperfine:直接启动到正常退出 {#hyperfine-startup} + +每行取该条件最新成功批次,独立采样,不合并批次。3 次预热、60 次正式样本;中位数、均值和标准差来自同一份 hyperfine 原始导出,单位为 ms。Python 准备和校验、semspec 处理及外部 CPU 亲和性设置均在计时外。 + +| | N | 中位数 ms | 均值 ms | 标准差 ms | +| --- | --- | --- | --- | --- | +| Native | 60 | 0.42 | 0.42 | 0.02 | +| pVisor host | 60 | 208.79 | 217.94 | 34.13 | +| pVisor staged | 60 | 153.85 | 154.58 | 5.88 | +| pVisor VM | 60 | 278.83 | 305.77 | 63.45 | +| Docker | 60 | 163.92 | 168.87 | 21.38 | +| Firecracker | — | — | — | — | +| QEMU | 60 | 722.08 | 711.24 | 26.75 | +| QEMU microvm | 60 | 339.47 | 355.08 | 86.50 | + +空白条件尚无通过校验的新批次。它测量命令到正常退出,不等于首条输出 Ready;各行仅并列展示水平,不提供随机配对改善比例或置信区间。实际命令、极值及逐行来源见 [统计 CSV](startup-semspec.csv) 和 [来源 JSON](../assets/benchmarks/startup-semspec.provenance.json)。 + +
+历史完整命令结果(包含包装器) + +| | N | 中位数 ms | 均值 ms | 标准差 ms | +| --- | --- | --- | --- | --- | +| Native | 60 | 2.13 | 2.17 | 0.23 | +| pVisor host | 60 | 165.12 | 315.84 | 259.74 | +| pVisor staged | — | — | — | — | +| pVisor VM | — | — | — | — | +| Docker | 60 | 167.27 | 381.08 | 509.32 | +| Firecracker | — | — | — | — | +| QEMU | 60 | 748.41 | 896.73 | 319.03 | +| QEMU microvm | 60 | 353.76 | 451.13 | 219.93 | + +[历史 CSV](startup-hyperfine.csv) 的计时包含外层 shell、taskset 和 timeout,保留原始口径。它与直接命令是不同批次、不同范围,不能通过相减估计包装器成本或解释为产品性能改善。 + +
+ + + ### 数据下载与复现 {#run} -[启动统计](startup.csv) · [VM 差异与 95% 区间](startup-stock-comparisons.csv) · [VM 源码与制品来源](startup-stock-provenance.csv) · [比较方法](methodology.md) · [复现手册](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/pvisor/README.md#explicit-firecracker-kernel-controls)。二进制、完整输入和原始日志保留在本地忽略的 `.data/`;CSV 保留统计来源与样本数,来源记录保留 CPU/RAM 配置、报告和制品摘要。 +[启动统计](startup.csv) · [VM 差异与 95% 区间](startup-stock-comparisons.csv) · [VM 源码与制品来源](startup-stock-provenance.csv) · [比较方法](methodology.md) · [历史复现手册](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/pvisor/README.md#explicit-firecracker-kernel-controls)。当前 Ready / Exit 复测见 [启动实验手册](https://github.com/DeepLink-org/pvisor/blob/main/benchmark.new/startup/README.md)。二进制、完整输入和原始日志保留在本地忽略的 `.data/`;CSV 保留统计来源与样本数,来源记录保留 CPU/RAM 配置、报告和制品摘要。 diff --git a/docs/src/zh/benchmarks/supervision-cost.md b/docs/src/zh/benchmarks/supervision-cost.md index 2ff93f538..e67d5f94c 100644 --- a/docs/src/zh/benchmarks/supervision-cost.md +++ b/docs/src/zh/benchmarks/supervision-cost.md @@ -12,6 +12,12 @@ | 小工作区或已准备并复用的 Git worktree | 小工作区及 20 文件预备视图的审查流程 Git 较快;长期复用未测 | | 估算人工监督成本 | 机器流程不包含人的阅读和判断时间 | + + +本页数值和结论仍属于原始历史批次;新框架尚无匹配的复测数据,见[复测覆盖](index.md#measurement-coverage)。 + + + ## Motivation {#motivation} 一次 Agent 任务除了执行工具,还需要独立工作区、查看 diff、保留指定改动和清理剩余结果。只比较单项文件操作,会漏掉这些成本。需要频繁创建任务工作区时,应比较完成同样结果的整个流程。 diff --git a/docs/src/zh/benchmarks/vm-memory/index.md b/docs/src/zh/benchmarks/vm-memory/index.md index 48e280ee6..f7aff01fc 100644 --- a/docs/src/zh/benchmarks/vm-memory/index.md +++ b/docs/src/zh/benchmarks/vm-memory/index.md @@ -14,6 +14,12 @@ | KSM | 观察 60 秒后,重复内容每实例节约 63.6%,相同随机内容节约 51.7%,独有随机内容节约 16.0% | | daemon 内存池 | 每实例常驻约 61–124 MiB;重复 / 相同随机 / 独有随机内容的节约为 53.7% / 39.6% / 6.5% | + + +本页数值和结论仍属于原始历史批次;新框架尚无匹配的复测数据,见[复测覆盖](../index.md#measurement-coverage)。 + + + ## 实验动机 {#motivation} 512 MiB 是 guest 可使用的容量,不是 VM 在宿主上已经占用的物理内存。选择压缩、卸载或共享时,需要看实际宿主占用,以及下一次工具的恢复代价。只看 guest 空闲量或压缩率,都不足以判断机器省下了多少 RAM。 diff --git a/docs/src/zh/design/concurrency-filesystem-summary.csv b/docs/src/zh/design/concurrency-filesystem-summary.csv new file mode 100644 index 000000000..4a30c08ad --- /dev/null +++ b/docs/src/zh/design/concurrency-filesystem-summary.csv @@ -0,0 +1,67 @@ +benchmark_id,batch,backend,metric,reference,condition,unit,n,failures,reference_p50_ms,candidate_p50_ms,reference_distribution,candidate_distribution,reference_clusters,candidate_clusters,median_difference_ms,ci95_low_ms,ci95_high_ms,change_percent,ci95_low_percent,ci95_high_percent,interpretation +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,ready_ms,before,baseline,ms,30,0,229.91647,229.98571349999997,unsplit,unsplit,"{""n"": 30, ""p50"": 229.91647, ""p95_reference"": 238.32857479999998, ""minimum"": 225.00713, ""maximum"": 239.060502, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 229.98571349999997, ""p95_reference"": 239.74732755, ""minimum"": 225.925142, ""maximum"": 241.800046, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.06924349999997048,-3.7560089999999775,1.5340036000000123,0.030116807203928175,-1.600658088796314,0.6705387744533059,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,result_ms,before,baseline,ms,30,0,1432.675709,1452.1272185,unsplit,unsplit,"{""n"": 30, ""p50"": 1432.675709, ""p95_reference"": 1483.4033009999998, ""minimum"": 1410.577951, ""maximum"": 1508.418277, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 1452.1272185, ""p95_reference"": 1482.17247615, ""minimum"": 1425.4714, ""maximum"": 1493.164094, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",19.45150949999993,1.4766259999998965,33.376978000000236,1.357704983605612,0.1027493854155681,2.341979201568112,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,completion_ms,before,baseline,ms,30,0,1736.98509,1755.9002185,unsplit,unsplit,"{""n"": 30, ""p50"": 1736.98509, ""p95_reference"": 1806.5802087, ""minimum"": 1709.818704, ""maximum"": 1843.440248, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 1755.9002185, ""p95_reference"": 1801.23240945, ""minimum"": 1710.367224, ""maximum"": 1822.801743, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",18.915128500000037,5.857880962499979,37.47388799999999,1.0889632046294784,0.334793959152343,2.161752363391889,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,worker_ms,before,baseline,ms,30,0,1200.7365585,1221.6369465,unsplit,unsplit,"{""n"": 30, ""p50"": 1200.7365585, ""p95_reference"": 1249.71875875, ""minimum"": 1183.925077, ""maximum"": 1272.398219, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 1221.6369465, ""p95_reference"": 1251.0438589999999, ""minimum"": 1197.346921, ""maximum"": 1254.41953, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",20.90038800000002,4.867007324999998,28.62760000000003,1.7406306031116037,0.40108238884013325,2.3982232479462073,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,metadata,before,baseline,ms,30,0,66.8129905,66.747274,unsplit,unsplit,"{""n"": 30, ""p50"": 66.8129905, ""p95_reference"": 68.5091225, ""minimum"": 65.630669, ""maximum"": 70.287737, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 66.747274, ""p95_reference"": 67.72837894999999, ""minimum"": 65.793975, ""maximum"": 69.340539, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.06571649999999352,-0.5632655,0.33756449999999916,-0.09835886630459312,-0.8391419689548707,0.5074020396881318,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,read,before,baseline,ms,30,0,66.82559699999999,67.200728,unsplit,unsplit,"{""n"": 30, ""p50"": 66.82559699999999, ""p95_reference"": 84.0615196, ""minimum"": 64.394717, ""maximum"": 86.841035, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 67.200728, ""p95_reference"": 87.6224648, ""minimum"": 64.505854, ""maximum"": 89.45771, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.3751310000000103,-0.246721462499999,1.3834005000000076,0.5613582471998058,-0.3686651121435963,2.070130098516554,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,write,before,baseline,ms,30,0,25.8121755,27.7569145,unsplit,unsplit,"{""n"": 30, ""p50"": 25.8121755, ""p95_reference"": 27.6612622, ""minimum"": 24.89516, ""maximum"": 38.060812, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 27.7569145, ""p95_reference"": 31.669841, ""minimum"": 26.927324, ""maximum"": 46.557162, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",1.944739000000002,1.5309370000000015,2.6798775,7.534192536386564,5.865745079707496,10.41130974741223,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,git,before,baseline,ms,30,0,139.85539599999998,142.645583,unsplit,unsplit,"{""n"": 30, ""p50"": 139.85539599999998, ""p95_reference"": 159.10001265, ""minimum"": 137.212516, ""maximum"": 160.940306, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 142.645583, ""p95_reference"": 151.54488995, ""minimum"": 138.077973, ""maximum"": 167.480336, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",2.790187000000003,0.23915699999997742,4.193277000000023,1.9950513743495435,0.16996744285950882,3.011356100774054,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,rg,before,baseline,ms,30,0,106.22186500000001,107.2148145,unsplit,unsplit,"{""n"": 30, ""p50"": 106.22186500000001, ""p95_reference"": 109.24850185, ""minimum"": 103.694053, ""maximum"": 111.563961, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 107.2148145, ""p95_reference"": 109.80437595000001, ""minimum"": 105.088613, ""maximum"": 114.976122, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.9929494999999946,0.17080017499999797,2.0470097124999853,0.9347882378077266,0.16032914825811095,1.9489317087555635,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,cargo,before,baseline,ms,30,0,69.4859085,71.7917695,unsplit,unsplit,"{""n"": 30, ""p50"": 69.4859085, ""p95_reference"": 89.68605625000001, ""minimum"": 66.90582, ""maximum"": 97.637597, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 71.7917695, ""p95_reference"": 94.70548355, ""minimum"": 68.274296, ""maximum"": 94.887111, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",2.3058610000000073,1.0185160000000053,3.925326999999996,3.318458446866246,1.4565101933340818,5.645965992760083,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,npm,before,baseline,ms,30,0,229.7664595,230.2638835,unsplit,unsplit,"{""n"": 30, ""p50"": 229.7664595, ""p95_reference"": 238.9916174, ""minimum"": 223.771816, ""maximum"": 244.285887, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 230.2638835, ""p95_reference"": 235.71617395, ""minimum"": 222.969277, ""maximum"": 238.44851, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.4974239999999952,-1.4246772249999993,2.522163000000006,0.21649112802732962,-0.6176680833597179,1.0962831613243518,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,ready_ms,baseline,candidate,ms,30,0,229.98571349999997,219.2519275,unsplit,unsplit,"{""n"": 30, ""p50"": 229.98571349999997, ""p95_reference"": 239.74732755, ""minimum"": 225.925142, ""maximum"": 241.800046, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 219.2519275, ""p95_reference"": 225.1728297, ""minimum"": 216.247081, ""maximum"": 227.081684, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-10.73378599999998,-11.616524499999997,-9.230597999999986,-4.667153379507627,-5.040109487846952,-4.0275789223127605,faster +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,result_ms,baseline,candidate,ms,30,0,1452.1272185,1424.0447745,unsplit,unsplit,"{""n"": 30, ""p50"": 1452.1272185, ""p95_reference"": 1482.17247615, ""minimum"": 1425.4714, ""maximum"": 1493.164094, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 1424.0447745, ""p95_reference"": 1475.2823721, ""minimum"": 1410.463259, ""maximum"": 1503.673034, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-28.082444000000123,-38.423754500000086,-8.237163025000063,-1.933883177880813,-2.627129382665516,-0.5668043495671742,faster +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,completion_ms,baseline,candidate,ms,30,0,1755.9002185,1668.1645155,unsplit,unsplit,"{""n"": 30, ""p50"": 1755.9002185, ""p95_reference"": 1801.23240945, ""minimum"": 1710.367224, ""maximum"": 1822.801743, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 1668.1645155, ""p95_reference"": 1718.1817415, ""minimum"": 1640.05777, ""maximum"": 1745.974529, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-87.73570299999983,-99.79265829999989,-66.70573049999985,-4.99662236359587,-5.645611656690141,-3.801581220258332,faster +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,worker_ms,baseline,candidate,ms,30,0,1221.6369465,1204.6902355,unsplit,unsplit,"{""n"": 30, ""p50"": 1221.6369465, ""p95_reference"": 1251.0438589999999, ""minimum"": 1197.346921, ""maximum"": 1254.41953, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 1204.6902355, ""p95_reference"": 1254.5410703, ""minimum"": 1192.402507, ""maximum"": 1284.236761, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-16.94671100000005,-21.77219178749984,0.02503700000011122,-1.3872133655217667,-1.7787872004971146,0.002047580554398465,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,metadata,baseline,candidate,ms,30,0,66.747274,66.34579550000001,unsplit,unsplit,"{""n"": 30, ""p50"": 66.747274, ""p95_reference"": 67.72837894999999, ""minimum"": 65.793975, ""maximum"": 69.340539, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 66.34579550000001, ""p95_reference"": 67.6513001, ""minimum"": 64.847312, ""maximum"": 69.493501, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.4014784999999961,-0.7117880000000127,0.025973675000000262,-0.6014904818434919,-1.0658511874154009,0.03905143622526831,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,read,baseline,candidate,ms,30,0,67.200728,67.6185575,unsplit,unsplit,"{""n"": 30, ""p50"": 67.200728, ""p95_reference"": 87.6224648, ""minimum"": 64.505854, ""maximum"": 89.45771, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 67.6185575, ""p95_reference"": 86.2219173, ""minimum"": 64.034195, ""maximum"": 91.423127, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.4178294999999963,-0.821345000000008,1.465355925000001,0.6217633535160472,-1.199008245463562,2.2030811609936567,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,write,baseline,candidate,ms,30,0,27.7569145,26.6695915,unsplit,unsplit,"{""n"": 30, ""p50"": 27.7569145, ""p95_reference"": 31.669841, ""minimum"": 26.927324, ""maximum"": 46.557162, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 26.6695915, ""p95_reference"": 28.10140115, ""minimum"": 25.499008, ""maximum"": 42.255647, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-1.0873230000000014,-1.8405830000000023,-0.6787167250000019,-3.9173050016059974,-6.469080863044775,-2.44476937509307,faster +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,git,baseline,candidate,ms,30,0,142.645583,141.874509,unsplit,unsplit,"{""n"": 30, ""p50"": 142.645583, ""p95_reference"": 151.54488995, ""minimum"": 138.077973, ""maximum"": 167.480336, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 141.874509, ""p95_reference"": 146.1360852, ""minimum"": 138.46138, ""maximum"": 166.552746, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.7710739999999987,-2.0200437624999963,1.264147237499974,-0.5405523141925772,-1.4056665800519654,0.897091209424094,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,rg,baseline,candidate,ms,30,0,107.2148145,107.5758025,unsplit,unsplit,"{""n"": 30, ""p50"": 107.2148145, ""p95_reference"": 109.80437595000001, ""minimum"": 105.088613, ""maximum"": 114.976122, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 107.5758025, ""p95_reference"": 109.17498499999999, ""minimum"": 104.800776, ""maximum"": 109.830677, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.36098799999999187,-0.41142899999999827,1.2563604999999853,0.33669600762120666,-0.38314212460381913,1.1816846254704583,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,cargo,baseline,candidate,ms,30,0,71.7917695,70.61406149999999,unsplit,unsplit,"{""n"": 30, ""p50"": 71.7917695, ""p95_reference"": 94.70548355, ""minimum"": 68.274296, ""maximum"": 94.887111, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 70.61406149999999, ""p95_reference"": 87.73722915, ""minimum"": 67.557371, ""maximum"": 92.931381, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-1.1777080000000097,-2.770897000000005,0.5887617624999866,-1.6404498847183424,-3.7814851081183347,0.8251468207752486,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,npm,baseline,candidate,ms,30,0,230.2638835,230.2610775,unsplit,unsplit,"{""n"": 30, ""p50"": 230.2638835, ""p95_reference"": 235.71617395, ""minimum"": 222.969277, ""maximum"": 238.44851, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 230.2610775, ""p95_reference"": 242.12161375, ""minimum"": 222.594656, ""maximum"": 251.761305, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.0028059999999925367,-2.284809999999993,2.131650499999978,-0.00121860187421996,-0.9857928417418615,0.9299392205147416,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,ready_ms,before,candidate,ms,30,0,229.91647,219.2519275,unsplit,unsplit,"{""n"": 30, ""p50"": 229.91647, ""p95_reference"": 238.32857479999998, ""minimum"": 225.00713, ""maximum"": 239.060502, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 219.2519275, ""p95_reference"": 225.1728297, ""minimum"": 216.247081, ""maximum"": 227.081684, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-10.66454250000001,-14.508426999999983,-9.276924499999978,-4.638442169888924,-6.201985461067475,-4.057051099123587,faster +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,result_ms,before,candidate,ms,30,0,1432.675709,1424.0447745,unsplit,unsplit,"{""n"": 30, ""p50"": 1432.675709, ""p95_reference"": 1483.4033009999998, ""minimum"": 1410.577951, ""maximum"": 1508.418277, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 1424.0447745, ""p95_reference"": 1475.2823721, ""minimum"": 1410.463259, ""maximum"": 1503.673034, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-8.630934500000194,-24.200380499999937,11.341323000000102,-0.6024346225584121,-1.6726311854847076,0.791700062704126,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,completion_ms,before,candidate,ms,30,0,1736.98509,1668.1645155,unsplit,unsplit,"{""n"": 30, ""p50"": 1736.98509, ""p95_reference"": 1806.5802087, ""minimum"": 1709.818704, ""maximum"": 1843.440248, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 1668.1645155, ""p95_reference"": 1718.1817415, ""minimum"": 1640.05777, ""maximum"": 1745.974529, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-68.8205744999998,-78.74124916250011,-48.03843048749988,-3.9620705379802557,-4.508901934704912,-2.7709160549363636,faster +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,worker_ms,before,candidate,ms,30,0,1200.7365585,1204.6902355,unsplit,unsplit,"{""n"": 30, ""p50"": 1200.7365585, ""p95_reference"": 1249.71875875, ""minimum"": 1183.925077, ""maximum"": 1272.398219, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 1204.6902355, ""p95_reference"": 1254.5410703, ""minimum"": 1192.402507, ""maximum"": 1284.236761, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",3.9536769999999706,-10.102030037499992,18.527396437499945,0.32927097721910936,-0.8336866554901787,1.5440042793461561,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,metadata,before,candidate,ms,30,0,66.8129905,66.34579550000001,unsplit,unsplit,"{""n"": 30, ""p50"": 66.8129905, ""p95_reference"": 68.5091225, ""minimum"": 65.630669, ""maximum"": 70.287737, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 66.34579550000001, ""p95_reference"": 67.6513001, ""minimum"": 64.847312, ""maximum"": 69.493501, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.4671949999999896,-0.8462214250000003,-0.09388767500000254,-0.6992577289292146,-1.2609061646048372,-0.14090263251865448,faster +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,read,before,candidate,ms,30,0,66.82559699999999,67.6185575,unsplit,unsplit,"{""n"": 30, ""p50"": 66.82559699999999, ""p95_reference"": 84.0615196, ""minimum"": 64.394717, ""maximum"": 86.841035, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 67.6185575, ""p95_reference"": 86.2219173, ""minimum"": 64.034195, ""maximum"": 91.423127, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.7929605000000066,-0.044775099999996834,1.700723999999994,1.186611920578895,-0.0667637260475182,2.555513183817193,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,write,before,candidate,ms,30,0,25.8121755,26.6695915,unsplit,unsplit,"{""n"": 30, ""p50"": 25.8121755, ""p95_reference"": 27.6612622, ""minimum"": 24.89516, ""maximum"": 38.060812, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 26.6695915, ""p95_reference"": 28.10140115, ""minimum"": 25.499008, ""maximum"": 42.255647, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.8574160000000006,0.4421379999999999,1.353806500000001,3.321750233722076,1.693633447021649,5.299561377085471,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,git,before,candidate,ms,30,0,139.85539599999998,141.874509,unsplit,unsplit,"{""n"": 30, ""p50"": 139.85539599999998, ""p95_reference"": 159.10001265, ""minimum"": 137.212516, ""maximum"": 160.940306, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 141.874509, ""p95_reference"": 146.1360852, ""minimum"": 138.46138, ""maximum"": 166.552746, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",2.0191130000000044,0.8281499999999937,3.2326907500000104,1.443714763783599,0.5872585675185338,2.3259170570486765,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,rg,before,candidate,ms,30,0,106.22186500000001,107.5758025,unsplit,unsplit,"{""n"": 30, ""p50"": 106.22186500000001, ""p95_reference"": 109.24850185, ""minimum"": 103.694053, ""maximum"": 111.563961, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 107.5758025, ""p95_reference"": 109.17498499999999, ""minimum"": 104.800776, ""maximum"": 109.830677, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",1.3539374999999865,0.4326582374999969,2.420104500000008,1.2746316401053503,0.4060921276966667,2.29953303821655,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,cargo,before,candidate,ms,30,0,69.4859085,70.61406149999999,unsplit,unsplit,"{""n"": 30, ""p50"": 69.4859085, ""p95_reference"": 89.68605625000001, ""minimum"": 66.90582, ""maximum"": 97.637597, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 70.61406149999999, ""p95_reference"": 87.73722915, ""minimum"": 67.557371, ""maximum"": 92.931381, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",1.1281529999999975,-0.19087100000000135,2.7702473749999963,1.6235709143818777,-0.2695991190721614,4.008438674779477,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-staged,npm,before,candidate,ms,30,0,229.7664595,230.2610775,unsplit,unsplit,"{""n"": 30, ""p50"": 229.7664595, ""p95_reference"": 238.9916174, ""minimum"": 223.771816, ""maximum"": 244.285887, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 230.2610775, ""p95_reference"": 242.12161375, ""minimum"": 222.594656, ""maximum"": 251.761305, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.49461800000000267,-1.3394670000000133,2.3905861500000043,0.2152698879881454,-0.5818155197230057,1.0426051565434238,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,ready_ms,before,baseline,ms,30,0,701.0899955,701.291806,unsplit,unsplit,"{""n"": 30, ""p50"": 701.0899955, ""p95_reference"": 732.14511455, ""minimum"": 686.675537, ""maximum"": 873.145236, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 701.291806, ""p95_reference"": 731.1870892, ""minimum"": 686.521945, ""maximum"": 789.906315, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.20181049999996503,-6.143235000000004,7.4441127000000655,0.028785248868956614,-0.8710870179678643,1.0656154816754777,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,result_ms,before,baseline,ms,30,0,4297.801669,4278.449968,unsplit,unsplit,"{""n"": 30, ""p50"": 4297.801669, ""p95_reference"": 5080.3648109000005, ""minimum"": 4074.466407, ""maximum"": 5228.259533, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 4278.449968, ""p95_reference"": 5340.32934275, ""minimum"": 4069.431508, ""maximum"": 5474.773979, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-19.351701000000503,-130.84436600000026,98.9951629999996,-0.45026975394384383,-2.969052346742973,2.347429750542762,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,completion_ms,before,baseline,ms,30,0,4544.0489055,4535.762954,unsplit,unsplit,"{""n"": 30, ""p50"": 4544.0489055, ""p95_reference"": 5334.32411415, ""minimum"": 4317.653732, ""maximum"": 5526.082774, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 4535.762954, ""p95_reference"": 5577.58314435, ""minimum"": 4314.00466, ""maximum"": 5766.958044, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-8.28595150000001,-116.6800845000007,104.18260949999967,-0.18234732222998096,-2.5157048607849952,2.3138633248019977,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,worker_ms,before,baseline,ms,30,0,3589.173895,3578.175109,unsplit,unsplit,"{""n"": 30, ""p50"": 3589.173895, ""p95_reference"": 4356.26645515, ""minimum"": 3375.558433, ""maximum"": 4522.132202, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 3578.175109, ""p95_reference"": 4633.7424845000005, ""minimum"": 3367.664205, ""maximum"": 4777.305497, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-10.99878600000011,-130.28897150000012,74.88542894999988,-0.30644338563038387,-3.514336657179251,2.101642282507704,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,metadata,before,baseline,ms,30,0,133.4563125,157.8011775,unsplit,unsplit,"{""n"": 30, ""p50"": 133.4563125, ""p95_reference"": 192.7246791, ""minimum"": 108.503417, ""maximum"": 194.824494, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 157.8011775, ""p95_reference"": 189.13611625000001, ""minimum"": 112.822408, ""maximum"": 198.907503, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",24.344865,12.818983487499969,38.27126057499998,18.241823518089497,9.172649821108937,31.596964415085505,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,read,before,baseline,ms,30,0,115.795024,117.672345,unsplit,unsplit,"{""n"": 30, ""p50"": 115.795024, ""p95_reference"": 136.34799395000002, ""minimum"": 110.380387, ""maximum"": 609.142557, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 117.672345, ""p95_reference"": 141.71966129999998, ""minimum"": 112.567169, ""maximum"": 142.482554, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",1.8773210000000091,-0.32354749999998944,4.782931500000004,1.621244968177571,-0.2742023414077699,4.172260618919554,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,write,before,baseline,ms,30,0,131.376992,131.0506595,unsplit,unsplit,"{""n"": 30, ""p50"": 131.376992, ""p95_reference"": 280.5306881000002, ""minimum"": 96.834779, ""maximum"": 499.98355, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 131.0506595, ""p95_reference"": 148.0101782, ""minimum"": 97.027691, ""maximum"": 173.047176, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.3263325000000066,-8.5659527125,6.082268499999998,-0.24839395013702692,-6.360689513400716,4.755462668297984,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,git,before,baseline,ms,30,0,,,separated-clusters,separated-clusters,"{""n"": 30, ""p50"": """", ""p95_reference"": 733.711548, ""minimum"": 347.08043, ""maximum"": 745.646024, ""distribution"": ""separated-clusters"", ""low_n"": 21, ""low_p50"": 374.894303, ""high_n"": 9, ""high_p50"": 723.043237}","{""n"": 30, ""p50"": """", ""p95_reference"": 764.0261530500001, ""minimum"": 353.68035, ""maximum"": 1143.341075, ""distribution"": ""separated-clusters"", ""low_n"": 24, ""low_p50"": 375.053872, ""high_n"": 6, ""high_p50"": 749.922686}",,,,,,,separated clusters; no whole-distribution ranking +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,rg,before,baseline,ms,30,0,424.35992799999997,426.7428575,unsplit,unsplit,"{""n"": 30, ""p50"": 424.35992799999997, ""p95_reference"": 445.81724305, ""minimum"": 373.277409, ""maximum"": 449.78536, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 426.7428575, ""p95_reference"": 448.67384825000005, ""minimum"": 379.105913, ""maximum"": 461.008823, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",2.3829295000000457,-10.78591141250001,13.572753500000033,0.5615349948876469,-2.499321157216366,3.2639920232744855,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,cargo,before,baseline,ms,30,0,483.237283,501.3797235,unsplit,unsplit,"{""n"": 30, ""p50"": 483.237283, ""p95_reference"": 551.8527210000001, ""minimum"": 460.089429, ""maximum"": 1005.241583, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 501.3797235, ""p95_reference"": 806.8878312000002, ""minimum"": 474.732044, ""maximum"": 913.063239, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",18.14244050000002,5.078862000000015,25.685059500000023,3.7543544627536507,1.036280621480845,5.358434870249336,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,npm,before,baseline,ms,30,0,928.2279195,923.2233120000001,unsplit,unsplit,"{""n"": 30, ""p50"": 928.2279195, ""p95_reference"": 1017.8536178, ""minimum"": 750.15394, ""maximum"": 1168.682019, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 923.2233120000001, ""p95_reference"": 1051.5576092000001, ""minimum"": 761.841513, ""maximum"": 1353.648422, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-5.004607499999906,-17.426156374999987,9.47210236249997,-0.5391571827203512,-1.8600305012168472,1.0223747436623447,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,ready_ms,baseline,candidate,ms,30,0,701.291806,693.9176055,unsplit,unsplit,"{""n"": 30, ""p50"": 701.291806, ""p95_reference"": 731.1870892, ""minimum"": 686.521945, ""maximum"": 789.906315, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 693.9176055, ""p95_reference"": 727.06945975, ""minimum"": 654.316747, ""maximum"": 728.832386, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-7.374200499999915,-15.10067749999996,0.21714737500001527,-1.05151670629956,-2.1385968354064477,0.030946466821430672,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,result_ms,baseline,candidate,ms,30,0,4278.449968,4446.6029255,unsplit,unsplit,"{""n"": 30, ""p50"": 4278.449968, ""p95_reference"": 5340.32934275, ""minimum"": 4069.431508, ""maximum"": 5474.773979, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 4446.6029255, ""p95_reference"": 4849.9728442000005, ""minimum"": 4296.389126, ""maximum"": 5433.299961, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",168.1529575000004,65.6855170000008,210.05962100000033,3.9302307788492197,1.4993552919430941,4.928041852411542,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,completion_ms,baseline,candidate,ms,30,0,4535.762954,4635.260874,unsplit,unsplit,"{""n"": 30, ""p50"": 4535.762954, ""p95_reference"": 5577.58314435, ""minimum"": 4314.00466, ""maximum"": 5766.958044, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 4635.260874, ""p95_reference"": 5053.494531, ""minimum"": 4486.242774, ""maximum"": 5650.125408, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",99.4979199999998,-1.9010724999998274,148.12697500000013,2.1936313914344696,-0.041062386808854434,3.278578269622737,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,worker_ms,baseline,candidate,ms,30,0,3578.175109,3749.0687745,unsplit,unsplit,"{""n"": 30, ""p50"": 3578.175109, ""p95_reference"": 4633.7424845000005, ""minimum"": 3367.664205, ""maximum"": 4777.305497, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 3749.0687745, ""p95_reference"": 4155.79789515, ""minimum"": 3601.525824, ""maximum"": 4739.00818, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",170.89366550000022,107.47650010000024,210.4752810750002,4.776000623059518,2.9590088269350274,5.923245553099513,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,metadata,baseline,candidate,ms,30,0,157.8011775,138.862869,unsplit,unsplit,"{""n"": 30, ""p50"": 157.8011775, ""p95_reference"": 189.13611625000001, ""minimum"": 112.822408, ""maximum"": 198.907503, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 138.862869, ""p95_reference"": 200.8448491, ""minimum"": 111.11752, ""maximum"": 203.982042, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-18.938308500000005,-35.205424174999976,1.4454382249999898,-12.001373373782343,-22.0605940758005,0.9223912548634038,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,read,baseline,candidate,ms,30,0,117.672345,116.8579125,unsplit,unsplit,"{""n"": 30, ""p50"": 117.672345, ""p95_reference"": 141.71966129999998, ""minimum"": 112.567169, ""maximum"": 142.482554, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 116.8579125, ""p95_reference"": 128.33210245, ""minimum"": 113.424067, ""maximum"": 139.308489, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.8144325000000094,-3.518810712499991,0.18627950000001192,-0.6921188661618061,-2.9508338240847722,0.15837790391393103,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,write,baseline,candidate,ms,30,0,131.0506595,163.677244,unsplit,unsplit,"{""n"": 30, ""p50"": 131.0506595, ""p95_reference"": 148.0101782, ""minimum"": 97.027691, ""maximum"": 173.047176, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 163.677244, ""p95_reference"": 193.86429005, ""minimum"": 130.111318, ""maximum"": 200.878049, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",32.62658450000001,15.380851000000007,42.35225749999998,24.89616200672382,11.71693302314134,33.32361947440947,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,git,baseline,candidate,ms,30,0,,,separated-clusters,separated-clusters,"{""n"": 30, ""p50"": """", ""p95_reference"": 764.0261530500001, ""minimum"": 353.68035, ""maximum"": 1143.341075, ""distribution"": ""separated-clusters"", ""low_n"": 24, ""low_p50"": 375.053872, ""high_n"": 6, ""high_p50"": 749.922686}","{""n"": 30, ""p50"": """", ""p95_reference"": 806.4616766, ""minimum"": 382.933572, ""maximum"": 820.942533, ""distribution"": ""separated-clusters"", ""low_n"": 25, ""low_p50"": 399.041918, ""high_n"": 5, ""high_p50"": 798.597088}",,,,,,,separated clusters; no whole-distribution ranking +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,rg,baseline,candidate,ms,30,0,426.7428575,450.55341599999997,unsplit,unsplit,"{""n"": 30, ""p50"": 426.7428575, ""p95_reference"": 448.67384825000005, ""minimum"": 379.105913, ""maximum"": 461.008823, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 450.55341599999997, ""p95_reference"": 462.7012209, ""minimum"": 427.947427, ""maximum"": 469.690888, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",23.810558499999956,16.56280799999996,36.91811001250002,5.579603286037416,3.8592397706755044,8.922258802187594,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,cargo,baseline,candidate,ms,30,0,501.3797235,502.76696549999997,unsplit,unsplit,"{""n"": 30, ""p50"": 501.3797235, ""p95_reference"": 806.8878312000002, ""minimum"": 474.732044, ""maximum"": 913.063239, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 502.76696549999997, ""p95_reference"": 524.2727038, ""minimum"": 478.655504, ""maximum"": 895.64709, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",1.387241999999958,-6.339356500000008,10.583257549999944,0.2766849026753615,-1.2563193692766594,2.137455146647018,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,npm,baseline,candidate,ms,30,0,923.2233120000001,973.343816,unsplit,unsplit,"{""n"": 30, ""p50"": 923.2233120000001, ""p95_reference"": 1051.5576092000001, ""minimum"": 761.841513, ""maximum"": 1353.648422, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 973.343816, ""p95_reference"": 1030.30126, ""minimum"": 911.547435, ""maximum"": 1201.185718, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",50.12050399999987,31.983457087499954,62.587000812500015,5.4288603145670855,3.417355788988163,6.780859620762852,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,ready_ms,before,candidate,ms,30,0,701.0899955,693.9176055,unsplit,unsplit,"{""n"": 30, ""p50"": 701.0899955, ""p95_reference"": 732.14511455, ""minimum"": 686.675537, ""maximum"": 873.145236, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 693.9176055, ""p95_reference"": 727.06945975, ""minimum"": 654.316747, ""maximum"": 728.832386, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-7.17238999999995,-14.30879517499997,-0.8413703500000779,-1.023034139131418,-2.030073917528008,-0.12121943873517678,faster +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,result_ms,before,candidate,ms,30,0,4297.801669,4446.6029255,unsplit,unsplit,"{""n"": 30, ""p50"": 4297.801669, ""p95_reference"": 5080.3648109000005, ""minimum"": 4074.466407, ""maximum"": 5228.259533, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 4446.6029255, ""p95_reference"": 4849.9728442000005, ""minimum"": 4296.389126, ""maximum"": 5433.299961, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",148.8012564999999,32.44331499999953,206.4904338374998,3.4622643844480328,0.7361868415249218,4.836304780587501,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,completion_ms,before,candidate,ms,30,0,4544.0489055,4635.260874,unsplit,unsplit,"{""n"": 30, ""p50"": 4544.0489055, ""p95_reference"": 5334.32411415, ""minimum"": 4317.653732, ""maximum"": 5526.082774, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 4635.260874, ""p95_reference"": 5053.494531, ""minimum"": 4486.242774, ""maximum"": 5650.125408, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",91.21196849999978,-13.554260000000795,145.19304476250045,2.0072840411026194,-0.293499352432236,3.211141901839739,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,worker_ms,before,candidate,ms,30,0,3589.173895,3749.0687745,unsplit,unsplit,"{""n"": 30, ""p50"": 3589.173895, ""p95_reference"": 4356.26645515, ""minimum"": 3375.558433, ""maximum"": 4522.132202, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 3749.0687745, ""p95_reference"": 4155.79789515, ""minimum"": 3601.525824, ""maximum"": 4739.00818, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",159.89487950000012,36.23646000000008,205.12455,4.454921499422082,0.9676407995443004,5.762080107641683,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,metadata,before,candidate,ms,30,0,133.4563125,138.862869,unsplit,unsplit,"{""n"": 30, ""p50"": 133.4563125, ""p95_reference"": 192.7246791, ""minimum"": 108.503417, ""maximum"": 194.824494, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 138.862869, ""p95_reference"": 200.8448491, ""minimum"": 111.11752, ""maximum"": 203.982042, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",5.4065564999999935,-11.300286999999997,28.476527612499915,4.0511807937147815,-8.231074393742155,22.348529030444993,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,read,before,candidate,ms,30,0,115.795024,116.8579125,unsplit,unsplit,"{""n"": 30, ""p50"": 115.795024, ""p95_reference"": 136.34799395000002, ""minimum"": 110.380387, ""maximum"": 609.142557, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 116.8579125, ""p95_reference"": 128.33210245, ""minimum"": 113.424067, ""maximum"": 139.308489, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",1.0628884999999997,-1.493983,3.1831505000000107,0.9179051597242971,-1.274610870267967,2.777127987017969,no detected difference +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,write,before,candidate,ms,30,0,131.376992,163.677244,unsplit,unsplit,"{""n"": 30, ""p50"": 131.376992, ""p95_reference"": 280.5306881000002, ""minimum"": 96.834779, ""maximum"": 499.98355, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 163.677244, ""p95_reference"": 193.86429005, ""minimum"": 130.111318, ""maximum"": 200.878049, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",32.300252,15.337790000000012,40.707677062499975,24.585927496345782,11.674571038067684,31.651474103201487,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,git,before,candidate,ms,30,0,,,separated-clusters,separated-clusters,"{""n"": 30, ""p50"": """", ""p95_reference"": 733.711548, ""minimum"": 347.08043, ""maximum"": 745.646024, ""distribution"": ""separated-clusters"", ""low_n"": 21, ""low_p50"": 374.894303, ""high_n"": 9, ""high_p50"": 723.043237}","{""n"": 30, ""p50"": """", ""p95_reference"": 806.4616766, ""minimum"": 382.933572, ""maximum"": 820.942533, ""distribution"": ""separated-clusters"", ""low_n"": 25, ""low_p50"": 399.041918, ""high_n"": 5, ""high_p50"": 798.597088}",,,,,,,separated clusters; no whole-distribution ranking +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,rg,before,candidate,ms,30,0,424.35992799999997,450.55341599999997,unsplit,unsplit,"{""n"": 30, ""p50"": 424.35992799999997, ""p95_reference"": 445.81724305, ""minimum"": 373.277409, ""maximum"": 449.78536, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 450.55341599999997, ""p95_reference"": 462.7012209, ""minimum"": 427.947427, ""maximum"": 469.690888, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",26.193488000000002,18.683151862499987,38.547659287500025,6.172469705952066,4.353792075138041,9.336122466372625,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,cargo,before,candidate,ms,30,0,483.237283,502.76696549999997,unsplit,unsplit,"{""n"": 30, ""p50"": 483.237283, ""p95_reference"": 551.8527210000001, ""minimum"": 460.089429, ""maximum"": 1005.241583, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 502.76696549999997, ""p95_reference"": 524.2727038, ""minimum"": 478.655504, ""maximum"": 895.64709, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",19.52968249999998,9.177695000000028,26.30648309999997,4.041427097420369,1.8714947485005462,5.516458189109752,slower +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,npm,before,candidate,ms,30,0,928.2279195,973.343816,unsplit,unsplit,"{""n"": 30, ""p50"": 928.2279195, ""p95_reference"": 1017.8536178, ""minimum"": 750.15394, ""maximum"": 1168.682019, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 973.343816, ""p95_reference"": 1030.30126, ""minimum"": 911.547435, ""maximum"": 1201.185718, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",45.11589649999996,28.81408799999997,56.54230879999987,4.860433041520884,3.0909553950674518,6.092084323792668,slower diff --git a/docs/src/zh/design/concurrency-retest-provenance.csv b/docs/src/zh/design/concurrency-retest-provenance.csv new file mode 100644 index 000000000..f5479737d --- /dev/null +++ b/docs/src/zh/design/concurrency-retest-provenance.csv @@ -0,0 +1,24 @@ +field,value +filesystem_report_sha256,39684288c49c075115731488f57c2fe85db432da07959ccd169266ea678e61a3 +filesystem_raw,benchmark/.data/concurrency-formal2-20261011/report.json +filesystem_launch_sha256,67b115314b2906cdedf8fa614fc397d411d64880c67e60bf2e88d59f9e96e2b8 +filesystem_guard_sha256,8bf04bf4f018f58e9e2b86321e1586566396228204177f6624ed29f27462ff59 +filesystem_protocol,30 seeded shuffled paired rounds; 3 warmups; fresh private user/mount/PID namespace and pivot_root per trial; fresh Host listener and VM launch timed; full 256 x 64 KiB written bytes verified; unchanged seven-operation workload; no profiling +filesystem_before_scope,"same frozen Oct10 source, reverses only 65e65c93 copy-up/dispatch and 09bd1d59 read/journal changes; stage sync default unchanged; not literal cf8 parent" +filesystem_baseline_scope,prior candidate6 optimized overlay and worker-pool queues +filesystem_binaries,"{""before"": ""b074594368e5655c0e6949dcd45e6eb958816f3cdfca53011911f6d059fcbfe8"", ""baseline"": ""378ee13ba8b8aafc0a588d51a9c3a79c1410c000857f323970cc13fc65d279e4"", ""candidate"": ""87ebb0658f2df6a84f9e4eca29c8d3d8bc01923817f04432780272c8b1b30309""}" +filesystem_source_manifests,"{""before"": ""1068608e3b4e611871e0e00a755b8ddb4e9d9c1a5e48b4b46597d353937b603e"", ""baseline"": ""aa30eda66bc1f1ee853404b7d41e8847f0722ce72adfb79fa6816e06258655d6"", ""candidate"": ""07a216161d0b64313ba03787630658f300a19788f705d318dfd88f9c5397726c""}" +startup_report_sha256,"{""pvisor-host"": ""30a46f312063eda97c46e7ec92a1ffdb777cc9b974aa2b7f5fb423071c966ded"", ""pvisor-staged"": ""f017d8c0e06575fa488642cf47cbf8f694896932917061b031f4b950d4d7edca"", ""pvisor-vm"": ""db50555e5a6466d7fd9e3578428e4c7605e66cc94dda939b6427b0d67e4b4963""}" +startup_raw,benchmark.new/startup/.data/concurrency-retest2-20261011 +startup_launch_sha256,693047df4814c012a4f19ef01924d4cddd04901f5280278ff3de5f3be3fa2e35 +startup_guard_sha256,3f40c557c2acc1bedfa314818441d5d8d1c0d558e64fe7ec40ea41a20a98b5d0 +startup_source_receipt_sha256,3f79d1fbb1c5e5b1d9ad3539b316654b2ecd37b96356b342deb3f116a3d6566b +startup_protocol,60 seeded shuffled paired rounds; 3 warmups; long-lived private namespaces and warmed Host listener per arm; exact Ready and normal zero Exit; GNU release/gateway and identical firmware; original startup workload and correctness gates +startup_before_scope,archived historical GNU old-pvisor build before cf8 changes +startup_baseline_scope,archived cf8 GNU gnu-pvisor build; version comparison includes intervening source changes; not single-change attribution +startup_binaries,"{""before"": ""51972e17777cfbab79b5ca18031d6d37ba47477917628d9e3bf0a81e3353fdff"", ""baseline"": ""b074594368e5655c0e6949dcd45e6eb958816f3cdfca53011911f6d059fcbfe8"", ""candidate"": ""87ebb0658f2df6a84f9e4eca29c8d3d8bc01923817f04432780272c8b1b30309""}" +candidate_scope,"frozen current dirty source build2; queue-local execution, hiprio lane, stealing, default staged guest writeback, leased I/O, Ready/Exit fixes, descriptor publication race fix" +budget,"private whole-parent cgroup: CPUs 0,1; CPUQuota 200%; MemoryMax 16 GiB; swap 0" +statistics,5000 paired-round bootstrap resamples; median difference and median ratio CI95; separated distributions have no global median ranking; P95 descriptive only; no P99 +interference,no detected same-UID builds/tests outside owned service; sampled every 0.5 s; may miss short/inaccessible/unrecognized activity +scope,"Linux KVM/FUSE, warm host caches, sequential jobs; not cold storage, concurrency throughput, other runtimes or macOS; failed/preflight batches retained separately" diff --git a/docs/src/zh/design/concurrency-startup-summary.csv b/docs/src/zh/design/concurrency-startup-summary.csv new file mode 100644 index 000000000..1c95d7082 --- /dev/null +++ b/docs/src/zh/design/concurrency-startup-summary.csv @@ -0,0 +1,19 @@ +benchmark_id,batch,backend,metric,reference,condition,unit,n,failures,reference_p50_ms,candidate_p50_ms,reference_distribution,candidate_distribution,reference_clusters,candidate_clusters,median_difference_ms,ci95_low_ms,ci95_high_ms,change_percent,ci95_low_percent,ci95_high_percent,interpretation +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-host,ready_ms,before,baseline,ms,60,0,13.210854,29.141069,unsplit,unsplit,"{""n"": 60, ""p50"": 13.210854, ""p95_reference"": 14.969226149999999, ""minimum"": 11.852243, ""maximum"": 205.123838, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 60, ""p50"": 29.141069, ""p95_reference"": 30.828012049999998, ""minimum"": 26.50353, ""maximum"": 31.261772, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",15.930215000000002,15.297128,16.4309922625,120.5842937935731,112.0264088185669,127.09639105401754,slower +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-host,exit_ms,before,baseline,ms,60,0,23.914116999999997,99.162519,unsplit,unsplit,"{""n"": 60, ""p50"": 23.914116999999997, ""p95_reference"": 24.6613889, ""minimum"": 23.231012, ""maximum"": 215.171903, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 60, ""p50"": 99.162519, ""p95_reference"": 100.65071339999999, ""minimum"": 97.331325, ""maximum"": 101.4216, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",75.248402,74.98108948749999,75.53936,314.6610096454743,312.27151498760674,318.21856071435786,slower +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-host,ready_ms,baseline,candidate,ms,60,0,29.141069,20.032587499999998,unsplit,unsplit,"{""n"": 60, ""p50"": 29.141069, ""p95_reference"": 30.828012049999998, ""minimum"": 26.50353, ""maximum"": 31.261772, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 60, ""p50"": 20.032587499999998, ""p95_reference"": 21.203905149999997, ""minimum"": 18.304547, ""maximum"": 21.709512, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-9.108481500000003,-9.505201824999999,-8.718452,-31.256511214465064,-32.34063201302348,-30.252958401952807,faster +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-host,exit_ms,baseline,candidate,ms,60,0,99.162519,24.1611885,unsplit,unsplit,"{""n"": 60, ""p50"": 99.162519, ""p95_reference"": 100.65071339999999, ""minimum"": 97.331325, ""maximum"": 101.4216, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 60, ""p50"": 24.1611885, ""p95_reference"": 25.4893269, ""minimum"": 22.370415, ""maximum"": 26.040997, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-75.0013305,-75.23979940000001,-74.59203987500001,-75.63475722112302,-75.82453689709905,-75.30913279068344,faster +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-host,ready_ms,before,candidate,ms,60,0,13.210854,20.032587499999998,unsplit,unsplit,"{""n"": 60, ""p50"": 13.210854, ""p95_reference"": 14.969226149999999, ""minimum"": 11.852243, ""maximum"": 205.123838, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 60, ""p50"": 20.032587499999998, ""p95_reference"": 21.203905149999997, ""minimum"": 18.304547, ""maximum"": 21.709512, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",6.821733499999999,6.159386462499999,7.267630299999997,51.63733926663636,45.122763251403676,56.37405576931433,slower +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-host,exit_ms,before,candidate,ms,60,0,23.914116999999997,24.1611885,unsplit,unsplit,"{""n"": 60, ""p50"": 23.914116999999997, ""p95_reference"": 24.6613889, ""minimum"": 23.231012, ""maximum"": 215.171903, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 60, ""p50"": 24.1611885, ""p95_reference"": 25.4893269, ""minimum"": 22.370415, ""maximum"": 26.040997, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.2470715000000041,0.022403000000000617,0.6303448375000009,1.0331617094622603,0.09355227592020565,2.6470244424196063,slower +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-staged,ready_ms,before,baseline,ms,60,0,25.0450905,41.23049,unsplit,unsplit,"{""n"": 60, ""p50"": 25.0450905, ""p95_reference"": 48.783252, ""minimum"": 22.323295, ""maximum"": 66.539693, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 60, ""p50"": 41.23049, ""p95_reference"": 44.29052765, ""minimum"": 38.807522, ""maximum"": 52.209267, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",16.185399500000003,15.648150999999995,16.617075000000003,64.62503898718195,61.857315182368744,66.76458729971617,slower +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-staged,exit_ms,before,baseline,ms,60,0,,118.4108385,separated-clusters,unsplit,"{""n"": 60, ""p50"": """", ""p95_reference"": 74.42863514999999, ""minimum"": 33.176303, ""maximum"": 84.36309, ""distribution"": ""separated-clusters"", ""low_n"": 53, ""low_p50"": 34.117038, ""high_n"": 7, ""high_p50"": 74.426694}","{""n"": 60, ""p50"": 118.4108385, ""p95_reference"": 139.05603995, ""minimum"": 107.356436, ""maximum"": 141.341628, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",,,,,,,separated clusters; no whole-distribution ranking +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-staged,ready_ms,baseline,candidate,ms,60,0,41.23049,,unsplit,separated-clusters,"{""n"": 60, ""p50"": 41.23049, ""p95_reference"": 44.29052765, ""minimum"": 38.807522, ""maximum"": 52.209267, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 60, ""p50"": """", ""p95_reference"": 154.09938019999998, ""minimum"": 30.150605, ""maximum"": 164.8574, ""distribution"": ""separated-clusters"", ""low_n"": 19, ""low_p50"": 32.167219, ""high_n"": 41, ""high_p50"": 121.953181}",,,,,,,separated clusters; no whole-distribution ranking +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-staged,exit_ms,baseline,candidate,ms,60,0,118.4108385,118.6742705,unsplit,unsplit,"{""n"": 60, ""p50"": 118.4108385, ""p95_reference"": 139.05603995, ""minimum"": 107.356436, ""maximum"": 141.341628, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 60, ""p50"": 118.6742705, ""p95_reference"": 163.34460224999998, ""minimum"": 39.08468, ""maximum"": 175.515683, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.26343200000000877,-31.53614085000001,16.60314992500001,0.22247287776786528,-26.828364218695413,14.937975299367043,no detected difference +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-staged,ready_ms,before,candidate,ms,60,0,25.0450905,,unsplit,separated-clusters,"{""n"": 60, ""p50"": 25.0450905, ""p95_reference"": 48.783252, ""minimum"": 22.323295, ""maximum"": 66.539693, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 60, ""p50"": """", ""p95_reference"": 154.09938019999998, ""minimum"": 30.150605, ""maximum"": 164.8574, ""distribution"": ""separated-clusters"", ""low_n"": 19, ""low_p50"": 32.167219, ""high_n"": 41, ""high_p50"": 121.953181}",,,,,,,separated clusters; no whole-distribution ranking +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-staged,exit_ms,before,candidate,ms,60,0,,118.6742705,separated-clusters,unsplit,"{""n"": 60, ""p50"": """", ""p95_reference"": 74.42863514999999, ""minimum"": 33.176303, ""maximum"": 84.36309, ""distribution"": ""separated-clusters"", ""low_n"": 53, ""low_p50"": 34.117038, ""high_n"": 7, ""high_p50"": 74.426694}","{""n"": 60, ""p50"": 118.6742705, ""p95_reference"": 163.34460224999998, ""minimum"": 39.08468, ""maximum"": 175.515683, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",,,,,,,separated clusters; no whole-distribution ranking +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-vm,ready_ms,before,baseline,ms,60,0,174.06437799999998,119.4055905,unsplit,unsplit,"{""n"": 60, ""p50"": 174.06437799999998, ""p95_reference"": 216.6176647, ""minimum"": 131.159754, ""maximum"": 231.683627, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 60, ""p50"": 119.4055905, ""p95_reference"": 126.87861115, ""minimum"": 112.32518, ""maximum"": 148.467629, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-54.658787499999974,-59.42378157499999,-45.55883091250003,-31.401478078415323,-33.28718065830785,-27.613199631421672,faster +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-vm,exit_ms,before,baseline,ms,60,0,211.83177,218.71427549999999,unsplit,unsplit,"{""n"": 60, ""p50"": 211.83177, ""p95_reference"": 257.17307515, ""minimum"": 155.286827, ""maximum"": 296.54068, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 60, ""p50"": 218.71427549999999, ""p95_reference"": 242.96695565, ""minimum"": 189.475487, ""maximum"": 251.073247, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",6.882505499999979,-4.323434649999996,23.11530400000001,3.249043096793258,-2.0062220876725156,11.166021081097233,no detected difference +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-vm,ready_ms,baseline,candidate,ms,60,0,119.4055905,115.24632700000001,unsplit,unsplit,"{""n"": 60, ""p50"": 119.4055905, ""p95_reference"": 126.87861115, ""minimum"": 112.32518, ""maximum"": 148.467629, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 60, ""p50"": 115.24632700000001, ""p95_reference"": 137.31941615, ""minimum"": 101.220694, ""maximum"": 162.491764, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-4.1592634999999945,-7.0784748624999905,0.7727227500000008,-3.4833071739635124,-5.906450779487837,0.6509769176801421,no detected difference +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-vm,exit_ms,baseline,candidate,ms,60,0,218.71427549999999,170.4499275,unsplit,unsplit,"{""n"": 60, ""p50"": 218.71427549999999, ""p95_reference"": 242.96695565, ""minimum"": 189.475487, ""maximum"": 251.073247, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 60, ""p50"": 170.4499275, ""p95_reference"": 201.5738823, ""minimum"": 140.112464, ""maximum"": 224.770585, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-48.264347999999984,-67.4287749125,-33.355708362500025,-22.06730579870173,-29.305294564855153,-15.698035238930327,faster +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-vm,ready_ms,before,candidate,ms,60,0,174.06437799999998,115.24632700000001,unsplit,unsplit,"{""n"": 60, ""p50"": 174.06437799999998, ""p95_reference"": 216.6176647, ""minimum"": 131.159754, ""maximum"": 231.683627, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 60, ""p50"": 115.24632700000001, ""p95_reference"": 137.31941615, ""minimum"": 101.220694, ""maximum"": 162.491764, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-58.81805099999997,-64.26843900000003,-47.911247949999996,-33.79097531374281,-36.16021254706435,-28.866040152361748,faster +B-STARTUP-REVISION-ENG,concurrency-retest2-20261011,pvisor-vm,exit_ms,before,candidate,ms,60,0,211.83177,170.4499275,unsplit,unsplit,"{""n"": 60, ""p50"": 211.83177, ""p95_reference"": 257.17307515, ""minimum"": 155.286827, ""maximum"": 296.54068, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 60, ""p50"": 170.4499275, ""p95_reference"": 201.5738823, ""minimum"": 140.112464, ""maximum"": 224.770585, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-41.381842500000005,-53.93050220000001,-27.752718999999985,-19.53523897760945,-24.942263955132848,-13.402372886512527,faster diff --git a/docs/src/zh/design/direct-queue-filesystem-summary.csv b/docs/src/zh/design/direct-queue-filesystem-summary.csv new file mode 100644 index 000000000..387d61fb7 --- /dev/null +++ b/docs/src/zh/design/direct-queue-filesystem-summary.csv @@ -0,0 +1,67 @@ +benchmark_id,batch,backend,metric,reference,condition,unit,n,failures,reference_p50_ms,candidate_p50_ms,reference_distribution,candidate_distribution,reference_clusters,candidate_clusters,median_difference_ms,ci95_low_ms,ci95_high_ms,change_percent,ci95_low_percent,ci95_high_percent,interpretation +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,ready_ms,before,baseline,ms,10,0,229.153498,218.766278,unsplit,unsplit,"{""n"": 10, ""p50"": 229.153498, ""p95_reference"": """", ""minimum"": 226.907656, ""maximum"": 235.116278, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 218.766278, ""p95_reference"": """", ""minimum"": 217.461773, ""maximum"": 223.010901, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-10.387220000000013,-12.487494999999996,-9.296683499999972,-4.532865564199251,-5.392903353957301,-4.071565519151276,faster +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,result_ms,before,baseline,ms,10,0,1444.5803395,1432.6245764999999,unsplit,unsplit,"{""n"": 10, ""p50"": 1444.5803395, ""p95_reference"": """", ""minimum"": 1437.881595, ""maximum"": 1468.744135, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 1432.6245764999999, ""p95_reference"": """", ""minimum"": 1423.942204, ""maximum"": 1485.340626, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-11.955763000000161,-24.857361000000083,-4.528592500000059,-0.8276288049260283,-1.7058429220306839,-0.3147141149056343,faster +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,completion_ms,before,baseline,ms,10,0,1754.5811735,1668.7888515,unsplit,unsplit,"{""n"": 10, ""p50"": 1754.5811735, ""p95_reference"": """", ""minimum"": 1732.989469, ""maximum"": 1780.666738, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 1668.7888515, ""p95_reference"": """", ""minimum"": 1662.500618, ""maximum"": 1742.622511, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-85.79232200000001,-100.13619300000005,-62.81696950000014,-4.889618291575726,-5.665124330067917,-3.5929848342111104,faster +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,worker_ms,before,baseline,ms,10,0,1214.5357079999999,1213.940276,unsplit,unsplit,"{""n"": 10, ""p50"": 1214.5357079999999, ""p95_reference"": """", ""minimum"": 1208.78866, ""maximum"": 1236.847748, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 1213.940276, ""p95_reference"": """", ""minimum"": 1205.377403, ""maximum"": 1266.145527, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.5954319999998461,-15.07675899999981,6.464432000000215,-0.04902548324251521,-1.2297231433468503,0.5336936876742415,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,metadata,before,baseline,ms,10,0,66.186594,66.306904,unsplit,unsplit,"{""n"": 10, ""p50"": 66.186594, ""p95_reference"": """", ""minimum"": 65.803535, ""maximum"": 67.278247, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 66.306904, ""p95_reference"": """", ""minimum"": 65.649597, ""maximum"": 66.767604, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.12031000000000347,-0.2652100000000104,0.2874724999999927,0.18177397072283163,-0.3997165160069982,0.43433644583674713,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,read,before,baseline,ms,10,0,67.97466700000001,68.0428395,unsplit,unsplit,"{""n"": 10, ""p50"": 67.97466700000001, ""p95_reference"": """", ""minimum"": 66.813841, ""maximum"": 88.24187, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 68.0428395, ""p95_reference"": """", ""minimum"": 66.569334, ""maximum"": 87.879223, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.06817249999998864,-1.3143540000000087,1.7080007499999905,0.10029103930730976,-1.9222466994853415,2.5195059224219505,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,write,before,baseline,ms,10,0,28.212829499999998,27.667371000000003,unsplit,unsplit,"{""n"": 10, ""p50"": 28.212829499999998, ""p95_reference"": """", ""minimum"": 27.059161, ""maximum"": 28.757852, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 27.667371000000003, ""p95_reference"": """", ""minimum"": 25.565924, ""maximum"": 33.203189, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.5454584999999952,-1.8269304999999996,0.8011204999999997,-1.9333704192980528,-6.463596467734812,2.8670908869605993,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,git,before,baseline,ms,10,0,142.2296785,143.3659445,unsplit,unsplit,"{""n"": 10, ""p50"": 142.2296785, ""p95_reference"": """", ""minimum"": 140.683512, ""maximum"": 142.917783, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 143.3659445, ""p95_reference"": """", ""minimum"": 140.2054, ""maximum"": 161.597772, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",1.1362660000000062,-1.1790165000000172,3.5052984999999808,0.7988951476115558,-0.830296189130042,2.4803955406863087,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,rg,before,baseline,ms,10,0,106.5825365,106.638556,unsplit,unsplit,"{""n"": 10, ""p50"": 106.5825365, ""p95_reference"": """", ""minimum"": 104.669916, ""maximum"": 107.633067, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 106.638556, ""p95_reference"": """", ""minimum"": 105.771502, ""maximum"": 107.456758, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.056019499999990785,-0.8575905000000006,1.1857430000000022,0.052559736181545524,-0.8005183675214744,1.1193511385933386,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,cargo,before,baseline,ms,10,0,77.55028250000001,77.498756,unsplit,unsplit,"{""n"": 10, ""p50"": 77.55028250000001, ""p95_reference"": """", ""minimum"": 75.865877, ""maximum"": 96.866359, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 77.498756, ""p95_reference"": """", ""minimum"": 75.108835, ""maximum"": 100.508157, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.05152650000000847,-7.921887725000003,1.2402804999999972,-0.06644269800050173,-9.309745385197033,1.6210217107728075,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,npm,before,baseline,ms,10,0,230.13633249999998,226.985576,unsplit,unsplit,"{""n"": 10, ""p50"": 230.13633249999998, ""p95_reference"": """", ""minimum"": 225.186954, ""maximum"": 233.312622, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 226.985576, ""p95_reference"": """", ""minimum"": 224.502827, ""maximum"": 232.975762, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-3.1507564999999715,-4.94182049999999,1.7339465000000303,-1.3690826067196404,-2.1471197427962805,0.7556910873117939,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,ready_ms,baseline,candidate,ms,10,0,218.766278,217.922999,unsplit,unsplit,"{""n"": 10, ""p50"": 218.766278, ""p95_reference"": """", ""minimum"": 217.461773, ""maximum"": 223.010901, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 217.922999, ""p95_reference"": """", ""minimum"": 216.724477, ""maximum"": 224.184182, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.8432789999999954,-2.0738920000000007,2.8129865000000223,-0.385470287152756,-0.9468303297746616,1.2847693340192423,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,result_ms,baseline,candidate,ms,10,0,1432.6245764999999,1431.14254,unsplit,unsplit,"{""n"": 10, ""p50"": 1432.6245764999999, ""p95_reference"": """", ""minimum"": 1423.942204, ""maximum"": 1485.340626, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 1431.14254, ""p95_reference"": """", ""minimum"": 1426.265762, ""maximum"": 1499.086515, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-1.482036499999822,-9.493872499999952,15.92296350000015,-0.10344904899094454,-0.6594381172062014,1.112087893300659,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,completion_ms,baseline,candidate,ms,10,0,1668.7888515,1667.3669375,unsplit,unsplit,"{""n"": 10, ""p50"": 1668.7888515, ""p95_reference"": """", ""minimum"": 1662.500618, ""maximum"": 1742.622511, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 1667.3669375, ""p95_reference"": """", ""minimum"": 1663.385239, ""maximum"": 1732.171492, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-1.4219140000000152,-20.763048000000026,28.91245650000019,-0.08520634583110143,-1.2305334450607441,1.7352342748735872,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,worker_ms,baseline,candidate,ms,10,0,1213.940276,1213.163396,unsplit,unsplit,"{""n"": 10, ""p50"": 1213.940276, ""p95_reference"": """", ""minimum"": 1205.377403, ""maximum"": 1266.145527, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 1213.163396, ""p95_reference"": """", ""minimum"": 1202.128989, ""maximum"": 1274.682132, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.7768800000001193,-7.0755079999999,16.04047350000019,-0.06399655859182785,-0.5797981066362867,1.3219537365593226,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,metadata,baseline,candidate,ms,10,0,66.306904,65.25977950000001,unsplit,unsplit,"{""n"": 10, ""p50"": 66.306904, ""p95_reference"": """", ""minimum"": 65.649597, ""maximum"": 66.767604, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 65.25977950000001, ""p95_reference"": """", ""minimum"": 64.547881, ""maximum"": 65.985975, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-1.0471244999999954,-1.3180705000000046,-0.30603550000000723,-1.579208855837988,-1.9779505425664867,-0.4649780385276192,faster +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,read,baseline,candidate,ms,10,0,68.0428395,68.746929,unsplit,unsplit,"{""n"": 10, ""p50"": 68.0428395, ""p95_reference"": """", ""minimum"": 66.569334, ""maximum"": 87.879223, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 68.746929, ""p95_reference"": """", ""minimum"": 66.530903, ""maximum"": 88.036867, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.7040894999999949,-1.3006580000000127,4.918150999999995,1.0347738353864377,-1.8581818451443133,7.1338735580186885,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,write,baseline,candidate,ms,10,0,27.667371000000003,27.1156815,unsplit,unsplit,"{""n"": 10, ""p50"": 27.667371000000003, ""p95_reference"": """", ""minimum"": 25.565924, ""maximum"": 33.203189, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 27.1156815, ""p95_reference"": """", ""minimum"": 26.0406, ""maximum"": 28.266014, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.5516895000000019,-1.6412134999999992,0.5558670000000028,-1.9940076706240095,-5.843610396315935,2.0963679767739274,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,git,baseline,candidate,ms,10,0,143.3659445,141.312361,unsplit,unsplit,"{""n"": 10, ""p50"": 143.3659445, ""p95_reference"": """", ""minimum"": 140.2054, ""maximum"": 161.597772, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 141.312361, ""p95_reference"": """", ""minimum"": 139.59504, ""maximum"": 158.597863, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-2.053583500000002,-5.070566000000014,1.3457075000000032,-1.4324067735626,-3.484313690633445,0.9519678346596727,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,rg,baseline,candidate,ms,10,0,106.638556,106.0887095,unsplit,unsplit,"{""n"": 10, ""p50"": 106.638556, ""p95_reference"": """", ""minimum"": 105.771502, ""maximum"": 107.456758, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 106.0887095, ""p95_reference"": """", ""minimum"": 104.835946, ""maximum"": 108.707052, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.549846500000001,-1.783239000000009,0.6942710000000005,-0.5156169781593833,-1.6650277414053782,0.6532974481806786,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,cargo,baseline,candidate,ms,10,0,77.498756,76.5364195,unsplit,unsplit,"{""n"": 10, ""p50"": 77.498756, ""p95_reference"": """", ""minimum"": 75.108835, ""maximum"": 100.508157, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 76.5364195, ""p95_reference"": """", ""minimum"": 75.187111, ""maximum"": 102.151344, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.9623365000000064,-1.8989100999999988,9.935165999999995,-1.2417444481302442,-2.4417307286991865,12.833699515010522,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,npm,baseline,candidate,ms,10,0,226.985576,230.30114500000002,unsplit,unsplit,"{""n"": 10, ""p50"": 226.985576, ""p95_reference"": """", ""minimum"": 224.502827, ""maximum"": 232.975762, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 230.30114500000002, ""p95_reference"": """", ""minimum"": 225.368933, ""maximum"": 239.572046, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",3.3155690000000106,0.6215554999999711,6.673878499999972,1.46069589902047,0.26870357347081875,2.9456143146573854,slower +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,ready_ms,before,candidate,ms,10,0,229.153498,217.922999,unsplit,unsplit,"{""n"": 10, ""p50"": 229.153498, ""p95_reference"": """", ""minimum"": 226.907656, ""maximum"": 235.116278, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 217.922999, ""p95_reference"": """", ""minimum"": 216.724477, ""maximum"": 224.184182, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-11.230499000000009,-13.345687000000027,-8.016419000000013,-4.900863001445432,-5.795577660349471,-3.5073220895628254,faster +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,result_ms,before,candidate,ms,10,0,1444.5803395,1431.14254,unsplit,unsplit,"{""n"": 10, ""p50"": 1444.5803395, ""p95_reference"": """", ""minimum"": 1437.881595, ""maximum"": 1468.744135, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 1431.14254, ""p95_reference"": """", ""minimum"": 1426.265762, ""maximum"": 1499.086515, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-13.437799499999983,-26.121981000000005,-0.828896499999928,-0.9302216797891005,-1.7936987420358985,-0.057617785344832306,faster +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,completion_ms,before,candidate,ms,10,0,1754.5811735,1667.3669375,unsplit,unsplit,"{""n"": 10, ""p50"": 1754.5811735, ""p95_reference"": """", ""minimum"": 1732.989469, ""maximum"": 1780.666738, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 1667.3669375, ""p95_reference"": """", ""minimum"": 1663.385239, ""maximum"": 1732.171492, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-87.21423600000003,-102.02723887499991,-57.22989500000017,-4.970658372335491,-5.774483061629977,-3.2617410846737416,faster +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,worker_ms,before,candidate,ms,10,0,1214.5357079999999,1213.163396,unsplit,unsplit,"{""n"": 10, ""p50"": 1214.5357079999999, ""p95_reference"": """", ""minimum"": 1208.78866, ""maximum"": 1236.847748, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 1213.163396, ""p95_reference"": """", ""minimum"": 1202.128989, ""maximum"": 1274.682132, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-1.3723119999999653,-15.082394000000022,10.879147999999986,-0.11299066721223161,-1.2278611706684206,0.8992147687199914,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,metadata,before,candidate,ms,10,0,66.186594,65.25977950000001,unsplit,unsplit,"{""n"": 10, ""p50"": 66.186594, ""p95_reference"": """", ""minimum"": 65.803535, ""maximum"": 67.278247, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 65.25977950000001, ""p95_reference"": """", ""minimum"": 64.547881, ""maximum"": 65.985975, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.9268144999999919,-1.3706619999999958,-0.44021650000000534,-1.4003054757584121,-2.0603253594512094,-0.6674864575038408,faster +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,read,before,candidate,ms,10,0,67.97466700000001,68.746929,unsplit,unsplit,"{""n"": 10, ""p50"": 67.97466700000001, ""p95_reference"": """", ""minimum"": 66.813841, ""maximum"": 88.24187, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 68.746929, ""p95_reference"": """", ""minimum"": 66.530903, ""maximum"": 88.036867, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.7722619999999836,-0.3650870000000168,3.934369000000004,1.136102660127758,-0.5325435168712422,5.644306404155475,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,write,before,candidate,ms,10,0,28.212829499999998,27.1156815,unsplit,unsplit,"{""n"": 10, ""p50"": 28.212829499999998, ""p95_reference"": """", ""minimum"": 27.059161, ""maximum"": 28.757852, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 27.1156815, ""p95_reference"": """", ""minimum"": 26.0406, ""maximum"": 28.266014, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-1.0971479999999971,-2.0724234999999993,-0.5905829999999987,-3.8888265354596774,-7.3045638502453825,-2.134984580202981,faster +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,git,before,candidate,ms,10,0,142.2296785,141.312361,unsplit,unsplit,"{""n"": 10, ""p50"": 142.2296785, ""p95_reference"": """", ""minimum"": 140.683512, ""maximum"": 142.917783, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 141.312361, ""p95_reference"": """", ""minimum"": 139.59504, ""maximum"": 158.597863, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.9173174999999958,-2.1900235000000237,1.6218834999999956,-0.644955054159102,-1.5381712664529479,1.14757302022368,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,rg,before,candidate,ms,10,0,106.5825365,106.0887095,unsplit,unsplit,"{""n"": 10, ""p50"": 106.5825365, ""p95_reference"": """", ""minimum"": 104.669916, ""maximum"": 107.633067, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 106.0887095, ""p95_reference"": """", ""minimum"": 104.835946, ""maximum"": 108.707052, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.49382700000001023,-1.8981907750000104,0.9848100250000014,-0.46332824890127355,-1.771328627173161,0.9253499391059903,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,cargo,before,candidate,ms,10,0,77.55028250000001,76.5364195,unsplit,unsplit,"{""n"": 10, ""p50"": 77.55028250000001, ""p95_reference"": """", ""minimum"": 75.865877, ""maximum"": 96.866359, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 76.5364195, ""p95_reference"": """", ""minimum"": 75.187111, ""maximum"": 102.151344, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-1.0138630000000148,-8.576559500000002,9.034319674999958,-1.3073620976171374,-9.917914123319392,11.44591497004314,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-staged,npm,before,candidate,ms,10,0,230.13633249999998,230.30114500000002,unsplit,unsplit,"{""n"": 10, ""p50"": 230.13633249999998, ""p95_reference"": """", ""minimum"": 225.186954, ""maximum"": 233.312622, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 230.30114500000002, ""p95_reference"": """", ""minimum"": 225.368933, ""maximum"": 239.572046, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",0.1648125000000391,-1.9061581749999885,4.023910999999998,0.07161515881028979,-0.8239097968774294,1.7558586825401346,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,ready_ms,before,baseline,ms,10,0,701.6391005,691.963534,unsplit,unsplit,"{""n"": 10, ""p50"": 701.6391005, ""p95_reference"": """", ""minimum"": 690.82293, ""maximum"": 712.260488, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 691.963534, ""p95_reference"": """", ""minimum"": 679.212557, ""maximum"": 700.735585, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-9.675566500000059,-15.009504250000077,-2.2094994999999926,-1.378994769975772,-2.128561365252646,-0.31659921452842354,faster +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,result_ms,before,baseline,ms,10,0,4322.3996565,4408.2079945000005,unsplit,unsplit,"{""n"": 10, ""p50"": 4322.3996565, ""p95_reference"": """", ""minimum"": 4148.19369, ""maximum"": 4634.98185, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 4408.2079945000005, ""p95_reference"": """", ""minimum"": 4327.636532, ""maximum"": 4875.767963, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",85.80833800000073,-75.06041950000053,190.58705650000047,1.985201388561153,-1.6958545855973228,4.476708091747295,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,completion_ms,before,baseline,ms,10,0,4560.905778,4595.446841,unsplit,unsplit,"{""n"": 10, ""p50"": 4560.905778, ""p95_reference"": """", ""minimum"": 4400.953022, ""maximum"": 4878.763604, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 4595.446841, ""p95_reference"": """", ""minimum"": 4506.06905, ""maximum"": 5064.932774, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",34.54106299999967,-143.22989999999936,133.20583400000032,0.75732901930603,-3.0089414851627305,2.951110582589078,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,worker_ms,before,baseline,ms,10,0,3621.289471,3707.288059,unsplit,unsplit,"{""n"": 10, ""p50"": 3621.289471, ""p95_reference"": """", ""minimum"": 3433.253015, ""maximum"": 3944.006877, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 3707.288059, ""p95_reference"": """", ""minimum"": 3635.668228, ""maximum"": 4183.811464, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",85.99858799999993,-70.33717099999967,203.0250715000002,2.3748056787145533,-1.8876871408780582,5.701304364284399,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,metadata,before,baseline,ms,10,0,123.04792850000001,148.9697515,unsplit,unsplit,"{""n"": 10, ""p50"": 123.04792850000001, ""p95_reference"": """", ""minimum"": 113.761069, ""maximum"": 173.507349, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 148.9697515, ""p95_reference"": """", ""minimum"": 111.944401, ""maximum"": 189.904797, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",25.92182299999999,-2.951689150000013,32.67203500000001,21.066444040136755,-2.409605705167983,27.683774560670326,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,read,before,baseline,ms,10,0,118.0688145,116.5817545,unsplit,unsplit,"{""n"": 10, ""p50"": 118.0688145, ""p95_reference"": """", ""minimum"": 113.033845, ""maximum"": 142.850213, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 116.5817545, ""p95_reference"": """", ""minimum"": 112.623695, ""maximum"": 136.255042, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-1.4870599999999996,-9.452190000000002,4.780556500000003,-1.2594858399293862,-7.499717665346894,4.158361006664624,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,write,before,baseline,ms,10,0,118.641548,152.998639,unsplit,unsplit,"{""n"": 10, ""p50"": 118.641548, ""p95_reference"": """", ""minimum"": 96.60634, ""maximum"": 149.088464, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 152.998639, ""p95_reference"": """", ""minimum"": 105.266935, ""maximum"": 173.889485, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",34.357091,13.240334999999988,57.9698425,28.95873459102203,10.481857588238054,56.164296908366374,slower +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,git,before,baseline,ms,10,0,,404.511602,separated-clusters,unsplit,"{""n"": 10, ""p50"": """", ""p95_reference"": """", ""minimum"": 354.844523, ""maximum"": 765.855372, ""distribution"": ""separated-clusters"", ""low_n"": 5, ""low_p50"": 364.978813, ""high_n"": 5, ""high_p50"": 731.631807}","{""n"": 10, ""p50"": 404.511602, ""p95_reference"": """", ""minimum"": 365.490143, ""maximum"": 824.110585, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",,,,,,,separated clusters; no whole-distribution ranking +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,rg,before,baseline,ms,10,0,422.51628949999997,453.05335,unsplit,unsplit,"{""n"": 10, ""p50"": 422.51628949999997, ""p95_reference"": """", ""minimum"": 392.012682, ""maximum"": 449.120192, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 453.05335, ""p95_reference"": """", ""minimum"": 431.242878, ""maximum"": 482.024183, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",30.537060500000052,11.138871687499986,50.36638599999998,7.227427973519607,2.5624897126117268,12.559394275873625,slower +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,cargo,before,baseline,ms,10,0,501.842823,508.938783,unsplit,unsplit,"{""n"": 10, ""p50"": 501.842823, ""p95_reference"": """", ""minimum"": 481.152788, ""maximum"": 529.957093, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 508.938783, ""p95_reference"": """", ""minimum"": 497.104239, ""maximum"": 531.244318, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",7.095959999999991,-8.212645000000066,15.285581999999977,1.413980568174833,-1.5841152415438153,3.0584737200522127,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,npm,before,baseline,ms,10,0,924.1405775000001,975.212892,unsplit,unsplit,"{""n"": 10, ""p50"": 924.1405775000001, ""p95_reference"": """", ""minimum"": 825.165724, ""maximum"": 951.471934, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 975.212892, ""p95_reference"": """", ""minimum"": 956.093243, ""maximum"": 994.02812, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",51.07231449999995,40.48251049999999,80.33377199999995,5.526465966699745,4.347094558481546,9.021085987970977,slower +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,ready_ms,baseline,candidate,ms,10,0,691.963534,693.3607625,unsplit,unsplit,"{""n"": 10, ""p50"": 691.963534, ""p95_reference"": """", ""minimum"": 679.212557, ""maximum"": 700.735585, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 693.3607625, ""p95_reference"": """", ""minimum"": 684.282021, ""maximum"": 726.019, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",1.3972284999999829,-7.613195999999903,5.321734500000048,0.2019222735514914,-1.0891083563037585,0.7712365914626328,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,result_ms,baseline,candidate,ms,10,0,4408.2079945000005,4303.31926,unsplit,unsplit,"{""n"": 10, ""p50"": 4408.2079945000005, ""p95_reference"": """", ""minimum"": 4327.636532, ""maximum"": 4875.767963, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 4303.31926, ""p95_reference"": """", ""minimum"": 4114.404626, ""maximum"": 4733.887275, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-104.88873450000028,-198.579205,264.49603149999984,-2.379396222475594,-4.4135739916857375,6.080819350509237,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,completion_ms,baseline,candidate,ms,10,0,4595.446841,4484.42847,unsplit,unsplit,"{""n"": 10, ""p50"": 4595.446841, ""p95_reference"": """", ""minimum"": 4506.06905, ""maximum"": 5064.932774, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 4484.42847, ""p95_reference"": """", ""minimum"": 4284.771117, ""maximum"": 4925.370465, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-111.01837100000012,-222.17086666250032,248.69843200000105,-2.4158340818896673,-4.7596864032378186,5.482919937523523,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,worker_ms,baseline,candidate,ms,10,0,3707.288059,3606.7074435,unsplit,unsplit,"{""n"": 10, ""p50"": 3707.288059, ""p95_reference"": """", ""minimum"": 3635.668228, ""maximum"": 4183.811464, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 3606.7074435, ""p95_reference"": """", ""minimum"": 3424.74753, ""maximum"": 4007.710068, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-100.58061550000002,-200.09853924999973,264.8636459999998,-2.7130509930520574,-5.283209164000757,7.245092923180008,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,metadata,baseline,candidate,ms,10,0,148.9697515,142.879463,unsplit,unsplit,"{""n"": 10, ""p50"": 148.9697515, ""p95_reference"": """", ""minimum"": 111.944401, ""maximum"": 189.904797, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 142.879463, ""p95_reference"": """", ""minimum"": 109.248394, ""maximum"": 193.687111, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-6.090288500000014,-34.00228049999998,57.33683100000002,-4.088271906662888,-22.10593881840791,43.986512348584064,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,read,baseline,candidate,ms,10,0,116.5817545,116.239688,unsplit,unsplit,"{""n"": 10, ""p50"": 116.5817545, ""p95_reference"": """", ""minimum"": 112.623695, ""maximum"": 136.255042, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 116.239688, ""p95_reference"": """", ""minimum"": 113.463571, ""maximum"": 143.747386, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.3420665000000014,-5.796062000000006,2.4831729999999936,-0.29341340887094614,-4.841108476027922,2.151856179849432,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,write,baseline,candidate,ms,10,0,152.998639,157.081132,unsplit,unsplit,"{""n"": 10, ""p50"": 152.998639, ""p95_reference"": """", ""minimum"": 105.266935, ""maximum"": 173.889485, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 157.081132, ""p95_reference"": """", ""minimum"": 109.271257, ""maximum"": 170.078216, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",4.0824929999999995,-21.790348499999993,28.414495000000016,2.668319814269715,-13.211373730440778,21.335000388714633,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,git,baseline,candidate,ms,10,0,404.511602,387.065142,unsplit,unsplit,"{""n"": 10, ""p50"": 404.511602, ""p95_reference"": """", ""minimum"": 365.490143, ""maximum"": 824.110585, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 387.065142, ""p95_reference"": """", ""minimum"": 366.41647, ""maximum"": 786.817882, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-17.446460000000002,-43.1554385,362.8267168374993,-4.312969001072064,-10.15133314100217,91.48113097145973,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,rg,baseline,candidate,ms,10,0,453.05335,418.8727155,unsplit,unsplit,"{""n"": 10, ""p50"": 453.05335, ""p95_reference"": """", ""minimum"": 431.242878, ""maximum"": 482.024183, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 418.8727155, ""p95_reference"": """", ""minimum"": 395.836215, ""maximum"": 436.257845, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-34.1806345,-52.35176000000001,-19.29760799999997,-7.544505409793345,-11.387569735656433,-4.329230687039798,faster +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,cargo,baseline,candidate,ms,10,0,508.938783,488.0712105,unsplit,unsplit,"{""n"": 10, ""p50"": 508.938783, ""p95_reference"": """", ""minimum"": 497.104239, ""maximum"": 531.244318, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 488.0712105, ""p95_reference"": """", ""minimum"": 479.838543, ""maximum"": 519.067537, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-20.867572499999994,-30.005172999999957,-11.134416000000044,-4.100212677248449,-5.849091368688708,-2.1933129399490214,faster +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,npm,baseline,candidate,ms,10,0,975.212892,929.924457,unsplit,unsplit,"{""n"": 10, ""p50"": 975.212892, ""p95_reference"": """", ""minimum"": 956.093243, ""maximum"": 994.02812, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 929.924457, ""p95_reference"": """", ""minimum"": 712.545115, ""maximum"": 955.363464, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-45.28843500000005,-142.84756531250002,-34.50912100000005,-4.643953681449076,-14.757461850438,-3.5390971840836283,faster +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,ready_ms,before,candidate,ms,10,0,701.6391005,693.3607625,unsplit,unsplit,"{""n"": 10, ""p50"": 701.6391005, ""p95_reference"": """", ""minimum"": 690.82293, ""maximum"": 712.260488, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 693.3607625, ""p95_reference"": """", ""minimum"": 684.282021, ""maximum"": 726.019, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-8.278338000000076,-15.691415499999948,-2.56550900000002,-1.179856994015982,-2.2075007869827967,-0.36862457696940076,faster +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,result_ms,before,candidate,ms,10,0,4322.3996565,4303.31926,unsplit,unsplit,"{""n"": 10, ""p50"": 4322.3996565, ""p95_reference"": """", ""minimum"": 4148.19369, ""maximum"": 4634.98185, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 4303.31926, ""p95_reference"": """", ""minimum"": 4114.404626, ""maximum"": 4733.887275, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-19.08039649999955,-208.33696899999995,352.35256200000003,-0.4414306407623969,-4.647749548701285,8.112365261407707,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,completion_ms,before,candidate,ms,10,0,4560.905778,4484.42847,unsplit,unsplit,"{""n"": 10, ""p50"": 4560.905778, ""p95_reference"": """", ""minimum"": 4400.953022, ""maximum"": 4878.763604, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 4484.42847, ""p95_reference"": """", ""minimum"": 4284.771117, ""maximum"": 4925.370465, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-76.47730800000045,-290.97629099999995,262.68939899999987,-1.6768008751440688,-6.101023686832433,5.809290056528682,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,worker_ms,before,candidate,ms,10,0,3621.289471,3606.7074435,unsplit,unsplit,"{""n"": 10, ""p50"": 3621.289471, ""p95_reference"": """", ""minimum"": 3433.253015, ""maximum"": 3944.006877, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 3606.7074435, ""p95_reference"": """", ""minimum"": 3424.74753, ""maximum"": 4007.710068, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-14.582027500000095,-202.75019199999997,356.52362300000004,-0.40267500338694306,-5.349564986425682,9.831150421507196,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,metadata,before,candidate,ms,10,0,123.04792850000001,142.879463,unsplit,unsplit,"{""n"": 10, ""p50"": 123.04792850000001, ""p95_reference"": """", ""minimum"": 113.761069, ""maximum"": 173.507349, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 142.879463, ""p95_reference"": """", ""minimum"": 109.248394, ""maximum"": 193.687111, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",19.831534499999975,-30.704955000000012,64.7004125,16.116918620048093,-20.591589894426665,54.068835933254064,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,read,before,candidate,ms,10,0,118.0688145,116.239688,unsplit,unsplit,"{""n"": 10, ""p50"": 118.0688145, ""p95_reference"": """", ""minimum"": 113.033845, ""maximum"": 142.850213, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 116.239688, ""p95_reference"": """", ""minimum"": 113.463571, ""maximum"": 143.747386, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-1.829126500000001,-7.181691000000015,0.24210399999999765,-1.549203748463146,-5.698219657006776,0.2087146918508198,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,write,before,candidate,ms,10,0,118.641548,157.081132,unsplit,unsplit,"{""n"": 10, ""p50"": 118.641548, ""p95_reference"": """", ""minimum"": 96.60634, ""maximum"": 149.088464, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 157.081132, ""p95_reference"": """", ""minimum"": 109.271257, ""maximum"": 170.078216, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",38.439583999999996,9.882897174999991,63.73562249999999,32.39976605834576,7.535407135453076,64.42140771151135,slower +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,git,before,candidate,ms,10,0,,387.065142,separated-clusters,unsplit,"{""n"": 10, ""p50"": """", ""p95_reference"": """", ""minimum"": 354.844523, ""maximum"": 765.855372, ""distribution"": ""separated-clusters"", ""low_n"": 5, ""low_p50"": 364.978813, ""high_n"": 5, ""high_p50"": 731.631807}","{""n"": 10, ""p50"": 387.065142, ""p95_reference"": """", ""minimum"": 366.41647, ""maximum"": 786.817882, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",,,,,,,separated clusters; no whole-distribution ranking +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,rg,before,candidate,ms,10,0,422.51628949999997,418.8727155,unsplit,unsplit,"{""n"": 10, ""p50"": 422.51628949999997, ""p95_reference"": """", ""minimum"": 392.012682, ""maximum"": 449.120192, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 418.8727155, ""p95_reference"": """", ""minimum"": 395.836215, ""maximum"": 436.257845, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-3.643573999999944,-26.00949750000001,16.516374150000015,-0.8623511307248477,-5.889376948386782,4.098432434016597,no detected difference +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,cargo,before,candidate,ms,10,0,501.842823,488.0712105,unsplit,unsplit,"{""n"": 10, ""p50"": 501.842823, ""p95_reference"": """", ""minimum"": 481.152788, ""maximum"": 529.957093, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 488.0712105, ""p95_reference"": """", ""minimum"": 479.838543, ""maximum"": 519.067537, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-13.771612500000003,-28.893867500000056,-6.115591000000052,-2.744208319583763,-5.563656181729161,-1.2188892407220542,faster +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,npm,before,candidate,ms,10,0,924.1405775000001,929.924457,unsplit,unsplit,"{""n"": 10, ""p50"": 924.1405775000001, ""p95_reference"": """", ""minimum"": 825.165724, ""maximum"": 951.471934, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 10, ""p50"": 929.924457, ""p95_reference"": """", ""minimum"": 712.545115, ""maximum"": 955.363464, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",5.783879499999898,-101.11153649999994,37.297305999999935,0.6258657655360711,-10.925673042081986,4.174025872447418,no detected difference diff --git a/docs/src/zh/design/direct-queue-provenance.csv b/docs/src/zh/design/direct-queue-provenance.csv new file mode 100644 index 000000000..75b356d09 --- /dev/null +++ b/docs/src/zh/design/direct-queue-provenance.csv @@ -0,0 +1,20 @@ +field,value +filesystem_report_sha256,76121114242e2372287f734ea14764d4072e14bee658cbfbe340302f3bddc26b +filesystem_raw,benchmark/.data/direct-queue-quick-20261011/report.json +filesystem_launch_sha256,e4e4e817fb556b102058d9febd8612901350ff33f2455a2357c7efa6b9574c4d +filesystem_guard_sha256,1fa71afe6f70756cab5446ac49315bd484f3e51ff78439a436ffcb388932e526 +filesystem_protocol,10 seeded shuffled paired rounds per arm/backend; exploratory 60-sample batch; cancelled original formal batch retained separately; 3 warmups; fresh private user/mount/PID namespace and pivot_root per trial; fresh Host listener and VM launch timed; full 256 x 64 KiB written bytes verified; unchanged seven-operation workload; no profiling +filesystem_before_scope,"prior candidate6 overlay optimization and shared worker-pool queues; engineering recovery reference, not literal cf8 parent" +filesystem_baseline_scope,"previous concurrency build2 with kick forwarder, busy-based stealing and mount-wide I/O drain" +filesystem_binaries,"{""before"": ""378ee13ba8b8aafc0a588d51a9c3a79c1410c000857f323970cc13fc65d279e4"", ""baseline"": ""87ebb0658f2df6a84f9e4eca29c8d3d8bc01923817f04432780272c8b1b30309"", ""candidate"": ""ae221676e4bb04231c88f2ce17c2570824f4064f8a3a63d6ef32817b0e2afa98""}" +filesystem_source_manifests,"{""before"": ""aa30eda66bc1f1ee853404b7d41e8847f0722ce72adfb79fa6816e06258655d6"", ""baseline"": ""07a216161d0b64313ba03787630658f300a19788f705d318dfd88f9c5397726c"", ""candidate"": ""f5d1759332f5393bf9ff56569018ccb443d889f4a028ee6df4563569329a5590""}" +candidate_scope,"direct per-queue kick wait/execute/publish; steal only heavyweight operation running >=2 ms with pending descriptors, revalidated under ring lock; scoped handle I/O fences and I/O admission outside namespace guard; writeback unchanged" +candidate_build_receipt_sha256,dd6121706bbc4dcff61a4b537611703bb181c5af02735c71806b8313fd6897bf +publication_script_sha256,a8759c3cb72866890ab8de267537a81b1ab5f50bd8a5c65b6d6150baa6a15355 +source_note,measurement build includes final production implementation; worktree subsequently only moves existing cfg(test) device tests to satisfy Clippy; no production behavior change +budget,"whole owned parent CPUs 0,1; CPUQuota 200%; RAM 16 GiB; swap 0; VM 2 vCPU/16 GiB" +statistics,5000 paired-round bootstrap resamples of median difference and median ratio CI95; separated distributions have no global median ranking; no P95/P99 at N=10; intervals exploratory +interference,none detected outside owned service; same-UID build/test polling every 0.5 s may miss short/inaccessible/unrecognized activity +scope,"B-FS-ENG exploratory; only 10 paired rounds per cell, not formal 30-round performance acceptance; Linux KVM/FUSE/Btrfs warm caches, sequential jobs; same seven-operation workload; unchanged staged control; no startup/cold/concurrency-throughput/macOS claim" +correctness,"all 60 exploratory measured tasks and 18 warmups pass original workload, Run Bundle, isolation and full 256x64KiB output bytes; inputs and artifacts reverified; preflight separate" +validation,363 VM library tests pass; 8 skipped; Clippy all targets and warnings-denied rustdoc pass diff --git a/docs/src/zh/design/experimental/guest-stage-cache/index.md b/docs/src/zh/design/experimental/guest-stage-cache/index.md new file mode 100644 index 000000000..71912e59c --- /dev/null +++ b/docs/src/zh/design/experimental/guest-stage-cache/index.md @@ -0,0 +1,637 @@ +# EXP-002(设计阶段):Guest 内核文件系统缓存与延迟后端绑定 + +让 guest 内核使用版本明确的文件系统视图,直接完成缓存命中的路径查询、属性查询和普通只读 open;在数据缺失、stage 修改或持久化时调用 VMM。宿主继续持有 stage、基线观察、访问策略、review/apply 与恢复的权威状态。 + +首版研究复用 FUSE 缓存与只读 zero-message open,后续增加分页元数据导入和 stage 缓存一致性协议。 + +| 项目 | 当前状态 | +|---|---| +| 阶段 | **设计阶段(RFC),2026-10-11;尚未实现或验收** | +| 实现读者 | guest 内核、VMM、文件系统与性能方向的研究实现团队 | +| 第一阶段 | Linux x86_64/KVM;独占、物理不可变的只读导出;普通文件与静态访问范围 | +| 核心机制 | 元数据导入、对象引用、guest 本地打开状态、首次回源时建立数据绑定 | +| stage 扩展 | 宿主提交修改,guest 按协议更新缓存;现有可写 open/copy-up 合同先保留 | +| 开关 | 尚无运行时开关或 CLI 接口;原型接入后默认关闭 | +| 实现语言 | 新增宿主与 guest 用户态代码优先 Rust;必要的 guest 内核扩展优先小范围 C 补丁 | +| EROFS | 可选的不可变 lower 表示,不是首版依赖;不承接 stage 写入 | +| 性能状态 | 尚无该方案的真实 VM 性能数据;请求减少与端到端收益分别验收 | +| 后续 profile | frozen 执行基线、独占 stage、受协议管理的缓存更新 | +| 接口状态 | 下列类型、字段和消息名表达拟议语义,尚未形成 wire ABI 或公开 Rust API | +| 实施入口 | [里程碑、分工和验收矩阵](#implementation-plan) | + +先验证现有 FUSE 缓存与 zero-message open 的适用范围,再研究分页元数据导入和混合 stage 视图。第一阶段的结果只适用于列明的只读 profile;扩展到可写 stage、live baseline 或其他平台需分别验收。 + +## 1. 问题提出 {#problem} + +### 1.1 文件系统请求为什么值得下沉 + +文件扫描和工具初始化会反复执行路径查找、属性查询和 open。有些文件只被打开以检查属性,根本没有读取内容。现有 guest FUSE 能缓存部分目录项、属性和数据,但未命中的元数据请求及当前普通 OPEN 仍经过 guest 内核、virtqueue、VMM 和宿主文件服务;后者还会创建后端 handle。lazy image 的索引已经在宿主准备好,并不意味着 guest 能直接使用这些知识。 + +因此,需要判断哪些请求可以在 guest 内核依据稳定视图完成,把不可变 lower 的元数据和受协议管理的 stage 状态交给 guest,同时保留宿主的修改、授权和持久化职责。目录扫描、open/fstat/close 和混合 stage 视图分别对应元数据供给、打开生命周期和缓存一致性三个问题。 + +现有[写合并对照](../../filesystem-performance-analysis.md#writeback-ab)支持关闭写合并后该写入负载改善,完整任务仍未检出加速。缓存方案针对元数据与打开路径的结构性成本,其端到端收益需要独立实验;写回、copy-up 和持久化成本仍需各自定位。 + +### 1.2 当前基础与设计缺口 + +VM 文件系统通过 guest virtio-fs 和 virtqueue 直接调用宿主 runner 中的 `FilesystemService`,与 host FUSE 复用 OverlayCore。VM lazy image 没有中间宿主 FUSE 挂载,元数据和按块读取仍由宿主 image backend 提供。[现有架构](https://github.com/DeepLink-org/pvisor/blob/main/crates/pvisor-vm/README.md)、[共享服务](../../overlayfs.md) + +| 当前基础 | 能复用的内容 | 需要增加或核验的内容 | +|---|---|---| +| guest FUSE 缓存 | 路径、属性和文件页缓存 | 准入、版本有效性、元数据导入与本地 open 条件 | +| FUSE zero-message open | 只读连接的无 OPEN 原型 | 无 handle 的数据服务、FLUSH/RELEASE、连接级限制 | +| VM OverlayFs | inode/handle、对象 I/O lease、修改和 freeze | 未绑定打开状态、对象保留、缓存更新协议 | +| OverlayCore | lower/upper 合成、copy-up、原像、冲突检测 | 为缓存提供明确语义输入,保留原合同 | +| lazy image 分页索引 | 版本化属性、目录索引、内容描述与校验 | guest 消费接口、稳定对象映射、权限完整性 | +| 现有 snapshot | CPU/device/RAM 静默与文件系统重绑定 | 缓存模式、对象引用和未绑定 FD 的保存与恢复 | + +当前只读 open 在 live baseline 下会进入基线观察;可写 open 会进行 copy-up。[open 分类与准备](https://github.com/DeepLink-org/pvisor/blob/main/crates/pvisor-overlay-core/src/service.rs)、[原像观察](https://github.com/DeepLink-org/pvisor/blob/main/crates/pvisor-overlay-core/src/core.rs) 因此,本地完成 open 的准入必须同时考虑观察合同,不能仅按是否写入数据分类。 + +当前普通 OverlayFs open 要求创建后端 handle。[VM open 实现](https://github.com/DeepLink-org/pvisor/blob/main/crates/pvisor-vm/src/devices/virtio/fs/overlay.rs) 已有 FUSE 标志定义并不代表可以直接开启这个方案。 + +host OverlayFs 已有 owned-view 缓存、修改效果收集和通知失败处理,可复用其语义经验与测试场景。[host 缓存合同](https://github.com/DeepLink-org/pvisor/blob/main/crates/pvisor-overlayfs/README.md) 该实现属于 host 挂载适配器,不能直接当作 guest 更新传输,也不能为复用而暴露其私有状态。 + +### 1.3 优化目标与衡量边界 + +目标是在元数据、权限、对象存活和版本条件完整时,让路径查询、属性查询及普通只读 open 不产生文件系统 VMM 请求。数据缓存命中也在 guest 完成;数据缺失、stage 修改以及必须保留的宿主观察或副作用继续回源。性能评价同时覆盖冷启动、稳态和完整 Attempt。 + +“零请求”指指定操作窗口内没有文件系统 VMM 请求。元数据导入、首次协议探测、引用回收和 teardown 分别记录。该目标不表示整个 VM 没有硬件退出、调度或其他设备活动。 + +## 2. 核心设计 {#core-design} + +### 2.1 总体架构与职责 + +1. guest 使用自身 VFS 的 dentry、inode、页缓存及文件打开状态,缓存导出的命名空间和元数据。 +2. VMM 保留 stage 修改、copy-up、数据供给、基线观察和持久化;guest 缓存是执行视图的副本。 +3. 普通只读 open 可以创建 guest 打开状态,在首次需要回源的数据操作时建立后端绑定。数据已在 guest 缓存中时,不因执行 read 就强制回源。 +4. 可写 open、创建、截断、删除、重命名和其他当前具有副作用的操作,第一版保留现有宿主路径。 +5. 先证明少量操作在同一合同下减少请求,再扩展 stage 视图和恢复;每个阶段单独验收。 + +```mermaid +flowchart LR + subgraph Guest[Guest] + App[Tool processes] --> VFS[VFS paths and open state] + VFS --> Cache[Namespace metadata and data cache] + Cache --> Local[Valid hit completed locally] + Cache --> Remote[Miss or required host operation] + end + subgraph Host[Host] + Metadata[Immutable metadata and access grants] --> Transport[virtio-fs and proposed cache protocol] + Transport --> Objects[Object references and backend bindings] + Objects --> Core[FilesystemService / OverlayCore] + Core --> Data[Native files / lazy content blocks] + Core --> Stage[Upper preimages and sealing] + Stage --> Apply[Review / apply / restore] + end + Remote --> Transport + Transport --> Cache +``` + +guest 内核持有缓存、本地文件打开状态和按协议取得的对象引用。VMM 持有会话、可信对象映射、native handle、权限验证和请求生命周期。OverlayCore 继续执行合成、copy-up、原像及 stage 语义。runtime/supervisor 继续仲裁 Attempt、冻结、封存、保存和恢复。 + +元数据构建与导入负责固定源 revision、校验索引、保留 Unix 原始路径字节及硬链接身份。缓存不能绕过 image/backend 的内容校验,不能从 lazy 稀疏占位文件的空洞读取真实内容。 + +### 2.2 本地执行与 VMM 操作边界 + +本地执行需要同时满足:缓存条目有效、相关元数据完整、对象存活已有保障、访问可本地判定,以及操作没有必须在宿主完成的观察或副作用。 + +| 操作 | 有条件的 guest 本地路径 | 必须回源的情况 | +|---|---|---| +| LOOKUP、GETATTR、stat/lstat | 版本有效的目录项和属性 | 页缺失、属性缺失、策略或观察要求回源 | +| 负查找 | 完整目录索引及 stage 覆盖状态证明不存在 | 不完整索引、版本缺口;错误不当作不存在 | +| READDIR | 已验证的目录视图和版本绑定的 cookie | 索引缺页、stage 版本变化、尚未支持的游标语义 | +| O_PATH | 使用有效路径缓存及对象引用 | 路径查询、权限或引用条件不满足 | +| 普通文件 O_RDONLY open | 静态权限完整、对象已保留、观察合同允许 | live 观察、动态策略、特殊文件、未知能力 | +| O_WRONLY/O_RDWR open | 首版保留现有 VMM 行为 | 当前 copy-up、权限和错误时机需保留 | +| O_CREAT/O_TRUNC/O_TMPFILE 等 | 可使用缓存准备输入 | 创建、截断、临时对象等副作用经过 VMM | +| read、pread、mmap 缺页 | 内容版本有效的页缓存 | 数据缺失、缓存失效、所需后端绑定尚未建立 | +| write、truncate、共享可写 mmap | 首版保持现有路径或明确拒绝未覆盖形态 | stage 修改、copy-up、原像和数据写入 | +| unlink、rename、link、mkdir、属性修改 | 原子应用宿主返回的缓存更新 | 修改授权、原像和 stage 提交经过 VMM | +| fsync、seal、checkpoint | 按已有合同刷新和静默 | 宿主负责的同步、封存和保存 | + +O_PATH 通常已由 Linux 在 VFS 层处理,不应把原本就没有 FUSE OPEN 的操作当成新增收益。普通 open 带 O_TRUNC 等标志时,副作用可能在任何 read/write 之前发生。[Linux open 语义](https://man7.org/linux/man-pages/man2/open.2.html) + +首版排除 live/mutable lower、外部写入 upper、多个 VM 共同修改同一 stage、动态宿主策略、特殊文件及未明确支持的锁和 I/O 形态。范围扩展需增加独立 profile 和对应验收项。完整 guest upper、全量迁移 overlay 到内核和改变 apply 合同,分别属于后续架构评估。 + +目标操作示例: + +```text +open("a", O_RDONLY) → guest 检查视图和权限,保留对象 A,创建本地打开状态 +fstat(fd) → 返回对象 A 的有效缓存属性 +read(fd) → 缓存命中则返回;缺失则按对象 A 回源 +close(fd) → 释放打开状态;对象缓存引用按协议回收 +``` + +### 2.3 对象、版本与引用 + +以下是评审用语义模型,不是最终序列化结构: + +| 概念 | 必须表达的内容 | 生命周期与用途 | +|---|---|---| +| 会话身份 | Attempt、stage、base revision、协议版本、权限语义、策略版本 | 挂载/恢复前由宿主验证;不同会话不复用 token | +| 对象身份 | 宿主分配或认可的 object ID、存活世代、类型、硬链接组 | 在引用存续期间不可因路径或 inode 数字重用而改变 | +| 后端身份 | 物理或远程 backing 的身份及世代 | copy-up/替换可改变后端;已有打开绑定遵循当前 VM 合同 | +| 目录版本 | 目录及相关覆盖状态的版本 | 创建、删除、重命名、whiteout/opaque 等更新 | +| 属性版本 | 所有权、权限、大小、时间、链接数等版本 | 与目录和内容版本分别校验 | +| 内容版本 | 对象数据及范围有效性的版本 | 写入和截断更新;数据页不能只检查目录版本 | +| 对象引用 | 已授予的保留范围、权限、owner、预算与回收状态 | 可以早于数据 FD;淘汰缓存不释放仍被打开状态持有的引用 | +| 打开状态 | 对象/后端绑定、flags、凭据上下文、偏移、绑定状态 | dup/fork 共享同一打开状态;独立 open 独立持有 | + +目录缓存以会话、父对象、原始 basename、相关目录版本和策略版本隔离。属性缓存以对象身份及属性版本隔离。数据页以对象/后端身份和内容版本隔离。实现可以组合字段或压缩存储,但必须保留上述区别。 + +#### 2.3.1 延迟绑定必须保留旧对象 + +```text +fd = open("a", O_RDONLY) # 本地打开对象 A +unlink("a") # VMM 修改 stage +create("a") # 同名对象 B +read(fd) # 必须仍访问 A +``` + +被 unlink 的已打开文件在相关引用释放前继续存在。[Linux unlink 语义](https://man7.org/linux/man-pages/man2/unlink.2.html) 第一次 read 必须携带 A 的对象引用,不能重新查找路径 a。 + +不可变 lower 可以保留整个 revision;无需为每次 guest open 创建宿主数据 FD。stage 对象可使用已经固定的 native 对象、描述符或私有对象保留机制,具体实现必须证明 unlink 后存活及资源上界。数据 FD 可以延迟,对象保留不能延迟。缺少这项保障时,open 回到原宿主路径。 + +stage 元数据交付时就必须附带已生效的宿主保留引用,guest 才能在该引用范围内增加本地打开引用。宿主可保守地保持缓存引用对应的对象,直到 guest 明确释放缓存和打开引用;代价是对象/FD 预算。仅交付路径、随后在首次 read 时 pin 对象,不满足这个条件。 + +对象存活世代与内容版本不同:同一可变文件被写入后,其他已打开 FD 应按现有语义看到更新;不能把每次写入都解释为新对象。copy-up 后已有 lower handle 保持原 backing 的现有 VM 行为也要保留。 + +#### 2.3.2 引用回收 + +缓存和打开状态分别持有引用。缓存淘汰只释放缓存部分;在途 I/O 继续持有其对象和 RAM lease。guest 通过有界批量回收或已有 FORGET 类机制释放缓存引用;整个会话 teardown 提供最终回收边界。 + +stage 删除的对象进入待回收状态,最后一个受认可引用释放后才回收 backing。达到引用/FD 预算时拒绝新准入或回到原路径,不能强制回收仍被 FD 使用的对象。guest 不回应回收时,由现有 supervisor 管理会话终止。 + +### 2.4 元数据包与 merged 缓存 + +#### 2.4.1 物理稳定性与视图稳定性 + +物理不可变要求内容、元数据、命名空间、父目录、mount 身份及硬链接别名在约定生命周期内稳定。只读挂载、摘要路径和 frozen baseline 分别表达其他属性,不能单独证明物理稳定性。[现有 lower 合同](https://github.com/DeepLink-org/pvisor/blob/main/crates/pvisor-overlay-core/README.md) + +只读导出可整体缓存。混合 stage 视图还必须保证:upper/work 由单一会话控制,所有修改经 VMM,higher-priority lower 的变化可被证明或处理,策略固定或具备可靠更新协议。准入失败时在启用前拒绝,不能仅增加 TTL 来承担未知外部变化。 + +#### 2.4.2 分页导入 + +启动交付有界的版本、校验目录、root/必要页及操作能力;后续可以按需取元数据页。目录索引、inode 属性、symlink 目标、硬链接组和内容位置分别表达。包已生成、页已交付和 inode/dentry 已建立是三个成本。 + +现有 [pvisor-paged-v1](../../shared-image-cache-storage.md) 可以提供字段与构建基础;guest 导入格式需单独评审。当前格式不保存 xattr,不能据此声明已具备 ACL 或完整安全属性。缺少权限输入时,不准入本地 open;属性查询也按所需字段回源。 + +内核消费前限制总字节、记录数、页引用、深度、名称/目标长度及 offset 运算;拒绝循环、重复、越界和未知必要特性。宿主校验和内核边界检查都保留。编码固定版本和字节序,不映射 Rust/C struct 的本机内存布局。 + +#### 2.4.3 merged 查找与负缓存 + +缓存先检查有效 stage 覆盖,再按明确 lower 顺序查询。whiteout、opaque 和非目录祖先会遮盖 lower;目录 rename 需要更新相关子树与路径缓存。合成配置文件和挂载点按现有入口的职责处理,元数据包不能覆盖它们。 + +只有完整、已校验的索引和有效 stage 状态才能证明不存在;未加载页不表示不存在。readdir cookie 绑定目录视图和版本,恢复/seek/并发修改按明确合同处理。网络、校验或版本错误不写入负缓存。 + +### 2.5 本地 open 与延迟绑定 + +#### 2.5.1 本地打开步骤 + +1. 取得一致的缓存视图,完成逐组件路径和 symlink 解析。 +2. 核对文件类型、flags、权限输入、路径授权和观察要求。 +3. 从已有对象引用中取得打开引用;保证对象不会在下一次 I/O 前消失。 +4. 创建 guest 打开状态并返回 FD;当前无需读取文件内容。 + +缺少任何条件时使用原 open 路径。O_CREAT、O_TRUNC、O_TMPFILE、权限清除和当前可写 copy-up 都是单独的宿主操作。延迟 copy-up 或改变原像观察时点属于独立合同迁移。 + +#### 2.5.2 打开状态机 + +```mermaid +stateDiagram-v2 + [*] --> Unbound: Local open and retention succeed + Unbound --> Unbound: Valid attribute or data cache hit + Unbound --> Binding: First backend resource required + Binding --> Bound: Host validates and binds + Binding --> Unbound: Retryable failure without binding effects + Binding --> Failed: Invalid object or unknown result + Bound --> Bound: Data and supported control operations + Unbound --> Closed: Last open reference released + Bound --> Closing: Last open reference released + Closing --> Closed: In-flight I/O drained and backend released + Failed --> Closed: Cleanup under failure contract +``` + +Binding 状态下的并发首次请求共享同一次绑定结果;不持有 inode 表或全视图锁等待 I/O。关闭、取消和 freeze 不能遗留未知的后端 handle。异步请求保留打开状态,现有 INTERRUPT 不提供实际取消保证,清理仍须等待结果或进入会话失败路径。 + +#### 2.5.3 数据请求 + +拟议请求携带受宿主认可的对象引用、后端/内容版本条件、打开上下文、offset 和 length。VMM 核对会话、授权、对象存活和边界后,通过已固定的 native backing 或 lazy backend 提供数据。 + +新协议可合并 BIND 与第一次 READ,避免额外往返。首版纯只读 FUSE 原型也可以通过 inode 加 fh=0 服务数据,并在宿主按对象缓存只读 backing;这条路径不等同于新增 BIND RPC,需分别验证。 + +read、mmap 缺页和执行文件加载都可能产生数据请求;数据已在有效 guest 页缓存中时可以本地完成。[FUSE I/O 缓存](https://docs.kernel.org/filesystems/fuse/fuse-io.html) write 第一版保留现有路径,正确处理部分成功、大小与时间变化、错误及原像顺序。 + +混合 stage 的内容缓存另设准入:现有 lower 打开绑定与 copy-up 后的新绑定可能属于不同 backing,而普通 FUSE inode 通常共用一个页缓存地址空间。实现必须证明页不会跨 backing 错误复用;必要时先限制内容缓存,再研究内核页缓存归属或单独迁移该语义。只维护概念上的内容版本字段,并不足以解决这个问题。 + +#### 2.5.4 open 的错误与 close + +本地 open 必须保留存在性、权限、类型、flags 和已承诺的观察/副作用错误。延迟宿主 FD 会影响宿主资源不足等错误的出现时点;profile 需明确并经语义评审,不能只比较最终数据正确性。 + +不申请后端绑定的只读 FD 可以本地释放打开状态,但需单独核验 FLUSH、RELEASE、FORGET、访问时间及引用回收。zero-message open 不自动保证整个 open/fstat/close 窗口没有请求。首次连接探测计入启动成本,稳态请求计数单独报告。 + +### 2.6 stage 修改与缓存一致性 + +#### 2.6.1 更新内容 + +VMM 返回有版本的修改结果,至少表达受影响的目录项、对象属性、后端绑定、硬链接别名、内容范围及 whiteout/opaque 或子树变化。guest 依据结果更新缓存,不能用猜测的本地结果代替宿主提交。 + +namespace 修改、普通数据写入和策略变化分别更新相关版本。旧 metadata/read 响应只有在对象及版本条件仍有效时才能填充缓存。失败也可能已产生部分数据或可见状态,需要返回或恢复实际效果。 + +#### 2.6.2 可见性顺序 + +复杂修改采用可验证的准备、屏障和发布协议: + +1. 确定影响范围和预期版本;别名或子树不明确时扩大屏障范围。 +2. guest 阻止范围内新缓存读取/打开,并协调已进入的操作;VMM 协调其原生 I/O。 +3. VMM 按现有合同记录原像、执行 copy-up/修改,生成新的版本和效果。 +4. guest 原子应用效果,丢弃旧响应,完成约定的可见性确认。 +5. 解除屏障后返回修改结果;相应宿主请求在发布窗口内遵循相同顺序。 + +真实线性化点、跨队列顺序和 ACK 必须在实现前写成可执行交错测试。首个 stage PoC 可以使用保守的全视图屏障,再测量影响并缩小范围。禁止持有会被响应线程需要的锁等待 guest ACK。 + +本地 open 的对象保留必须与修改屏障协调:新 FD 不能在对象删除已提交但缓存仍旧时取得失效引用;已打开 FD 继续保留原对象。元数据命中和后端 I/O 不能在同一因果链上组合不兼容的版本。 + +#### 2.6.3 传输与提交不明 + +混合 stage 原型需要可靠的更新传输、序列检测、缺口恢复和背压。可评估操作回复扩展或独立控制通道;host `/dev/fuse` 通知机制不能直接当作当前 virtio-fs 已完成的能力。 + +漏更新、序列跳跃或 token/版本不匹配时先禁止对应缓存路径,进入验证后的重同步。已打开 FD 的对象绑定保留;不能通过丢掉全部 FD 来假装缓存已重建。无法建立一致状态时停止会话。 + +请求 ID 用于关联,首版不据此承诺持久 exactly-once。READ 在明确对象条件下可重试;修改超时或取消后不盲目重发,核对可信 stage/日志或沿现有失败恢复路径处理。 + +#### 2.6.4 协议评审的最小消息集合 + +以下仅规定信息和因果关系;具体编码、错误码、限额、兼容性及是否复用现有 opcode,在对应里程碑评审后确定。 + +| 消息类别 | 必需信息 | 成功与失败边界 | +|---|---|---| +| 视图初始化 | 会话、协议/profile、base/stage 身份、策略版本、预算、索引根、起始更新序列 | 完整准入成功后才允许本地操作;失败不产生半可用视图 | +| 元数据页请求 | 视图、页/对象、预期版本;回复含校验和、完整性与引用授权 | 旧页不得覆盖新状态;缺页与不存在区分 | +| 数据回源/绑定 | 对象保留引用、backing/内容条件、已授权打开上下文、offset/length | 可合并首个 READ;失败区分无副作用、已产生资源及结果不明 | +| stage 修改及效果 | 现有操作输入、预期版本、请求关联;回复含实际效果与更新序列 | 宿主保留原像与授权;部分成功也发布真实效果 | +| 更新确认/重同步 | 已应用序列、缺口、屏障范围和恢复视图 | 缺口期间停用受影响缓存;确认不等于持久化 | +| 引用释放 | 会话、对象/绑定引用、批次与 owner | 重复或迟到释放按明确合同处理;不释放仍在使用的对象 | + +会话静默、seal 和 snapshot 继续由既有控制 owner 调度。上述消息不能另建一套终态或 Host 权限控制通道。 + +### 2.7 语义与权限不变量 {#invariants} + +1. **宿主权限不变。** guest 不能自行授予文件读取、stage 修改、apply、seal 或 Host 控制权限。 +2. **执行视图与冲突基线独立。** 可见 compose 文件 B、apply baseline A、upper C 分别保存;修改 B 后 apply 仍对照 A,不能把 B 的摘要代替 A。 +3. **对象身份稳定。** open 绑定对象及其存活身份;rename、unlink、同名重建和缓存淘汰不会把旧 FD 转到新对象。 +4. **内容相同不等于同一 inode。** 内容去重、硬链接组、目录项和打开状态分别建模。 +5. **merged 缓存尊重覆盖。** upper、whiteout、opaque、目录重命名和策略共同决定可见结果;lower 不变不能证明 merged 结果不变。 +6. **观察合同保留。** 现有 live 内容打开和负查找的观察不能因缓存命中静默消失;首版选择允许跳过这些观察的 profile。 +7. **错误不表示不存在。** 超时、校验失败、权限拒绝、版本不匹配和后端故障分别传播。 +8. **提交与可见性有明确顺序。** stage 修改及缓存更新有共同的线性化规则;旧响应不能覆盖新状态。 +9. **生命周期受控。** 对象引用、后端 handle、在途请求与缓存都有 owner、预算和释放协议。 +10. **快照保持原有打开语义。** 冻结和重绑定不能通过按当前路径重开文件来替代原对象。 + +#### 2.7.1 权限、基线观察与信任边界 + +宿主在交付元数据时裁剪不可见路径、受保护别名和对象访问范围;必要的访问模式、ownership、ACL/xattr 输入要完整。原始路径字节保留,symlink 不能使 guest 请求任意宿主路径。对象摘要仅表达内容身份,不能作为跨租户读取权限。 + +guest 做本地 Linux 权限检查;VMM 对数据和修改请求继续验证宿主认可的授权。会话 token、guest uid 或修改清单不能替代 Host 控制凭据,不能修改宿主权限账本。[Host/Guest 权限分离](../../decisions/0008-host-guest-control-authority.md) + +需要每次宿主回调的动态策略或审计 profile 不进入本地 open 快速路径。策略变化先撤销新缓存准入并协调现存引用;恢复只在权限与引用合同重建后继续。 + +live baseline 的首次内容打开会观察 apply baseline,缺失查找也可能记录 Absent。把这些事件延迟到首次 read/write 会改变当前合同。首版使用无需这些读观察的稳定只读 profile;frozen stage 修改仍由宿主按真实 baseline 记录原像。guest 提供的摘要或事件不能成为冲突检测的唯一依据。 + +缓存读取涉及的 atime、force-sync 属性查询及权限检查也需按当前 profile 验证;缺乏完整语义的操作回源。通过只读挂载或改变文件时间来掩盖不稳定性,不构成准入证据。 + +### 2.8 生命周期与恢复 + +#### 2.8.1 失败处理与模式切换 + +| 事件 | 处理要求 | +|---|---| +| 导入失败或能力不支持 | 在启用缓存前拒绝;配置失败保持原配置可用,不留下半初始化视图 | +| 元数据不完整 | 允许已验证能力按范围使用,缺失部分回源;不能推导不存在或权限成功 | +| 校验/协议错误 | 返回明确错误;拒绝污染缓存,不写成 ENOENT | +| 对象或世代不匹配 | 重同步缓存或按对象错误处理;旧 FD 不按当前路径重新绑定 | +| 资源耗尽 | 按预算拒绝准入或释放无引用项;保留有效 FD 和在途请求 | +| 修改结果不明 | 停止相关推进并核对真实效果;不能报成功或安全回滚 | +| guest/VMM 退出 | 由已有 supervisor 终结会话,排空/释放对象和后端资源 | +| seal/apply/drop | 使用现有 Host 生命周期边界,禁止与运行中的 stage writer 竞争 | + +连接级 no-open 行为启用后,不能靠返回值逐 inode 恢复原 open 路径。准入和回退边界在挂载前明确;运行中的协议模式改变需排空、保存绑定并经过专门设计。 + +#### 2.8.2 snapshot、fork 与重绑定 + +快照需要绑定 base revision、stage 身份/状态、协议与缓存模式、权限语义/策略版本、对象引用及打开绑定。guest RAM 中的 inode/dentry、FD、页缓存与 VMM 设备状态必须对应同一静默边界。 + +复用现有控制 owner:停止新准入,停驻 CPU,排空已执行设备访问并发布完成,保存一致的 RAM/device/Host 文件系统状态,完成现有 backing 提交与封存。未执行 ring 请求遵循已有保存和恢复合同。排空 virtqueue 不意味着 guest 脏页已持久化;seal 仍需满足原有 writer 停止及数据/目录同步条件。 + +若该流程要求 guest 执行刷新或确认,必须在 CPU 仍可执行的阶段完成,并保留其所需设备请求通路;不能停驻 CPU 或关闭通路后等待 guest 响应。保存含脏页的整机状态与导出已持久化 stage 是不同验收目标。 + +恢复在 guest 继续执行前验证 revision、模式和权限,并重建可信对象映射。Unbound FD 要保留可供未来绑定的原对象;Bound FD 保留对应 backing、flags 和偏移。被删除的 stage 对象若仍有 FD 引用,需要独立保存其 backing,不能只保存当前 namespace。 + +native FD 数字不能序列化为恢复依据。可淘汰缓存与不可丢弃的打开引用分别处理;fork 后 upper/对象引用 owner 独立,immutable base 可以共享。当前 snapshot 不支持的形态应明确拒绝,不能用某个只读 PoC 的恢复成功扩大能力范围。 + + +### 2.9 实施计划与验收 {#implementation-plan} + +本计划面向 guest 内核、VMM、文件系统和性能方向的研究实现团队。先在真实 VM 中验证只读缓存与本地 open,再导入元数据,最后接入 stage 更新和恢复。所有里程碑目前均未完成;命令是后续实现的验证入口,不是本次已执行的测试记录。 + +实现须保持 [设计中的不变量](#invariants)。阶段范围、协议模式和已通过的 profile 分别记录,不能用只读结果代替可写 workspace 的验收。 + +#### 2.9.1 里程碑与决策门 + +| 阶段 | 产出 | 进入下一阶段的条件 | +|---|---|---| +| M0:合同与基线 | 当前行为、支持范围、请求诊断、冻结输入和制品 | 明确可本地执行的操作与不可省略的宿主工作 | +| M1:只读机制原型 | FUSE 缓存、连接级 no-open、无 handle 数据服务 | 真实 KVM 语义通过,完整请求计数可解释,资源可回收 | +| M2:元数据导入 | 版本化包、内核导入、权限完整性、有界分页 | 冷启动和稳态均正确;错误、缺页、负缓存有明确处理 | +| M3:stage 感知 | 对象保留、混合 open、修改效果、更新传输与屏障 | 身份、别名、部分失败和交错测试通过 | +| M4:生命周期 | seal、snapshot、恢复、fork、退出与兼容性 | RAM/device/Host 状态一致,打开引用可恢复,失败可收敛 | +| M5:性能与接入 | 注册工程 A/B、资源结果、维护决策、实验开关 | 目标负载有净收益,非目标负载及资源开销满足预登记预算 | + +M1 可不修改 guest 内核;它验证预热后的机制,不能证明已实现元数据打包或优化冷启动。M2 之后才验收打包导入。M3、M4 在共同语义模型稳定后可分工研究,但两者的合同需共同评审,不能分别假定对方负责对象存活或更新排空。 + +##### M0:先固定合同和基线 + +1. 固定 Linux x86_64/KVM、guest 内核/配置、VMM、rootfs、lower、upper/work 和工具输入;记录源码和制品身份。 +2. 对照现有 open、copy-up、live/frozen baseline、负查找、目录 cookie、权限和错误时机。分别记录当前保证、原型范围和拟议合同变化。 +3. 定义只读准入证据:物理不可变来源、稳定 revision、静态授权、对象保留、权限字段完整、独占会话生命周期。不能仅用只读挂载或内容摘要代替。 +4. 在独立诊断批次记录 INIT、LOOKUP、GETATTR、READDIR、OPEN、READ、FLUSH、RELEASE、FORGET 和失败请求;区分初始化、priming、指定操作窗口及 teardown。 +5. 核对原本已命中的 FUSE 缓存和 O_PATH 行为。只对原先实际发生的请求讨论减少量。 + +产出:合同清单、准入/能力矩阵、诊断 harness、完整计数记录和待评审事项。无法证明来源稳定的输入不进入 M1;失败记录不能转述为性能基线。 + +##### M1:独立只读连接上的最小原型 + +1. 使用受限只读 share,保持当前可写 workspace 路径。按连接协商和验证 no-open 行为,不直接对混合可写挂载返回 ENOSYS。 +2. 实现按可信 inode/对象服务 fh=0 的 READ 等已支持数据请求;保留整个不可变 revision,以保证本地 open 到首次 read 之间对象仍存活。 +3. 宿主按对象复用有界的只读 backing;规定并发首次打开、失败、淘汰、FORGET 和 teardown 的 owner。宿主数据 FD 数量不得随重复 open 无界增长。 +4. 检验 open/fstat/close 的完整窗口。单独记录首次 OPEN/FLUSH 探测;必要的 FLUSH/控制操作只有在 profile 合同允许时才能省略。 +5. 验证普通 read、并发 pread、mmap 缺页和 exec;有效 guest 数据缓存命中不建立后端数据绑定。每个数据实验保留完整字节校验。 +6. 保留资源不足和错误时机记录;需要改变合同的行为单独评审。未支持的锁、特殊文件和 I/O 形态明确拒绝或不准入。 + +退出条件:真实 KVM 中只读语义通过;稳态 metadata/open 请求计数达到所声明目标;没有隐藏 FLUSH、错误回源或 FD 泄漏;首次探测、预热和资源成本已记录。出现机制收益后再投入导入和 stage 协议;性能未改善时先解释成本,不扩大功能范围。 + +##### M2:把元数据包接入 guest 内核 + +1. 明确采用全量导入还是有界分页;复用 lazy 索引构建经验,但单独定义 guest 格式、版本、校验、完整性与能力字段。 +2. 从 root/必要页开始导入 inode、目录项、原始 Unix 名称、symlink、硬链接组和内容描述;区分未加载、完整不存在和错误。 +3. 内核导入器做长度、offset、数量、深度、引用和循环检查。现有格式没有 xattr 时,不能声称已支持 ACL/安全属性;首版输入必须满足明确的权限 profile。 +4. 在当前固定内核上维护小范围 C 补丁,复用 VFS/FUSE 对象。协议和宿主解析优先 Rust;不为首版重写完整文件系统。 +5. 内核导入与页回源使用明确的锁和预算。并发页 miss 合并,不在全视图锁内等待 VMM;淘汰不回收仍被 FD 或 I/O 使用的对象。 +6. 分别测包构建、交付、内核解析、inode/dentry 实例化、首个 lookup 和稳态查询。预热扫描不能记成元数据包导入完成。 + +退出条件:新 guest 的冷查询正确;完整索引可证明负查找;缺页和旧版本处理正确;恶意或损坏包不导致内核越界;峰值内存和导入成本在预登记预算内。包可分页时,应明确哪些第一次查询仍需要 VMM。 + +##### M3:stage 感知的混合视图 + +1. 从 frozen baseline、独占 upper/work、所有修改经 VMM 的 profile 开始。外部 stage writer、live mutable lower 和多 VM 共写明确排除。 +2. stage 元数据交付时授予已生效的对象保留引用;验证本地 open 后 unlink、rename、同名重建和缓存淘汰。首次 read 按旧对象绑定,不能按路径重开。 +3. 评审 per-object 本地 open 能力或内核扩展。连接级 no-open 无法仅对合格只读文件启用;O_WRONLY/O_RDWR、创建和截断先保留当前 VMM/copy-up 路径。 +4. 收集修改前后真实效果:目录项、属性、内容、backing、硬链接别名、whiteout/opaque 与子树。失败已产生部分效果时也发布实际状态。 +5. 实现更新序列、屏障、可靠传输、确认、缺口恢复和有界背压。首个原型可用全视图屏障,再根据诊断缩小范围。 +6. 复用 host owned-view 缓存的语义和测试经验;通过明确输入/结果共享合同,不搬出私有 cache 状态,不假定 host FUSE 通知已能到 guest。 +7. 内容缓存单独验收。保持 lower 旧打开绑定与 copy-up 新绑定的现有语义,证明同一 FUSE inode 的共享页缓存不会混淆 backing;未证明前限制该内容缓存能力。 +8. 增加可执行交错测试:旧页晚到、修改与本地 open 竞争、跨队列发布、ACK 等待、丢更新、重复释放、未知提交和部分失败。 + +退出条件:单线程语义、并发线性化、对象生命周期和失效覆盖均通过;缓存故障不会留下可继续使用的旧视图。若局部协议复杂度或屏障成本过高,记录限制并保留 M2 的只读能力。 + +##### M4:恢复与运行生命周期 + +1. 接入既有 Attempt/supervisor、CPU/device 静默和文件系统 freeze owner。需要 guest 刷新/确认时,在 CPU 和相应设备通路仍可工作时完成。 +2. 保存协议模式、base/stage、策略、对象引用和打开绑定;把缓存与不可丢弃引用分别处理。native FD 数字不进入持久恢复合同。 +3. 覆盖 Unbound、Bound、正在绑定、在途 I/O 和缓存更新未排空的边界。冻结后状态须明确,不留下等待被冻结 guest 的工作线程。 +4. 被删除但仍被打开的 stage 对象要有可恢复 backing;无法保存时明确拒绝该 profile 的 checkpoint,不能重开当前同名文件。 +5. fork 后 upper 和对象 owner 独立,immutable base 按有效 revision 共享。验证父子写入、淘汰、释放与退出互不污染。 +6. 分别验收完整 VM 保存/恢复与 stage seal/export;virtqueue 排空不能代替 guest 脏页处理,缓存更新 ACK 不能代替持久化。 +7. 拒绝未知必要协议特性和不兼容快照模式。协议升级与运行中切换需要单独设计,不通过逐 inode 回退绕开连接级 no-open。 + +退出条件:缓存启用的 profile 不削弱已有 seal/snapshot/fork 合同;故障释放和恢复可复核。KVM、HVF、其他架构和不同挂载形态分别记状态。 + +##### M5:性能验收与产品接入 + +先登记实验协议和预算,再构建冻结候选制品。正式性能批次通过后,决定是否接入真实实验开关;开关默认关闭,有实际能力检查,CLI、Host 请求、runner spec 和恢复模式一致。 + +只有在适用范围内有净收益、非目标负载未超过允许退化、资源成本可接受时才扩展。没有收益或维护成本高于收益时,可退役具体路线并保留实验结论。用户性能页面仅在用户结论确实变化后更新。 + +#### 2.9.2 工作包与代码接点 + +| 工作包 | 主要接点 | 负责的交付 | +|---|---|---| +| guest 内核缓存 | [fw](https://github.com/DeepLink-org/pvisor/blob/main/fw/README.md)、FUSE/VFS 补丁与内核配置 | 元数据导入、本地 open、页缓存归属、版本检查、内核预算 | +| VMM 文件服务 | [virtio-fs](https://github.com/DeepLink-org/pvisor/tree/main/crates/pvisor-vm/src/devices/virtio/fs)、[VM API](https://github.com/DeepLink-org/pvisor/blob/main/crates/pvisor-vm/README.md) | 可信对象引用、无 handle 数据服务、绑定、队列与更新协议、设备保存 | +| stage 与观察语义 | [OverlayCore](https://github.com/DeepLink-org/pvisor/blob/main/crates/pvisor-overlay-core/README.md)、[FilesystemService](https://github.com/DeepLink-org/pvisor/blob/main/crates/pvisor-overlay-core/src/service.rs) | 合成/copy-up、baseline、实际修改效果、部分失败和 seal | +| host 缓存经验 | [OverlayFs](https://github.com/DeepLink-org/pvisor/blob/main/crates/pvisor-overlayfs/README.md) | 对照已有 owned-view 合同、失效覆盖和失败测试,保留适配器边界 | +| image 与导入格式 | [共享镜像设计](../../shared-image-cache-storage.md) | revision、索引、内容校验、权限数据完整性、格式与构建证据 | +| guest 用户态控制 | [pvisor-guest](https://github.com/DeepLink-org/pvisor/blob/main/crates/pvisor-guest/README.md) | 必要的初始化/确认;不把常规 stat/open 转到 guest 用户态服务 | +| 生命周期与接入 | [runtime](https://github.com/DeepLink-org/pvisor/tree/main/crates/pvisor/src/runtime)、[environment snapshot](https://github.com/DeepLink-org/pvisor/tree/main/crates/pvisor/src/environment_snapshot) | 准入、freeze、恢复、fork、实验配置与 Host 权限边界 | +| 验证与性能 | [semspec](https://github.com/DeepLink-org/pvisor/blob/main/tools/semspec/DESIGN.md)、[benchmark](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/README.md) | 候选规格、故障与交错测试、真实 VM harness、原始证据和统计 | + +先共同评审对象模型、profile 和失败合同,再拆分工作包。共享接口由拥有者定义,不复制 DTO;`pvisor-vm`、`pvisor-overlayfs`、`pvisor-journal` 保持现有 API 边界。尚未迁移的 crate 只做必要的局部修改,不借实验改成全工作区重构。改文件前逐级阅读目录 README。 + +#### 2.9.3 正确性验收矩阵 + +以下是候选用例,不是已人工批准的 semspec。每例记录 profile、前置状态、并发顺序、结果、错误和资源释放;单元测试与真实 VM 测试分别标识。 + +| 用例组 | 操作与干扰 | 必须验证 | +|---|---|---| +| 元数据命中 | lookup/stat/lstat/readdir,完整与缺页目录 | 正确字段、顺序、cookie;缺页不变成不存在 | +| 本地只读 open | O_RDONLY → fstat → close,无数据操作 | 对象引用与权限正确;完整请求窗口符合声明 | +| 原有本地路径 | O_PATH、已有 dentry/attr 命中 | 与旧实现对照,不把原本零请求计作新增收益 | +| 副作用 open | O_CREAT/O_EXCL/O_TRUNC/O_TMPFILE、可写 open | 现有错误、copy-up、原像和副作用时机保留 | +| 权限与策略 | 拒绝路径、别名、mode/uid/gid、缺失 ACL/xattr | 不越权、不伪造成功;动态策略不静默绕过 | +| 首次回源 | 同一 FD 并发首次 read,失败和关闭竞争 | 一次受控绑定、完整字节、无泄漏和死锁 | +| 有效内容命中 | read/pread/mmap/exec,跨 EOF 与短读 | 页版本及 backing 正确;命中不强制建立数据 FD | +| 旧 FD 存活 | 本地 open 后 unlink/rename/同名重建/淘汰 | 旧 FD 始终访问原对象,新 open 访问新对象 | +| 对象与内容身份 | 两个同内容文件、硬链接、inode 数字重用 | 去重不制造别名;真实硬链接保持一致 | +| copy-up 与缓存 | lower FD、upper 新 FD、write/truncate、旧页晚到 | 原绑定语义不变;页不跨 backing 错误复用 | +| 合成覆盖 | whiteout、opaque、目录 rename、非目录祖先 | lower 遮盖正确,子树与别名完整更新 | +| B/A/C 与观察 | compose B、baseline A、upper C;live/负查找 | apply 仍对照 A;不准入或完整保留必要宿主观察 | +| 更新交错 | lookup/open 与修改竞争,跨队列乱序 | 线性化规则成立,旧响应不能填充新视图 | +| 更新故障 | 丢更新、序列缺口、ACK 延迟、队列满 | 停用相关缓存并重同步/失败;不能继续读旧视图 | +| 部分修改与未知提交 | 短写、后续错误、超时、INTERRUPT | 报告真实效果;不盲目重放或宣称已回滚 | +| 格式与路径 | 非 UTF-8 名称、symlink、越界/循环/损坏页 | 原始字节与权限边界正确,内核拒绝非法输入 | +| 预算与回收 | 大目录、FD/引用压力、FORGET、重复关闭、退出 | 有界内存/队列,无误回收;最终资源回到 owner 基线 | +| snapshot 与 fork | Unbound/Bound FD、unlink 后 FD、在途状态 | 原对象可恢复,模式/权限匹配,父子 stage 隔离 | +| seal 与退出 | guest 脏页、未排空更新、VMM/guest 崩溃 | 持久化合同保持;失败收敛,不能仅以 drain 判成功 | +| 准入拒绝 | mutable lower、外部 upper writer、特殊文件 | 启用前拒绝或按明确范围回源,不遗留半可用模式 | + +先用可控交错 runner 测试协议和对象生命周期,再用真实 KVM guest 验证 syscall/VFS 行为。宿主 mock 的 PASS 不等同于真实内核行为通过。 + +#### 2.9.4 实现后的验证入口 + +按实际修改选择命令,完成目标 crate 测试、受影响消费者编译和必要的真实平台验证: + +```bash +just test pvisor-vm +just test pvisor-overlay-core +just test pvisor-overlayfs +just test pvisor-guest +just test pvisor +``` + +涉及固件补丁或配置时: + +```bash +just fw build +just fw test +``` + +固件测试验证构建/打包合同,不能替代真实 guest 文件系统测试。记录固定内核、patch 集、配置、VMM/固件哈希与 `libkrunfw.SOURCE`。测试所需 `/dev/kvm`、`/dev/fuse`、权限或平台不具备时,报告失败/跳过及未覆盖能力。 + +修改公开 API 时,从 crate 外部经 `api` 测试合同,执行已有边界 guards,并以 warnings denied 检查 rustdoc;不为测试暴露内部状态。修改 semspec 用例时遵守 [semspec 设计](https://github.com/DeepLink-org/pvisor/blob/main/tools/semspec/DESIGN.md),执行 `just semspec lint docs/src/zh/cases`;修改 runner 时用 `just test-semspec`。AI 不执行 approve/revoke,不改真实 `REVIEWED.toml` 或 `.approved/`,不弱化已有 claims/checks/xfail。 + +#### 2.9.5 性能实验协议 + +##### 2.9.5.1 注册与可复现入口 + +正式工程对照归属 [B-FS-ENG](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/README.md#b-fs-eng),请求成本诊断归属 [B-FS-DIAG](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/README.md#b-fs-diag)。实现与测量前复核 [注册表](https://github.com/DeepLink-org/pvisor/blob/main/benchmark.new/registry.csv)及 [filesystem 入口](https://github.com/DeepLink-org/pvisor/blob/main/benchmark.new/filesystem/README.md)。当前 `filesystem/ab.json` 和 `diagnostic.json` 仍要求具体单条件命令;先准备可重复执行的 harness,再开始测量。 + +现有 DAX 配对实验属于独立机制,不作为本方案的证据。单独执行各条件的采样器也不会自动形成随机配对;本方案需要显式生成配对顺序、执行记录和同轮统计。 + +原始输入清单、harness、冻结源码、制品、日志、逐次样本与校验保存在实验所属 `.data/`;加工 CSV 和结论在工程报告中引用。不将研究 A/B 写入用户性能表。 + +##### 2.9.5.2 对照与资源预算 + +优先使用相同二进制、固件、输入和配置,仅切换真实缓存机制;必须更换内核/制品时,冻结同一源码基线,只应用所测改动,分别记录身份和差异。 + +固定完整 VM 进程组的 CPU affinity/quota、guest vCPU/RAM、宿主内存/swap 限额、临时存储、工具缓存目录、IO 并发、写回、同步、baseline、权限和 seal 策略。不要把 guest 多用的内存或额外宿主线程预算解释为缓存收益。 + +测量前登记目标负载、最小有用改善、允许退化、导入/更新成本和内存/FD 上限。共同预算至少覆盖 guest 元数据/inode/dentry/页缓存、宿主对象引用/FD、在途请求/更新队列以及 helper/node 的真实成本;共享文件的实际内存归属单独解释,不重复相加或省略。 + +##### 2.9.5.3 负载与冷/热条件 + +| 负载 | 主要问题 | 必要分列 | +|---|---|---| +| lookup/stat、负查找、目录遍历 | 元数据导入是否减少请求 | 新 guest、已导入未实例化、稳态、版本更新后 | +| open/fstat/close | 本地 open 是否兑现 | 首次探测、预热、稳态;包含 FLUSH 等全部请求 | +| 小文件 open/read/close | 延迟绑定与复用是否有效 | 首次数据访问、guest 热缓存、对象/FD 回收 | +| 顺序读、并发 pread、mmap、exec | 数据供给与页缓存成本 | 缺页与命中、同 FD 与重开、完整字节校验 | +| create/write/truncate/rename | stage 维护是否造成退化 | legacy 路径、屏障/效果开销、部分失败与 seal | +| git/rg/Cargo/npm 等真实工具 | 请求减少是否变成工具收益 | 每项工具、完整任务、输入与工具缓存条件 | +| 大目录与多 VM | 导入/引用压力是否降低密度 | 峰值内存、淘汰、资源配额、吞吐与尾部 | +| checkpoint/恢复/fork | 新状态是否增加生命周期成本 | 保存/恢复、未绑定与已绑定 FD、私有 stage | + +宿主缓存温度和 guest 缓存温度分别定义。新 guest 不代表宿主冷磁盘;预热数据或解释器会影响首次访问。冷缓存实验采用隔离可复现输入/环境,不在共享开发机上随意 drop_caches。 + +##### 2.9.5.4 采样与有效性 + +每条件至少 30 个正式样本,按预登记 seed 随机平衡配对;每组先做 3 次预热并保留记录。诊断、试验性调参和失败重跑分别成批,不与冻结后的正式样本混合。 + +每次验证输出、完整内容、宿主原文件未被修改、upper 合成结果与持久化边界。记录所有失败及其处理:校验错误、非零退出、制品/输入变化、预算不符、OOM 或外部构建/测试干扰按预定协议处理,不能将失败过滤后称为零失败。有效的慢样本不因耗时被删除。 + +预检确认仪器和负载可用,再冻结正式批次。发现新问题需停批、修复、重新冻结,并保留原失败批次;不持续追加样本直至区间看起来显著。 + +##### 2.9.5.5 计时边界与统计 + +分别报告工具内部时间和完整 Attempt 时间。后者至少覆盖每次元数据准备/交付、VM 启动/导入、工具、seal、请求的 checkpoint/export 和 teardown。离线镜像构建另列;给出首次使用和复用的成本,不能把新增导入移到计时区间外。 + +报告逐次数据、两组中位数、同轮配对变化及 95% bootstrap 置信区间。先检查分布;出现分离簇时报告簇比例和条件,不作整体工具排名。区间跨零表示未检出差异,不等于已证明性能相同;30 个样本不报告可靠 P99。 + +同时报告 CPU、IO、峰值/稳态内存、对象引用/FD 数、缓存命中和回收成本。并发吞吐与密度使用独立负载和预算验收,不由单 VM 时间外推。 + +##### 2.9.5.6 独立诊断与机制证明 + +正式计时关闭会影响性能的 profile/tracing,另跑诊断批次。记录全部 PID/component/instance 的最终计数、协议序列和资源状态;缺记录、额外实例或失败均要解释,不能固定忽略线程实例。inclusive spans 不相加为精确时间归因。 + +“metadata/open 零请求”验收只适用于声明的稳态窗口,完整计数包括 FLUSH/RELEASE/FORGET 等操作。初始化、priming、导入、引用回收和 teardown 各有独立记录;不能把它们隐藏为免费操作。内容命中也要确认没有隐式数据 FD 创建。 + +阶段成果依次表达为:语义正确、机制减少请求、目标负载改善、端到端和资源净收益。前一项不能代替后一项;该方案也不能仅凭 metadata/open 改善就声称解决了写回退化。 + +#### 2.9.6 评审和交接材料 + +每个阶段提交可独立评审的小改动和下列材料: + +1. 支持 profile、准入证据、操作/错误合同与未支持范围。 +2. 对象/版本模型、线性化点、消息限额、owner 与失败恢复说明。 +3. 目标 crate/API、内核 patch、配置、格式版本及兼容性变化。 +4. 正确性矩阵的通过/失败/跳过、真实平台范围和未覆盖项。 +5. 冻结源码、制品/输入身份、可复现命令、原始证据和统计方法。 +6. 冷/热与全生命周期性能、资源预算、维护成本和晋级/停止建议。 + +第一份交接成果应是 M0 合同和 M1 的受限只读闭环。之后依据对象保留、导入成本和 stage 更新的实验证据选择路线;EROFS 保留为独立 lower 对照,不自动扩大为重写完整可写文件系统。 + +### 2.10 实施与维护规则 {#maintenance} + +1. 区分当前实现、拟议协议、受限原型和已验收能力;协议字段与消息名在评审前均为语义草案。 +2. 每项本地操作都写清对象存活、权限、基线观察和版本条件;对象引用不能只依赖路径。 +3. 将目录版本、属性版本、内容版本和后端身份分别建模;丢失更新或提交结果不明时进入明确的恢复或失败路径。 +4. 性能实验遵守 [benchmark 规则](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/README.md),找到对应注册条目后再测量;原始数据保存到所属实验目录的 `.data/`。 +5. 实现归属现有 crate 与 `fw/`,遵守各目录 README 和 API 边界;设计登记不创建空 CLI 开关。 +6. 新语义规格遵守 [semspec 设计](https://github.com/DeepLink-org/pvisor/blob/main/tools/semspec/DESIGN.md)。候选验收项、测试 PASS 和人工批准分别记录,AI 不操作真实审批账本或批准快照。 + +## 3. 方案讨论和对比 {#alternatives} + +### 3.1 文件系统路线比较 + +| 路线 | 主要收益假设 | 关键代价与边界 | 选择 | +|---|---|---|---| +| 仅延长现有 FUSE TTL | 减少已缓存元数据的重复请求 | 不交付冷元数据,也不直接省去普通 OPEN;仍需证明视图稳定和失效正确 | 保留为低成本对照 | +| FUSE 缓存 + 元数据导入 + 延迟后端绑定 | 让更多元数据查询与普通只读 open 在 guest 完成 | 需要对象保留、权限完整性、stage 更新和页缓存归属协议 | 推荐主线,先做独立只读原型 | +| EROFS 不可变 lower + 宿主 stage | 复用原生只读目录、inode 和 open 路径 | 需要镜像/数据块供给、混合视图和设备快照适配;stage 一致性仍需解决 | 作为独立 lower 对照 | +| 完整 guest upper/原生可写文件系统 + stage 导出 | 有机会将更多写入和 namespace 操作留在 guest | 改变原像观察、stage 权威状态、seal/apply 与恢复合同,工程范围大 | 当前方案之外,单独立项评估 | + +上述收益是待验证的机制假设。对比使用相同输入、权限、资源预算和生命周期边界,不以只读 lower 的结果替代可写 workspace 的结论。 + +#### 3.1.1 首选:渐进扩展现有 FUSE 路径 + +Linux FUSE 已有连接级 no-open 机制,后续 open 和 release 可以在内核处理。[Linux 6.12 实现](https://github.com/torvalds/linux/blob/v6.12/fs/fuse/file.c) 它适合独立只读导出的原型。混合可写挂载若要只跳过符合条件的 open,需要 per-object/操作能力或 guest 内核扩展,不能直接全局停掉当前 open。 + +先用已知输入和预热验证本地操作及无 handle 数据服务,完整计算预热成本;随后接分页元数据导入。预热原型不代表已实现打包索引或解决冷启动。 + +#### 3.1.2 EROFS 作为可选 lower + +EROFS 提供 guest 原生的只读 inode、目录和内容布局,可作为不可变 lower 的另一条实现路线。[EROFS 文档](https://docs.kernel.org/filesystems/erofs.html) stage 仍由现有宿主机制管理。EROFS 格式、内容块供给及混合视图接入需要适配;采用它不自动完成远端 lazy 或 stage 协议。 + +当前 x86 固件配置已有 OverlayFS/tmpfs/virtio-blk,但未启用 EROFS/ext4;VMM blk 也不在默认 feature 中。[固件配置](https://github.com/DeepLink-org/pvisor/blob/main/fw/config-libkrunfw_x86_64)、[VMM features](https://github.com/DeepLink-org/pvisor/blob/main/crates/pvisor-vm/Cargo.toml) 使用这条路线需分别评审设备与快照生命周期。 + +### 3.2 全量导入与分页导入 + +| 策略 | 优点 | 代价与适用条件 | +|---|---|---| +| 全量导入 | 导入完成后,范围内首次元数据查询可以直接命中;便于建立完整负查找证明 | 将准备、传输和内核对象建立成本集中到启动;未访问的目录也占预算 | +| 按需分页 | 小工作集可少导入元数据,有界按需分配 | 首次缺页仍需 VMM;目录完整性、请求合并和旧页拒绝更复杂 | +| root/必要页先交付,其余分页并有界预取 | 保留快速启动入口,根据访问范围扩展缓存 | 需要清楚区分已交付、已校验和已实例化,控制预取与淘汰 | + +首版从有界 root/必要页开始,并保留全量导入作为对照。最终选择依据冷启动、首个 lookup、目录遍历、峰值内存和多 VM 密度,不能只比较预热后的查询时间。 + +### 3.3 C、Rust 与 API 归属 + +| 实现位置 | 优先选择 | 原因与代价 | +|---|---|---| +| guest FUSE/VFS 扩展 | 小范围 C 补丁 | 复用现有内核接口与缓存;生命周期和输入边界由实现明确维护 | +| 宿主协议、对象管理与 guest 用户态控制 | Rust | 沿用现有 crate、所有权与 API 模型;协议和对象预算仍需显式设计 | +| 自研 Rust 内核文件系统 | 单独的研究工作包 | 需要文件系统安全抽象、no_std 工具链及固定内核适配;首版维护成本较高 | + +新增宿主对象管理、内容供给、stage 更新和恢复代码优先 Rust;guest 用户态控制继续使用 `pvisor-guest`。必要的 guest FUSE/VFS 扩展优先维护小范围 C 补丁,复用内核已有缓存。 + +当前固件固定 [Linux 6.12.109](https://github.com/DeepLink-org/pvisor/blob/main/fw/Makefile),该系列 Rust kernel crate 未提供完整文件系统接口;内核 Rust 需要安全抽象和 no_std 工具链。[该系列源码](https://github.com/torvalds/linux/blob/v6.12/rust/kernel/lib.rs)、[Rust 内核接口说明](https://www.kernel.org/doc/html/v6.12/rust/general-information.html) 若选自研 Rust 内核模块,接口建设、回移和跨架构验证单独列项。 + +跨 crate 合同集中于拥有者的 API,不复制等价 DTO,不暴露私有 VMM 或 Core 状态。一次只迁移一个边界,支持平台使用统一声明和明确 Unsupported。固件补丁遵循 Linux 许可证及对应源码交付流程。 + +### 3.4 资源预算与收益判断 + +缓存预算覆盖元数据页、guest inode/dentry、本地打开状态、对象保留、宿主 FD、内容页、在途 I/O 和更新队列。既有 node/cache/helper 也计入整机预算。共享元数据或内容不代表各 VM 的 inode/dentry 和页缓存免费。 + +等待 metadata/data miss 不持有全视图锁。按对象或数据块合并重复请求,有界预取;更新队列过载时暂停准入或进入重同步,不能无界积压。缓存淘汰和引用回收的开销单独统计。 + +预期最先改善 metadata 密集、目录扫描以及 open 后只检查属性的负载。创建大量新文件的 WRITE/CREATE 仍经过 VMM;该设计不直接证明已解决现有小文件写入退化。首次冷读取仍受数据供给、copy-up、哈希和持久化影响。 + +```text +完整成本 = 元数据准备/交付 + VM 启动/导入 + 工具执行 + + seal + 请求的 checkpoint/export + teardown +候选净收益 = 实际减少的后端工作 - 导入/维护/回收/恢复新增成本 +``` + +共享镜像的离线构建和每次 Attempt 的导入分别记录;给出首次使用及复用成本。正式 A/B 比较总耗时、CPU、内存、IO 和吞吐,诊断批次解释请求减少。性能门槛和容忍预算在测量前登记,按置信区间和完整正确性判断。 + +### 3.5 研究问题与决策门 + +| 问题 | 决策所需证据 | +|---|---| +| 元数据全量导入还是分页按需? | 冷启动、首个 lookup、目录扫描、峰值内存和多 VM 密度 | +| 独立只读连接能减少哪些 open/close 请求? | INIT/LOOKUP/GETATTR/OPEN/FLUSH/RELEASE/FORGET 完整计数与语义验证 | +| 如何保留未绑定的 stage 对象? | unlink/rename/同名重建/淘汰的交错测试,FD 和引用预算 | +| 混合视图如何做到精确更新? | alias/subtree 覆盖、乱序响应、部分失败、线性化和背压测试 | +| 需要哪些 guest 内核扩展? | 固定内核上的最小补丁、构建/恢复兼容性和维护成本 | +| FUSE 缓存或 EROFS 哪条更合适? | 相同输入、权限、预算与冷/热条件下的受控工程 A/B | +| 净收益能否保留到产品流程? | 含启动、seal、保存/恢复及资源成本的端到端结果 | + +每个问题由 [实施计划](#implementation-plan)中的独立工作包产出可复核的代码、测试和原始证据。只读机制成功后,再决定 stage 扩展和产品接入范围。 diff --git a/experimental/README.md b/docs/src/zh/design/experimental/index.md similarity index 79% rename from experimental/README.md rename to docs/src/zh/design/experimental/index.md index ddf2a2344..0ec084f6e 100644 --- a/experimental/README.md +++ b/docs/src/zh/design/experimental/index.md @@ -6,7 +6,8 @@ | ID | 能力 | 状态 | 目标 | |---|---|---|---| -| EXP-001 | [vCPU 空闲观测与自动内存卸载](vcpu-idle-offload/README.md) | M0:观测原型(KVM 实测,HVF 未验收) | 不修改 Agent 和 guest 内核,利用 VM 等待窗口自动 offload,并由宿主事件/deadline 恢复 | +| EXP-001 | [vCPU 空闲观测与自动内存卸载](vcpu-idle-offload/index.md) | **观测原型阶段**(M0;KVM 实测,HVF 未验收) | 不修改 Agent 和 guest 内核,利用 VM 等待窗口自动 offload,并由宿主事件/deadline 恢复 | +| EXP-002 | [Guest 内核文件系统缓存与延迟后端绑定](guest-stage-cache/index.md) | **设计阶段**(RFC,未实现) | guest 本地完成有效缓存内的元数据查询与普通只读 open;VMM 保留 stage、数据回源和持久化权限 | ## CLI 特性入口 @@ -20,12 +21,12 @@ pvisor run --executor vm --feature workload-aware-memory-offloading -- /bin/slee 第一项注册为 `workload-aware-memory-offloading`,阶段 `experimental`、默认 `false`,开启 EXP-001 M0 观测,不开启自动卸载。最终 executor 必须为 VM;平台范围为 Linux x86_64/KVM 与 Apple Silicon macOS/HVF,HVF 尚未运行验收。配置支持 `[features]` 的 `workload-aware-memory-offloading = true`;CLI 启用覆盖配置的 false,省略保留配置。没有 CLI disable 或持久 feature enable 操作。既有 cold RAM 等参数不在本次迁移范围。 -功能选择通过 Host 请求、RunConfig、runner spec 显式传递;native runner 实际调用 `VcpuObservationControl`。新增实验能力需先完成真实接入和支持范围检查,再加入 registry,不能登记空开关。用户接口详见[CLI 参考](../docs/src/zh/reference/cli.md#features)。 +功能选择通过 Host 请求、RunConfig、runner spec 显式传递;native runner 实际调用 `VcpuObservationControl`。新增实验能力需先完成真实接入和支持范围检查,再加入 registry,不能登记空开关。用户接口详见[CLI 参考](../../reference/cli.md#features)。 ## 状态与晋级 -- **方案登记**:明确问题、现有基础、缺口、范围、失败语义与验收门槛;不表示已实现。 -- **观测原型**:只采集机会与拒绝原因,不自动改变执行状态。 +- **设计阶段**:明确问题、现有基础、缺口、范围、失败语义与验收门槛;尚未实现。 +- **观测原型阶段**:只采集机会与拒绝原因,不自动改变执行状态。 - **受限 PoC**:默认关闭、显式启用,在列明平台/profile下实现闭环。 - **实验可用**:正确性与故障门禁通过,有可追溯制品和适用范围。 - **稳定或退役**:经过 API、文档、交付及维护评审后晋级;无净收益或维护成本过高时退役。 @@ -34,7 +35,7 @@ pvisor run --executor vm --feature workload-aware-memory-offloading -- /bin/slee ## 目录规则 -1. 每项能力用稳定 ID 和描述性子目录,README 记录当前状态与阅读入口。 +1. 每项能力用稳定 ID 和描述性子目录,`index.md` 合并当前状态、设计、实施计划和验证记录;每项能力只维护一个文档。 2. 设计与测量、已实现与拟议接口、启发式与确定保证分别表达。 3. 默认不启用;未经验证的选项不得进入正式 CLI 示例或作为已交付能力宣传。 4. 优先复用现有 Runtime、控制 owner、CPU/device quiescence、存储和证据模型,不另造终态所有者。 @@ -47,7 +48,4 @@ pvisor run --executor vm --feature workload-aware-memory-offloading -- /bin/slee EXP-001 优先采用 host-driven 方案:后端提供 vCPU 观测,宿主聚合等待窗口并仲裁资源操作;guest PV 协作保留为可选增强,不是第一版前提。 -- [设计与边界](vcpu-idle-offload/design.md) -- [实施计划与验收](vcpu-idle-offload/implementation-plan.md) - -EXP-001 已实现默认关闭的 M0 观测 API、后端接点与真实 VM 实验。KVM 实测未发现可靠等待窗口;自动卸载、wake/deadline 闭环仍未实现。详见[第一版实现与验证](vcpu-idle-offload/validation.md)。 +EXP-001 已实现默认关闭的 M0 观测 API、后端接点与真实 VM 实验。KVM 实测未发现可靠等待窗口;自动卸载、wake/deadline 闭环仍未实现。详见[第一版实现与验证](vcpu-idle-offload/index.md#validation)。 diff --git a/docs/src/zh/design/experimental/vcpu-idle-offload/index.md b/docs/src/zh/design/experimental/vcpu-idle-offload/index.md new file mode 100644 index 000000000..4c8bfb4c4 --- /dev/null +++ b/docs/src/zh/design/experimental/vcpu-idle-offload/index.md @@ -0,0 +1,306 @@ +# EXP-001(观测原型):vCPU 空闲观测与自动内存卸载 + +| 项目 | 当前状态 | +|---|---| +| 阶段 | **观测原型阶段(M0);尚未通过全部退出门槛** | +| 核心目标 | 利用 VM 等待窗口降低驻留内存,宿主事件到来时自动恢复 | +| 默认行为 | 保持现有行为;尚无自动 offload 开关 | +| 首选路径 | pVisor vCPU/宿主观测,不修改 Agent 或 guest 内核 | +| HVF | 已接入 wait/timeout 观测与全 vCPU 聚合;未实际编译/运行验收 | +| KVM | 真实 VM API 与实验通过;KVM_RUN 内 Unknown,尚无可靠 idle 信号 | +| 现有基础 | pause/resume、RAM offload、device memory gate、Host backing commit | +| 主要新增 | 等待窗口观测、全 vCPU 聚合、策略 owner、wake/deadline 与 lost-wakeup 防护 | +| 尚未完成 | 完整 idle 来源、wake/deadline、自动 offload、HVF 验收与卸载收益实验 | + +Agent 等待模型响应、输入或外部工具时,VM可能不需要执行,但仍保留大量驻留RAM。短等待让CPU自然休眠;有足够长的等待窗口时,评估自动offload,减少内存时间成本。唤醒后继续同一个live Attempt,不将其冒充持久checkpoint或重新启动。 + +选择是否卸载由宿主决定,依据窗口长度、内存压力、设备安全、恢复预算和支持范围;不对每次线程阻塞立即卸载。 + +## 1. 问题提出 {#problem} + +### 1.1 等待窗口与驻留内存 + +Agent 等待模型响应、网络或外部工具时,VM 可能长时间没有有效计算,却仍持有驻留 RAM。普通 CPU 休眠降低计算开销;内存回收还需要知道等待窗口是否足够长、如何停驻安全,以及何时恢复同一个 Attempt。 + +设计需要回答三个问题:如何识别全部相关 vCPU 的可利用等待窗口;如何在网络、控制或 timer 到来时可靠恢复;节省的内存时间是否足以覆盖观察、提交和恢复成本。 + +### 1.2 后端基础与观测缺口 + +| 后端 | 现有接点 | 拟议方案 | 限制 | +|---|---|---|---| +| HVF | `vmm/macos/vstate.rs`的WaitForEvent/Timeout及等待channel;`hvf/mod.rs`读取虚拟timer | 等待进入/离开序列、可用deadline,独立owner聚合全部vCPU | WFE可能是同步等待;需验证其它timer/IRQ来源,不能只凭一个寄存器声称完整 | +| KVM | vCPU线程与`vmm/linux/vstate.rs`运行循环 | 先观察CPU时间/调度等候选窗口;核验可选KVM宿主事件来源 | KVM_RUN未返回可能正在执行;低CPU也可能宿主抢占;当前Hlt分支是Stopped,禁止直接改语义 | + +KVM tracepoint/eBPF或用户态halt处理只是候选路线,具体内核接口、权限、开销与兼容性未核验。第一阶段不改irqchip、不要求全局sysctl或特权追踪。无法可靠区分时标Unknown并拒绝自动操作。 + +当前 M0 的真实 KVM 实验尚未发现可靠等待信号,HVF wait 接点尚未实机验收。[验证记录](#validation)分别保存现有结果与未覆盖范围;自动策略依赖的 wake/deadline 闭环仍需实现。 + +### 1.3 目标与范围 + +不修改Agent与guest内核,从pVisor vCPU和宿主事件侧发现等待窗口,自动卸载RAM并在需要执行时恢复。观测回答“何时值得卸载”;现有CPU/device quiescence和backing commit回答“如何安全卸载”。 + +不证明所有Linux进程阻塞,不做通用checkpoint/migration,不冻结guest时间,不支持任意设备或所有内存机制叠加,不承诺生产密度倍率。 + +## 2. 核心设计 {#core-design} + +### 2.1 架构、职责与接入 + +```mermaid +flowchart TD + Backend[KVM / HVF vCPU observation] --> Aggregate[All-vCPU windows and epochs] + Events[Host network / control / deadline] --> Wake[Independent wake latch] + Aggregate --> Owner[Host policy owner] + Wake --> Owner + Owner --> Control[Existing pause/offload/resume control] + Control --> Boundary[CPU park / device gate drain] + Boundary --> Commit[RAM writeback / Host backing commit] + Commit --> Park[Offloaded] + Park --> Owner +``` + +- `pvisor-vm`归属后端观测及CPU/device控制,新增公共契约必须集中于`api.rs`,不为实验暴露私有VMM模块。 +- `pvisor`归属策略、Host backing commit、Attempt生命周期、配置和可解释事件。 +- OverlayNet等事件源在guest RAM访问或可能阻塞的channel之前通知wake。 +- 单一control owner串行仲裁自动策略与手动pause/resume/checkpoint/cancel,不另造completion owner。 + +观测状态、快照与 `VcpuObservationControl` 已在 `api.rs` 实现,详见 [验证记录](#validation)。Host 自动策略 owner、wake/deadline 与自动 offload 状态机仍为设计,尚未实现。 + +### 2.2 观测聚合与候选窗口 + +M0 已定义观测状态:Executing、HandlingExit、WaitingForEvent、HostDescheduled、ManualPaused、Stopped、Unknown。只有全online vCPU等待且满足策略时产生候选。禁止把HostDescheduled归为guest idle;等待状态是机会,不是Linux runqueue真值。 + +### 2.3 wake、deadline 与提交顺序 + +至少需要Attempt身份、CPU topology generation、观测sequence、idle epoch、pending wake sequence、停驻原因、可用deadline和clock domain。 + +wake在Candidate/Quiescing/Offloading/Offloaded阶段均latch;已有pending事件不得忽略。进入停止边界后重验观测和事件sequence。Host backing提交期间收到wake先排队,提交完成后立即恢复,不能提前打开writers。 + +网络数据、EOF、错误、连接完成优先作为语义wake,不用socket writable/普通ACK无条件触发。wake先于virtqueue/memory gate访问,由独立owner处理,设备线程不得持queue/proxy锁同步resume。恢复后重查挂起事件/ring,不仅依赖新EPOLLET边沿。 + +最早有效timer deadline需要覆盖目标profile,明确guest/host时间转换与提前恢复margin;未知或太短就跳过。watchdog只能作为PoC容错,不能代替精确timer支持。当前pause保留wall-time,不通过冻结时间隐藏过期timer。 + +### 2.4 状态机与失败处理 + +```mermaid +stateDiagram-v2 + Observing --> Candidate: All-vCPU waiting window + Candidate --> Observing: Expired or policy rejected + Candidate --> Quiescing: Wake registered and profile supported + Quiescing --> Offloading: Safety boundary revalidated + Offloading --> Offloaded: Runner and Host commit complete + Offloading --> Resuming: Process latched wake after commit + Offloaded --> Resuming: Event or deadline + Resuming --> Observing: Gate and CPU restored + Quiescing --> Failed: Transition failed + Offloading --> Failed: Writeback or commit unknown + Resuming --> Failed: Resume failed +``` + +busy/unsupported/short-window是破坏性transition之前的正常拒绝。现有drain超时会fail_control,不能把poisoned runner当普通拒绝后继续执行。正常取消一个已开始的transition必须证明状态恢复,不能直接跳回Observing。 + +自动wake只解除该owner/epoch的自动停驻;人工pause、snapshot freeze、failed状态保持原语义。guest不能伪造Host权限或绕过资源限额。观察误判主要影响性能,但丢wake、timer缺失或绕过commit会破坏执行正确性。 + +### 2.5 初期 profile + +- 普通writable shared file-backed RAM、raw offload、同live Attempt。 +- 先单vCPU,SMP聚合/epoch验收后扩展。 +- 网络wake优先现有OverlayNet IPv4 TCP;明确timer与控制wake支持。 +- 无在途virtio-fs请求;慢I/O跳过,不对已有RAM lease强行discard。 +- 排除cold pager/pool、restored private COW、未守护设备、未知wake来源。 +- 后端deadline/wake未验收时,保持observe-only;不因HVF接点更明确就宣传macOS全平台已支持。 + + +### 2.6 当前约束 {#constraints} + +- 第一版不要求guest内核通知;仅保留PV通知作为将来精度增强。 +- 自动策略的观察是机会信号,不是“所有Linux进程都阻塞”的证明;安全卸载仍依赖CPU/设备quiescence与backing提交。 +- 初期只支持明确的普通file-backed RAM;现有cold pager/pool与whole-VM offload互斥,private restored COW不支持该discard路径。 +- deadline/wake支持不完整的平台或设备,只观察,不自动卸载。 +- 不解除人工pause、snapshot freeze或失败停驻,不授予guest Host控制权限。 + +现有手动offload数据只支持基础成本分析,不能宣称自动策略已经提升生产密度。当前观测结果不能授权自动卸载,设计不是验收结果。 + +### 2.7 实施计划与验收 {#implementation-plan} + +> 已实现 M0 观测 API、后端接点和实验;自动 idle/offload 尚未实现。已完成项与未验收项分开,详见 [验证记录](#validation),不承诺固定排期。 + +#### M0:基线与Observe-only + +- [ ] 核对当前后端idle、IRQ、timer、offload、device gate和Host commit链。 +- [x] 定义跨后端观测契约与Unknown语义,遵守VM公共API边界。 +- [x] 注册vCPU身份/topology generation、enter/exit sequence、等待窗口与拒绝原因。 +- [ ] HVF从现有wait/timeout路径采集;KVM先验证低成本候选观测,区分guest等待与宿主抢占。 +- [x] 接入单一 VM-local collector,只记录、不调用pause/offload;Host策略owner尚未实现。 +- [ ] 完成跨平台采集开销、退出清理与容量验收;KVM已完成有界采样与工程A/B,HVF尚未验收。 + +退出条件:能解释真实任务的等待窗口,不因无KVM exit、低CPU或人工pause误报精确idle;平台未提供可靠信息时Unknown。 + +#### M1:可控wake闭环 + +- [ ] 增加独立wake latch,覆盖候选/排空/卸载/停驻全部窗口。 +- [ ] 网络数据/EOF/错误/连接完成在RAM访问与有界发送等待前登记。 +- [ ] 建立deadline clock domain、提前恢复margin与timer取消/重编程规则。 +- [ ] 将wake交现有Host控制owner,保证runner ACK后的backing commit顺序。 +- [ ] 仲裁自动、人工、snapshot、cancel与失败状态。 +- [ ] 验证resume后重查queue/backend,不丢已消费边沿。 + +退出条件:故障注入和可控交错证明lost wake不出现;deadline未知profile拒绝自动深停驻。mock通过后仍需真实VM验证。 + +#### M2:单vCPU受限raw offload PoC + +- [ ] 默认关闭;拟议enable配置经实现评审后才命名、接CLI。 +- [ ] 普通file-backed RAM支持检查,排除pager/pool/private COW等。 +- [ ] 在途FS/未守护设备提前拒绝,不以真实drain失败作为常规策略。 +- [ ] 初期grace、deadline guard、重入冷却、wake频率抑制。 +- [ ] 真实delayed network、timer、控制wake下恢复同Attempt并校验全部内容。 +- [ ] offload/store/恢复失败沿用fail-stop与unknown,终态不伪造。 + +退出条件:正确性、权限与生命周期负对照通过;仅对验收的平台/profile标记受限PoC。 + +#### M3:SMP与更多事件 + +- [ ] 全online vCPU聚合、跨CPUwake/IPI、迁移/topology变化竞态。 +- [ ] 一个CPU运行时不卸载;退出/恢复旧epoch不复用。 +- [ ] 扩展vsock、终端、signal等事件,不在proxy/queue锁内同步切换。 +- [ ] 多连接、streaming、backpressure、恢复风暴与deadline竞争。 + +退出条件:平台支持矩阵逐格记录,不以SMP mock替真实guest验收。 + +#### M4:收益实验与是否继续 + +- [ ] 按`benchmark/README.md`先登记自动策略的问题、角色、runner与统计协议。 +- [ ] 同任务/硬件/预算/制品,自动策略on/off随机配对,多轮;手动策略可独立对照。 +- [ ] waiting/compute/短阻塞/streaming/timer-heavy/恢复风暴分开。 +- [ ] memory-time integral、cgroup/PSS/cache、全组peak、CPU/IO、恢复及完整任务延迟、失败和有效吞吐。 +- [ ] 计入观察者/全局扫描器等组外成本的说明,不用低PSS或配置容量估生产密度。 +- [ ] 正确性失败不算收益;保留失败、干扰与完整来源,不挑样本。 + +晋级条件:在明确用户负载上有净资源收益且满足QoS,测试可复现、操作可解释。若thrash、CPU/IO或恢复代价抵消收益,就调整策略或退役,不为保留功能强行上线。 + +#### 代码归属建议 + +| 归属 | 职责 | 验证 | +|---|---|---| +| `crates/pvisor-vm/src/api.rs`与私有后端 | 观测契约、wait/deadline、CPU控制与device gate | API boundary、状态/epoch、timer与RAM正确性 | +| `crates/pvisor/src/executor/vm/` | 观测/控制通道、backing提交、支持profile | 排空/提交/wake并发、错误fail-stop | +| `crates/pvisor/src/runtime/` | Attempt仲裁、自动策略与事件 | manual/auto/snapshot/cancel不越权 | +| `crates/pvisor-overlaynet/src/vm.rs`及后续设备事件源 | wake-before-RAM与外部socket生命周期 | 数据/EOF/错误/背压/已pending事件 | +| 原crate测试与产品集成测试 | mock交错与真实VM验证 | 分平台记录通过/失败/跳过 | +| `benchmark/` | 已登记策略实验 | 独立数据/收据/样本,不放本目录 | + +这张表规定职责,不预先规定新文件名或引入依赖;实施时按已有代码结构收敛。 + +#### 必测负对照 + +| 场景 | 预期 | +|---|---| +| compute busy、宿主CPU拥塞 | 不将低CPU误判为确定guest等待;策略误判也不能破坏安全边界 | +| sleep/futex deadline、timer取消 | 不丢timer,不冻结时间;未知deadline拒绝 | +| 任意卸载窗口到来的wake | 锁存并最终交付,不需guest自己重新发事件 | +| 慢FS持RAM lease | 正常提前跳过;真实transition失败必须fail-stop | +| 手动pause或snapshot freeze | 网络/自动deadline不能擅自解除 | +| backing commit中网络到达 | 等提交完成后恢复,RAM writer不早开 | +| KVM Hlt/guest shutdown | 保留shutdown语义,不变成自动idle | +| 已offloaded时cancel/exit | 清理与终态正确,不保留永远无法唤醒的owner | +| 不支持的pager/pool/COW | 明确拒绝且不改变运行状态 | + +#### 验证记录模板 + +每次推进填写:日期、平台、stage/profile、源码SHA+dirty manifest、binary/firmware/rootfs身份、命令、结果、失败/跳过、未覆盖项、证据路径、结论。实际记录见 [第一版实现与验证](#validation) 和 [KVM 工程实验报告](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/pvisor/vcpu_idle_report.md)。 + +默认使用目标包的`just test PACKAGE`;真实VM/特殊runner按已有契约运行。语义审批与PASS分开,不由AI操作真实ledger。实验实现成功后再同步用户文档和正式支持矩阵。 + +### 2.8 第一版实现与验证 {#validation} + +#### 当前结论 + +**已交付 M0 observe-only 观测原型和真实 VM 实验,不是自动 offload 第一版闭环。** KVM 实测证明当前用户态接点不足以识别等待:sleep/busy/短 timer/SMP 条件几乎都为 Unknown。不能为了完成自动策略而把低 CPU、未返回的 KVM_RUN 或 shutdown HLT 改作 idle。 + +HVF 等待路径已接入,但本 Linux 工作区没有实际 macOS 编译或运行验收。M0 未完成全部退出门槛;M1/M2 未实施,不新增自动 offload CLI 开关。 + +#### 实际代码与 API + +- `crates/pvisor-vm/src/api.rs`:`VcpuObservationControl`、状态/拒绝原因、逐 CPU 记录与聚合快照,公共契约集中于该文件。 +- `crates/pvisor-vm/src/vcpu_observation.rs`:默认关闭的单 VM collector,无后台线程和事件队列,内存 O(vCPU 数),session/sequence/topology generation/idle epoch、等待累计与拒绝原因。 +- `crates/pvisor-vm/src/vmm/{builder,mod}.rs`:collector 生命周期与 CPU 注入。 +- `crates/pvisor-vm/src/vmm/{linux,macos}/vstate.rs`:后端观测,人工停驻与停止保持原控制语义。 +- `crates/pvisor/examples/vm_vcpu_observe.rs`:真实 SDK VM、有界采样和 guest 正确性负载。 + +```rust +use pvisor_vm::api::VcpuObservationControl; + +handle.set_vcpu_observation(true)?; +let snapshot = handle.vcpu_observation()?; +handle.set_vcpu_observation(false)?; +``` + +此 API 不调用 pause/resume/offload。中途启用不 kick CPU,状态保守 Unknown,直到下一接点。KVM 的 Executing 是执行入口接点,KVM_RUN 内 Unknown,返回后 HandlingExit;HLT 仍为停止。HVF WaitingForEvent 表示 wait/select 接点窗口,可能立即消费已就绪事件,不等价于精确睡眠或 Linux runqueue 事实。 + +全配置 CPU 的 online 指后端生命周期,不证明 guest Linux CPU-online。停止/尚未启动的槽不冒充全 CPU idle。即使全等待成立,仍以 WakeDeadlineUnavailable 拒绝自动操作。 + +#### 实验与结果 + +注册 **B-VCPU-IDLE-ENG**,工程 A/B,不进入用户 benchmark 正文。完整[协议](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/pvisor/vcpu_idle_plan.md)、[报告](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/pvisor/vcpu_idle_report.md)及[复现入口](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/pvisor/README.md#vcpu-observation-m0)。 + +矩阵:sleep、busy、2 ms 短 timer 分别使用 1/2 vCPU;另设 2 vCPU 仅 CPU 0 busy。每格 fresh 256 MiB VM、3 秒 workload、10 ms 采样,observer off/on seeded 随机配对,每 case 5 pairs,共 70 VMs。guest multiprocessing 独立进程与亲和性验证防止 GIL 串行化,逐 worker 校验 SHA-256、工作区间、退出与输出。 + +| 项目 | 实际结果 | +|---|---| +| 原始预检 | 0/14,通过失败均保留;Python 3.14 forkserver bootstrap 错误 | +| 显式 fork 修复后独立预检 | 14/14 有效,0 失败 | +| 正式批 | 70/70 有效,0 失败、0 timeout,35 完整 pairs | +| 周期采样 | 10,613 snapshots,16,666 条 CPU 状态 | +| Unknown | 16,657 条,99.946% | +| WaitingForEvent / 全等待 epoch | 0 / 0 | +| 配对任务 wall 差异 | 全部 case 的 95% CI 包含 0,未检出差异 | +| 配对 VM CPU 差异中位数 | +48.253~+88.676 ms;2-vCPU busy CI 包含 0,其余为正 | +| snapshot + JSON Value 构造时间 | 描述性中位数 24.566 µs,不包含 JSONL 写入 | + +开销包含 collector、sampler 和证据 I/O,非纯接点成本。GNU debug、单宿主、每 case n=5,无 cgroup 限额或连续宿主干扰检测,不推导 release、P95/P99、生产 density 或整机节省。未调用 offload,没有内存回收效果或恢复延迟结果。 + +#### 验证与制品边界 + +- 最终 `just test pvisor-vm`:345 passed、8 skipped;含真实 KVM busy-loop/OUT/HLT 特殊测试、SMP/session/并发采样/退出/容量/统计一致性回归。 +- Python runner:16 个单测通过,包含 Python 默认 forkserver 下的显式 fork 回归、超时、来源、拓扑和输出拒绝。 +- example debug 构建、修改文件格式检查通过;Linux VM 库 Clippy 通过。tests Clippy 被依赖的现有 pvisor 告警阻断,未改无关代码。 +- macOS/HVF 编译与真实运行未验收;无 lost-wakeup、完整 timer/deadline 或自动恢复测试。 + +正式测量绑定冻结 binary SHA-256 `af6830774dfd9fef042893c7491c38077377cf7032afeb1add3af433ca83f239`;report SHA-256 `036094ed87fcc5ffd7d7c73d28cd932350bbd16499fae6b8ae28f4655e5bf339`。来源与资产完整摘要在报告,原始证据在 `benchmark/pvisor/.data/vcpu-m0-fork-pairs5-20261007/`。 + +**实测后修复了快照时间戳锁内一致性,以及 disable 截断逐 CPU 等待记账两项问题。** 最终单测包含修复,但冻结 binary 未替换、真实 A/B 未重跑;上述实验只属于修复前冻结版本。工作区同时有外部开发,旧 receipt 会拒绝当前源码;下一次测量需 NEW build/cohort,不修改历史结果。 + +#### 下一版实施门槛与实验 + +1. **KVM 信号问题优先**:验证宿主 KVM halt/wake 观测接口是否能按 VM/vCPU 关联、区分抢占并控制权限/开销。不先修改 irqchip、HLT shutdown 或全局 sysctl;无可靠来源则保持 Unknown。 +2. **HVF 实机验收**:同矩阵核实 wait 接点、短 timer、单 CPU busy SMP 与 pending event。补 host 拥塞负对照及更低开销的 aggregate-only 采样,不把接点窗口当未来 deadline。 +3. **M1 正确性实验**:wake-before-RAM、候选/排空/提交/停驻四阶段注入事件;timer 重编程/取消、人工 pause、cancel、失败,证明不丢事件、不越权恢复。 +4. **M2/M4 回收 A/B**:上述闭环通过后,在普通 file-backed raw、单 vCPU、明确网络/timer profile 对比 off/on。独立登记/补全协议,测等待 0.1/1/5/30/60 秒、计算/streaming/短 timer/恢复风暴,重复/随机工作集;固定组预算、完整 cgroup/PSS/cache/helper、memory-time integral、CPU/IO/peak、完整任务延迟和有效结果。当前均未测,不预设节约倍率。 + +这次实验的价值是及时暴露 Linux 路线的观测缺口,而非给尚不存在的自动卸载策略提供漂亮收益数字。 + +## 3. 方案讨论和对比 {#alternatives} + +### 3.1 等待观测路线比较 + +| 路线 | 优点 | 代价与局限 | 选择 | +|---|---|---|---| +| 现有用户态 vCPU/宿主接点 | 复用 VMM,Agent 和 guest 内核无需改动 | HVF wait 窗口仍需 timer/IRQ 验证;KVM_RUN 内无法据此确定等待 | M0 主线,信息不足时保持 observe-only | +| KVM 宿主观测扩展 | 有机会补充 halt/wake 等等待来源 | 接口、权限、VM/vCPU 关联、抢占区分和开销均待验证 | Linux 后续研究重点 | +| guest PV 协作 | 有机会提供更明确的等待与 deadline 输入 | 需要 guest 协议/内核适配,仍由 Host 控制安全边界和权限 | 可选增强,独立验收 | +| 低 CPU/固定时间启发式 | 采集简单,适合寻找候选机会 | 无法证明 guest 等待,也不提供完整 wake/deadline | 不单独作为自动卸载依据 | + +任何观测路线都必须经过现有 quiescence 和 backing commit 边界。路线选择影响机会识别的精度与成本,不能绕过安全停驻和唤醒协议。 + +### 3.2 策略与完整成本 + +先固定可解释的grace、deadline guard、最低再停驻间隔和thrash抑制,再探索pressure-aware策略。参数由实验决定,不从单次手动offload数据设置生产默认。 + +主要目标是内存时间积分下降,同时满足任务QoS;包括完整cgroup/PSS/cache/helper、CPU/IO、峰值、恢复延迟、吞吐、失败和有效结果。短阻塞只CPU休眠,长窗口才回收RAM,更长停驻再研究压缩backing。 + +### 3.3 实施选择与决策门 + +先证明host-only观测足够有用;guest PV可选增强留待后续,不因追求“精确全部进程状态”无限扩大首版范围。 + +先保留 host-driven、observe-only 主线,补齐可靠观测和 wake/deadline 后,再验证单 vCPU、普通 file-backed RAM 的 raw offload。压缩 backing、更多设备和 SMP 扩展按收益与正确性分别推进。 + +进入自动策略前,至少取得可靠信号或明确拒绝、无丢失 wake、完整 timer 支持以及真实 VM 恢复的证据。随后用相同任务与资源预算比较内存时间、CPU/IO、完整任务延迟和有效吞吐;成本抵消收益时调整路线或退役。阶段分工见[实施计划](#implementation-plan),已有证据见[验证记录](#validation)。 diff --git a/docs/src/zh/design/filesystem-evidence-overview.csv b/docs/src/zh/design/filesystem-evidence-overview.csv new file mode 100644 index 000000000..0d56cea90 --- /dev/null +++ b/docs/src/zh/design/filesystem-evidence-overview.csv @@ -0,0 +1,12 @@ +benchmark_id,batch,backend,metric,reference,condition,n,unit,candidate_p50_ms,change_percent,ci95_low_percent,ci95_high_percent,interpretation,role,reference_p50_ms,source_csv,source_csv_sha256,evidence_section +B-FS-ENG,fr10fixf3,pvisor-staged,completion_ms,baseline,candidate,30,ms,1774.147928,-2.862063347700472,-3.5741408369402943,-1.2217776782787793,faster,engineering A/B,1826.4212615000001,host-idle-dispatch-summary.csv,90bb16084577d55021246c32f0e3f33054be0cb7e31c3c51c5bda9ecf5949177,host-idle-dispatch +B-FS-ENG,fr10vmfixf,pvisor-vm,completion_ms,baseline,candidate,30,ms,4592.3552565,-3.153046526049663,-6.139649356853594,-1.2345936261486057,faster,engineering A/B,4741.868579,vm-completion-dispatch-summary.csv,4f43be23fb31b82f643745f69c31a4c98148f7ca068b107e9b0aaf20a30ecc20,vm-completion-dispatch +B-FS-ENG,concurrency-formal2-20261011,pvisor-vm,worker_ms,baseline,candidate,30,ms,3749.0687745,4.776000623059518,2.9590088269350274,5.923245553099513,slower,engineering A/B,3578.175109,concurrency-filesystem-summary.csv,115a25eb7ec87484eeb3da87156fb49d67b5d7f37feb086784a740efca759119,concurrency-retest +B-FS-ENG,direct-queue-quick-20261011,pvisor-vm,worker_ms,baseline,candidate,10,ms,3606.7074435,-2.7130509930520574,-5.283209164000757,7.245092923180008,no detected difference,engineering A/B,3707.288059,direct-queue-filesystem-summary.csv,032c3a218c413524a2aaabf82f4f733909b4685d51a6a3f590ceb69679bc54ee,direct-queue-fast +B-FS-ENG,writeback-quick-20261011,pvisor-vm,write,enabled,disabled,30,ms,131.6145675,-18.80685324106817,-25.058474230843046,-12.86551705502134,faster,engineering A/B,162.10058700000002,writeback-filesystem-summary.csv,77c425a2580cd9f36fbf9790ae1d3d87d4524dd02160e5b3ec6483a7ecbf275c,writeback-ab +B-FS-ENG,dax-20261011-final,immutable-share,read_repeat,dax=never,dax=inode,30,ms,4.580477,-17.384227766361672,-21.395220652225067,-10.101683557067531,faster,engineering A/B,5.544313,readonly-dax-summary.csv,439eb91eb306051feb296f9ff49d45bbc73881134874098fe469c93d5a189729,readonly-dax-ab +B-FS-ENG,dax-20261011-final,immutable-share,parallel_first,dax=never,dax=inode,30,ms,38.2735545,-29.687926607405725,-32.155415115930666,-28.250685731462653,faster,engineering A/B,54.43383,readonly-dax-summary.csv,439eb91eb306051feb296f9ff49d45bbc73881134874098fe469c93d5a189729,readonly-dax-ab +B-FS-ENG,dax-20261011-final,immutable-share,mmap_first,dax=never,dax=inode,30,ms,104.75073,339.5902132918816,328.766260843115,352.398630591051,slower,engineering A/B,23.829177,readonly-dax-summary.csv,439eb91eb306051feb296f9ff49d45bbc73881134874098fe469c93d5a189729,readonly-dax-ab +B-FS-ENG,dax-20261011-final,immutable-share,small_repeat,dax=never,dax=inode,30,ms,36.6305735,44.36788056884071,36.46610040529619,47.98132060479314,slower,engineering A/B,25.373077000000002,readonly-dax-summary.csv,439eb91eb306051feb296f9ff49d45bbc73881134874098fe469c93d5a189729,readonly-dax-ab +B-FS-ENG,fof1,pvisor-vm,completion_ms,baseline,selected,30,ms,4467.5308425,-1.7337445136365037,-5.698320121926485,-0.22006512668154682,faster,engineering A/B,4546.352988,filesystem-optimization-n30-summary.csv,65b0287892724c266ebc39db20adcc138ac180aedb6b455cc0f713517d6b25dd,filesystem-optimization-n30 +B-FS-ENG,fof1,pvisor-vm,write,baseline,selected,30,ms,137.107249,-14.18593265348862,-17.580605675377935,-9.820266917526087,faster,engineering A/B,159.77246300000002,filesystem-optimization-n30-summary.csv,65b0287892724c266ebc39db20adcc138ac180aedb6b455cc0f713517d6b25dd,filesystem-optimization-n30 diff --git a/docs/src/zh/design/filesystem-measurement-status.csv b/docs/src/zh/design/filesystem-measurement-status.csv new file mode 100644 index 000000000..1128458fe --- /dev/null +++ b/docs/src/zh/design/filesystem-measurement-status.csv @@ -0,0 +1,7 @@ +benchmark_id,batch,role,state,formal_rows_retained,expected_formal_rows,published_samples,measured_failures,interference_count,reason,binary_sha256,report_sha256,acceptance_record,acceptance_sha256 +B-FS-TOOLS,f10f2,user-facing,accepted,240,240,240,0,0,"Complete same-cohort correctness, input and interference gates passed",c6965986ac119544f0886e06806fcf41b814f54b54ad655978046a2449797c42,c2f544b79356db3fcda8f4ed34d944861a64f2190c18747c30441201271502ff,benchmark/pvisor/.data/filesystem-current-20261010/formal2-launch.json,ef557fc359cf7806bf2f6526acce2a4a76af4b0ac679caf6072ce208bdc02227 +B-FS-TOOLS,f11f,user-facing,rejected,102,240,0,0,3,External build/test interference; incomplete cohort; no final input gate,a4cfdbc6e2657f9ff6a43a4c640cd472c1c94c965c9d388924e58727dc55556d,d0c41cc5cc100115f47922577f4eb19087cdd6ee76d89ae7152c673de5a140c1,benchmark/pvisor/.data/filesystem-retest-20261011/ACCEPTANCE.json,7f68dc6572a457985ec1c302193b8183b0e1b857f1a120449cd8e57649b09652 +B-FS-TOOLS,f11f2,user-facing,rejected,192,240,0,0,1,External build/test interference; incomplete cohort; no final input gate,a4cfdbc6e2657f9ff6a43a4c640cd472c1c94c965c9d388924e58727dc55556d,01873d904892451fd16930e469c9794d12af5054d7895218ccdd623fe70ea103,benchmark/pvisor/.data/filesystem-retest-20261011/ACCEPTANCE.json,7f68dc6572a457985ec1c302193b8183b0e1b857f1a120449cd8e57649b09652 +B-FS-TOOLS,fs11f4,user-facing,accepted,240,240,240,0,0,"Current source/defaults; full correctness, before/after input, budget, retained output and interference gates passed",939f72289a72f73ba64d645c891842ed4a9408c7eaef4c54d5fa04aed0371e85,99eb2ee32bd9e595694c39434f33bc4a914b949e88d3c2b345733932e6884369,benchmark.new/filesystem/.data/cross-runtime-20261011T052947Z/attempt4/formal-launch.json,b799e35c825a4cb9a5a152728993a6d2cf19a038588097773026d5f8e8f92142 +B-FS-TOOLS,fo8e2d57ff-f,user-facing,accepted,240,240,240,0,0,"Independent complete full cohort: bytes, outputs, bundles, input/build identities, resource budget and interference gates passed",8840b16cdc0610e381eec66b0f75f1e86efba76feb358c9eae5b3d923854b592,d37b961370f9d8ba3487fd1a6a7a85cf5e85e5de6b32ae91285eb69b9dbfdb92,benchmark.new/filesystem/.data/fs-opt-report-20261011/publication-audit.json,ec38a6bc0311c07e45a756ed6ac0099270c680e432a3aa02252e78ea07f66528 +B-FS-TOOLS,fo5a4e7d2b-f,user-facing,accepted,240,240,240,0,0,"Independent complete full cohort: bytes, outputs, bundles, input/build identities, resource budget and interference gates passed",de4889d932ed8bb5ab376cb09ae43327084a2d017f66db5c54946b78b50446ac,0127dbdd8a04317be06a0235e5b1cec75dc298b3c6e312f22f1382b8d19f7262,benchmark.new/filesystem/.data/fs-docs-20261012/publication-audit.json,45f479ab7709723e781bfd6b78d8ace3dfa2a8914a687173babfd9ee23daf24e diff --git a/docs/src/zh/design/filesystem-optimization-n30-provenance.csv b/docs/src/zh/design/filesystem-optimization-n30-provenance.csv new file mode 100644 index 000000000..03916a385 --- /dev/null +++ b/docs/src/zh/design/filesystem-optimization-n30-provenance.csv @@ -0,0 +1,18 @@ +benchmark_id,batch,field,value +B-FS-ENG,fof1,raw_location,benchmark/.data/fof1/report.json +B-FS-ENG,fof1,report_sha256,df8ddd01aa86ef0a61a04d4157dc8e02a6c935ebacecf3ccfb448ab624ded7b1 +B-FS-ENG,fof1,acceptance_record,benchmark/.data/fof1/derived/audit.json +B-FS-ENG,fof1,acceptance_sha256,3a599448f7a7737f38e52b7d2943555d0a8cc4d74e030ebabbea5461d25fca29 +B-FS-ENG,fof1,summary_sha256,65b0287892724c266ebc39db20adcc138ac180aedb6b455cc0f713517d6b25dd +B-FS-ENG,fof1,role,independent formal engineering A/B; not pooled; not user-facing phase/whole-command replacement +B-FS-ENG,fof1,samples_per_arm,30 +B-FS-ENG,fof1,warmups_per_arm,3 +B-FS-ENG,fof1,failures,0 +B-FS-ENG,fof1,arguments,"{""backends"": ""pvisor-vm"", ""env"": [], ""output"": ""/home/reiase/workspace/pvisor/benchmark/.data/fof1"", ""preflight"": false, ""samples"": 30, ""seed"": 20261011, ""variants"": ""baseline=/home/reiase/workspace/pvisor/benchmark.new/filesystem/.data/cross-runtime-20261011T052947Z/build,selected=/home/reiase/workspace/pvisor/benchmark.new/filesystem/.data/fs-opt-build3-20261011/candidate"", ""warmups"": 3}" +B-FS-ENG,fof1,receipts,"{""baseline"": {""cargo"": ""cargo 1.98.1 (797e8a9bc 2026-08-05)\n"", ""command"": [""cargo"", ""build"", ""--locked"", ""--offline"", ""--release"", ""-p"", ""pvisor-cli"", ""--bin"", ""pvisor"", ""--features"", ""gateway"", ""--target-dir"", ""/home/reiase/workspace/pvisor/target/filesystem-current-build-20261010""], ""compiled_units"": {""cli"": {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""gateway\""]"", ""features"": ""[\""default\"", \""gateway\""]"", ""profile"": 4686446429936418732, ""rustc"": 1625334936438085654, ""rustflags"": []}, ""libraries"": [{""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__disabled\"", \""static\""]"", ""features"": ""[]"", ""name"": ""bzip2_sys"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__docs_rs\"", \""codec\"", \""compat\"", \""default\"", \""full\"", \""futures-io\"", \""futures-util\"", \""hashbrown\"", \""io\"", \""io-util\"", \""join-map\"", \""net\"", \""rt\"", \""slab\"", \""time\"", \""tracing\""]"", ""features"": ""[\""codec\"", \""default\"", \""futures-util\"", \""io\"", \""rt\""]"", ""name"": ""tokio_util"", ""profile"": 10043413591317300293, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__internal_bench\"", \""alloc\"", \""arbitrary\"", \""clock\"", \""core-error\"", \""default\"", \""defmt\"", \""iana-time-zone\"", \""js-sys\"", \""libc\"", \""now\"", \""oldtime\"", \""pure-rust-locales\"", \""rkyv\"", \""rkyv-16\"", \""rkyv-32\"", \""rkyv-64\"", \""rkyv-validation\"", \""serde\"", \""std\"", \""unstable-locales\"", \""wasm-bindgen\"", \""wasmbind\"", \""winapi\"", \""windows-link\""]"", ""features"": ""[\""alloc\"", \""clock\"", \""default\"", \""iana-time-zone\"", \""js-sys\"", \""now\"", \""oldtime\"", \""serde\"", \""std\"", \""wasm-bindgen\"", \""wasmbind\"", \""winapi\"", \""windows-link\""]"", ""name"": ""chrono"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__internal_happy_eyeballs_tests\"", \""client\"", \""client-legacy\"", \""client-pool\"", \""client-proxy\"", \""client-proxy-system\"", \""default\"", \""full\"", \""http1\"", \""http2\"", \""server\"", \""server-auto\"", \""server-graceful\"", \""service\"", \""tokio\"", \""tracing\""]"", ""features"": ""[\""client\"", \""client-legacy\"", \""client-proxy\"", \""default\"", \""http1\"", \""http2\"", \""server\"", \""service\"", \""tokio\""]"", ""name"": ""hyper_util"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__internal_use_only_features_that_work_on_stable\"", \""alloc\"", \""derive\"", \""float-nightly\"", \""simd\"", \""simd-nightly\"", \""std\"", \""zerocopy-derive\""]"", ""features"": ""[\""derive\"", \""simd\"", \""zerocopy-derive\""]"", ""name"": ""zerocopy"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__private\"", \""__private_docs\"", \""default\"", \""form\"", \""http1\"", \""http2\"", \""json\"", \""macros\"", \""matched-path\"", \""multipart\"", \""original-uri\"", \""query\"", \""tokio\"", \""tower-log\"", \""tracing\"", \""ws\""]"", ""features"": ""[\""http1\"", \""http2\"", \""json\"", \""tokio\""]"", ""name"": ""axum"", ""profile"": 7938159781367629850, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__private_docs\"", \""tracing\""]"", ""features"": ""[]"", ""name"": ""axum_core"", ""profile"": 9498297305443351059, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__rustls\"", \""__rustls-ring\"", \""__tls\"", \""blocking\"", \""brotli\"", \""charset\"", \""cookies\"", \""default\"", \""default-tls\"", \""deflate\"", \""gzip\"", \""h2\"", \""hickory-dns\"", \""http2\"", \""http3\"", \""json\"", \""macos-system-configuration\"", \""multipart\"", \""native-tls\"", \""native-tls-alpn\"", \""native-tls-vendored\"", \""rustls-tls\"", \""rustls-tls-manual-roots\"", \""rustls-tls-manual-roots-no-provider\"", \""rustls-tls-native-roots\"", \""rustls-tls-native-roots-no-provider\"", \""rustls-tls-no-provider\"", \""rustls-tls-webpki-roots\"", \""rustls-tls-webpki-roots-no-provider\"", \""socks\"", \""stream\"", \""system-proxy\"", \""trust-dns\"", \""zstd\""]"", ""features"": ""[\""__rustls\"", \""__rustls-ring\"", \""__tls\"", \""blocking\"", \""deflate\"", \""gzip\"", \""h2\"", \""http2\"", \""json\"", \""rustls-tls\"", \""rustls-tls-native-roots\"", \""rustls-tls-native-roots-no-provider\"", \""rustls-tls-webpki-roots\"", \""rustls-tls-webpki-roots-no-provider\"", \""stream\""]"", ""name"": ""reqwest"", ""profile"": 17166782826556458903, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__test_helpers\"", \""default\""]"", ""features"": ""[\""default\""]"", ""name"": ""matchit"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""_netsim\"", \""_proto-fragmentation\"", \""alloc\"", \""assembler-max-segment-count-1\"", \""assembler-max-segment-count-16\"", \""assembler-max-segment-count-2\"", \""assembler-max-segment-count-3\"", \""assembler-max-segment-count-32\"", \""assembler-max-segment-count-4\"", \""assembler-max-segment-count-8\"", \""async\"", \""auto-icmp-echo-reply\"", \""default\"", \""defmt\"", \""dns-max-name-size-128\"", \""dns-max-name-size-255\"", \""dns-max-name-size-64\"", \""dns-max-result-count-1\"", \""dns-max-result-count-16\"", \""dns-max-result-count-2\"", \""dns-max-result-count-3\"", \""dns-max-result-count-32\"", \""dns-max-result-count-4\"", \""dns-max-result-count-8\"", \""dns-max-server-count-1\"", \""dns-max-server-count-16\"", \""dns-max-server-count-2\"", \""dns-max-server-count-3\"", \""dns-max-server-count-32\"", \""dns-max-server-count-4\"", \""dns-max-server-count-8\"", \""fragmentation-buffer-size-1024\"", \""fragmentation-buffer-size-1500\"", \""fragmentation-buffer-size-16384\"", \""fragmentation-buffer-size-2048\"", \""fragmentation-buffer-size-256\"", \""fragmentation-buffer-size-32768\"", \""fragmentation-buffer-size-4096\"", \""fragmentation-buffer-size-512\"", \""fragmentation-buffer-size-65536\"", \""fragmentation-buffer-size-8192\"", \""iface-max-addr-count-1\"", \""iface-max-addr-count-2\"", \""iface-max-addr-count-3\"", \""iface-max-addr-count-4\"", \""iface-max-addr-count-5\"", \""iface-max-addr-count-6\"", \""iface-max-addr-count-7\"", \""iface-max-addr-count-8\"", \""iface-max-multicast-group-count-1\"", \""iface-max-multicast-group-count-1024\"", \""iface-max-multicast-group-count-128\"", \""iface-max-multicast-group-count-16\"", \""iface-max-multicast-group-count-2\"", \""iface-max-multicast-group-count-256\"", \""iface-max-multicast-group-count-3\"", \""iface-max-multicast-group-count-32\"", \""iface-max-multicast-group-count-4\"", \""iface-max-multicast-group-count-5\"", \""iface-max-multicast-group-count-512\"", \""iface-max-multicast-group-count-6\"", \""iface-max-multicast-group-count-64\"", \""iface-max-multicast-group-count-7\"", \""iface-max-multicast-group-count-8\"", \""iface-max-prefix-count-1\"", \""iface-max-prefix-count-2\"", \""iface-max-prefix-count-3\"", \""iface-max-prefix-count-4\"", \""iface-max-prefix-count-5\"", \""iface-max-prefix-count-6\"", \""iface-max-prefix-count-7\"", \""iface-max-prefix-count-8\"", \""iface-max-route-count-0\"", \""iface-max-route-count-1\"", \""iface-max-route-count-1024\"", \""iface-max-route-count-128\"", \""iface-max-route-count-16\"", \""iface-max-route-count-2\"", \""iface-max-route-count-256\"", \""iface-max-route-count-3\"", \""iface-max-route-count-32\"", \""iface-max-route-count-4\"", \""iface-max-route-count-5\"", \""iface-max-route-count-512\"", \""iface-max-route-count-6\"", \""iface-max-route-count-64\"", \""iface-max-route-count-7\"", \""iface-max-route-count-8\"", \""iface-max-sixlowpan-address-context-count-1\"", \""iface-max-sixlowpan-address-context-count-1024\"", \""iface-max-sixlowpan-address-context-count-128\"", \""iface-max-sixlowpan-address-context-count-16\"", \""iface-max-sixlowpan-address-context-count-2\"", \""iface-max-sixlowpan-address-context-count-256\"", \""iface-max-sixlowpan-address-context-count-3\"", \""iface-max-sixlowpan-address-context-count-32\"", \""iface-max-sixlowpan-address-context-count-4\"", \""iface-max-sixlowpan-address-context-count-5\"", \""iface-max-sixlowpan-address-context-count-512\"", \""iface-max-sixlowpan-address-context-count-6\"", \""iface-max-sixlowpan-address-context-count-64\"", \""iface-max-sixlowpan-address-context-count-7\"", \""iface-max-sixlowpan-address-context-count-8\"", \""iface-neighbor-cache-count-1\"", \""iface-neighbor-cache-count-1024\"", \""iface-neighbor-cache-count-128\"", \""iface-neighbor-cache-count-16\"", \""iface-neighbor-cache-count-2\"", \""iface-neighbor-cache-count-256\"", \""iface-neighbor-cache-count-3\"", \""iface-neighbor-cache-count-32\"", \""iface-neighbor-cache-count-4\"", \""iface-neighbor-cache-count-5\"", \""iface-neighbor-cache-count-512\"", \""iface-neighbor-cache-count-6\"", \""iface-neighbor-cache-count-64\"", \""iface-neighbor-cache-count-7\"", \""iface-neighbor-cache-count-8\"", \""ipv6-hbh-max-options-1\"", \""ipv6-hbh-max-options-16\"", \""ipv6-hbh-max-options-2\"", \""ipv6-hbh-max-options-3\"", \""ipv6-hbh-max-options-32\"", \""ipv6-hbh-max-options-4\"", \""ipv6-hbh-max-options-8\"", \""libc\"", \""log\"", \""medium-ethernet\"", \""medium-ieee802154\"", \""medium-ip\"", \""multicast\"", \""packetmeta-id\"", \""phy-raw_socket\"", \""phy-tuntap_interface\"", \""proto-dhcpv4\"", \""proto-dns\"", \""proto-ipsec\"", \""proto-ipsec-ah\"", \""proto-ipsec-esp\"", \""proto-ipv4\"", \""proto-ipv4-fragmentation\"", \""proto-ipv6\"", \""proto-ipv6-fragmentation\"", \""proto-ipv6-hbh\"", \""proto-ipv6-routing\"", \""proto-ipv6-slaac\"", \""proto-rpl\"", \""proto-sixlowpan\"", \""proto-sixlowpan-fragmentation\"", \""reassembly-buffer-count-1\"", \""reassembly-buffer-count-16\"", \""reassembly-buffer-count-2\"", \""reassembly-buffer-count-3\"", \""reassembly-buffer-count-32\"", \""reassembly-buffer-count-4\"", \""reassembly-buffer-count-8\"", \""reassembly-buffer-size-1024\"", \""reassembly-buffer-size-1500\"", \""reassembly-buffer-size-16384\"", \""reassembly-buffer-size-2048\"", \""reassembly-buffer-size-256\"", \""reassembly-buffer-size-32768\"", \""reassembly-buffer-size-4096\"", \""reassembly-buffer-size-512\"", \""reassembly-buffer-size-65536\"", \""reassembly-buffer-size-8192\"", \""rpl-parents-buffer-count-16\"", \""rpl-parents-buffer-count-2\"", \""rpl-parents-buffer-count-32\"", \""rpl-parents-buffer-count-4\"", \""rpl-parents-buffer-count-8\"", \""rpl-relations-buffer-count-1\"", \""rpl-relations-buffer-count-128\"", \""rpl-relations-buffer-count-16\"", \""rpl-relations-buffer-count-2\"", \""rpl-relations-buffer-count-32\"", \""rpl-relations-buffer-count-4\"", \""rpl-relations-buffer-count-64\"", \""rpl-relations-buffer-count-8\"", \""socket\"", \""socket-dhcpv4\"", \""socket-dns\"", \""socket-icmp\"", \""socket-mdns\"", \""socket-raw\"", \""socket-tcp\"", \""socket-tcp-cubic\"", \""socket-tcp-pause-synack\"", \""socket-tcp-reno\"", \""socket-udp\"", \""std\"", \""verbose\""]"", ""features"": ""[\""alloc\"", \""log\"", \""medium-ethernet\"", \""proto-dhcpv4\"", \""proto-dns\"", \""proto-ipv4\"", \""socket\"", \""socket-tcp\"", \""socket-tcp-pause-synack\"", \""socket-udp\"", \""std\""]"", ""name"": ""smoltcp"", ""profile"": 13333777962520875826, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""aarch64\"", \""alloc\"", \""default\"", \""derive\"", \""serde\"", \""simd\"", \""std\"", \""zeroize_derive\""]"", ""features"": ""[\""alloc\"", \""default\""]"", ""name"": ""zeroize"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""abi-7-10\"", \""abi-7-11\"", \""abi-7-12\"", \""abi-7-13\"", \""abi-7-14\"", \""abi-7-15\"", \""abi-7-16\"", \""abi-7-17\"", \""abi-7-18\"", \""abi-7-19\"", \""abi-7-20\"", \""abi-7-21\"", \""abi-7-22\"", \""abi-7-23\"", \""abi-7-24\"", \""abi-7-25\"", \""abi-7-26\"", \""abi-7-27\"", \""abi-7-28\"", \""abi-7-29\"", \""abi-7-30\"", \""abi-7-31\"", \""abi-7-9\"", \""default\"", \""libfuse\"", \""macfuse-4-compat\"", \""macfuse-5\"", \""pkg-config\"", \""serde\"", \""serializable\""]"", ""features"": ""[\""abi-7-10\"", \""abi-7-11\"", \""abi-7-12\"", \""abi-7-13\"", \""abi-7-14\"", \""abi-7-15\"", \""abi-7-16\"", \""abi-7-17\"", \""abi-7-18\"", \""abi-7-19\"", \""abi-7-20\"", \""abi-7-21\"", \""abi-7-22\"", \""abi-7-23\"", \""abi-7-24\"", \""abi-7-25\"", \""abi-7-26\"", \""abi-7-27\"", \""abi-7-28\"", \""abi-7-29\"", \""abi-7-30\"", \""abi-7-31\"", \""abi-7-9\""]"", ""name"": ""fuser"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""acct\"", \""aio\"", \""default\"", \""dir\"", \""env\"", \""event\"", \""fanotify\"", \""feature\"", \""fs\"", \""hostname\"", \""inotify\"", \""ioctl\"", \""kmod\"", \""memoffset\"", \""mman\"", \""mount\"", \""mqueue\"", \""net\"", \""personality\"", \""pin-utils\"", \""poll\"", \""process\"", \""pthread\"", \""ptrace\"", \""quota\"", \""reboot\"", \""resource\"", \""sched\"", \""signal\"", \""socket\"", \""syslog\"", \""term\"", \""time\"", \""ucontext\"", \""uio\"", \""user\"", \""zerocopy\""]"", ""features"": ""[\""default\"", \""fs\"", \""ioctl\"", \""memoffset\"", \""net\"", \""poll\"", \""socket\"", \""term\""]"", ""name"": ""nix"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""acct\"", \""aio\"", \""default\"", \""dir\"", \""env\"", \""event\"", \""fanotify\"", \""feature\"", \""fs\"", \""hostname\"", \""inotify\"", \""ioctl\"", \""kmod\"", \""memoffset\"", \""mman\"", \""mount\"", \""mqueue\"", \""net\"", \""personality\"", \""pin-utils\"", \""poll\"", \""process\"", \""pthread\"", \""ptrace\"", \""quota\"", \""reboot\"", \""resource\"", \""sched\"", \""signal\"", \""socket\"", \""term\"", \""time\"", \""ucontext\"", \""uio\"", \""user\"", \""zerocopy\""]"", ""features"": ""[\""default\"", \""feature\"", \""fs\"", \""user\""]"", ""name"": ""nix"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""add-extension\"", \""async-compression\"", \""auth\"", \""base64\"", \""catch-panic\"", \""compression-br\"", \""compression-deflate\"", \""compression-full\"", \""compression-gzip\"", \""compression-zstd\"", \""cors\"", \""decompression-br\"", \""decompression-deflate\"", \""decompression-full\"", \""decompression-gzip\"", \""decompression-zstd\"", \""default\"", \""follow-redirect\"", \""fs\"", \""full\"", \""futures-core\"", \""futures-util\"", \""httpdate\"", \""iri-string\"", \""limit\"", \""map-request-body\"", \""map-response-body\"", \""metrics\"", \""mime\"", \""mime_guess\"", \""normalize-path\"", \""percent-encoding\"", \""propagate-header\"", \""redirect\"", \""request-id\"", \""sensitive-headers\"", \""set-header\"", \""set-status\"", \""timeout\"", \""tokio\"", \""tokio-util\"", \""tower\"", \""trace\"", \""tracing\"", \""util\"", \""uuid\"", \""validate-request\""]"", ""features"": ""[\""async-compression\"", \""decompression-deflate\"", \""decompression-gzip\"", \""follow-redirect\"", \""futures-core\"", \""futures-util\"", \""iri-string\"", \""tokio\"", \""tokio-util\"", \""tower\""]"", ""name"": ""tower_http"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""ahash\"", \""alloc\"", \""allocator-api2\"", \""compiler_builtins\"", \""core\"", \""default\"", \""equivalent\"", \""inline-more\"", \""nightly\"", \""raw\"", \""rayon\"", \""rkyv\"", \""rustc-dep-of-std\"", \""rustc-internal-api\"", \""serde\""]"", ""features"": ""[\""raw\""]"", ""name"": ""hashbrown"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""align\"", \""const-extern-fn\"", \""default\"", \""extra_traits\"", \""rustc-dep-of-std\"", \""rustc-std-workspace-core\"", \""std\"", \""use_std\""]"", ""features"": ""[\""default\"", \""extra_traits\"", \""std\""]"", ""name"": ""libc"", ""profile"": 4248016837003788142, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""align\"", \""const-extern-fn\"", \""default\"", \""extra_traits\"", \""rustc-dep-of-std\"", \""rustc-std-workspace-core\"", \""std\"", \""use_std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""libc"", ""profile"": 4793633013790561351, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""all-algorithms\"", \""brotli\"", \""bzip2\"", \""deflate\"", \""deflate64\"", \""flate2\"", \""gzip\"", \""libzstd\"", \""lz4\"", \""lzma\"", \""memchr\"", \""xz\"", \""xz-parallel\"", \""xz2\"", \""zlib\"", \""zstd\"", \""zstd-safe\"", \""zstdmt\""]"", ""features"": ""[\""flate2\"", \""gzip\"", \""memchr\"", \""zlib\""]"", ""name"": ""compression_codecs"", ""profile"": 4107725171516170900, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""all-apis\"", \""alloc\"", \""core\"", \""default\"", \""event\"", \""fs\"", \""io_uring\"", \""libc\"", \""libc_errno\"", \""linux_4_11\"", \""linux_5_1\"", \""linux_5_11\"", \""linux_latest\"", \""mm\"", \""mount\"", \""net\"", \""param\"", \""pipe\"", \""process\"", \""pty\"", \""rand\"", \""runtime\"", \""rustc-dep-of-std\"", \""rustc-std-workspace-alloc\"", \""shm\"", \""std\"", \""stdio\"", \""system\"", \""termios\"", \""thread\"", \""time\"", \""try_close\"", \""use-explicitly-provided-auxv\"", \""use-libc\"", \""use-libc-auxv\""]"", ""features"": ""[\""alloc\"", \""default\"", \""fs\"", \""std\""]"", ""name"": ""rustix"", ""profile"": 4272393398390303732, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""all\"", \""all-algorithms\"", \""all-implementations\"", \""brotli\"", \""bzip2\"", \""deflate\"", \""deflate64\"", \""futures-io\"", \""gzip\"", \""lz4\"", \""lzma\"", \""tokio\"", \""xz\"", \""xz-parallel\"", \""xz2\"", \""zlib\"", \""zstd\"", \""zstdmt\""]"", ""features"": ""[\""gzip\"", \""tokio\"", \""zlib\""]"", ""name"": ""async_compression"", ""profile"": 4107725171516170900, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""all\""]"", ""features"": ""[\""all\""]"", ""name"": ""socket2"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""allocator-api2\"", \""core\"", \""default\"", \""default-hasher\"", \""equivalent\"", \""inline-more\"", \""nightly\"", \""raw-entry\"", \""rayon\"", \""rustc-dep-of-std\"", \""rustc-internal-api\"", \""serde\""]"", ""features"": ""[\""allocator-api2\"", \""default\"", \""default-hasher\"", \""equivalent\"", \""inline-more\"", \""raw-entry\""]"", ""name"": ""hashbrown"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""allocator-api2\"", \""core\"", \""default\"", \""default-hasher\"", \""equivalent\"", \""inline-more\"", \""nightly\"", \""raw-entry\"", \""rayon\"", \""rustc-dep-of-std\"", \""rustc-internal-api\"", \""serde\""]"", ""features"": ""[]"", ""name"": ""hashbrown"", ""profile"": 10067188661882979502, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""arbitrary\"", \""bytemuck\"", \""ctutils\"", \""extra-sizes\"", \""serde\"", \""subtle\"", \""zerocopy\"", \""zeroize\""]"", ""features"": ""[]"", ""name"": ""hybrid_array"", ""profile"": 7230693119043292679, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""arbitrary_precision\"", \""default\"", \""float_roundtrip\"", \""indexmap\"", \""preserve_order\"", \""raw_value\"", \""std\"", \""unbounded_depth\""]"", ""features"": ""[\""default\"", \""raw_value\"", \""std\""]"", ""name"": ""serde_json"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""async-await\"", \""async-await-macro\"", \""bilock\"", \""cfg-target-has-atomic\"", \""channel\"", \""compat\"", \""default\"", \""futures-channel\"", \""futures-io\"", \""futures-macro\"", \""futures-sink\"", \""futures_01\"", \""io\"", \""io-compat\"", \""libc\"", \""memchr\"", \""portable-atomic\"", \""sink\"", \""slab\"", \""spin\"", \""std\"", \""tokio-io\"", \""unstable\"", \""write-all-vectored\""]"", ""features"": ""[\""alloc\"", \""async-await\"", \""async-await-macro\"", \""default\"", \""futures-io\"", \""futures-macro\"", \""futures-sink\"", \""io\"", \""memchr\"", \""sink\"", \""slab\"", \""std\""]"", ""name"": ""futures_util"", ""profile"": 8157235479358073570, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""async-await\"", \""bilock\"", \""cfg-target-has-atomic\"", \""compat\"", \""default\"", \""executor\"", \""futures-executor\"", \""io-compat\"", \""spin\"", \""std\"", \""thread-pool\"", \""unstable\"", \""write-all-vectored\""]"", ""features"": ""[\""alloc\""]"", ""name"": ""futures"", ""profile"": 8157235479358073570, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""atomic-polyfill\"", \""critical-section\"", \""default\"", \""parking_lot\"", \""portable-atomic\"", \""race\"", \""std\"", \""unstable\""]"", ""features"": ""[\""alloc\"", \""default\"", \""race\"", \""std\""]"", ""name"": ""once_cell"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""aws-lc-rs\"", \""aws-lc-rs-fips\"", \""aws-lc-rs-unstable\"", \""default\"", \""ring\"", \""std\""]"", ""features"": ""[\""alloc\"", \""ring\"", \""std\""]"", ""name"": ""webpki"", ""profile"": 13987763031497049980, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""baked\"", \""deserialize_bincode_1\"", \""deserialize_json\"", \""deserialize_postcard_1\"", \""export\"", \""logging\"", \""serde\"", \""std\"", \""sync\"", \""zerotrie\""]"", ""features"": ""[\""baked\""]"", ""name"": ""icu_provider"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""bincode_derive\"", \""default\"", \""derive\"", \""serde\"", \""std\""]"", ""features"": ""[\""bincode_derive\"", \""derive\""]"", ""name"": ""bincode"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""blobby\"", \""block-api\"", \""default\"", \""dev\"", \""getrandom\"", \""mac\"", \""oid\"", \""rand_core\"", \""zeroize\""]"", ""features"": ""[\""alloc\"", \""block-api\"", \""default\"", \""oid\""]"", ""name"": ""digest"", ""profile"": 2606296542320589278, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""blobby\"", \""block-buffer\"", \""const-oid\"", \""core-api\"", \""default\"", \""dev\"", \""mac\"", \""oid\"", \""rand_core\"", \""std\"", \""subtle\""]"", ""features"": ""[\""block-buffer\"", \""core-api\"", \""default\""]"", ""name"": ""digest"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""block-boundary\"", \""core\"", \""default\"", \""rustc-dep-of-std\"", \""serde\"", \""simd\"", \""simd-adler32\"", \""std\"", \""with-alloc\""]"", ""features"": ""[\""default\"", \""simd\"", \""simd-adler32\"", \""std\"", \""with-alloc\""]"", ""name"": ""miniz_oxide"", ""profile"": 13333777962520875826, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""bytes\"", \""defmt\"", \""embedded-io-v0.7\"", \""mpmc_large\"", \""nightly\"", \""portable-atomic\"", \""portable-atomic-critical-section\"", \""portable-atomic-unsafe-assume-single-core\"", \""serde\"", \""ufmt\"", \""zeroize\""]"", ""features"": ""[]"", ""name"": ""heapless"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""cfg-target-has-atomic\"", \""default\"", \""futures-sink\"", \""sink\"", \""std\"", \""unstable\""]"", ""features"": ""[\""alloc\"", \""default\"", \""futures-sink\"", \""sink\"", \""std\""]"", ""name"": ""futures_channel"", ""profile"": 8157235479358073570, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""cfg-target-has-atomic\"", \""default\"", \""portable-atomic\"", \""std\"", \""unstable\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""futures_core"", ""profile"": 8157235479358073570, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""cfg-target-has-atomic\"", \""default\"", \""std\"", \""unstable\""]"", ""features"": ""[\""alloc\"", \""std\""]"", ""name"": ""futures_task"", ""profile"": 8157235479358073570, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""chacha\"", \""default\"", \""log\"", \""serde\"", \""simd_support\"", \""std\"", \""std_rng\"", \""sys_rng\"", \""thread_rng\"", \""unbiased\""]"", ""features"": ""[\""alloc\"", \""std\"", \""std_rng\"", \""sys_rng\"", \""thread_rng\""]"", ""name"": ""rand"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""compiled_data\"", \""datagen\"", \""default\"", \""harfbuzz_traits\"", \""serde\"", \""unicode_bidi\""]"", ""features"": ""[\""compiled_data\""]"", ""name"": ""icu_properties"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""compiled_data\"", \""default\"", \""std\""]"", ""features"": ""[\""alloc\"", \""compiled_data\"", \""std\""]"", ""name"": ""idna"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""core\"", \""default\"", \""libc\"", \""logging\"", \""rustc-dep-of-std\"", \""std\"", \""use_std\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""memchr"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""databake\"", \""default\"", \""dense\"", \""litemap\"", \""serde\"", \""yoke\"", \""zerofrom\"", \""zerovec\""]"", ""features"": ""[\""yoke\"", \""zerofrom\""]"", ""name"": ""zerotrie"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""databake\"", \""default\"", \""serde\"", \""std\"", \""zerovec\""]"", ""features"": ""[\""zerovec\""]"", ""name"": ""tinystr"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""databake\"", \""default\"", \""serde\"", \""testing\"", \""yoke\""]"", ""features"": ""[]"", ""name"": ""litemap"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""databake\"", \""default\"", \""serde\"", \""writeable\"", \""zerovec\""]"", ""features"": ""[\""zerovec\""]"", ""name"": ""potential_utf"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""databake\"", \""derive\"", \""hashmap\"", \""schemars\"", \""serde\"", \""std\"", \""yoke\""]"", ""features"": ""[\""derive\"", \""yoke\""]"", ""name"": ""zerovec"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""databake\"", \""serde\"", \""zerovec\""]"", ""features"": ""[\""zerovec\""]"", ""name"": ""icu_locale_core"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""databake\"", \""serde\""]"", ""features"": ""[]"", ""name"": ""icu_collections"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""debug\"", \""default\"", \""simd\"", \""std\"", \""unstable-doc\"", \""unstable-recover\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""winnow"", ""profile"": 16551665274674009540, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""defmt\"", \""js\"", \""logging\"", \""perf-inline\"", \""serde\"", \""static\"", \""static-tz\"", \""std\"", \""tz-fat\"", \""tz-system\"", \""tzdb-bundle-always\"", \""tzdb-bundle-platform\"", \""tzdb-concatenated\"", \""tzdb-zoneinfo\""]"", ""features"": ""[\""alloc\"", \""std\""]"", ""name"": ""jiff"", ""profile"": 7947306171983741744, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""defmt\"", \""logging\"", \""std\"", \""tz-fat\""]"", ""features"": ""[\""alloc\"", \""std\""]"", ""name"": ""jiff_core"", ""profile"": 7947306171983741744, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""derive\"", \""rc\"", \""serde_derive\"", \""std\"", \""unstable\""]"", ""features"": ""[\""default\"", \""derive\"", \""rc\"", \""serde_derive\"", \""std\""]"", ""name"": ""serde"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""derive\"", \""serde\"", \""zerofrom\""]"", ""features"": ""[\""derive\"", \""zerofrom\""]"", ""name"": ""yoke"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""derive\""]"", ""features"": ""[\""derive\""]"", ""name"": ""zerofrom"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""dev_urandom_fallback\"", \""less-safe-getrandom-custom-or-rdrand\"", \""less-safe-getrandom-espidf\"", \""slow_tests\"", \""std\"", \""test_logging\"", \""unstable-testing-arm-no-hw\"", \""unstable-testing-arm-no-neon\"", \""wasm32_unknown_unknown_js\""]"", ""features"": ""[\""alloc\"", \""default\"", \""dev_urandom_fallback\"", \""std\""]"", ""name"": ""ring"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""dfa\"", \""dfa-build\"", \""dfa-onepass\"", \""dfa-search\"", \""hybrid\"", \""internal-instrument\"", \""internal-instrument-pikevm\"", \""logging\"", \""meta\"", \""nfa\"", \""nfa-backtrack\"", \""nfa-pikevm\"", \""nfa-thompson\"", \""perf\"", \""perf-inline\"", \""perf-literal\"", \""perf-literal-multisubstring\"", \""perf-literal-substring\"", \""std\"", \""syntax\"", \""unicode\"", \""unicode-age\"", \""unicode-bool\"", \""unicode-case\"", \""unicode-gencat\"", \""unicode-perl\"", \""unicode-script\"", \""unicode-segment\"", \""unicode-word-boundary\""]"", ""features"": ""[\""alloc\"", \""dfa-onepass\"", \""hybrid\"", \""meta\"", \""nfa\"", \""nfa-backtrack\"", \""nfa-pikevm\"", \""nfa-thompson\"", \""perf\"", \""perf-inline\"", \""perf-literal\"", \""perf-literal-multisubstring\"", \""perf-literal-substring\"", \""std\"", \""syntax\"", \""unicode\"", \""unicode-age\"", \""unicode-bool\"", \""unicode-case\"", \""unicode-gencat\"", \""unicode-perl\"", \""unicode-script\"", \""unicode-segment\"", \""unicode-word-boundary\""]"", ""name"": ""regex_automata"", ""profile"": 2547513045437502204, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""either\""]"", ""features"": ""[]"", ""name"": ""writeable"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""fresh-rust\"", \""nightly\"", \""serde\"", \""std\""]"", ""features"": ""[\""alloc\""]"", ""name"": ""allocator_api2"", ""profile"": 3361788016818567686, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""getrandom\"", \""js\"", \""std\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""fastrand"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""log\"", \""nightly\"", \""os_rng\"", \""serde\"", \""simd_support\"", \""small_rng\"", \""std\"", \""std_rng\"", \""thread_rng\"", \""unbiased\""]"", ""features"": ""[\""alloc\"", \""default\"", \""os_rng\"", \""small_rng\"", \""std\"", \""std_rng\"", \""thread_rng\""]"", ""name"": ""rand"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""map\"", \""std\""]"", ""features"": ""[\""alloc\"", \""map\"", \""std\""]"", ""name"": ""managed"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""memchr\"", \""serde\"", \""std\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""iri_string"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""oid\"", \""zeroize\""]"", ""features"": ""[\""alloc\"", \""default\"", \""oid\""]"", ""name"": ""sha2"", ""profile"": 8048078827458334496, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""rc\"", \""result\"", \""std\"", \""unstable\""]"", ""features"": ""[\""alloc\"", \""default\"", \""rc\"", \""result\"", \""std\""]"", ""name"": ""serde_core"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""serde\"", \""std\"", \""unicode\""]"", ""features"": ""[\""alloc\"", \""std\""]"", ""name"": ""bstr"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""std\"", \""web\"", \""web-time\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""rustls_pki_types"", ""profile"": 8134619797147437084, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""base64"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""form_urlencoded"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""futures_sink"", ""profile"": 8157235479358073570, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""percent_encoding"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""toml_write"", ""profile"": 11768715044625735406, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""features"": ""[]"", ""name"": ""stable_deref_trait"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""kv\"", \""kv_serde\"", \""kv_std\"", \""kv_sval\"", \""kv_unstable\"", \""kv_unstable_serde\"", \""kv_unstable_std\"", \""kv_unstable_sval\"", \""max_level_debug\"", \""max_level_error\"", \""max_level_info\"", \""max_level_off\"", \""max_level_trace\"", \""max_level_warn\"", \""release_max_level_debug\"", \""release_max_level_error\"", \""release_max_level_info\"", \""release_max_level_off\"", \""release_max_level_trace\"", \""release_max_level_warn\"", \""serde\"", \""serde_core\"", \""std\"", \""sval\"", \""sval_ref\"", \""value-bag\""]"", ""features"": ""[\""alloc\"", \""std\""]"", ""name"": ""log"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""amd-sev\"", \""aws-nitro\"", \""bitfield\"", \""blk\"", \""default\"", \""efi\"", \""gpu\"", \""init-blob\"", \""input\"", \""iocuddle\"", \""kbs-types\"", \""krun_display\"", \""krun_input\"", \""net\"", \""pw\"", \""rutabaga_gfx\"", \""serde\"", \""snd\"", \""tdx\"", \""tee\"", \""test_utils\"", \""thiserror\"", \""virgl_resource_map2\"", \""zerocopy\""]"", ""features"": ""[\""default\"", \""init-blob\"", \""net\""]"", ""name"": ""pvisor_vm"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""ansi\"", \""bitflags\"", \""cursor-icon\"", \""default\"", \""log\"", \""serde\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""vte"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""any_c_zlib\"", \""any_impl\"", \""any_zlib\"", \""cloudflare-zlib-sys\"", \""cloudflare_zlib\"", \""default\"", \""document-features\"", \""libz-ng-sys\"", \""libz-sys\"", \""miniz-sys\"", \""miniz_oxide\"", \""rust_backend\"", \""zlib\"", \""zlib-default\"", \""zlib-ng\"", \""zlib-ng-compat\"", \""zlib-rs\""]"", ""features"": ""[\""any_impl\"", \""default\"", \""miniz_oxide\"", \""rust_backend\""]"", ""name"": ""flate2"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""async-tokio\"", \""default\"", \""document-features\"", \""encoding\"", \""encoding_rs\"", \""escape-html\"", \""overlapped-lists\"", \""serde\"", \""serde-types\"", \""serialize\"", \""tokio\""]"", ""features"": ""[\""default\"", \""overlapped-lists\"", \""serde\"", \""serialize\""]"", ""name"": ""quick_xml"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""atomic\"", \""borsh\"", \""bytemuck\"", \""default\"", \""fast-rng\"", \""js\"", \""macro-diagnostics\"", \""md5\"", \""rng\"", \""rng-getrandom\"", \""rng-rand\"", \""serde\"", \""sha1\"", \""slog\"", \""std\"", \""uuid-rng-internal-lib\"", \""v1\"", \""v3\"", \""v4\"", \""v5\"", \""v6\"", \""v7\"", \""v8\"", \""zerocopy\""]"", ""features"": ""[\""default\"", \""rng\"", \""std\"", \""v4\""]"", ""name"": ""uuid"", ""profile"": 5623565614784616014, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""bincode\"", \""const_generics\"", \""const_new\"", \""debugger_visualizer\"", \""drain_filter\"", \""drain_keep_rest\"", \""impl_bincode\"", \""malloc_size_of\"", \""may_dangle\"", \""serde\"", \""specialization\"", \""union\"", \""unty\"", \""write\""]"", ""features"": ""[\""const_generics\"", \""const_new\""]"", ""name"": ""smallvec"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""borsh\"", \""default\"", \""quickcheck\"", \""rayon\"", \""serde\"", \""std\"", \""sval\"", \""test_debug\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""indexmap"", ""profile"": 10937888587037481829, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""bytemuck\"", \""example_generated\"", \""serde\"", \""serde_core\"", \""std\""]"", ""features"": ""[\""std\""]"", ""name"": ""bitflags"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""db\""]"", ""features"": ""[]"", ""name"": ""const_oid"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""default\"", \""log\"", \""serde\"", \""serde1\"", \""simd-accel\""]"", ""features"": ""[\""default\"", \""log\""]"", ""name"": ""globset"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""default\"", \""std\"", \""unicode\"", \""unicode-age\"", \""unicode-bool\"", \""unicode-case\"", \""unicode-gencat\"", \""unicode-perl\"", \""unicode-script\"", \""unicode-segment\""]"", ""features"": ""[\""default\"", \""std\"", \""unicode\"", \""unicode-age\"", \""unicode-bool\"", \""unicode-case\"", \""unicode-gencat\"", \""unicode-perl\"", \""unicode-script\"", \""unicode-segment\""]"", ""name"": ""regex_syntax"", ""profile"": 2547513045437502204, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""inline\"", \""raw-api\"", \""rayon\"", \""serde\"", \""typesize\""]"", ""features"": ""[]"", ""name"": ""dashmap"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arc-swap\"", \""backend-atomic\"", \""backend-bitmap\"", \""backend-mmap\"", \""bitflags\"", \""default\"", \""rawfd\"", \""vmm-sys-util\"", \""xen\""]"", ""features"": ""[\""backend-mmap\"", \""default\"", \""rawfd\""]"", ""name"": ""vm_memory"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arc_lock\"", \""atomic_usize\"", \""default\"", \""nightly\"", \""owning_ref\"", \""serde\""]"", ""features"": ""[\""atomic_usize\"", \""default\""]"", ""name"": ""lock_api"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arc_lock\"", \""deadlock_detection\"", \""default\"", \""hardware-lock-elision\"", \""nightly\"", \""owning_ref\"", \""send_guard\"", \""serde\""]"", ""features"": ""[\""default\""]"", ""name"": ""parking_lot"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arrays\"", \""bindgen\"", \""debug\"", \""default\"", \""doc-cfg\"", \""experimental\"", \""fat-lto\"", \""legacy\"", \""no_asm\"", \""pkg-config\"", \""seekable\"", \""std\"", \""thin\"", \""thin-lto\"", \""zdict_builder\"", \""zstdmt\""]"", ""features"": ""[\""arrays\"", \""legacy\"", \""std\"", \""zdict_builder\""]"", ""name"": ""zstd_safe"", ""profile"": 10592522859712577474, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arrays\"", \""bindgen\"", \""debug\"", \""default\"", \""doc-cfg\"", \""experimental\"", \""fat-lto\"", \""legacy\"", \""no_asm\"", \""pkg-config\"", \""thin\"", \""thin-lto\"", \""wasm\"", \""zdict_builder\"", \""zstdmt\""]"", ""features"": ""[\""arrays\"", \""default\"", \""legacy\"", \""zdict_builder\""]"", ""name"": ""zstd"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""asm\"", \""default\"", \""force-soft\"", \""loongarch64_asm\"", \""md5-asm\"", \""oid\"", \""std\""]"", ""features"": ""[]"", ""name"": ""md5"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""async-await\"", \""attributes\"", \""default\"", \""log\"", \""log-always\"", \""max_level_debug\"", \""max_level_error\"", \""max_level_info\"", \""max_level_off\"", \""max_level_trace\"", \""max_level_warn\"", \""release_max_level_debug\"", \""release_max_level_error\"", \""release_max_level_info\"", \""release_max_level_off\"", \""release_max_level_trace\"", \""release_max_level_warn\"", \""std\"", \""tracing-attributes\"", \""valuable\""]"", ""features"": ""[\""attributes\"", \""default\"", \""std\"", \""tracing-attributes\""]"", ""name"": ""tracing"", ""profile"": 1647937577980832904, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""async-await\""]"", ""features"": ""[]"", ""name"": ""tracing_attributes"", ""profile"": 6207960371462447473, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""async\"", \""default\"", \""sync\"", \""sync-wrappers\"", \""vm-memory\""]"", ""features"": ""[\""async\"", \""default\"", \""sync-wrappers\""]"", ""name"": ""imago"", ""profile"": 15916455528278280289, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""auto-color\"", \""color\"", \""default\"", \""humantime\"", \""kv\"", \""regex\"", \""unstable-kv\""]"", ""features"": ""[\""auto-color\"", \""color\"", \""default\"", \""humantime\"", \""regex\""]"", ""name"": ""env_logger"", ""profile"": 10632806285984276979, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""auto\"", \""default\"", \""test\"", \""wincon\""]"", ""features"": ""[\""auto\"", \""default\"", \""wincon\""]"", ""name"": ""anstream"", ""profile"": 4227688284201523262, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""auto\"", \""default\"", \""test\"", \""wincon\""]"", ""features"": ""[\""auto\"", \""wincon\""]"", ""name"": ""anstream"", ""profile"": 10632806285984276979, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""auxvec\"", \""bootparam\"", \""btrfs\"", \""core\"", \""default\"", \""elf\"", \""elf_uapi\"", \""errno\"", \""general\"", \""if_arp\"", \""if_ether\"", \""if_packet\"", \""if_tun\"", \""image\"", \""io_uring\"", \""ioctl\"", \""landlock\"", \""loop_device\"", \""mempolicy\"", \""net\"", \""netlink\"", \""no_std\"", \""prctl\"", \""ptrace\"", \""rustc-dep-of-std\"", \""std\"", \""system\"", \""vm_sockets\"", \""xdp\""]"", ""features"": ""[\""auxvec\"", \""default\"", \""elf\"", \""errno\"", \""general\"", \""ioctl\"", \""no_std\"", \""std\""]"", ""name"": ""linux_raw_sys"", ""profile"": 14036798396267011297, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""aws-lc-rs\"", \""aws_lc_rs\"", \""brotli\"", \""custom-provider\"", \""default\"", \""fips\"", \""hashbrown\"", \""log\"", \""logging\"", \""prefer-post-quantum\"", \""read_buf\"", \""ring\"", \""rustversion\"", \""std\"", \""tls12\"", \""zlib\""]"", ""features"": ""[\""ring\"", \""std\"", \""tls12\""]"", ""name"": ""rustls"", ""profile"": 16187626775398413875, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""aws-lc-rs\"", \""aws_lc_rs\"", \""brotli\"", \""default\"", \""early-data\"", \""fips\"", \""logging\"", \""ring\"", \""tls12\"", \""zlib\""]"", ""features"": ""[\""ring\"", \""tls12\""]"", ""name"": ""tokio_rustls"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""aws-lc-rs\"", \""default\"", \""fips\"", \""http1\"", \""http2\"", \""log\"", \""logging\"", \""native-tokio\"", \""ring\"", \""rustls-native-certs\"", \""rustls-platform-verifier\"", \""tls12\"", \""webpki-roots\"", \""webpki-tokio\""]"", ""features"": ""[\""http1\"", \""http2\"", \""native-tokio\"", \""ring\"", \""rustls-native-certs\"", \""tls12\"", \""webpki-roots\"", \""webpki-tokio\""]"", ""name"": ""hyper_rustls"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""aws\"", \""azure\"", \""base64\"", \""cloud\"", \""default\"", \""form_urlencoded\"", \""fs\"", \""gcp\"", \""http\"", \""http-body-util\"", \""httparse\"", \""hyper\"", \""integration\"", \""md-5\"", \""quick-xml\"", \""rand\"", \""reqwest\"", \""ring\"", \""rustls-pki-types\"", \""serde\"", \""serde_json\"", \""serde_urlencoded\"", \""tls-webpki-roots\"", \""tokio\"", \""walkdir\""]"", ""features"": ""[\""aws\"", \""base64\"", \""cloud\"", \""form_urlencoded\"", \""http-body-util\"", \""hyper\"", \""md-5\"", \""quick-xml\"", \""rand\"", \""reqwest\"", \""ring\"", \""serde\"", \""serde_json\"", \""serde_urlencoded\"", \""tls-webpki-roots\"", \""tokio\""]"", ""name"": ""object_store"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""backtrace\"", \""deadlock_detection\"", \""nightly\"", \""petgraph\""]"", ""features"": ""[]"", ""name"": ""parking_lot_core"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""backtrace\"", \""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""anyhow"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""balance\"", \""buffer\"", \""discover\"", \""filter\"", \""full\"", \""futures-core\"", \""futures-util\"", \""hdrhistogram\"", \""hedge\"", \""indexmap\"", \""limit\"", \""load\"", \""load-shed\"", \""log\"", \""make\"", \""pin-project-lite\"", \""ready-cache\"", \""reconnect\"", \""retry\"", \""slab\"", \""spawn-ready\"", \""steer\"", \""sync_wrapper\"", \""timeout\"", \""tokio\"", \""tokio-stream\"", \""tokio-util\"", \""tracing\"", \""util\""]"", ""features"": ""[\""futures-core\"", \""futures-util\"", \""make\"", \""pin-project-lite\"", \""retry\"", \""sync_wrapper\"", \""timeout\"", \""tokio\"", \""util\""]"", ""name"": ""tower"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""bindgen\"", \""debug\"", \""default\"", \""experimental\"", \""fat-lto\"", \""legacy\"", \""no_asm\"", \""no_wasm_shim\"", \""non-cargo\"", \""pkg-config\"", \""seekable\"", \""std\"", \""thin\"", \""thin-lto\"", \""zdict_builder\"", \""zstdmt\""]"", ""features"": ""[\""legacy\"", \""std\"", \""zdict_builder\""]"", ""name"": ""zstd_sys"", ""profile"": 10592522859712577474, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""borsh\"", \""default\"", \""serde\"", \""std\"", \""zeroize\""]"", ""features"": ""[]"", ""name"": ""arrayvec"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""bytes\"", \""default\"", \""fs\"", \""full\"", \""io-std\"", \""io-uring\"", \""io-util\"", \""libc\"", \""macros\"", \""mio\"", \""net\"", \""parking_lot\"", \""process\"", \""rt\"", \""rt-multi-thread\"", \""signal\"", \""signal-hook-registry\"", \""socket2\"", \""sync\"", \""taskdump\"", \""test-util\"", \""time\"", \""tokio-macros\"", \""tracing\"", \""windows-sys\""]"", ""features"": ""[\""bytes\"", \""default\"", \""fs\"", \""io-util\"", \""libc\"", \""macros\"", \""mio\"", \""net\"", \""process\"", \""rt\"", \""rt-multi-thread\"", \""signal\"", \""signal-hook-registry\"", \""socket2\"", \""sync\"", \""time\"", \""tokio-macros\""]"", ""name"": ""tokio"", ""profile"": 10043413591317300293, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""bzimage\"", \""default\"", \""elf\"", \""pe\""]"", ""features"": ""[\""bzimage\"", \""default\"", \""elf\"", \""pe\""]"", ""name"": ""linux_loader"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""capi\"", \""client\"", \""default\"", \""ffi\"", \""full\"", \""http1\"", \""http2\"", \""nightly\"", \""server\"", \""tracing\""]"", ""features"": ""[\""client\"", \""default\"", \""http1\"", \""http2\"", \""server\""]"", ""name"": ""hyper"", ""profile"": 5907227947356068632, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""cargo\"", \""color\"", \""debug\"", \""default\"", \""deprecated\"", \""derive\"", \""env\"", \""error-context\"", \""help\"", \""std\"", \""string\"", \""suggestions\"", \""unicode\"", \""unstable-derive-ui-tests\"", \""unstable-doc\"", \""unstable-ext\"", \""unstable-markdown\"", \""unstable-styles\"", \""unstable-v5\"", \""usage\"", \""wrap_help\""]"", ""features"": ""[\""color\"", \""default\"", \""derive\"", \""env\"", \""error-context\"", \""help\"", \""std\"", \""string\"", \""suggestions\"", \""usage\""]"", ""name"": ""clap"", ""profile"": 4628411354643344498, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""cargo\"", \""color\"", \""debug\"", \""default\"", \""deprecated\"", \""env\"", \""error-context\"", \""help\"", \""std\"", \""string\"", \""suggestions\"", \""unicode\"", \""unstable-doc\"", \""unstable-ext\"", \""unstable-styles\"", \""unstable-v5\"", \""usage\"", \""wrap_help\""]"", ""features"": ""[\""color\"", \""env\"", \""error-context\"", \""help\"", \""std\"", \""string\"", \""suggestions\"", \""usage\""]"", ""name"": ""clap_builder"", ""profile"": 4628411354643344498, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""channel\"", \""default\"", \""full\""]"", ""features"": ""[\""default\""]"", ""name"": ""http_body_util"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""cipher\"", \""default\"", \""legacy\"", \""rng\"", \""xchacha\"", \""zeroize\""]"", ""features"": ""[\""rng\""]"", ""name"": ""chacha20"", ""profile"": 4285365006624603084, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""cjk\"", \""core\"", \""default\"", \""no_std\"", \""rustc-dep-of-std\"", \""std\""]"", ""features"": ""[\""cjk\"", \""default\""]"", ""name"": ""unicode_width"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""cli\"", \""default\""]"", ""features"": ""[]"", ""name"": ""pvisor_overlayfs"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""clone-impls\"", \""default\"", \""derive\"", \""extra-traits\"", \""fold\"", \""full\"", \""parsing\"", \""printing\"", \""proc-macro\"", \""test\"", \""visit\"", \""visit-mut\""]"", ""features"": ""[\""clone-impls\"", \""default\"", \""derive\"", \""extra-traits\"", \""fold\"", \""full\"", \""parsing\"", \""printing\"", \""proc-macro\"", \""visit\"", \""visit-mut\""]"", ""name"": ""syn"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""compiled_data\"", \""datagen\"", \""default\"", \""harfbuzz_traits\"", \""icu_properties\"", \""serde\"", \""utf16_iter\"", \""utf8_iter\"", \""write16\""]"", ""features"": ""[\""compiled_data\""]"", ""name"": ""icu_normalizer"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""compiled_data\""]"", ""features"": ""[\""compiled_data\""]"", ""name"": ""idna_adapter"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""compiler_builtins\"", \""core\"", \""custom\"", \""js\"", \""js-sys\"", \""linux_disable_fallback\"", \""rdrand\"", \""rustc-dep-of-std\"", \""std\"", \""test-in-browser\"", \""wasm-bindgen\""]"", ""features"": ""[]"", ""name"": ""getrandom"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""compiler_builtins\"", \""core\"", \""default\"", \""example_generated\"", \""rustc-dep-of-std\""]"", ""features"": ""[\""default\""]"", ""name"": ""bitflags"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""const-generics\"", \""core_hint_black_box\"", \""default\"", \""i128\"", \""nightly\"", \""std\""]"", ""features"": ""[]"", ""name"": ""subtle"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""const-generics\"", \""default\"", \""nightly\"", \""std\""]"", ""features"": ""[]"", ""name"": ""simd_adler32"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""const-generics\"", \""i128\"", \""scale-info\"", \""scale_info\"", \""strict\""]"", ""features"": ""[\""const-generics\""]"", ""name"": ""typenum"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""core\"", \""default\"", \""rustc-dep-of-std\"", \""std\""]"", ""features"": ""[]"", ""name"": ""adler2"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""core\"", \""default\"", \""utf8\""]"", ""features"": ""[\""default\"", \""utf8\""]"", ""name"": ""anstyle_parse"", ""profile"": 10632806285984276979, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""core\"", \""default\"", \""utf8\""]"", ""features"": ""[\""default\"", \""utf8\""]"", ""name"": ""anstyle_parse"", ""profile"": 4227688284201523262, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""core\"", \""rustc-dep-of-std\""]"", ""features"": ""[]"", ""name"": ""cfg_if"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""core\"", \""rustc-dep-of-std\""]"", ""features"": ""[]"", ""name"": ""cfg_if"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""count_instructions_test\"", \""default\"", \""std\""]"", ""features"": ""[\""std\""]"", ""name"": ""constant_time_eq"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""debug\"", \""default\"", \""deprecated\"", \""raw-deprecated\"", \""unstable-markdown\"", \""unstable-v5\""]"", ""features"": ""[\""default\""]"", ""name"": ""clap_derive"", ""profile"": 5346920246396443047, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""debugger_visualizer\"", \""default\"", \""expose_internals\"", \""serde\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""url"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""deserialize_in_place\""]"", ""features"": ""[\""default\""]"", ""name"": ""serde_derive"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""digest\"", \""mmap\"", \""neon\"", \""no_avx2\"", \""no_avx512\"", \""no_neon\"", \""no_sse2\"", \""no_sse41\"", \""prefer_intrinsics\"", \""pure\"", \""rayon\"", \""serde\"", \""std\"", \""traits-preview\"", \""wasm32_simd\"", \""zeroize\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""blake3"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""display\"", \""indexmap\"", \""parse\"", \""preserve_order\"", \""unbounded\""]"", ""features"": ""[\""default\"", \""display\"", \""parse\""]"", ""name"": ""toml"", ""profile"": 11768715044625735406, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""display\"", \""parse\"", \""perf\"", \""serde\"", \""unbounded\"", \""unstable-debug\""]"", ""features"": ""[\""default\"", \""display\"", \""parse\"", \""serde\""]"", ""name"": ""toml_edit"", ""profile"": 11768715044625735406, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""echo-server\""]"", ""features"": ""[]"", ""name"": ""pvisor_gateway"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""extra-platforms\"", \""serde\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""bytes"", ""profile"": 11003502350373712158, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""fs\"", \""full\"", \""io-util\"", \""net\"", \""signal\"", \""sync\"", \""time\"", \""tokio-util\""]"", ""features"": ""[\""default\"", \""time\""]"", ""name"": ""tokio_stream"", ""profile"": 10043413591317300293, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""gateway\""]"", ""features"": ""[\""default\"", \""gateway\""]"", ""name"": ""pvisor_cli"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""gateway\""]"", ""features"": ""[\""gateway\""]"", ""name"": ""pvisor"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""getrandom\"", \""nightly\""]"", ""features"": ""[\""default\"", \""getrandom\""]"", ""name"": ""tempfile"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""hashbrown\"", \""nightly\""]"", ""features"": ""[\""default\"", \""hashbrown\""]"", ""name"": ""lru"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""heapless\"", \""json\"", \""schemars\"", \""schemars08\"", \""schemars1\"", \""ser_as_str\"", \""serde\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""ipnet"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""i128\"", \""libm\"", \""std\""]"", ""features"": ""[]"", ""name"": ""num_traits"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""i128\"", \""std\""]"", ""features"": ""[]"", ""name"": ""byteorder"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""is_sync\""]"", ""features"": ""[\""default\""]"", ""name"": ""maybe_async"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""libbz2-rs-sys\"", \""static\""]"", ""features"": ""[\""default\""]"", ""name"": ""bzip2"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""log\"", \""net\"", \""os-ext\"", \""os-poll\""]"", ""features"": ""[\""net\"", \""os-ext\"", \""os-poll\""]"", ""name"": ""mio"", ""profile"": 16626080924472169626, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""logging\"", \""pattern\"", \""perf\"", \""perf-backtrack\"", \""perf-cache\"", \""perf-dfa\"", \""perf-dfa-full\"", \""perf-inline\"", \""perf-literal\"", \""perf-onepass\"", \""std\"", \""unicode\"", \""unicode-age\"", \""unicode-bool\"", \""unicode-case\"", \""unicode-gencat\"", \""unicode-perl\"", \""unicode-script\"", \""unicode-segment\"", \""unstable\"", \""use_std\""]"", ""features"": ""[\""default\"", \""perf\"", \""perf-backtrack\"", \""perf-cache\"", \""perf-dfa\"", \""perf-inline\"", \""perf-literal\"", \""perf-onepass\"", \""std\"", \""unicode\"", \""unicode-age\"", \""unicode-bool\"", \""unicode-case\"", \""unicode-gencat\"", \""unicode-perl\"", \""unicode-script\"", \""unicode-segment\""]"", ""name"": ""regex"", ""profile"": 2547513045437502204, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""logging\"", \""perf-literal\"", \""std\""]"", ""features"": ""[\""default\"", \""perf-literal\"", \""std\""]"", ""name"": ""aho_corasick"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""loom\"", \""nightly\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""crossbeam_utils"", ""profile"": 10433883304768107316, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""nightly\"", \""proc-macro\"", \""span-locations\""]"", ""features"": ""[\""default\"", \""proc-macro\""]"", ""name"": ""proc_macro2"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""nightly\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""crc32fast"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""nightly\""]"", ""features"": ""[\""default\""]"", ""name"": ""utf8parse"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""no_simd\"", \""simd\"", \""std\""]"", ""features"": ""[\""simd\"", \""std\""]"", ""name"": ""ppv_lite86"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""once_cell\"", \""std\"", \""valuable\""]"", ""features"": ""[\""once_cell\"", \""std\""]"", ""name"": ""tracing_core"", ""profile"": 1647937577980832904, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""os_rng\"", \""serde\"", \""std\""]"", ""features"": ""[\""std\""]"", ""name"": ""rand_chacha"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""proc-macro\""]"", ""features"": ""[\""default\"", \""proc-macro\""]"", ""name"": ""quote"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""proc-macro\""]"", ""features"": ""[\""default\"", \""proc-macro\""]"", ""name"": ""synstructure"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""regex\"", \""std\""]"", ""features"": ""[\""regex\"", \""std\""]"", ""name"": ""env_filter"", ""profile"": 10632806285984276979, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""serde\"", \""std\"", \""use_std\""]"", ""features"": ""[\""std\"", \""use_std\""]"", ""name"": ""either"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""serde\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""semver"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""serde\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""slab"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""set\""]"", ""features"": ""[\""default\""]"", ""name"": ""hostname"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\"", \""unstable\""]"", ""features"": ""[\""std\""]"", ""name"": ""futures_io"", ""profile"": 8157235479358073570, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""anstyle"", ""profile"": 10632806285984276979, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""crossbeam_channel"", ""profile"": 10433883304768107316, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""errno"", ""profile"": 14231071289633644017, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""fnv"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""http"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""httparse"", ""profile"": 1675385711784277470, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""shlex"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""thiserror"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[]"", ""name"": ""displaydoc"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[]"", ""name"": ""foldhash"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""unstable_const\"", \""unstable_offset_of\""]"", ""features"": ""[\""default\""]"", ""name"": ""memoffset"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""unsupported\""]"", ""features"": ""[\""default\"", \""unsupported\""]"", ""name"": ""xattr"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""use_alloc\"", \""use_std\""]"", ""features"": ""[\""default\"", \""use_alloc\"", \""use_std\""]"", ""name"": ""itertools"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""use_std\""]"", ""features"": ""[]"", ""name"": ""scopeguard"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""xattr\""]"", ""features"": ""[\""default\"", \""xattr\""]"", ""name"": ""tar"", ""profile"": 4339154717131654023, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\""]"", ""features"": ""[\""default\""]"", ""name"": ""is_terminal_polyfill"", ""profile"": 5812794364236305338, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""fallback\""]"", ""features"": ""[\""fallback\""]"", ""name"": ""iana_time_zone"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""fam-wrappers\"", \""serde\"", \""vmm-sys-util\""]"", ""features"": ""[\""fam-wrappers\"", \""serde\"", \""vmm-sys-util\""]"", ""name"": ""kvm_bindings"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""futures\"", \""futures-core\""]"", ""features"": ""[\""futures\"", \""futures-core\""]"", ""name"": ""sync_wrapper"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""getrandom\"", \""rand_core\"", \""std\""]"", ""features"": ""[]"", ""name"": ""crypto_common"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""getrandom\"", \""rand_core\"", \""zeroize\""]"", ""features"": ""[]"", ""name"": ""crypto_common"", ""profile"": 2606296542320589278, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""jobserver\"", \""parallel\""]"", ""features"": ""[\""parallel\""]"", ""name"": ""cc"", ""profile"": 12458195255332598410, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""more_lengths\"", \""serde\"", \""zeroize\""]"", ""features"": ""[\""more_lengths\""]"", ""name"": ""generic_array"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""mu\""]"", ""features"": ""[]"", ""name"": ""humantime"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""no-panic\"", \""small\""]"", ""features"": ""[]"", ""name"": ""ryu"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""no-panic\""]"", ""features"": ""[]"", ""name"": ""itoa"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""no-panic\""]"", ""features"": ""[]"", ""name"": ""zmij"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""no_std\"", \""spin\""]"", ""features"": ""[]"", ""name"": ""page_size"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""os_rng\"", \""serde\"", \""std\""]"", ""features"": ""[\""os_rng\"", \""std\""]"", ""name"": ""rand_core"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""portable-atomic\""]"", ""features"": ""[]"", ""name"": ""atomic_waker"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""proc-macro2\""]"", ""features"": ""[]"", ""name"": ""virtue"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""serde\"", \""serde_derive\"", \""with-serde\""]"", ""features"": ""[\""serde\"", \""serde_derive\"", \""with-serde\""]"", ""name"": ""vmm_sys_util"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""serde\"", \""serde_support\""]"", ""features"": ""[]"", ""name"": ""caps"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""serde\""]"", ""features"": ""[\""serde\""]"", ""name"": ""serde_spanned"", ""profile"": 11768715044625735406, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""serde\""]"", ""features"": ""[\""serde\""]"", ""name"": ""toml_datetime"", ""profile"": 11768715044625735406, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""std\"", \""sys_rng\"", \""wasm_js\""]"", ""features"": ""[\""std\"", \""sys_rng\""]"", ""name"": ""getrandom"", ""profile"": 7847730558161019686, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""std\"", \""wasm_js\""]"", ""features"": ""[\""std\""]"", ""name"": ""getrandom"", ""profile"": 3240661740686181567, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""stream\"", \""unstable\""]"", ""features"": ""[]"", ""name"": ""h2"", ""profile"": 13333777962520875826, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""zeroize\""]"", ""features"": ""[]"", ""name"": ""block_buffer"", ""profile"": 2548229083023945596, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""anstyle_query"", ""profile"": 8192080853972711267, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""arrayref"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""async_trait"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""autocfg"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""bincode_derive"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""block_buffer"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""cfg_aliases"", ""profile"": 11980061553486606077, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""clap_lex"", ""profile"": 4628411354643344498, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""colorchoice"", ""profile"": 10632806285984276979, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""compression_core"", ""profile"": 4107725171516170900, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""cpufeatures"", ""profile"": 2548229083023945596, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""dirs"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""dirs_sys"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""equivalent"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""filetime"", ""profile"": 4796329815077315148, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""find_msvc_tools"", ""profile"": 12458195255332598410, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""fs2"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""futures_macro"", ""profile"": 1218413102171684145, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""hash32"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""heck"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""http_body"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""httpdate"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""icu_normalizer_data"", ""profile"": 18083946540466509289, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""icu_properties_data"", ""profile"": 18083946540466509289, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""jobserver"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""kvm_ioctls"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""libloading"", ""profile"": 12556093047751754406, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""libloading"", ""profile"": 6679834303860230680, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""mime"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""openssl_probe"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""option_ext"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""pin_project_lite"", ""profile"": 14034241328553317513, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""pkg_config"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""pvisor_core"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""pvisor_guest"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""pvisor_journal"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""pvisor_overlay_core"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""pvisor_overlaynet"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""pvisor_replay"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""rand_core"", ""profile"": 11804347602484681322, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""rustc_version"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""rustls_native_certs"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""serde_path_to_error"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""serde_urlencoded"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""serde_yaml"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""signal_hook_registry"", ""profile"": 8111324945914507537, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""strsim"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""thiserror_impl"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""tokio_macros"", ""profile"": 5029862450655259090, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""tower_layer"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""tower_service"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""try_lock"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""unicode_ident"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""unsafe_libyaml"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""untrusted"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""unty"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""utf8_iter"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""version_check"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""virtio_bindings"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""vt100"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""want"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""webpki_roots"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""yoke_derive"", ""profile"": 4981139003136920394, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""zerocopy_derive"", ""profile"": 16810066265625103578, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""zerofrom_derive"", ""profile"": 4981139003136920394, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""zerovec_derive"", ""profile"": 4981139003136920394, ""rustc"": 1625334936438085654, ""rustflags"": []}]}, ""environment"": {""CARGO_BUILD_JOBS"": ""2"", ""CARGO_NET_OFFLINE"": ""true""}, ""firmware_sha256"": ""b61f68dac3ef20a88e1ee387733e4baed2f7c02edac940c8882e0b549dac95e4"", ""pvisor_sha256"": ""939f72289a72f73ba64d645c891842ed4a9408c7eaef4c54d5fa04aed0371e85"", ""rustc"": ""rustc 1.98.1 (48a229cea 2026-09-01)\nbinary: rustc\ncommit-hash: 48a229ceaefd4985c50990b14116b6d856af0985\ncommit-date: 2026-09-01\nhost: x86_64-unknown-linux-gnu\nrelease: 1.98.1\nLLVM version: 22.1.8\n"", ""source_identity"": {""head"": ""b3e2f03509396b8ebe840a9ccdefa63424bc1afe"", ""manifest_sha256"": ""07b27144366d96dbac053bd18288a50c7468b231d0e8570b3f400b38c28d0f8f"", ""recorded_at"": ""2026-10-11T05:29:47.568168+00:00"", ""scope"": ""complete current tracked/unignored files excluding generated target and raw .data"", ""status"": "" M .github/workflows/ci.yml\n M .github/workflows/docs.yml\n M .github/workflows/pvisor-benchmark.yml\n M README.md\n M benchmark/README.md\n M crates/pvisor-cli/README.md\n M crates/pvisor-cli/src/cli/host_process.rs\n M crates/pvisor-cli/src/cli/host_service.rs\n M crates/pvisor-cli/src/cli/runtime.rs\n M crates/pvisor-overlay-core/src/core.rs\n M crates/pvisor-overlayfs/README.md\n M crates/pvisor-overlayfs/src/api.rs\n M crates/pvisor-overlayfs/src/dispatch.rs\n M crates/pvisor-overlayfs/src/fs.rs\n M crates/pvisor-vm/Cargo.toml\n M crates/pvisor-vm/README.md\n M crates/pvisor-vm/src/api.rs\n M crates/pvisor-vm/src/builder.rs\n M crates/pvisor-vm/src/contract_tests/vm_snapshot_fs.rs\n M crates/pvisor-vm/src/devices/virtio/device.rs\n M crates/pvisor-vm/src/devices/virtio/fs/augment_fs.rs\n M crates/pvisor-vm/src/devices/virtio/fs/device.rs\n M crates/pvisor-vm/src/devices/virtio/fs/filesystem.rs\n M crates/pvisor-vm/src/devices/virtio/fs/fuse.rs\n M crates/pvisor-vm/src/devices/virtio/fs/linux/passthrough.rs\n M crates/pvisor-vm/src/devices/virtio/fs/linux/passthrough_snapshot.rs\n M crates/pvisor-vm/src/devices/virtio/fs/macos/passthrough.rs\n M crates/pvisor-vm/src/devices/virtio/fs/mod.rs\n M crates/pvisor-vm/src/devices/virtio/fs/overlay.rs\n M crates/pvisor-vm/src/devices/virtio/fs/read_only.rs\n M crates/pvisor-vm/src/devices/virtio/fs/server.rs\n M crates/pvisor-vm/src/devices/virtio/fs/snapshot.rs\n M crates/pvisor-vm/src/devices/virtio/fs/worker.rs\n M crates/pvisor-vm/src/devices/virtio/gpu/device.rs\n M crates/pvisor-vm/src/devices/virtio/mmio.rs\n M crates/pvisor-vm/src/portable.rs\n M crates/pvisor-vm/src/vmm/builder.rs\n M crates/pvisor-vm/src/vmm/linux/vstate.rs\n M crates/pvisor-vm/tests/api_contract.rs\n M docs/README.md\n M docs/src/en/benchmarks/agent-tasks.md\n M docs/src/en/benchmarks/cluster-scalability.md\n M docs/src/en/benchmarks/compare-rl-infra.md\n M docs/src/en/benchmarks/density.md\n M docs/src/en/benchmarks/evidence-sources.csv\n M docs/src/en/benchmarks/filesystem.csv\n M docs/src/en/benchmarks/filesystem.md\n M docs/src/en/benchmarks/index.csv\n M docs/src/en/benchmarks/index.md\n M docs/src/en/benchmarks/isolation-tests.md\n M docs/src/en/benchmarks/lazy-image-startup.md\n M docs/src/en/benchmarks/methodology.md\n M docs/src/en/benchmarks/network.md\n M docs/src/en/benchmarks/replay-fidelity.md\n M docs/src/en/benchmarks/startup.md\n M docs/src/en/benchmarks/supervision-cost.md\n M docs/src/en/benchmarks/vm-memory/index.md\n M docs/src/en/design/agent-task-performance-analysis.md\n M docs/src/en/design/benchmark-methodology-evidence.md\n M docs/src/en/design/cluster-performance-analysis.md\n M docs/src/en/design/filesystem-performance-analysis.md\n M docs/src/en/design/index.md\n M docs/src/en/design/vm-memory-performance-analysis.md\n M docs/src/en/design/vm-startup-performance-analysis.md\n M docs/src/zh/benchmarks/agent-tasks.md\n M docs/src/zh/benchmarks/cluster-scalability.md\n M docs/src/zh/benchmarks/compare-rl-infra.md\n M docs/src/zh/benchmarks/density.md\n M docs/src/zh/benchmarks/evidence-sources.csv\n M docs/src/zh/benchmarks/filesystem.csv\n M docs/src/zh/benchmarks/filesystem.md\n M docs/src/zh/benchmarks/index.csv\n M docs/src/zh/benchmarks/index.md\n M docs/src/zh/benchmarks/isolation-tests.md\n M docs/src/zh/benchmarks/lazy-image-startup.md\n M docs/src/zh/benchmarks/methodology.md\n M docs/src/zh/benchmarks/network.md\n M docs/src/zh/benchmarks/replay-fidelity.md\n M docs/src/zh/benchmarks/startup.md\n M docs/src/zh/benchmarks/supervision-cost.md\n M docs/src/zh/benchmarks/vm-memory/index.md\n M docs/src/zh/design/agent-task-performance-analysis.md\n M docs/src/zh/design/benchmark-methodology-evidence.md\n M docs/src/zh/design/cluster-performance-analysis.md\n M docs/src/zh/design/filesystem-performance-analysis.md\n M docs/src/zh/design/index.md\n M docs/src/zh/design/vm-memory-performance-analysis.md\n M docs/src/zh/design/vm-startup-performance-analysis.md\n M docs/translations.json\n M docs/zensical.en.toml\n M docs/zensical.zh.toml\n D experimental/README.md\n D experimental/vcpu-idle-offload/README.md\n D experimental/vcpu-idle-offload/design.md\n D experimental/vcpu-idle-offload/implementation-plan.md\n D experimental/vcpu-idle-offload/validation.md\n M justfile\n M tests/justfile\n M tests/test_docs.py\n M tools/semspec/Cargo.lock\n M tools/semspec/Cargo.toml\n M tools/semspec/DESIGN.md\n M tools/semspec/README.md\n M tools/semspec/src/helpers.rs\n M tools/semspec/src/lib.rs\n M tools/semspec/src/main.rs\n?? benchmark.new/\n?? crates/pvisor-vm/src/devices/virtio/fs/dax.rs\n?? crates/pvisor-vm/src/devices/virtio/fs/worker/\n?? crates/pvisor-vm/src/probes/readonly_dax_case.rs\n?? docs/semspec_tables.py\n?? docs/src/en/benchmarks/filesystem-cache-policy.csv\n?? docs/src/en/benchmarks/filesystem-comparisons.csv\n?? docs/src/en/benchmarks/filesystem-provenance.csv\n?? docs/src/en/benchmarks/filesystem-semspec.csv\n?? docs/src/en/benchmarks/filesystem-semspec.render.json\n?? docs/src/en/benchmarks/filesystem-semspec.render.provenance.json\n?? docs/src/en/benchmarks/measurement-coverage.csv\n?? docs/src/en/benchmarks/measurement-coverage.render.json\n?? docs/src/en/benchmarks/replay-semspec.csv\n?? docs/src/en/benchmarks/replay-semspec.render.json\n?? docs/src/en/benchmarks/replay-semspec.render.provenance.json\n?? docs/src/en/benchmarks/startup-hyperfine-legacy.render.json\n?? docs/src/en/benchmarks/startup-hyperfine.csv\n?? docs/src/en/benchmarks/startup-ready-exit.csv\n?? docs/src/en/benchmarks/startup-ready-exit.exit.render.json\n?? docs/src/en/benchmarks/startup-ready-exit.provenance.json\n?? docs/src/en/benchmarks/startup-ready-exit.ready.render.json\n?? docs/src/en/benchmarks/startup-ready-exit.render.json\n?? docs/src/en/benchmarks/startup-ready.csv\n?? docs/src/en/benchmarks/startup-ready.provenance.json\n?? docs/src/en/benchmarks/startup-ready.render.json\n?? docs/src/en/benchmarks/startup-reboot-comparison.csv\n?? docs/src/en/benchmarks/startup-semspec.csv\n?? docs/src/en/benchmarks/startup-semspec.provenance.json\n?? docs/src/en/benchmarks/startup-semspec.render.json\n?? docs/src/en/benchmarks/startup-timing.csv\n?? docs/src/en/benchmarks/startup-timing.exit.render.json\n?? docs/src/en/benchmarks/startup-timing.provenance.json\n?? docs/src/en/benchmarks/startup-timing.ready.render.json\n?? docs/src/en/benchmarks/startup-timing.render.json\n?? docs/src/en/benchmarks/tables/\n?? docs/src/en/benchmarks/vm-memory/tables/\n?? docs/src/en/design/concurrency-filesystem-summary.csv\n?? docs/src/en/design/concurrency-retest-provenance.csv\n?? docs/src/en/design/concurrency-startup-summary.csv\n?? docs/src/en/design/direct-queue-filesystem-summary.csv\n?? docs/src/en/design/direct-queue-provenance.csv\n?? docs/src/en/design/filesystem-evidence-overview.csv\n?? docs/src/en/design/filesystem-measurement-status.csv\n?? docs/src/en/design/host-copy-up-summary.csv\n?? docs/src/en/design/host-idle-dispatch-provenance.csv\n?? docs/src/en/design/host-idle-dispatch-summary.csv\n?? docs/src/en/design/host-read-summary.csv\n?? docs/src/en/design/journal-lock-summary.csv\n?? docs/src/en/design/readonly-dax-provenance.csv\n?? docs/src/en/design/readonly-dax-summary.csv\n?? docs/src/en/design/tables/\n?? docs/src/en/design/vm-completion-dispatch-provenance.csv\n?? docs/src/en/design/vm-completion-dispatch-summary.csv\n?? docs/src/en/design/writeback-filesystem-summary.csv\n?? docs/src/en/design/writeback-provenance.csv\n?? docs/src/zh/benchmarks/filesystem-cache-policy.csv\n?? docs/src/zh/benchmarks/filesystem-comparisons.csv\n?? docs/src/zh/benchmarks/filesystem-provenance.csv\n?? docs/src/zh/benchmarks/filesystem-semspec.csv\n?? docs/src/zh/benchmarks/filesystem-semspec.render.json\n?? docs/src/zh/benchmarks/filesystem-semspec.render.provenance.json\n?? docs/src/zh/benchmarks/measurement-coverage.csv\n?? docs/src/zh/benchmarks/measurement-coverage.render.json\n?? docs/src/zh/benchmarks/replay-semspec.csv\n?? docs/src/zh/benchmarks/replay-semspec.render.json\n?? docs/src/zh/benchmarks/replay-semspec.render.provenance.json\n?? docs/src/zh/benchmarks/startup-hyperfine-legacy.render.json\n?? docs/src/zh/benchmarks/startup-hyperfine.csv\n?? docs/src/zh/benchmarks/startup-ready-exit.csv\n?? docs/src/zh/benchmarks/startup-ready-exit.exit.render.json\n?? docs/src/zh/benchmarks/startup-ready-exit.provenance.json\n?? docs/src/zh/benchmarks/startup-ready-exit.ready.render.json\n?? docs/src/zh/benchmarks/startup-ready-exit.render.json\n?? docs/src/zh/benchmarks/startup-ready.csv\n?? docs/src/zh/benchmarks/startup-ready.provenance.json\n?? docs/src/zh/benchmarks/startup-ready.render.json\n?? docs/src/zh/benchmarks/startup-reboot-comparison.csv\n?? docs/src/zh/benchmarks/startup-semspec.csv\n?? docs/src/zh/benchmarks/startup-semspec.provenance.json\n?? docs/src/zh/benchmarks/startup-semspec.render.json\n?? docs/src/zh/benchmarks/startup-timing.csv\n?? docs/src/zh/benchmarks/startup-timing.exit.render.json\n?? docs/src/zh/benchmarks/startup-timing.provenance.json\n?? docs/src/zh/benchmarks/startup-timing.ready.render.json\n?? docs/src/zh/benchmarks/startup-timing.render.json\n?? docs/src/zh/benchmarks/tables/\n?? docs/src/zh/benchmarks/vm-memory/tables/\n?? docs/src/zh/design/concurrency-filesystem-summary.csv\n?? docs/src/zh/design/concurrency-retest-provenance.csv\n?? docs/src/zh/design/concurrency-startup-summary.csv\n?? docs/src/zh/design/direct-queue-filesystem-summary.csv\n?? docs/src/zh/design/direct-queue-provenance.csv\n?? docs/src/zh/design/experimental/\n?? docs/src/zh/design/filesystem-evidence-overview.csv\n?? docs/src/zh/design/filesystem-measurement-status.csv\n?? docs/src/zh/design/host-copy-up-summary.csv\n?? docs/src/zh/design/host-idle-dispatch-provenance.csv\n?? docs/src/zh/design/host-idle-dispatch-summary.csv\n?? docs/src/zh/design/host-read-summary.csv\n?? docs/src/zh/design/journal-lock-summary.csv\n?? docs/src/zh/design/readonly-dax-provenance.csv\n?? docs/src/zh/design/readonly-dax-summary.csv\n?? docs/src/zh/design/tables/\n?? docs/src/zh/design/vm-completion-dispatch-provenance.csv\n?? docs/src/zh/design/vm-completion-dispatch-summary.csv\n?? docs/src/zh/design/writeback-filesystem-summary.csv\n?? docs/src/zh/design/writeback-provenance.csv\n?? tests/test_semspec_tables.py\n?? tools/semspec/src/table.rs\n?? tools/semspec/tests/tables.rs\n""}, ""source_manifest_sha256"": ""07b27144366d96dbac053bd18288a50c7468b231d0e8570b3f400b38c28d0f8f""}, ""selected"": {""cargo"": ""cargo 1.98.1 (797e8a9bc 2026-08-05)\n"", ""command"": [""cargo"", ""build"", ""--locked"", ""--offline"", ""--release"", ""-p"", ""pvisor-cli"", ""--bin"", ""pvisor"", ""--features"", ""gateway"", ""--target-dir"", ""/home/reiase/workspace/pvisor/target/filesystem-current-build-20261010""], ""compiled_units"": {""cli"": {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""gateway\""]"", ""features"": ""[\""default\"", \""gateway\""]"", ""profile"": 4686446429936418732, ""rustc"": 1625334936438085654, ""rustflags"": []}, ""libraries"": [{""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__disabled\"", \""static\""]"", ""features"": ""[]"", ""name"": ""bzip2_sys"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__docs_rs\"", \""codec\"", \""compat\"", \""default\"", \""full\"", \""futures-io\"", \""futures-util\"", \""hashbrown\"", \""io\"", \""io-util\"", \""join-map\"", \""net\"", \""rt\"", \""slab\"", \""time\"", \""tracing\""]"", ""features"": ""[\""codec\"", \""default\"", \""futures-util\"", \""io\"", \""rt\""]"", ""name"": ""tokio_util"", ""profile"": 10043413591317300293, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__internal_bench\"", \""alloc\"", \""arbitrary\"", \""clock\"", \""core-error\"", \""default\"", \""defmt\"", \""iana-time-zone\"", \""js-sys\"", \""libc\"", \""now\"", \""oldtime\"", \""pure-rust-locales\"", \""rkyv\"", \""rkyv-16\"", \""rkyv-32\"", \""rkyv-64\"", \""rkyv-validation\"", \""serde\"", \""std\"", \""unstable-locales\"", \""wasm-bindgen\"", \""wasmbind\"", \""winapi\"", \""windows-link\""]"", ""features"": ""[\""alloc\"", \""clock\"", \""default\"", \""iana-time-zone\"", \""js-sys\"", \""now\"", \""oldtime\"", \""serde\"", \""std\"", \""wasm-bindgen\"", \""wasmbind\"", \""winapi\"", \""windows-link\""]"", ""name"": ""chrono"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__internal_happy_eyeballs_tests\"", \""client\"", \""client-legacy\"", \""client-pool\"", \""client-proxy\"", \""client-proxy-system\"", \""default\"", \""full\"", \""http1\"", \""http2\"", \""server\"", \""server-auto\"", \""server-graceful\"", \""service\"", \""tokio\"", \""tracing\""]"", ""features"": ""[\""client\"", \""client-legacy\"", \""client-proxy\"", \""default\"", \""http1\"", \""http2\"", \""server\"", \""service\"", \""tokio\""]"", ""name"": ""hyper_util"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__internal_use_only_features_that_work_on_stable\"", \""alloc\"", \""derive\"", \""float-nightly\"", \""simd\"", \""simd-nightly\"", \""std\"", \""zerocopy-derive\""]"", ""features"": ""[\""derive\"", \""simd\"", \""zerocopy-derive\""]"", ""name"": ""zerocopy"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__private\"", \""__private_docs\"", \""default\"", \""form\"", \""http1\"", \""http2\"", \""json\"", \""macros\"", \""matched-path\"", \""multipart\"", \""original-uri\"", \""query\"", \""tokio\"", \""tower-log\"", \""tracing\"", \""ws\""]"", ""features"": ""[\""http1\"", \""http2\"", \""json\"", \""tokio\""]"", ""name"": ""axum"", ""profile"": 7938159781367629850, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__private_docs\"", \""tracing\""]"", ""features"": ""[]"", ""name"": ""axum_core"", ""profile"": 9498297305443351059, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__rustls\"", \""__rustls-ring\"", \""__tls\"", \""blocking\"", \""brotli\"", \""charset\"", \""cookies\"", \""default\"", \""default-tls\"", \""deflate\"", \""gzip\"", \""h2\"", \""hickory-dns\"", \""http2\"", \""http3\"", \""json\"", \""macos-system-configuration\"", \""multipart\"", \""native-tls\"", \""native-tls-alpn\"", \""native-tls-vendored\"", \""rustls-tls\"", \""rustls-tls-manual-roots\"", \""rustls-tls-manual-roots-no-provider\"", \""rustls-tls-native-roots\"", \""rustls-tls-native-roots-no-provider\"", \""rustls-tls-no-provider\"", \""rustls-tls-webpki-roots\"", \""rustls-tls-webpki-roots-no-provider\"", \""socks\"", \""stream\"", \""system-proxy\"", \""trust-dns\"", \""zstd\""]"", ""features"": ""[\""__rustls\"", \""__rustls-ring\"", \""__tls\"", \""blocking\"", \""deflate\"", \""gzip\"", \""h2\"", \""http2\"", \""json\"", \""rustls-tls\"", \""rustls-tls-native-roots\"", \""rustls-tls-native-roots-no-provider\"", \""rustls-tls-webpki-roots\"", \""rustls-tls-webpki-roots-no-provider\"", \""stream\""]"", ""name"": ""reqwest"", ""profile"": 17166782826556458903, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""__test_helpers\"", \""default\""]"", ""features"": ""[\""default\""]"", ""name"": ""matchit"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""_netsim\"", \""_proto-fragmentation\"", \""alloc\"", \""assembler-max-segment-count-1\"", \""assembler-max-segment-count-16\"", \""assembler-max-segment-count-2\"", \""assembler-max-segment-count-3\"", \""assembler-max-segment-count-32\"", \""assembler-max-segment-count-4\"", \""assembler-max-segment-count-8\"", \""async\"", \""auto-icmp-echo-reply\"", \""default\"", \""defmt\"", \""dns-max-name-size-128\"", \""dns-max-name-size-255\"", \""dns-max-name-size-64\"", \""dns-max-result-count-1\"", \""dns-max-result-count-16\"", \""dns-max-result-count-2\"", \""dns-max-result-count-3\"", \""dns-max-result-count-32\"", \""dns-max-result-count-4\"", \""dns-max-result-count-8\"", \""dns-max-server-count-1\"", \""dns-max-server-count-16\"", \""dns-max-server-count-2\"", \""dns-max-server-count-3\"", \""dns-max-server-count-32\"", \""dns-max-server-count-4\"", \""dns-max-server-count-8\"", \""fragmentation-buffer-size-1024\"", \""fragmentation-buffer-size-1500\"", \""fragmentation-buffer-size-16384\"", \""fragmentation-buffer-size-2048\"", \""fragmentation-buffer-size-256\"", \""fragmentation-buffer-size-32768\"", \""fragmentation-buffer-size-4096\"", \""fragmentation-buffer-size-512\"", \""fragmentation-buffer-size-65536\"", \""fragmentation-buffer-size-8192\"", \""iface-max-addr-count-1\"", \""iface-max-addr-count-2\"", \""iface-max-addr-count-3\"", \""iface-max-addr-count-4\"", \""iface-max-addr-count-5\"", \""iface-max-addr-count-6\"", \""iface-max-addr-count-7\"", \""iface-max-addr-count-8\"", \""iface-max-multicast-group-count-1\"", \""iface-max-multicast-group-count-1024\"", \""iface-max-multicast-group-count-128\"", \""iface-max-multicast-group-count-16\"", \""iface-max-multicast-group-count-2\"", \""iface-max-multicast-group-count-256\"", \""iface-max-multicast-group-count-3\"", \""iface-max-multicast-group-count-32\"", \""iface-max-multicast-group-count-4\"", \""iface-max-multicast-group-count-5\"", \""iface-max-multicast-group-count-512\"", \""iface-max-multicast-group-count-6\"", \""iface-max-multicast-group-count-64\"", \""iface-max-multicast-group-count-7\"", \""iface-max-multicast-group-count-8\"", \""iface-max-prefix-count-1\"", \""iface-max-prefix-count-2\"", \""iface-max-prefix-count-3\"", \""iface-max-prefix-count-4\"", \""iface-max-prefix-count-5\"", \""iface-max-prefix-count-6\"", \""iface-max-prefix-count-7\"", \""iface-max-prefix-count-8\"", \""iface-max-route-count-0\"", \""iface-max-route-count-1\"", \""iface-max-route-count-1024\"", \""iface-max-route-count-128\"", \""iface-max-route-count-16\"", \""iface-max-route-count-2\"", \""iface-max-route-count-256\"", \""iface-max-route-count-3\"", \""iface-max-route-count-32\"", \""iface-max-route-count-4\"", \""iface-max-route-count-5\"", \""iface-max-route-count-512\"", \""iface-max-route-count-6\"", \""iface-max-route-count-64\"", \""iface-max-route-count-7\"", \""iface-max-route-count-8\"", \""iface-max-sixlowpan-address-context-count-1\"", \""iface-max-sixlowpan-address-context-count-1024\"", \""iface-max-sixlowpan-address-context-count-128\"", \""iface-max-sixlowpan-address-context-count-16\"", \""iface-max-sixlowpan-address-context-count-2\"", \""iface-max-sixlowpan-address-context-count-256\"", \""iface-max-sixlowpan-address-context-count-3\"", \""iface-max-sixlowpan-address-context-count-32\"", \""iface-max-sixlowpan-address-context-count-4\"", \""iface-max-sixlowpan-address-context-count-5\"", \""iface-max-sixlowpan-address-context-count-512\"", \""iface-max-sixlowpan-address-context-count-6\"", \""iface-max-sixlowpan-address-context-count-64\"", \""iface-max-sixlowpan-address-context-count-7\"", \""iface-max-sixlowpan-address-context-count-8\"", \""iface-neighbor-cache-count-1\"", \""iface-neighbor-cache-count-1024\"", \""iface-neighbor-cache-count-128\"", \""iface-neighbor-cache-count-16\"", \""iface-neighbor-cache-count-2\"", \""iface-neighbor-cache-count-256\"", \""iface-neighbor-cache-count-3\"", \""iface-neighbor-cache-count-32\"", \""iface-neighbor-cache-count-4\"", \""iface-neighbor-cache-count-5\"", \""iface-neighbor-cache-count-512\"", \""iface-neighbor-cache-count-6\"", \""iface-neighbor-cache-count-64\"", \""iface-neighbor-cache-count-7\"", \""iface-neighbor-cache-count-8\"", \""ipv6-hbh-max-options-1\"", \""ipv6-hbh-max-options-16\"", \""ipv6-hbh-max-options-2\"", \""ipv6-hbh-max-options-3\"", \""ipv6-hbh-max-options-32\"", \""ipv6-hbh-max-options-4\"", \""ipv6-hbh-max-options-8\"", \""libc\"", \""log\"", \""medium-ethernet\"", \""medium-ieee802154\"", \""medium-ip\"", \""multicast\"", \""packetmeta-id\"", \""phy-raw_socket\"", \""phy-tuntap_interface\"", \""proto-dhcpv4\"", \""proto-dns\"", \""proto-ipsec\"", \""proto-ipsec-ah\"", \""proto-ipsec-esp\"", \""proto-ipv4\"", \""proto-ipv4-fragmentation\"", \""proto-ipv6\"", \""proto-ipv6-fragmentation\"", \""proto-ipv6-hbh\"", \""proto-ipv6-routing\"", \""proto-ipv6-slaac\"", \""proto-rpl\"", \""proto-sixlowpan\"", \""proto-sixlowpan-fragmentation\"", \""reassembly-buffer-count-1\"", \""reassembly-buffer-count-16\"", \""reassembly-buffer-count-2\"", \""reassembly-buffer-count-3\"", \""reassembly-buffer-count-32\"", \""reassembly-buffer-count-4\"", \""reassembly-buffer-count-8\"", \""reassembly-buffer-size-1024\"", \""reassembly-buffer-size-1500\"", \""reassembly-buffer-size-16384\"", \""reassembly-buffer-size-2048\"", \""reassembly-buffer-size-256\"", \""reassembly-buffer-size-32768\"", \""reassembly-buffer-size-4096\"", \""reassembly-buffer-size-512\"", \""reassembly-buffer-size-65536\"", \""reassembly-buffer-size-8192\"", \""rpl-parents-buffer-count-16\"", \""rpl-parents-buffer-count-2\"", \""rpl-parents-buffer-count-32\"", \""rpl-parents-buffer-count-4\"", \""rpl-parents-buffer-count-8\"", \""rpl-relations-buffer-count-1\"", \""rpl-relations-buffer-count-128\"", \""rpl-relations-buffer-count-16\"", \""rpl-relations-buffer-count-2\"", \""rpl-relations-buffer-count-32\"", \""rpl-relations-buffer-count-4\"", \""rpl-relations-buffer-count-64\"", \""rpl-relations-buffer-count-8\"", \""socket\"", \""socket-dhcpv4\"", \""socket-dns\"", \""socket-icmp\"", \""socket-mdns\"", \""socket-raw\"", \""socket-tcp\"", \""socket-tcp-cubic\"", \""socket-tcp-pause-synack\"", \""socket-tcp-reno\"", \""socket-udp\"", \""std\"", \""verbose\""]"", ""features"": ""[\""alloc\"", \""log\"", \""medium-ethernet\"", \""proto-dhcpv4\"", \""proto-dns\"", \""proto-ipv4\"", \""socket\"", \""socket-tcp\"", \""socket-tcp-pause-synack\"", \""socket-udp\"", \""std\""]"", ""name"": ""smoltcp"", ""profile"": 13333777962520875826, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""aarch64\"", \""alloc\"", \""default\"", \""derive\"", \""serde\"", \""simd\"", \""std\"", \""zeroize_derive\""]"", ""features"": ""[\""alloc\"", \""default\""]"", ""name"": ""zeroize"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""abi-7-10\"", \""abi-7-11\"", \""abi-7-12\"", \""abi-7-13\"", \""abi-7-14\"", \""abi-7-15\"", \""abi-7-16\"", \""abi-7-17\"", \""abi-7-18\"", \""abi-7-19\"", \""abi-7-20\"", \""abi-7-21\"", \""abi-7-22\"", \""abi-7-23\"", \""abi-7-24\"", \""abi-7-25\"", \""abi-7-26\"", \""abi-7-27\"", \""abi-7-28\"", \""abi-7-29\"", \""abi-7-30\"", \""abi-7-31\"", \""abi-7-9\"", \""default\"", \""libfuse\"", \""macfuse-4-compat\"", \""macfuse-5\"", \""pkg-config\"", \""serde\"", \""serializable\""]"", ""features"": ""[\""abi-7-10\"", \""abi-7-11\"", \""abi-7-12\"", \""abi-7-13\"", \""abi-7-14\"", \""abi-7-15\"", \""abi-7-16\"", \""abi-7-17\"", \""abi-7-18\"", \""abi-7-19\"", \""abi-7-20\"", \""abi-7-21\"", \""abi-7-22\"", \""abi-7-23\"", \""abi-7-24\"", \""abi-7-25\"", \""abi-7-26\"", \""abi-7-27\"", \""abi-7-28\"", \""abi-7-29\"", \""abi-7-30\"", \""abi-7-31\"", \""abi-7-9\""]"", ""name"": ""fuser"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""acct\"", \""aio\"", \""default\"", \""dir\"", \""env\"", \""event\"", \""fanotify\"", \""feature\"", \""fs\"", \""hostname\"", \""inotify\"", \""ioctl\"", \""kmod\"", \""memoffset\"", \""mman\"", \""mount\"", \""mqueue\"", \""net\"", \""personality\"", \""pin-utils\"", \""poll\"", \""process\"", \""pthread\"", \""ptrace\"", \""quota\"", \""reboot\"", \""resource\"", \""sched\"", \""signal\"", \""socket\"", \""syslog\"", \""term\"", \""time\"", \""ucontext\"", \""uio\"", \""user\"", \""zerocopy\""]"", ""features"": ""[\""default\"", \""fs\"", \""ioctl\"", \""memoffset\"", \""net\"", \""poll\"", \""socket\"", \""term\""]"", ""name"": ""nix"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""acct\"", \""aio\"", \""default\"", \""dir\"", \""env\"", \""event\"", \""fanotify\"", \""feature\"", \""fs\"", \""hostname\"", \""inotify\"", \""ioctl\"", \""kmod\"", \""memoffset\"", \""mman\"", \""mount\"", \""mqueue\"", \""net\"", \""personality\"", \""pin-utils\"", \""poll\"", \""process\"", \""pthread\"", \""ptrace\"", \""quota\"", \""reboot\"", \""resource\"", \""sched\"", \""signal\"", \""socket\"", \""term\"", \""time\"", \""ucontext\"", \""uio\"", \""user\"", \""zerocopy\""]"", ""features"": ""[\""default\"", \""feature\"", \""fs\"", \""user\""]"", ""name"": ""nix"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""add-extension\"", \""async-compression\"", \""auth\"", \""base64\"", \""catch-panic\"", \""compression-br\"", \""compression-deflate\"", \""compression-full\"", \""compression-gzip\"", \""compression-zstd\"", \""cors\"", \""decompression-br\"", \""decompression-deflate\"", \""decompression-full\"", \""decompression-gzip\"", \""decompression-zstd\"", \""default\"", \""follow-redirect\"", \""fs\"", \""full\"", \""futures-core\"", \""futures-util\"", \""httpdate\"", \""iri-string\"", \""limit\"", \""map-request-body\"", \""map-response-body\"", \""metrics\"", \""mime\"", \""mime_guess\"", \""normalize-path\"", \""percent-encoding\"", \""propagate-header\"", \""redirect\"", \""request-id\"", \""sensitive-headers\"", \""set-header\"", \""set-status\"", \""timeout\"", \""tokio\"", \""tokio-util\"", \""tower\"", \""trace\"", \""tracing\"", \""util\"", \""uuid\"", \""validate-request\""]"", ""features"": ""[\""async-compression\"", \""decompression-deflate\"", \""decompression-gzip\"", \""follow-redirect\"", \""futures-core\"", \""futures-util\"", \""iri-string\"", \""tokio\"", \""tokio-util\"", \""tower\""]"", ""name"": ""tower_http"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""ahash\"", \""alloc\"", \""allocator-api2\"", \""compiler_builtins\"", \""core\"", \""default\"", \""equivalent\"", \""inline-more\"", \""nightly\"", \""raw\"", \""rayon\"", \""rkyv\"", \""rustc-dep-of-std\"", \""rustc-internal-api\"", \""serde\""]"", ""features"": ""[\""raw\""]"", ""name"": ""hashbrown"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""align\"", \""const-extern-fn\"", \""default\"", \""extra_traits\"", \""rustc-dep-of-std\"", \""rustc-std-workspace-core\"", \""std\"", \""use_std\""]"", ""features"": ""[\""default\"", \""extra_traits\"", \""std\""]"", ""name"": ""libc"", ""profile"": 4248016837003788142, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""align\"", \""const-extern-fn\"", \""default\"", \""extra_traits\"", \""rustc-dep-of-std\"", \""rustc-std-workspace-core\"", \""std\"", \""use_std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""libc"", ""profile"": 4793633013790561351, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""all-algorithms\"", \""brotli\"", \""bzip2\"", \""deflate\"", \""deflate64\"", \""flate2\"", \""gzip\"", \""libzstd\"", \""lz4\"", \""lzma\"", \""memchr\"", \""xz\"", \""xz-parallel\"", \""xz2\"", \""zlib\"", \""zstd\"", \""zstd-safe\"", \""zstdmt\""]"", ""features"": ""[\""flate2\"", \""gzip\"", \""memchr\"", \""zlib\""]"", ""name"": ""compression_codecs"", ""profile"": 4107725171516170900, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""all-apis\"", \""alloc\"", \""core\"", \""default\"", \""event\"", \""fs\"", \""io_uring\"", \""libc\"", \""libc_errno\"", \""linux_4_11\"", \""linux_5_1\"", \""linux_5_11\"", \""linux_latest\"", \""mm\"", \""mount\"", \""net\"", \""param\"", \""pipe\"", \""process\"", \""pty\"", \""rand\"", \""runtime\"", \""rustc-dep-of-std\"", \""rustc-std-workspace-alloc\"", \""shm\"", \""std\"", \""stdio\"", \""system\"", \""termios\"", \""thread\"", \""time\"", \""try_close\"", \""use-explicitly-provided-auxv\"", \""use-libc\"", \""use-libc-auxv\""]"", ""features"": ""[\""alloc\"", \""default\"", \""fs\"", \""std\""]"", ""name"": ""rustix"", ""profile"": 4272393398390303732, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""all\"", \""all-algorithms\"", \""all-implementations\"", \""brotli\"", \""bzip2\"", \""deflate\"", \""deflate64\"", \""futures-io\"", \""gzip\"", \""lz4\"", \""lzma\"", \""tokio\"", \""xz\"", \""xz-parallel\"", \""xz2\"", \""zlib\"", \""zstd\"", \""zstdmt\""]"", ""features"": ""[\""gzip\"", \""tokio\"", \""zlib\""]"", ""name"": ""async_compression"", ""profile"": 4107725171516170900, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""all\""]"", ""features"": ""[\""all\""]"", ""name"": ""socket2"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""allocator-api2\"", \""core\"", \""default\"", \""default-hasher\"", \""equivalent\"", \""inline-more\"", \""nightly\"", \""raw-entry\"", \""rayon\"", \""rustc-dep-of-std\"", \""rustc-internal-api\"", \""serde\""]"", ""features"": ""[\""allocator-api2\"", \""default\"", \""default-hasher\"", \""equivalent\"", \""inline-more\"", \""raw-entry\""]"", ""name"": ""hashbrown"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""allocator-api2\"", \""core\"", \""default\"", \""default-hasher\"", \""equivalent\"", \""inline-more\"", \""nightly\"", \""raw-entry\"", \""rayon\"", \""rustc-dep-of-std\"", \""rustc-internal-api\"", \""serde\""]"", ""features"": ""[]"", ""name"": ""hashbrown"", ""profile"": 10067188661882979502, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""arbitrary\"", \""bytemuck\"", \""ctutils\"", \""extra-sizes\"", \""serde\"", \""subtle\"", \""zerocopy\"", \""zeroize\""]"", ""features"": ""[]"", ""name"": ""hybrid_array"", ""profile"": 7230693119043292679, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""arbitrary_precision\"", \""default\"", \""float_roundtrip\"", \""indexmap\"", \""preserve_order\"", \""raw_value\"", \""std\"", \""unbounded_depth\""]"", ""features"": ""[\""default\"", \""raw_value\"", \""std\""]"", ""name"": ""serde_json"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""async-await\"", \""async-await-macro\"", \""bilock\"", \""cfg-target-has-atomic\"", \""channel\"", \""compat\"", \""default\"", \""futures-channel\"", \""futures-io\"", \""futures-macro\"", \""futures-sink\"", \""futures_01\"", \""io\"", \""io-compat\"", \""libc\"", \""memchr\"", \""portable-atomic\"", \""sink\"", \""slab\"", \""spin\"", \""std\"", \""tokio-io\"", \""unstable\"", \""write-all-vectored\""]"", ""features"": ""[\""alloc\"", \""async-await\"", \""async-await-macro\"", \""default\"", \""futures-io\"", \""futures-macro\"", \""futures-sink\"", \""io\"", \""memchr\"", \""sink\"", \""slab\"", \""std\""]"", ""name"": ""futures_util"", ""profile"": 8157235479358073570, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""async-await\"", \""bilock\"", \""cfg-target-has-atomic\"", \""compat\"", \""default\"", \""executor\"", \""futures-executor\"", \""io-compat\"", \""spin\"", \""std\"", \""thread-pool\"", \""unstable\"", \""write-all-vectored\""]"", ""features"": ""[\""alloc\""]"", ""name"": ""futures"", ""profile"": 8157235479358073570, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""atomic-polyfill\"", \""critical-section\"", \""default\"", \""parking_lot\"", \""portable-atomic\"", \""race\"", \""std\"", \""unstable\""]"", ""features"": ""[\""alloc\"", \""default\"", \""race\"", \""std\""]"", ""name"": ""once_cell"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""aws-lc-rs\"", \""aws-lc-rs-fips\"", \""aws-lc-rs-unstable\"", \""default\"", \""ring\"", \""std\""]"", ""features"": ""[\""alloc\"", \""ring\"", \""std\""]"", ""name"": ""webpki"", ""profile"": 13987763031497049980, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""baked\"", \""deserialize_bincode_1\"", \""deserialize_json\"", \""deserialize_postcard_1\"", \""export\"", \""logging\"", \""serde\"", \""std\"", \""sync\"", \""zerotrie\""]"", ""features"": ""[\""baked\""]"", ""name"": ""icu_provider"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""bincode_derive\"", \""default\"", \""derive\"", \""serde\"", \""std\""]"", ""features"": ""[\""bincode_derive\"", \""derive\""]"", ""name"": ""bincode"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""blobby\"", \""block-api\"", \""default\"", \""dev\"", \""getrandom\"", \""mac\"", \""oid\"", \""rand_core\"", \""zeroize\""]"", ""features"": ""[\""alloc\"", \""block-api\"", \""default\"", \""oid\""]"", ""name"": ""digest"", ""profile"": 2606296542320589278, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""blobby\"", \""block-buffer\"", \""const-oid\"", \""core-api\"", \""default\"", \""dev\"", \""mac\"", \""oid\"", \""rand_core\"", \""std\"", \""subtle\""]"", ""features"": ""[\""block-buffer\"", \""core-api\"", \""default\""]"", ""name"": ""digest"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""block-boundary\"", \""core\"", \""default\"", \""rustc-dep-of-std\"", \""serde\"", \""simd\"", \""simd-adler32\"", \""std\"", \""with-alloc\""]"", ""features"": ""[\""default\"", \""simd\"", \""simd-adler32\"", \""std\"", \""with-alloc\""]"", ""name"": ""miniz_oxide"", ""profile"": 13333777962520875826, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""bytes\"", \""defmt\"", \""embedded-io-v0.7\"", \""mpmc_large\"", \""nightly\"", \""portable-atomic\"", \""portable-atomic-critical-section\"", \""portable-atomic-unsafe-assume-single-core\"", \""serde\"", \""ufmt\"", \""zeroize\""]"", ""features"": ""[]"", ""name"": ""heapless"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""cfg-target-has-atomic\"", \""default\"", \""futures-sink\"", \""sink\"", \""std\"", \""unstable\""]"", ""features"": ""[\""alloc\"", \""default\"", \""futures-sink\"", \""sink\"", \""std\""]"", ""name"": ""futures_channel"", ""profile"": 8157235479358073570, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""cfg-target-has-atomic\"", \""default\"", \""portable-atomic\"", \""std\"", \""unstable\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""futures_core"", ""profile"": 8157235479358073570, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""cfg-target-has-atomic\"", \""default\"", \""std\"", \""unstable\""]"", ""features"": ""[\""alloc\"", \""std\""]"", ""name"": ""futures_task"", ""profile"": 8157235479358073570, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""chacha\"", \""default\"", \""log\"", \""serde\"", \""simd_support\"", \""std\"", \""std_rng\"", \""sys_rng\"", \""thread_rng\"", \""unbiased\""]"", ""features"": ""[\""alloc\"", \""std\"", \""std_rng\"", \""sys_rng\"", \""thread_rng\""]"", ""name"": ""rand"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""compiled_data\"", \""datagen\"", \""default\"", \""harfbuzz_traits\"", \""serde\"", \""unicode_bidi\""]"", ""features"": ""[\""compiled_data\""]"", ""name"": ""icu_properties"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""compiled_data\"", \""default\"", \""std\""]"", ""features"": ""[\""alloc\"", \""compiled_data\"", \""std\""]"", ""name"": ""idna"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""core\"", \""default\"", \""libc\"", \""logging\"", \""rustc-dep-of-std\"", \""std\"", \""use_std\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""memchr"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""databake\"", \""default\"", \""dense\"", \""litemap\"", \""serde\"", \""yoke\"", \""zerofrom\"", \""zerovec\""]"", ""features"": ""[\""yoke\"", \""zerofrom\""]"", ""name"": ""zerotrie"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""databake\"", \""default\"", \""serde\"", \""std\"", \""zerovec\""]"", ""features"": ""[\""zerovec\""]"", ""name"": ""tinystr"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""databake\"", \""default\"", \""serde\"", \""testing\"", \""yoke\""]"", ""features"": ""[]"", ""name"": ""litemap"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""databake\"", \""default\"", \""serde\"", \""writeable\"", \""zerovec\""]"", ""features"": ""[\""zerovec\""]"", ""name"": ""potential_utf"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""databake\"", \""derive\"", \""hashmap\"", \""schemars\"", \""serde\"", \""std\"", \""yoke\""]"", ""features"": ""[\""derive\"", \""yoke\""]"", ""name"": ""zerovec"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""databake\"", \""serde\"", \""zerovec\""]"", ""features"": ""[\""zerovec\""]"", ""name"": ""icu_locale_core"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""databake\"", \""serde\""]"", ""features"": ""[]"", ""name"": ""icu_collections"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""debug\"", \""default\"", \""simd\"", \""std\"", \""unstable-doc\"", \""unstable-recover\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""winnow"", ""profile"": 16551665274674009540, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""defmt\"", \""js\"", \""logging\"", \""perf-inline\"", \""serde\"", \""static\"", \""static-tz\"", \""std\"", \""tz-fat\"", \""tz-system\"", \""tzdb-bundle-always\"", \""tzdb-bundle-platform\"", \""tzdb-concatenated\"", \""tzdb-zoneinfo\""]"", ""features"": ""[\""alloc\"", \""std\""]"", ""name"": ""jiff"", ""profile"": 7947306171983741744, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""defmt\"", \""logging\"", \""std\"", \""tz-fat\""]"", ""features"": ""[\""alloc\"", \""std\""]"", ""name"": ""jiff_core"", ""profile"": 7947306171983741744, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""derive\"", \""rc\"", \""serde_derive\"", \""std\"", \""unstable\""]"", ""features"": ""[\""default\"", \""derive\"", \""rc\"", \""serde_derive\"", \""std\""]"", ""name"": ""serde"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""derive\"", \""serde\"", \""zerofrom\""]"", ""features"": ""[\""derive\"", \""zerofrom\""]"", ""name"": ""yoke"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""derive\""]"", ""features"": ""[\""derive\""]"", ""name"": ""zerofrom"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""dev_urandom_fallback\"", \""less-safe-getrandom-custom-or-rdrand\"", \""less-safe-getrandom-espidf\"", \""slow_tests\"", \""std\"", \""test_logging\"", \""unstable-testing-arm-no-hw\"", \""unstable-testing-arm-no-neon\"", \""wasm32_unknown_unknown_js\""]"", ""features"": ""[\""alloc\"", \""default\"", \""dev_urandom_fallback\"", \""std\""]"", ""name"": ""ring"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""dfa\"", \""dfa-build\"", \""dfa-onepass\"", \""dfa-search\"", \""hybrid\"", \""internal-instrument\"", \""internal-instrument-pikevm\"", \""logging\"", \""meta\"", \""nfa\"", \""nfa-backtrack\"", \""nfa-pikevm\"", \""nfa-thompson\"", \""perf\"", \""perf-inline\"", \""perf-literal\"", \""perf-literal-multisubstring\"", \""perf-literal-substring\"", \""std\"", \""syntax\"", \""unicode\"", \""unicode-age\"", \""unicode-bool\"", \""unicode-case\"", \""unicode-gencat\"", \""unicode-perl\"", \""unicode-script\"", \""unicode-segment\"", \""unicode-word-boundary\""]"", ""features"": ""[\""alloc\"", \""dfa-onepass\"", \""hybrid\"", \""meta\"", \""nfa\"", \""nfa-backtrack\"", \""nfa-pikevm\"", \""nfa-thompson\"", \""perf\"", \""perf-inline\"", \""perf-literal\"", \""perf-literal-multisubstring\"", \""perf-literal-substring\"", \""std\"", \""syntax\"", \""unicode\"", \""unicode-age\"", \""unicode-bool\"", \""unicode-case\"", \""unicode-gencat\"", \""unicode-perl\"", \""unicode-script\"", \""unicode-segment\"", \""unicode-word-boundary\""]"", ""name"": ""regex_automata"", ""profile"": 2547513045437502204, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""either\""]"", ""features"": ""[]"", ""name"": ""writeable"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""fresh-rust\"", \""nightly\"", \""serde\"", \""std\""]"", ""features"": ""[\""alloc\""]"", ""name"": ""allocator_api2"", ""profile"": 3361788016818567686, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""getrandom\"", \""js\"", \""std\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""fastrand"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""log\"", \""nightly\"", \""os_rng\"", \""serde\"", \""simd_support\"", \""small_rng\"", \""std\"", \""std_rng\"", \""thread_rng\"", \""unbiased\""]"", ""features"": ""[\""alloc\"", \""default\"", \""os_rng\"", \""small_rng\"", \""std\"", \""std_rng\"", \""thread_rng\""]"", ""name"": ""rand"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""map\"", \""std\""]"", ""features"": ""[\""alloc\"", \""map\"", \""std\""]"", ""name"": ""managed"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""memchr\"", \""serde\"", \""std\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""iri_string"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""oid\"", \""zeroize\""]"", ""features"": ""[\""alloc\"", \""default\"", \""oid\""]"", ""name"": ""sha2"", ""profile"": 8048078827458334496, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""rc\"", \""result\"", \""std\"", \""unstable\""]"", ""features"": ""[\""alloc\"", \""default\"", \""rc\"", \""result\"", \""std\""]"", ""name"": ""serde_core"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""serde\"", \""std\"", \""unicode\""]"", ""features"": ""[\""alloc\"", \""std\""]"", ""name"": ""bstr"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""std\"", \""web\"", \""web-time\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""rustls_pki_types"", ""profile"": 8134619797147437084, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""base64"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""form_urlencoded"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""futures_sink"", ""profile"": 8157235479358073570, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""percent_encoding"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""name"": ""toml_write"", ""profile"": 11768715044625735406, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""default\"", \""std\""]"", ""features"": ""[]"", ""name"": ""stable_deref_trait"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""alloc\"", \""kv\"", \""kv_serde\"", \""kv_std\"", \""kv_sval\"", \""kv_unstable\"", \""kv_unstable_serde\"", \""kv_unstable_std\"", \""kv_unstable_sval\"", \""max_level_debug\"", \""max_level_error\"", \""max_level_info\"", \""max_level_off\"", \""max_level_trace\"", \""max_level_warn\"", \""release_max_level_debug\"", \""release_max_level_error\"", \""release_max_level_info\"", \""release_max_level_off\"", \""release_max_level_trace\"", \""release_max_level_warn\"", \""serde\"", \""serde_core\"", \""std\"", \""sval\"", \""sval_ref\"", \""value-bag\""]"", ""features"": ""[\""alloc\"", \""std\""]"", ""name"": ""log"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""amd-sev\"", \""aws-nitro\"", \""bitfield\"", \""blk\"", \""default\"", \""efi\"", \""gpu\"", \""init-blob\"", \""input\"", \""iocuddle\"", \""kbs-types\"", \""krun_display\"", \""krun_input\"", \""net\"", \""pw\"", \""rutabaga_gfx\"", \""serde\"", \""snd\"", \""tdx\"", \""tee\"", \""test_utils\"", \""thiserror\"", \""virgl_resource_map2\"", \""zerocopy\""]"", ""features"": ""[\""default\"", \""init-blob\"", \""net\""]"", ""name"": ""pvisor_vm"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""ansi\"", \""bitflags\"", \""cursor-icon\"", \""default\"", \""log\"", \""serde\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""vte"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""any_c_zlib\"", \""any_impl\"", \""any_zlib\"", \""cloudflare-zlib-sys\"", \""cloudflare_zlib\"", \""default\"", \""document-features\"", \""libz-ng-sys\"", \""libz-sys\"", \""miniz-sys\"", \""miniz_oxide\"", \""rust_backend\"", \""zlib\"", \""zlib-default\"", \""zlib-ng\"", \""zlib-ng-compat\"", \""zlib-rs\""]"", ""features"": ""[\""any_impl\"", \""default\"", \""miniz_oxide\"", \""rust_backend\""]"", ""name"": ""flate2"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""async-tokio\"", \""default\"", \""document-features\"", \""encoding\"", \""encoding_rs\"", \""escape-html\"", \""overlapped-lists\"", \""serde\"", \""serde-types\"", \""serialize\"", \""tokio\""]"", ""features"": ""[\""default\"", \""overlapped-lists\"", \""serde\"", \""serialize\""]"", ""name"": ""quick_xml"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""atomic\"", \""borsh\"", \""bytemuck\"", \""default\"", \""fast-rng\"", \""js\"", \""macro-diagnostics\"", \""md5\"", \""rng\"", \""rng-getrandom\"", \""rng-rand\"", \""serde\"", \""sha1\"", \""slog\"", \""std\"", \""uuid-rng-internal-lib\"", \""v1\"", \""v3\"", \""v4\"", \""v5\"", \""v6\"", \""v7\"", \""v8\"", \""zerocopy\""]"", ""features"": ""[\""default\"", \""rng\"", \""std\"", \""v4\""]"", ""name"": ""uuid"", ""profile"": 5623565614784616014, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""bincode\"", \""const_generics\"", \""const_new\"", \""debugger_visualizer\"", \""drain_filter\"", \""drain_keep_rest\"", \""impl_bincode\"", \""malloc_size_of\"", \""may_dangle\"", \""serde\"", \""specialization\"", \""union\"", \""unty\"", \""write\""]"", ""features"": ""[\""const_generics\"", \""const_new\""]"", ""name"": ""smallvec"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""borsh\"", \""default\"", \""quickcheck\"", \""rayon\"", \""serde\"", \""std\"", \""sval\"", \""test_debug\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""indexmap"", ""profile"": 10937888587037481829, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""bytemuck\"", \""example_generated\"", \""serde\"", \""serde_core\"", \""std\""]"", ""features"": ""[\""std\""]"", ""name"": ""bitflags"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""db\""]"", ""features"": ""[]"", ""name"": ""const_oid"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""default\"", \""log\"", \""serde\"", \""serde1\"", \""simd-accel\""]"", ""features"": ""[\""default\"", \""log\""]"", ""name"": ""globset"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""default\"", \""std\"", \""unicode\"", \""unicode-age\"", \""unicode-bool\"", \""unicode-case\"", \""unicode-gencat\"", \""unicode-perl\"", \""unicode-script\"", \""unicode-segment\""]"", ""features"": ""[\""default\"", \""std\"", \""unicode\"", \""unicode-age\"", \""unicode-bool\"", \""unicode-case\"", \""unicode-gencat\"", \""unicode-perl\"", \""unicode-script\"", \""unicode-segment\""]"", ""name"": ""regex_syntax"", ""profile"": 2547513045437502204, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arbitrary\"", \""inline\"", \""raw-api\"", \""rayon\"", \""serde\"", \""typesize\""]"", ""features"": ""[]"", ""name"": ""dashmap"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arc-swap\"", \""backend-atomic\"", \""backend-bitmap\"", \""backend-mmap\"", \""bitflags\"", \""default\"", \""rawfd\"", \""vmm-sys-util\"", \""xen\""]"", ""features"": ""[\""backend-mmap\"", \""default\"", \""rawfd\""]"", ""name"": ""vm_memory"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arc_lock\"", \""atomic_usize\"", \""default\"", \""nightly\"", \""owning_ref\"", \""serde\""]"", ""features"": ""[\""atomic_usize\"", \""default\""]"", ""name"": ""lock_api"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arc_lock\"", \""deadlock_detection\"", \""default\"", \""hardware-lock-elision\"", \""nightly\"", \""owning_ref\"", \""send_guard\"", \""serde\""]"", ""features"": ""[\""default\""]"", ""name"": ""parking_lot"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arrays\"", \""bindgen\"", \""debug\"", \""default\"", \""doc-cfg\"", \""experimental\"", \""fat-lto\"", \""legacy\"", \""no_asm\"", \""pkg-config\"", \""seekable\"", \""std\"", \""thin\"", \""thin-lto\"", \""zdict_builder\"", \""zstdmt\""]"", ""features"": ""[\""arrays\"", \""legacy\"", \""std\"", \""zdict_builder\""]"", ""name"": ""zstd_safe"", ""profile"": 10592522859712577474, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""arrays\"", \""bindgen\"", \""debug\"", \""default\"", \""doc-cfg\"", \""experimental\"", \""fat-lto\"", \""legacy\"", \""no_asm\"", \""pkg-config\"", \""thin\"", \""thin-lto\"", \""wasm\"", \""zdict_builder\"", \""zstdmt\""]"", ""features"": ""[\""arrays\"", \""default\"", \""legacy\"", \""zdict_builder\""]"", ""name"": ""zstd"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""asm\"", \""default\"", \""force-soft\"", \""loongarch64_asm\"", \""md5-asm\"", \""oid\"", \""std\""]"", ""features"": ""[]"", ""name"": ""md5"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""async-await\"", \""attributes\"", \""default\"", \""log\"", \""log-always\"", \""max_level_debug\"", \""max_level_error\"", \""max_level_info\"", \""max_level_off\"", \""max_level_trace\"", \""max_level_warn\"", \""release_max_level_debug\"", \""release_max_level_error\"", \""release_max_level_info\"", \""release_max_level_off\"", \""release_max_level_trace\"", \""release_max_level_warn\"", \""std\"", \""tracing-attributes\"", \""valuable\""]"", ""features"": ""[\""attributes\"", \""default\"", \""std\"", \""tracing-attributes\""]"", ""name"": ""tracing"", ""profile"": 1647937577980832904, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""async-await\""]"", ""features"": ""[]"", ""name"": ""tracing_attributes"", ""profile"": 6207960371462447473, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""async\"", \""default\"", \""sync\"", \""sync-wrappers\"", \""vm-memory\""]"", ""features"": ""[\""async\"", \""default\"", \""sync-wrappers\""]"", ""name"": ""imago"", ""profile"": 15916455528278280289, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""auto-color\"", \""color\"", \""default\"", \""humantime\"", \""kv\"", \""regex\"", \""unstable-kv\""]"", ""features"": ""[\""auto-color\"", \""color\"", \""default\"", \""humantime\"", \""regex\""]"", ""name"": ""env_logger"", ""profile"": 10632806285984276979, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""auto\"", \""default\"", \""test\"", \""wincon\""]"", ""features"": ""[\""auto\"", \""default\"", \""wincon\""]"", ""name"": ""anstream"", ""profile"": 4227688284201523262, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""auto\"", \""default\"", \""test\"", \""wincon\""]"", ""features"": ""[\""auto\"", \""wincon\""]"", ""name"": ""anstream"", ""profile"": 10632806285984276979, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""auxvec\"", \""bootparam\"", \""btrfs\"", \""core\"", \""default\"", \""elf\"", \""elf_uapi\"", \""errno\"", \""general\"", \""if_arp\"", \""if_ether\"", \""if_packet\"", \""if_tun\"", \""image\"", \""io_uring\"", \""ioctl\"", \""landlock\"", \""loop_device\"", \""mempolicy\"", \""net\"", \""netlink\"", \""no_std\"", \""prctl\"", \""ptrace\"", \""rustc-dep-of-std\"", \""std\"", \""system\"", \""vm_sockets\"", \""xdp\""]"", ""features"": ""[\""auxvec\"", \""default\"", \""elf\"", \""errno\"", \""general\"", \""ioctl\"", \""no_std\"", \""std\""]"", ""name"": ""linux_raw_sys"", ""profile"": 14036798396267011297, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""aws-lc-rs\"", \""aws_lc_rs\"", \""brotli\"", \""custom-provider\"", \""default\"", \""fips\"", \""hashbrown\"", \""log\"", \""logging\"", \""prefer-post-quantum\"", \""read_buf\"", \""ring\"", \""rustversion\"", \""std\"", \""tls12\"", \""zlib\""]"", ""features"": ""[\""ring\"", \""std\"", \""tls12\""]"", ""name"": ""rustls"", ""profile"": 16187626775398413875, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""aws-lc-rs\"", \""aws_lc_rs\"", \""brotli\"", \""default\"", \""early-data\"", \""fips\"", \""logging\"", \""ring\"", \""tls12\"", \""zlib\""]"", ""features"": ""[\""ring\"", \""tls12\""]"", ""name"": ""tokio_rustls"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""aws-lc-rs\"", \""default\"", \""fips\"", \""http1\"", \""http2\"", \""log\"", \""logging\"", \""native-tokio\"", \""ring\"", \""rustls-native-certs\"", \""rustls-platform-verifier\"", \""tls12\"", \""webpki-roots\"", \""webpki-tokio\""]"", ""features"": ""[\""http1\"", \""http2\"", \""native-tokio\"", \""ring\"", \""rustls-native-certs\"", \""tls12\"", \""webpki-roots\"", \""webpki-tokio\""]"", ""name"": ""hyper_rustls"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""aws\"", \""azure\"", \""base64\"", \""cloud\"", \""default\"", \""form_urlencoded\"", \""fs\"", \""gcp\"", \""http\"", \""http-body-util\"", \""httparse\"", \""hyper\"", \""integration\"", \""md-5\"", \""quick-xml\"", \""rand\"", \""reqwest\"", \""ring\"", \""rustls-pki-types\"", \""serde\"", \""serde_json\"", \""serde_urlencoded\"", \""tls-webpki-roots\"", \""tokio\"", \""walkdir\""]"", ""features"": ""[\""aws\"", \""base64\"", \""cloud\"", \""form_urlencoded\"", \""http-body-util\"", \""hyper\"", \""md-5\"", \""quick-xml\"", \""rand\"", \""reqwest\"", \""ring\"", \""serde\"", \""serde_json\"", \""serde_urlencoded\"", \""tls-webpki-roots\"", \""tokio\""]"", ""name"": ""object_store"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""backtrace\"", \""deadlock_detection\"", \""nightly\"", \""petgraph\""]"", ""features"": ""[]"", ""name"": ""parking_lot_core"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""backtrace\"", \""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""anyhow"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""balance\"", \""buffer\"", \""discover\"", \""filter\"", \""full\"", \""futures-core\"", \""futures-util\"", \""hdrhistogram\"", \""hedge\"", \""indexmap\"", \""limit\"", \""load\"", \""load-shed\"", \""log\"", \""make\"", \""pin-project-lite\"", \""ready-cache\"", \""reconnect\"", \""retry\"", \""slab\"", \""spawn-ready\"", \""steer\"", \""sync_wrapper\"", \""timeout\"", \""tokio\"", \""tokio-stream\"", \""tokio-util\"", \""tracing\"", \""util\""]"", ""features"": ""[\""futures-core\"", \""futures-util\"", \""make\"", \""pin-project-lite\"", \""retry\"", \""sync_wrapper\"", \""timeout\"", \""tokio\"", \""util\""]"", ""name"": ""tower"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""bindgen\"", \""debug\"", \""default\"", \""experimental\"", \""fat-lto\"", \""legacy\"", \""no_asm\"", \""no_wasm_shim\"", \""non-cargo\"", \""pkg-config\"", \""seekable\"", \""std\"", \""thin\"", \""thin-lto\"", \""zdict_builder\"", \""zstdmt\""]"", ""features"": ""[\""legacy\"", \""std\"", \""zdict_builder\""]"", ""name"": ""zstd_sys"", ""profile"": 10592522859712577474, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""borsh\"", \""default\"", \""serde\"", \""std\"", \""zeroize\""]"", ""features"": ""[]"", ""name"": ""arrayvec"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""bytes\"", \""default\"", \""fs\"", \""full\"", \""io-std\"", \""io-uring\"", \""io-util\"", \""libc\"", \""macros\"", \""mio\"", \""net\"", \""parking_lot\"", \""process\"", \""rt\"", \""rt-multi-thread\"", \""signal\"", \""signal-hook-registry\"", \""socket2\"", \""sync\"", \""taskdump\"", \""test-util\"", \""time\"", \""tokio-macros\"", \""tracing\"", \""windows-sys\""]"", ""features"": ""[\""bytes\"", \""default\"", \""fs\"", \""io-util\"", \""libc\"", \""macros\"", \""mio\"", \""net\"", \""process\"", \""rt\"", \""rt-multi-thread\"", \""signal\"", \""signal-hook-registry\"", \""socket2\"", \""sync\"", \""time\"", \""tokio-macros\""]"", ""name"": ""tokio"", ""profile"": 10043413591317300293, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""bzimage\"", \""default\"", \""elf\"", \""pe\""]"", ""features"": ""[\""bzimage\"", \""default\"", \""elf\"", \""pe\""]"", ""name"": ""linux_loader"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""capi\"", \""client\"", \""default\"", \""ffi\"", \""full\"", \""http1\"", \""http2\"", \""nightly\"", \""server\"", \""tracing\""]"", ""features"": ""[\""client\"", \""default\"", \""http1\"", \""http2\"", \""server\""]"", ""name"": ""hyper"", ""profile"": 5907227947356068632, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""cargo\"", \""color\"", \""debug\"", \""default\"", \""deprecated\"", \""derive\"", \""env\"", \""error-context\"", \""help\"", \""std\"", \""string\"", \""suggestions\"", \""unicode\"", \""unstable-derive-ui-tests\"", \""unstable-doc\"", \""unstable-ext\"", \""unstable-markdown\"", \""unstable-styles\"", \""unstable-v5\"", \""usage\"", \""wrap_help\""]"", ""features"": ""[\""color\"", \""default\"", \""derive\"", \""env\"", \""error-context\"", \""help\"", \""std\"", \""string\"", \""suggestions\"", \""usage\""]"", ""name"": ""clap"", ""profile"": 4628411354643344498, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""cargo\"", \""color\"", \""debug\"", \""default\"", \""deprecated\"", \""env\"", \""error-context\"", \""help\"", \""std\"", \""string\"", \""suggestions\"", \""unicode\"", \""unstable-doc\"", \""unstable-ext\"", \""unstable-styles\"", \""unstable-v5\"", \""usage\"", \""wrap_help\""]"", ""features"": ""[\""color\"", \""env\"", \""error-context\"", \""help\"", \""std\"", \""string\"", \""suggestions\"", \""usage\""]"", ""name"": ""clap_builder"", ""profile"": 4628411354643344498, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""channel\"", \""default\"", \""full\""]"", ""features"": ""[\""default\""]"", ""name"": ""http_body_util"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""cipher\"", \""default\"", \""legacy\"", \""rng\"", \""xchacha\"", \""zeroize\""]"", ""features"": ""[\""rng\""]"", ""name"": ""chacha20"", ""profile"": 4285365006624603084, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""cjk\"", \""core\"", \""default\"", \""no_std\"", \""rustc-dep-of-std\"", \""std\""]"", ""features"": ""[\""cjk\"", \""default\""]"", ""name"": ""unicode_width"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""cli\"", \""default\""]"", ""features"": ""[]"", ""name"": ""pvisor_overlayfs"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""clone-impls\"", \""default\"", \""derive\"", \""extra-traits\"", \""fold\"", \""full\"", \""parsing\"", \""printing\"", \""proc-macro\"", \""test\"", \""visit\"", \""visit-mut\""]"", ""features"": ""[\""clone-impls\"", \""default\"", \""derive\"", \""extra-traits\"", \""fold\"", \""full\"", \""parsing\"", \""printing\"", \""proc-macro\"", \""visit\"", \""visit-mut\""]"", ""name"": ""syn"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""compiled_data\"", \""datagen\"", \""default\"", \""harfbuzz_traits\"", \""icu_properties\"", \""serde\"", \""utf16_iter\"", \""utf8_iter\"", \""write16\""]"", ""features"": ""[\""compiled_data\""]"", ""name"": ""icu_normalizer"", ""profile"": 12904623307388672164, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""compiled_data\""]"", ""features"": ""[\""compiled_data\""]"", ""name"": ""idna_adapter"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""compiler_builtins\"", \""core\"", \""custom\"", \""js\"", \""js-sys\"", \""linux_disable_fallback\"", \""rdrand\"", \""rustc-dep-of-std\"", \""std\"", \""test-in-browser\"", \""wasm-bindgen\""]"", ""features"": ""[]"", ""name"": ""getrandom"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""compiler_builtins\"", \""core\"", \""default\"", \""example_generated\"", \""rustc-dep-of-std\""]"", ""features"": ""[\""default\""]"", ""name"": ""bitflags"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""const-generics\"", \""core_hint_black_box\"", \""default\"", \""i128\"", \""nightly\"", \""std\""]"", ""features"": ""[]"", ""name"": ""subtle"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""const-generics\"", \""default\"", \""nightly\"", \""std\""]"", ""features"": ""[]"", ""name"": ""simd_adler32"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""const-generics\"", \""i128\"", \""scale-info\"", \""scale_info\"", \""strict\""]"", ""features"": ""[\""const-generics\""]"", ""name"": ""typenum"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""core\"", \""default\"", \""rustc-dep-of-std\"", \""std\""]"", ""features"": ""[]"", ""name"": ""adler2"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""core\"", \""default\"", \""utf8\""]"", ""features"": ""[\""default\"", \""utf8\""]"", ""name"": ""anstyle_parse"", ""profile"": 10632806285984276979, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""core\"", \""default\"", \""utf8\""]"", ""features"": ""[\""default\"", \""utf8\""]"", ""name"": ""anstyle_parse"", ""profile"": 4227688284201523262, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""core\"", \""rustc-dep-of-std\""]"", ""features"": ""[]"", ""name"": ""cfg_if"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""core\"", \""rustc-dep-of-std\""]"", ""features"": ""[]"", ""name"": ""cfg_if"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""count_instructions_test\"", \""default\"", \""std\""]"", ""features"": ""[\""std\""]"", ""name"": ""constant_time_eq"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""debug\"", \""default\"", \""deprecated\"", \""raw-deprecated\"", \""unstable-markdown\"", \""unstable-v5\""]"", ""features"": ""[\""default\""]"", ""name"": ""clap_derive"", ""profile"": 5346920246396443047, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""debugger_visualizer\"", \""default\"", \""expose_internals\"", \""serde\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""url"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""deserialize_in_place\""]"", ""features"": ""[\""default\""]"", ""name"": ""serde_derive"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""digest\"", \""mmap\"", \""neon\"", \""no_avx2\"", \""no_avx512\"", \""no_neon\"", \""no_sse2\"", \""no_sse41\"", \""prefer_intrinsics\"", \""pure\"", \""rayon\"", \""serde\"", \""std\"", \""traits-preview\"", \""wasm32_simd\"", \""zeroize\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""blake3"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""display\"", \""indexmap\"", \""parse\"", \""preserve_order\"", \""unbounded\""]"", ""features"": ""[\""default\"", \""display\"", \""parse\""]"", ""name"": ""toml"", ""profile"": 11768715044625735406, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""display\"", \""parse\"", \""perf\"", \""serde\"", \""unbounded\"", \""unstable-debug\""]"", ""features"": ""[\""default\"", \""display\"", \""parse\"", \""serde\""]"", ""name"": ""toml_edit"", ""profile"": 11768715044625735406, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""echo-server\""]"", ""features"": ""[]"", ""name"": ""pvisor_gateway"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""extra-platforms\"", \""serde\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""bytes"", ""profile"": 11003502350373712158, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""fs\"", \""full\"", \""io-util\"", \""net\"", \""signal\"", \""sync\"", \""time\"", \""tokio-util\""]"", ""features"": ""[\""default\"", \""time\""]"", ""name"": ""tokio_stream"", ""profile"": 10043413591317300293, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""gateway\""]"", ""features"": ""[\""default\"", \""gateway\""]"", ""name"": ""pvisor_cli"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""gateway\""]"", ""features"": ""[\""gateway\""]"", ""name"": ""pvisor"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""getrandom\"", \""nightly\""]"", ""features"": ""[\""default\"", \""getrandom\""]"", ""name"": ""tempfile"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""hashbrown\"", \""nightly\""]"", ""features"": ""[\""default\"", \""hashbrown\""]"", ""name"": ""lru"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""heapless\"", \""json\"", \""schemars\"", \""schemars08\"", \""schemars1\"", \""ser_as_str\"", \""serde\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""ipnet"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""i128\"", \""libm\"", \""std\""]"", ""features"": ""[]"", ""name"": ""num_traits"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""i128\"", \""std\""]"", ""features"": ""[]"", ""name"": ""byteorder"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""is_sync\""]"", ""features"": ""[\""default\""]"", ""name"": ""maybe_async"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""libbz2-rs-sys\"", \""static\""]"", ""features"": ""[\""default\""]"", ""name"": ""bzip2"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""log\"", \""net\"", \""os-ext\"", \""os-poll\""]"", ""features"": ""[\""net\"", \""os-ext\"", \""os-poll\""]"", ""name"": ""mio"", ""profile"": 16626080924472169626, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""logging\"", \""pattern\"", \""perf\"", \""perf-backtrack\"", \""perf-cache\"", \""perf-dfa\"", \""perf-dfa-full\"", \""perf-inline\"", \""perf-literal\"", \""perf-onepass\"", \""std\"", \""unicode\"", \""unicode-age\"", \""unicode-bool\"", \""unicode-case\"", \""unicode-gencat\"", \""unicode-perl\"", \""unicode-script\"", \""unicode-segment\"", \""unstable\"", \""use_std\""]"", ""features"": ""[\""default\"", \""perf\"", \""perf-backtrack\"", \""perf-cache\"", \""perf-dfa\"", \""perf-inline\"", \""perf-literal\"", \""perf-onepass\"", \""std\"", \""unicode\"", \""unicode-age\"", \""unicode-bool\"", \""unicode-case\"", \""unicode-gencat\"", \""unicode-perl\"", \""unicode-script\"", \""unicode-segment\""]"", ""name"": ""regex"", ""profile"": 2547513045437502204, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""logging\"", \""perf-literal\"", \""std\""]"", ""features"": ""[\""default\"", \""perf-literal\"", \""std\""]"", ""name"": ""aho_corasick"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""loom\"", \""nightly\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""crossbeam_utils"", ""profile"": 10433883304768107316, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""nightly\"", \""proc-macro\"", \""span-locations\""]"", ""features"": ""[\""default\"", \""proc-macro\""]"", ""name"": ""proc_macro2"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""nightly\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""crc32fast"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""nightly\""]"", ""features"": ""[\""default\""]"", ""name"": ""utf8parse"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""no_simd\"", \""simd\"", \""std\""]"", ""features"": ""[\""simd\"", \""std\""]"", ""name"": ""ppv_lite86"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""once_cell\"", \""std\"", \""valuable\""]"", ""features"": ""[\""once_cell\"", \""std\""]"", ""name"": ""tracing_core"", ""profile"": 1647937577980832904, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""os_rng\"", \""serde\"", \""std\""]"", ""features"": ""[\""std\""]"", ""name"": ""rand_chacha"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""proc-macro\""]"", ""features"": ""[\""default\"", \""proc-macro\""]"", ""name"": ""quote"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""proc-macro\""]"", ""features"": ""[\""default\"", \""proc-macro\""]"", ""name"": ""synstructure"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""regex\"", \""std\""]"", ""features"": ""[\""regex\"", \""std\""]"", ""name"": ""env_filter"", ""profile"": 10632806285984276979, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""serde\"", \""std\"", \""use_std\""]"", ""features"": ""[\""std\"", \""use_std\""]"", ""name"": ""either"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""serde\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""semver"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""serde\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""slab"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""set\""]"", ""features"": ""[\""default\""]"", ""name"": ""hostname"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\"", \""unstable\""]"", ""features"": ""[\""std\""]"", ""name"": ""futures_io"", ""profile"": 8157235479358073570, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""anstyle"", ""profile"": 10632806285984276979, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""crossbeam_channel"", ""profile"": 10433883304768107316, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""errno"", ""profile"": 14231071289633644017, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""fnv"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""http"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""httparse"", ""profile"": 1675385711784277470, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""shlex"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[\""default\"", \""std\""]"", ""name"": ""thiserror"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[]"", ""name"": ""displaydoc"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""std\""]"", ""features"": ""[]"", ""name"": ""foldhash"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""unstable_const\"", \""unstable_offset_of\""]"", ""features"": ""[\""default\""]"", ""name"": ""memoffset"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""unsupported\""]"", ""features"": ""[\""default\"", \""unsupported\""]"", ""name"": ""xattr"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""use_alloc\"", \""use_std\""]"", ""features"": ""[\""default\"", \""use_alloc\"", \""use_std\""]"", ""name"": ""itertools"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""use_std\""]"", ""features"": ""[]"", ""name"": ""scopeguard"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\"", \""xattr\""]"", ""features"": ""[\""default\"", \""xattr\""]"", ""name"": ""tar"", ""profile"": 4339154717131654023, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""default\""]"", ""features"": ""[\""default\""]"", ""name"": ""is_terminal_polyfill"", ""profile"": 5812794364236305338, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""fallback\""]"", ""features"": ""[\""fallback\""]"", ""name"": ""iana_time_zone"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""fam-wrappers\"", \""serde\"", \""vmm-sys-util\""]"", ""features"": ""[\""fam-wrappers\"", \""serde\"", \""vmm-sys-util\""]"", ""name"": ""kvm_bindings"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""futures\"", \""futures-core\""]"", ""features"": ""[\""futures\"", \""futures-core\""]"", ""name"": ""sync_wrapper"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""getrandom\"", \""rand_core\"", \""std\""]"", ""features"": ""[]"", ""name"": ""crypto_common"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""getrandom\"", \""rand_core\"", \""zeroize\""]"", ""features"": ""[]"", ""name"": ""crypto_common"", ""profile"": 2606296542320589278, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""jobserver\"", \""parallel\""]"", ""features"": ""[\""parallel\""]"", ""name"": ""cc"", ""profile"": 12458195255332598410, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""more_lengths\"", \""serde\"", \""zeroize\""]"", ""features"": ""[\""more_lengths\""]"", ""name"": ""generic_array"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""mu\""]"", ""features"": ""[]"", ""name"": ""humantime"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""no-panic\"", \""small\""]"", ""features"": ""[]"", ""name"": ""ryu"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""no-panic\""]"", ""features"": ""[]"", ""name"": ""itoa"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""no-panic\""]"", ""features"": ""[]"", ""name"": ""zmij"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""no_std\"", \""spin\""]"", ""features"": ""[]"", ""name"": ""page_size"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""os_rng\"", \""serde\"", \""std\""]"", ""features"": ""[\""os_rng\"", \""std\""]"", ""name"": ""rand_core"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""portable-atomic\""]"", ""features"": ""[]"", ""name"": ""atomic_waker"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""proc-macro2\""]"", ""features"": ""[]"", ""name"": ""virtue"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""serde\"", \""serde_derive\"", \""with-serde\""]"", ""features"": ""[\""serde\"", \""serde_derive\"", \""with-serde\""]"", ""name"": ""vmm_sys_util"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""serde\"", \""serde_support\""]"", ""features"": ""[]"", ""name"": ""caps"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""serde\""]"", ""features"": ""[\""serde\""]"", ""name"": ""serde_spanned"", ""profile"": 11768715044625735406, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""serde\""]"", ""features"": ""[\""serde\""]"", ""name"": ""toml_datetime"", ""profile"": 11768715044625735406, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""std\"", \""sys_rng\"", \""wasm_js\""]"", ""features"": ""[\""std\"", \""sys_rng\""]"", ""name"": ""getrandom"", ""profile"": 7847730558161019686, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""std\"", \""wasm_js\""]"", ""features"": ""[\""std\""]"", ""name"": ""getrandom"", ""profile"": 3240661740686181567, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""stream\"", \""unstable\""]"", ""features"": ""[]"", ""name"": ""h2"", ""profile"": 13333777962520875826, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[\""zeroize\""]"", ""features"": ""[]"", ""name"": ""block_buffer"", ""profile"": 2548229083023945596, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""anstyle_query"", ""profile"": 8192080853972711267, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""arrayref"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""async_trait"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""autocfg"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""bincode_derive"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""block_buffer"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""cfg_aliases"", ""profile"": 11980061553486606077, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""clap_lex"", ""profile"": 4628411354643344498, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""colorchoice"", ""profile"": 10632806285984276979, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""compression_core"", ""profile"": 4107725171516170900, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""cpufeatures"", ""profile"": 2548229083023945596, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""dirs"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""dirs_sys"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""equivalent"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""filetime"", ""profile"": 4796329815077315148, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""find_msvc_tools"", ""profile"": 12458195255332598410, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""fs2"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""futures_macro"", ""profile"": 1218413102171684145, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""hash32"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""heck"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""http_body"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""httpdate"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""icu_normalizer_data"", ""profile"": 18083946540466509289, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""icu_properties_data"", ""profile"": 18083946540466509289, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""jobserver"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""kvm_ioctls"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""libloading"", ""profile"": 12556093047751754406, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""libloading"", ""profile"": 6679834303860230680, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""mime"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""openssl_probe"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""option_ext"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""pin_project_lite"", ""profile"": 14034241328553317513, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""pkg_config"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""pvisor_core"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""pvisor_guest"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""pvisor_journal"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""pvisor_overlay_core"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""pvisor_overlaynet"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""pvisor_replay"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""rand_core"", ""profile"": 11804347602484681322, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""rustc_version"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""rustls_native_certs"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""serde_path_to_error"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""serde_urlencoded"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""serde_yaml"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""signal_hook_registry"", ""profile"": 8111324945914507537, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""strsim"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""thiserror_impl"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""tokio_macros"", ""profile"": 5029862450655259090, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""tower_layer"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""tower_service"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""try_lock"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""unicode_ident"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""unsafe_libyaml"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""untrusted"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""unty"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""utf8_iter"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""version_check"", ""profile"": 11093885493384348844, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""virtio_bindings"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""vt100"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""want"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""webpki_roots"", ""profile"": 14095327416537791432, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""yoke_derive"", ""profile"": 4981139003136920394, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""zerocopy_derive"", ""profile"": 16810066265625103578, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""zerofrom_derive"", ""profile"": 4981139003136920394, ""rustc"": 1625334936438085654, ""rustflags"": []}, {""compile_kind"": 0, ""config"": 9396254390672932401, ""declared_features"": ""[]"", ""features"": ""[]"", ""name"": ""zerovec_derive"", ""profile"": 4981139003136920394, ""rustc"": 1625334936438085654, ""rustflags"": []}]}, ""environment"": {""CARGO_BUILD_JOBS"": ""2"", ""CARGO_NET_OFFLINE"": ""true""}, ""firmware_sha256"": ""b61f68dac3ef20a88e1ee387733e4baed2f7c02edac940c8882e0b549dac95e4"", ""pvisor_sha256"": ""8840b16cdc0610e381eec66b0f75f1e86efba76feb358c9eae5b3d923854b592"", ""rustc"": ""rustc 1.98.1 (48a229cea 2026-09-01)\nbinary: rustc\ncommit-hash: 48a229ceaefd4985c50990b14116b6d856af0985\ncommit-date: 2026-09-01\nhost: x86_64-unknown-linux-gnu\nrelease: 1.98.1\nLLVM version: 22.1.8\n"", ""source_identity"": {""head"": ""b3e2f03509396b8ebe840a9ccdefa63424bc1afe"", ""manifest_sha256"": ""5311e7c05ca1fc114c60416920c204866e5a6079f509343a91b95f6bf0d37b24"", ""recorded_at"": ""2026-10-11T06:42:21.492933+00:00"", ""scope"": ""complete current tracked/unignored files excluding generated target and raw .data"", ""status"": "" M .github/workflows/ci.yml\n M .github/workflows/docs.yml\n M .github/workflows/pvisor-benchmark.yml\n M README.md\n M benchmark/README.md\n M crates/pvisor-cli/README.md\n M crates/pvisor-cli/src/cli/host_process.rs\n M crates/pvisor-cli/src/cli/host_service.rs\n M crates/pvisor-cli/src/cli/runtime.rs\n M crates/pvisor-overlay-core/src/core.rs\n M crates/pvisor-overlayfs/README.md\n M crates/pvisor-overlayfs/src/api.rs\n M crates/pvisor-overlayfs/src/dispatch.rs\n M crates/pvisor-overlayfs/src/fs.rs\n M crates/pvisor-vm/Cargo.toml\n M crates/pvisor-vm/README.md\n M crates/pvisor-vm/src/api.rs\n M crates/pvisor-vm/src/builder.rs\n M crates/pvisor-vm/src/contract_tests/vm_snapshot_fs.rs\n M crates/pvisor-vm/src/devices/virtio/descriptor_utils.rs\n M crates/pvisor-vm/src/devices/virtio/device.rs\n M crates/pvisor-vm/src/devices/virtio/fs/augment_fs.rs\n M crates/pvisor-vm/src/devices/virtio/fs/device.rs\n M crates/pvisor-vm/src/devices/virtio/fs/filesystem.rs\n M crates/pvisor-vm/src/devices/virtio/fs/fuse.rs\n M crates/pvisor-vm/src/devices/virtio/fs/linux/passthrough.rs\n M crates/pvisor-vm/src/devices/virtio/fs/linux/passthrough_snapshot.rs\n M crates/pvisor-vm/src/devices/virtio/fs/macos/passthrough.rs\n M crates/pvisor-vm/src/devices/virtio/fs/mod.rs\n M crates/pvisor-vm/src/devices/virtio/fs/overlay.rs\n M crates/pvisor-vm/src/devices/virtio/fs/read_only.rs\n M crates/pvisor-vm/src/devices/virtio/fs/server.rs\n M crates/pvisor-vm/src/devices/virtio/fs/snapshot.rs\n M crates/pvisor-vm/src/devices/virtio/fs/worker.rs\n M crates/pvisor-vm/src/devices/virtio/gpu/device.rs\n M crates/pvisor-vm/src/devices/virtio/mmio.rs\n M crates/pvisor-vm/src/portable.rs\n M crates/pvisor-vm/src/vmm/builder.rs\n M crates/pvisor-vm/src/vmm/linux/vstate.rs\n M crates/pvisor-vm/tests/api_contract.rs\n M docs/README.md\n M docs/src/en/benchmarks/agent-tasks.md\n M docs/src/en/benchmarks/cluster-scalability.md\n M docs/src/en/benchmarks/compare-rl-infra.md\n M docs/src/en/benchmarks/density.md\n M docs/src/en/benchmarks/evidence-sources.csv\n M docs/src/en/benchmarks/filesystem.csv\n M docs/src/en/benchmarks/filesystem.md\n M docs/src/en/benchmarks/index.csv\n M docs/src/en/benchmarks/index.md\n M docs/src/en/benchmarks/isolation-tests.md\n M docs/src/en/benchmarks/lazy-image-startup.md\n M docs/src/en/benchmarks/methodology.md\n M docs/src/en/benchmarks/network.md\n M docs/src/en/benchmarks/replay-fidelity.md\n M docs/src/en/benchmarks/startup.md\n M docs/src/en/benchmarks/supervision-cost.md\n M docs/src/en/benchmarks/vm-memory/index.md\n M docs/src/en/design/filesystem-performance-analysis.md\n M docs/src/en/design/index.md\n M docs/src/en/design/vm-startup-performance-analysis.md\n M docs/src/zh/benchmarks/agent-tasks.md\n M docs/src/zh/benchmarks/cluster-scalability.md\n M docs/src/zh/benchmarks/compare-rl-infra.md\n M docs/src/zh/benchmarks/density.md\n M docs/src/zh/benchmarks/evidence-sources.csv\n M docs/src/zh/benchmarks/filesystem.csv\n M docs/src/zh/benchmarks/filesystem.md\n M docs/src/zh/benchmarks/index.csv\n M docs/src/zh/benchmarks/index.md\n M docs/src/zh/benchmarks/isolation-tests.md\n M docs/src/zh/benchmarks/lazy-image-startup.md\n M docs/src/zh/benchmarks/methodology.md\n M docs/src/zh/benchmarks/network.md\n M docs/src/zh/benchmarks/replay-fidelity.md\n M docs/src/zh/benchmarks/startup.md\n M docs/src/zh/benchmarks/supervision-cost.md\n M docs/src/zh/benchmarks/vm-memory/index.md\n M docs/src/zh/design/filesystem-performance-analysis.md\n M docs/src/zh/design/index.md\n M docs/src/zh/design/vm-startup-performance-analysis.md\n M docs/translations.json\n M docs/zensical.en.toml\n M docs/zensical.zh.toml\n D experimental/README.md\n D experimental/vcpu-idle-offload/README.md\n D experimental/vcpu-idle-offload/design.md\n D experimental/vcpu-idle-offload/implementation-plan.md\n D experimental/vcpu-idle-offload/validation.md\n M justfile\n M tests/justfile\n M tests/test_docs.py\n M tools/semspec/Cargo.lock\n M tools/semspec/Cargo.toml\n M tools/semspec/DESIGN.md\n M tools/semspec/README.md\n M tools/semspec/src/helpers.rs\n M tools/semspec/src/lib.rs\n M tools/semspec/src/main.rs\n?? benchmark.new/\n?? crates/pvisor-vm/src/devices/virtio/fs/dax.rs\n?? crates/pvisor-vm/src/devices/virtio/fs/worker/\n?? crates/pvisor-vm/src/probes/readonly_dax_case.rs\n?? docs/semspec_tables.py\n?? docs/src/en/benchmarks/filesystem-cache-policy.csv\n?? docs/src/en/benchmarks/filesystem-comparisons.csv\n?? docs/src/en/benchmarks/filesystem-provenance.csv\n?? docs/src/en/benchmarks/filesystem-semspec.csv\n?? docs/src/en/benchmarks/filesystem-semspec.render.provenance.json\n?? docs/src/en/benchmarks/measurement-coverage.csv\n?? docs/src/en/benchmarks/measurement-coverage.render.json\n?? docs/src/en/benchmarks/replay-semspec.csv\n?? docs/src/en/benchmarks/replay-semspec.render.json\n?? docs/src/en/benchmarks/replay-semspec.render.provenance.json\n?? docs/src/en/benchmarks/startup-hyperfine.csv\n?? docs/src/en/benchmarks/startup-ready-exit.csv\n?? docs/src/en/benchmarks/startup-ready-exit.provenance.json\n?? docs/src/en/benchmarks/startup-ready.csv\n?? docs/src/en/benchmarks/startup-ready.provenance.json\n?? docs/src/en/benchmarks/startup-reboot-comparison.csv\n?? docs/src/en/benchmarks/startup-semspec.csv\n?? docs/src/en/benchmarks/startup-semspec.provenance.json\n?? docs/src/en/benchmarks/startup-timing.csv\n?? docs/src/en/benchmarks/startup-timing.exit.render.json\n?? docs/src/en/benchmarks/startup-timing.provenance.json\n?? docs/src/en/benchmarks/startup-timing.ready.render.json\n?? docs/src/en/benchmarks/startup-timing.render.json\n?? docs/src/en/design/concurrency-filesystem-summary.csv\n?? docs/src/en/design/concurrency-retest-provenance.csv\n?? docs/src/en/design/concurrency-startup-summary.csv\n?? docs/src/en/design/direct-queue-filesystem-summary.csv\n?? docs/src/en/design/direct-queue-provenance.csv\n?? docs/src/en/design/experimental/\n?? docs/src/en/design/filesystem-evidence-overview.csv\n?? docs/src/en/design/filesystem-measurement-status.csv\n?? docs/src/en/design/host-copy-up-summary.csv\n?? docs/src/en/design/host-idle-dispatch-provenance.csv\n?? docs/src/en/design/host-idle-dispatch-summary.csv\n?? docs/src/en/design/host-read-summary.csv\n?? docs/src/en/design/journal-lock-summary.csv\n?? docs/src/en/design/readonly-dax-provenance.csv\n?? docs/src/en/design/readonly-dax-summary.csv\n?? docs/src/en/design/vm-completion-dispatch-provenance.csv\n?? docs/src/en/design/vm-completion-dispatch-summary.csv\n?? docs/src/en/design/writeback-filesystem-summary.csv\n?? docs/src/en/design/writeback-provenance.csv\n?? docs/src/zh/benchmarks/filesystem-cache-policy.csv\n?? docs/src/zh/benchmarks/filesystem-comparisons.csv\n?? docs/src/zh/benchmarks/filesystem-provenance.csv\n?? docs/src/zh/benchmarks/filesystem-semspec.csv\n?? docs/src/zh/benchmarks/filesystem-semspec.render.provenance.json\n?? docs/src/zh/benchmarks/measurement-coverage.csv\n?? docs/src/zh/benchmarks/measurement-coverage.render.json\n?? docs/src/zh/benchmarks/replay-semspec.csv\n?? docs/src/zh/benchmarks/replay-semspec.render.json\n?? docs/src/zh/benchmarks/replay-semspec.render.provenance.json\n?? docs/src/zh/benchmarks/startup-hyperfine.csv\n?? docs/src/zh/benchmarks/startup-ready-exit.csv\n?? docs/src/zh/benchmarks/startup-ready-exit.provenance.json\n?? docs/src/zh/benchmarks/startup-ready.csv\n?? docs/src/zh/benchmarks/startup-ready.provenance.json\n?? docs/src/zh/benchmarks/startup-reboot-comparison.csv\n?? docs/src/zh/benchmarks/startup-semspec.csv\n?? docs/src/zh/benchmarks/startup-semspec.provenance.json\n?? docs/src/zh/benchmarks/startup-timing.csv\n?? docs/src/zh/benchmarks/startup-timing.exit.render.json\n?? docs/src/zh/benchmarks/startup-timing.provenance.json\n?? docs/src/zh/benchmarks/startup-timing.ready.render.json\n?? docs/src/zh/benchmarks/startup-timing.render.json\n?? docs/src/zh/design/concurrency-filesystem-summary.csv\n?? docs/src/zh/design/concurrency-retest-provenance.csv\n?? docs/src/zh/design/concurrency-startup-summary.csv\n?? docs/src/zh/design/direct-queue-filesystem-summary.csv\n?? docs/src/zh/design/direct-queue-provenance.csv\n?? docs/src/zh/design/experimental/\n?? docs/src/zh/design/filesystem-evidence-overview.csv\n?? docs/src/zh/design/filesystem-measurement-status.csv\n?? docs/src/zh/design/host-copy-up-summary.csv\n?? docs/src/zh/design/host-idle-dispatch-provenance.csv\n?? docs/src/zh/design/host-idle-dispatch-summary.csv\n?? docs/src/zh/design/host-read-summary.csv\n?? docs/src/zh/design/journal-lock-summary.csv\n?? docs/src/zh/design/readonly-dax-provenance.csv\n?? docs/src/zh/design/readonly-dax-summary.csv\n?? docs/src/zh/design/vm-completion-dispatch-provenance.csv\n?? docs/src/zh/design/vm-completion-dispatch-summary.csv\n?? docs/src/zh/design/writeback-filesystem-summary.csv\n?? docs/src/zh/design/writeback-provenance.csv\n?? tests/test_semspec_tables.py\n?? tools/semspec/src/table.rs\n?? tools/semspec/tests/tables.rs\n""}, ""source_manifest_sha256"": ""5311e7c05ca1fc114c60416920c204866e5a6079f509343a91b95f6bf0d37b24""}}" +B-FS-ENG,fof1,environment_overrides,"{""baseline"": {}, ""selected"": {}}" +B-FS-ENG,fof1,harness_manifest_sha256,08cf17c064b561e88d02ad8686479cb64663c98205c0edb2527f34ac5e95d622 +B-FS-ENG,fof1,helper_sha256,"{""child.py"": ""e000fec596b843255ffd06963e981f4a338bba11393cfc51ec308d5351fa33f5"", ""common.py"": ""1f284d8ead6ece72c3247d8742c41564e504da0d2de8f03c4c5dede62f8906cd"", ""launch.py"": ""099103de2d2d1b61b1eceed82855a1dc87003315b427ca470545d0b231f0bac4"", ""paired.py"": ""597773aff5c9383c6f7c4f900e4d30627e5565e722ece91c74b0880118bcea9e"", ""protocol.json"": ""c4ce1b9ee4b7d3fb634d44872e02ad630bbf7e3677e9ddb7699808af3122040b"", ""summarize.py"": ""19be5a05a3d1774a55b6409425efa0505ad0e915274403aea6c52a731cc59b58""}" +B-FS-ENG,fof1,protocol_sha256,c4ce1b9ee4b7d3fb634d44872e02ad630bbf7e3677e9ddb7699808af3122040b +B-FS-ENG,fof1,order_sha256,d56bb3783c17241a3d15e54d5431c80b361d86384370a52f4341728cf9516d49 +B-FS-ENG,fof1,inputs,"{""bytes"": 13455954066, ""directories"": 3256, ""directory_metadata_coverage"": ""complete"", ""files"": 75309, ""input_manifest_sha256"": ""730df0172d73d3c260c741eebf7ac0926dea0add1f05a176c6a39176e9469518"", ""scope"": ""complete rootfs inventory including directory modes; declared asset bytes/modes/symlinks; OCI image and host interpreter verification are separate"", ""symlinks"": 14}" diff --git a/docs/src/zh/design/filesystem-optimization-n30-summary.csv b/docs/src/zh/design/filesystem-optimization-n30-summary.csv new file mode 100644 index 000000000..3869728d1 --- /dev/null +++ b/docs/src/zh/design/filesystem-optimization-n30-summary.csv @@ -0,0 +1,12 @@ +benchmark_id,batch,role,backend,metric,unit,reference,candidate,n,failures,reference_p50_ms,candidate_p50_ms,median_difference_ms,ci95_low_ms,ci95_high_ms,change_percent,ci95_low_percent,ci95_high_percent,reference_distribution,candidate_distribution,interpretation,distribution_limitation +B-FS-ENG,fof1,formal engineering A/B,pvisor-vm,ready_ms,ms,baseline,selected,30,0,693.899161,692.7548735,-1.1442875000000186,-5.2979636499999545,2.705507987499978,-0.16490688623270744,-0.7612537138123532,0.3902048022017106,"{""n"": 30, ""p50"": 693.899161, ""p95_reference"": 704.77595625, ""minimum"": 685.063406, ""maximum"": 759.587939, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 692.7548735, ""p95_reference"": 703.0278088, ""minimum"": 684.449459, ""maximum"": 748.874626, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",no detected difference,predeclared descriptive split rule +B-FS-ENG,fof1,formal engineering A/B,pvisor-vm,result_ms,ms,baseline,selected,30,0,4333.5223785,4257.057506,-76.46487249999973,-285.78103699999974,-7.64772748749933,-1.7644970031622909,-6.290803300512538,-0.17697442920818976,"{""n"": 30, ""p50"": 4333.5223785, ""p95_reference"": 4765.5494103500005, ""minimum"": 4098.158195, ""maximum"": 4909.774685, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 4257.057506, ""p95_reference"": 4832.62272865, ""minimum"": 4091.989833, ""maximum"": 5047.659524, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",faster,predeclared descriptive split rule +B-FS-ENG,fof1,formal engineering A/B,pvisor-vm,completion_ms,ms,baseline,selected,30,0,4546.352988,4467.5308425,-78.82214549999935,-269.8794343625004,-9.921920700000758,-1.7337445136365037,-5.698320121926485,-0.22006512668154682,"{""n"": 30, ""p50"": 4546.352988, ""p95_reference"": 4973.5086638, ""minimum"": 4306.462009, ""maximum"": 5129.24075, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 4467.5308425, ""p95_reference"": 5088.49077675, ""minimum"": 4283.208342, ""maximum"": 5274.443229, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",faster,predeclared descriptive split rule +B-FS-ENG,fof1,formal engineering A/B,pvisor-vm,worker_ms,ms,baseline,selected,30,0,3641.731702,3562.537183,-79.19451900000013,-284.4672740000001,-9.523004500000297,-2.1746390311100483,-7.391955081182977,-0.2652750212475352,"{""n"": 30, ""p50"": 3641.731702, ""p95_reference"": 4037.1929185500003, ""minimum"": 3409.246215, ""maximum"": 4216.111067, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 3562.537183, ""p95_reference"": 4138.1722642, ""minimum"": 3400.59452, ""maximum"": 4349.952972, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",faster,predeclared descriptive split rule +B-FS-ENG,fof1,formal engineering A/B,pvisor-vm,metadata,ms,baseline,selected,30,0,149.126784,132.075482,-17.051301999999993,-36.769790887500015,9.156760999999989,-11.434097579680923,-23.736129395694633,6.361913246535744,"{""n"": 30, ""p50"": 149.126784, ""p95_reference"": 184.22898425, ""minimum"": 108.754779, ""maximum"": 190.56574, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 132.075482, ""p95_reference"": 176.371968, ""minimum"": 107.316462, ""maximum"": 186.0651, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",no detected difference,predeclared descriptive split rule +B-FS-ENG,fof1,formal engineering A/B,pvisor-vm,read,ms,baseline,selected,30,0,114.58174600000001,115.6647925,1.0830464999999947,-0.43468500000000176,4.999490499999993,0.9452173123631669,-0.37351130052453163,4.376549075174441,"{""n"": 30, ""p50"": 114.58174600000001, ""p95_reference"": 121.67214895000001, ""minimum"": 111.850294, ""maximum"": 121.963128, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 115.6647925, ""p95_reference"": 122.11053675000001, ""minimum"": 112.518417, ""maximum"": 127.715376, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",no detected difference,predeclared descriptive split rule +B-FS-ENG,fof1,formal engineering A/B,pvisor-vm,write,ms,baseline,selected,30,0,159.77246300000002,137.107249,-22.66521400000002,-28.724122962499994,-14.978764500000011,-14.18593265348862,-17.580605675377935,-9.820266917526087,"{""n"": 30, ""p50"": 159.77246300000002, ""p95_reference"": 187.98593385, ""minimum"": 130.825608, ""maximum"": 260.216677, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 137.107249, ""p95_reference"": 146.59808115, ""minimum"": 96.838472, ""maximum"": 150.630048, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",faster,predeclared descriptive split rule +B-FS-ENG,fof1,formal engineering A/B,pvisor-vm,git,ms,baseline,selected,30,0,,,,,,,,,"{""n"": 30, ""p50"": """", ""p95_reference"": 776.07380215, ""minimum"": 366.416615, ""maximum"": 778.366539, ""distribution"": ""separated-clusters"", ""low_n"": 22, ""low_p50"": 381.60562849999997, ""high_n"": 8, ""high_p50"": 770.0179430000001}","{""n"": 30, ""p50"": """", ""p95_reference"": 745.14214455, ""minimum"": 347.109328, ""maximum"": 749.268411, ""distribution"": ""separated-clusters"", ""low_n"": 24, ""low_p50"": 369.18431999999996, ""high_n"": 6, ""high_p50"": 736.4385605}",separated clusters; no overall ranking,predeclared descriptive split rule +B-FS-ENG,fof1,formal engineering A/B,pvisor-vm,rg,ms,baseline,selected,30,0,422.94244849999995,424.78182749999996,1.839379000000008,-5.798639499999979,9.07188750000006,0.4349005417932261,-1.3610608883080078,2.1765528699795755,"{""n"": 30, ""p50"": 422.94244849999995, ""p95_reference"": 435.3985634, ""minimum"": 383.878402, ""maximum"": 436.144075, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 424.78182749999996, ""p95_reference"": 436.33608189999995, ""minimum"": 394.482648, ""maximum"": 440.127836, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",no detected difference,predeclared descriptive split rule +B-FS-ENG,fof1,formal engineering A/B,pvisor-vm,cargo,ms,baseline,selected,30,0,484.6475145,482.3360645,-2.3114499999999794,-6.5772249999999985,4.432882387499992,-0.4769342523884945,-1.3474574103963688,0.915387684540544,"{""n"": 30, ""p50"": 484.6475145, ""p95_reference"": 498.9358284, ""minimum"": 468.659131, ""maximum"": 500.977308, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 482.3360645, ""p95_reference"": 500.41343274999997, ""minimum"": 470.665895, ""maximum"": 518.905058, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",no detected difference,predeclared descriptive split rule +B-FS-ENG,fof1,formal engineering A/B,pvisor-vm,npm,ms,baseline,selected,30,0,937.0268659999999,920.087084,-16.939781999999923,-34.37900493749996,-0.9755494999999428,-1.8078224450823743,-3.6317278925731222,-0.10490188738011552,"{""n"": 30, ""p50"": 937.0268659999999, ""p95_reference"": 988.38562445, ""minimum"": 767.385957, ""maximum"": 1457.152233, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 920.087084, ""p95_reference"": 1431.9912869500001, ""minimum"": 758.941401, ""maximum"": 1646.893086, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",faster,predeclared descriptive split rule diff --git a/docs/src/zh/design/filesystem-performance-analysis.md b/docs/src/zh/design/filesystem-performance-analysis.md index 1a67d4d6a..1a1fdcb6b 100644 --- a/docs/src/zh/design/filesystem-performance-analysis.md +++ b/docs/src/zh/design/filesystem-performance-analysis.md @@ -1,5 +1,421 @@ # 文件系统性能:技术分析与实验记录 +## 当前结论与测量覆盖 {#filesystem-status} + +**最新独立 N30 配对对照:所选 C3(新 VM 写合并默认关):启动至正常退出变化 -1.73%,95% CI [-5.70, -0.22]%;写入 256 文件变化 -14.19%,95% CI [-17.58, -9.82]%。结果归因于该次代码与默认配置组合,未建立全面恢复历史性能的结论。** + +独立批次详见[Linux:文件系统优化与写合并默认关闭的 N30 配对对照](#filesystem-optimization-n30)。以下已有汇总保留各历史批次的结论。 + +历史工程结论: + +**文件系统退化尚未得到整体修复。** 直接队列服务的小样本复测支持部分工具改善,整体任务仍未检出差异;关闭写合并能减少写入成本,但没有检出完整七工具任务加速。DAX 保留显式启用入口,默认关闭,其首次 mmap 与重复小文件读取有明确退化。 + +下表分别引用各自批次的同条件对照,不能累加百分比或比较跨批次绝对时间。变化为候选相对该批基线,负值表示耗时下降;区间为 95% 配对 bootstrap 百分比区间。直接队列每臂 N=10,其余每臂 N=30;全部为工程结果。 + +| 对照 / 指标 | 每臂 N | 基线 ms | 候选 ms | 变化 | 95% CI % | 结论 | +| --- | --- | --- | --- | --- | --- | --- | +| [staged 空闲调度:完整任务](#host-idle-dispatch) | 30 | 1826.42 | 1774.15 | -2.86% | [-3.57, -1.22] | 更快 | +| [VM worker 修复:完整任务](#vm-completion-dispatch) | 30 | 4741.87 | 4592.36 | -3.15% | [-6.14, -1.23] | 更快 | +| [VM 并发重构:内部总耗时](#concurrency-retest) | 30 | 3578.18 | 3749.07 | +4.78% | [+2.96, +5.92] | 更慢 | +| [VM 直接队列:内部总耗时](#direct-queue-fast) | 10 | 3707.29 | 3606.71 | -2.71% | [-5.28, +7.25] | 未检出差异 | +| [关闭写合并:写入](#writeback-ab) | 30 | 162.10 | 131.61 | -18.81% | [-25.06, -12.87] | 更快 | +| [开启 DAX:顺序重复读取](#readonly-dax-ab) | 30 | 5.54 | 4.58 | -17.38% | [-21.40, -10.10] | 更快 | +| [开启 DAX:双线程首次读取](#readonly-dax-ab) | 30 | 54.43 | 38.27 | -29.69% | [-32.16, -28.25] | 更快 | +| [开启 DAX:首次 mmap 页遍历](#readonly-dax-ab) | 30 | 23.83 | 104.75 | +339.59% | [+328.77, +352.40] | 更慢 | +| [开启 DAX:小文件重复读取](#readonly-dax-ab) | 30 | 25.37 | 36.63 | +44.37% | [+36.47, +47.98] | 更慢 | + +普通 rootfs/workspace 默认不创建 DAX 窗口;不可变 share 保留显式 DAX 配置。新建 VM 写合并默认关闭,`PVISOR_VM_FS_WRITEBACK=1` / `0` 可显式配置;默认恢复已协商快照时保留原模式,显式 `PVISOR_VM_FS_WRITEBACK=0` 会拒绝已开启写合并的快照;stage 同步默认串行。历史工程表的制品与条件保留,不将其百分比累加或折算成当前用户批次的收益。 + +2026-10-12 所选冻结源码/默认配置已完成跨运行时七工具复测:八个后端各 30 次,共 240 个有效任务,DAX 关闭、新 VM 写合并默认关闭,见[工具等待](../benchmarks/filesystem.md#reference-fs)。新用户数据来自独立 `fo5a4e7d2b-f` 批次,不与历史合并,也不计算跨批次因果加速。旧有效与拒绝批次继续分别保留,见[验收范围 CSV](filesystem-measurement-status.csv)。 + +macOS 的[复制期间响应性与 journal 锁](#host-concurrency)、Linux HOST 的[不可变 lower 缓存](#immutable-lower-cache)各有独立有效实验;[扩展内核缓存](#host-cache-evidence)只有正确性和请求计数通过,正式时间未验收。冷磁盘、大型仓库和并发吞吐没有匹配的新测量。[工程结论索引 CSV](filesystem-evidence-overview.csv)保留表格的批次、指标、来源和完整精度。 + +## Linux:文件系统优化与写合并默认关闭的 N30 配对对照 {#filesystem-optimization-n30} + +独立 B-FS-ENG 批次 `fof1`,每臂 N=30、3 次预热。下表只归因于这次同条件版本及默认配置组合;不与筛选批次、历史开关实验或跨运行时用户批次合并。 + +基线为 SHA939 冻结构建,所选 C3 为 SHA884 冻结构建,包含队列热路径、handle lease 与 Linux 普通文件元数据路径优化,以及新 VM 写合并默认关闭。两臂均不设置 WRITEBACK/WORKERS 环境覆盖,基线采用自身默认开启,C3 采用自身默认关闭;因此只归因于代码与默认配置的组合。冻结源码、制品和固件身份见下载来源。Linux/KVM/FUSE/Btrfs,热宿主缓存;每次新 VM、私有 user/mount/PID namespace、owned pivot_root、独立工作区与工具缓存,整父组 CPU 0,1、两核 quota、16 GiB、swap 0,guest 2 vCPU/16 GiB。每次新 Host listener 的启动计入完整任务,不能与 listener 复用的跨后端用户批次相减。每臂 3 次预热、30 个 seed 20261011 随机配对,60 正式任务及 6 预热全部通过七工具输出、256×64KiB 全字节、Run Bundle、宿主隔离、输入/制品/预算与外部干扰门禁。环境观察只证明可见进程 exec 时变量继承,不证明精确 FUSE INIT 协商;短暂、不可见或未识别干扰可能漏检。不开 profiling,不按速度剔除;此前 N5/N10 筛选只用于选定 C3,不进入这批样本或置信区间。 + +**所选 C3(新 VM 写合并默认关)** + +| 指标 | 每臂 N | 基线 ms | 候选 ms | 变化 | 95% CI ms | 95% CI % | 结论 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| 首条有效输出 Ready | 30 | 693.90 | 692.75 | -0.16% | [-5.30, +2.71] | [-0.76, +0.39] | 未检出差异 | +| 启动至完整结果 | 30 | 4333.52 | 4257.06 | -1.76% | [-285.78, -7.65] | [-6.29, -0.18] | 更快 | +| 启动至正常退出 | 30 | 4546.35 | 4467.53 | -1.73% | [-269.88, -9.92] | [-5.70, -0.22] | 更快 | +| 内部七工具总耗时 | 30 | 3641.73 | 3562.54 | -2.17% | [-284.47, -9.52] | [-7.39, -0.27] | 更快 | +| 遍历 2,048 文件 | 30 | 149.13 | 132.08 | -11.43% | [-36.77, +9.16] | [-23.74, +6.36] | 未检出差异 | +| 读取并校验 64 MiB | 30 | 114.58 | 115.66 | +0.95% | [-0.43, +5.00] | [-0.37, +4.38] | 未检出差异 | +| 写入 256 文件 | 30 | 159.77 | 137.11 | -14.19% | [-28.72, -14.98] | [-17.58, -9.82] | 更快 | +| git status | 30 | 381.61 (n=22); 770.02 (n=8) | 369.18 (n=24); 736.44 (n=6) | — | — | — | 分离簇,不作整体排名 | +| Ripgrep 搜索 | 30 | 422.94 | 424.78 | +0.43% | [-5.80, +9.07] | [-1.36, +2.18] | 未检出差异 | +| 离线 Cargo 编译 | 30 | 484.65 | 482.34 | -0.48% | [-6.58, +4.43] | [-1.35, +0.92] | 未检出差异 | +| 离线 npm 安装 | 30 | 937.03 | 920.09 | -1.81% | [-34.38, -0.98] | [-3.63, -0.10] | 更快 | + +单位 ms;负变化表示耗时下降。区间来自同批 5,000 次配对 bootstrap;有效慢样本保留,分离簇不报告整体 P50、百分比或排名,不报告 P95/P99。 + +[同批完整精度 CSV](filesystem-optimization-n30-summary.csv) · [制品、配置与来源](filesystem-optimization-n30-provenance.csv)。原始证据为本地 `benchmark/.data/fof1/report.json`,验收为 `benchmark/.data/fof1/derived/audit.json`。 + +## Linux:关闭 guest 写合并的配对对照 {#writeback-ab} + +**关闭写合并使 256 × 64 KiB 写入从 162.10 ms 降至 131.61 ms,减少 18.81%,95% CI [-25.06%, -12.87%]。** 完整任务和内部七工具总耗时均未检出差异;rg 和 Cargo 分别增加 1.82%、1.08%,Git 与关闭组 npm 呈分离簇。因此关闭写合并的收益仅能归于这组写入负载。 + +### 对照与计时边界 {#writeback-protocol} + +B-FS-ENG 使用同一个冻结 GNU release/gateway 制品,SHA256 为 `ae221676e4bb04231c88f2ce17c2570824f4064f8a3a63d6ef32817b0e2afa98`,包含直接队列服务、重操作 stealing 和局部 I/O 屏障。两组仅将宿主 runner 的 `PVISOR_VM_FS_WRITEBACK` 设为 `1` / `0`,原环境由专用 worker 恢复并核对;相同固件、七工具负载、输入、工作区与工具缓存策略,rootfs/workspace 均无 DAX 窗口。 + +Linux/KVM/FUSE/Btrfs,热宿主缓存、每次新 VM、独立 user/mount/PID namespace 和 owned pivot_root。整父组 CPU 0,1、两核 quota、16 GiB、swap 0;guest 2 vCPU/16 GiB。输入和 namespace 准备不计时,新的 Host listener、VM 启动和退出计入完整任务;单项为原 workload 的内部时间,包含输出校验。profiling 关闭。 + +每臂 3 次预热、30 个固定种子随机配对轮次,共 60 次正式任务;66 次运行全部通过输出、正常退出、Run Bundle、宿主隔离及 256 个 upper 文件的完整字节检查,输入和制品摘要一致,无 OOM、0 失败、0 剔除。0.5 秒外部构建/测试扫描未检出干扰,可能漏掉短暂或不可见活动。5,000 次配对 bootstrap;有效慢样本保留,先检查分布,不报告 P95/P99。 + +### 七项操作与完整等待 {#writeback-results} + +单位 ms,每臂 N=30;变化为关闭相对开启,95% CI 为中位耗时差。分离分布展示每簇中位数与样本数,不给整体 P50、百分比或排名。 + +| 指标 | 写合并开 ms | 写合并关 ms | 变化 | 95% CI ms | 结论 | +| --- | --- | --- | --- | --- | --- | +| 首条有效输出 Ready | 696.95 | 694.99 | -0.28% | [-4.96, +2.39] | 未检出差异 | +| 启动至完整结果 | 4409.19 | 4363.77 | -1.03% | [-247.19, +262.05] | 未检出差异 | +| 启动至正常退出 | 4616.37 | 4564.87 | -1.12% | [-250.56, +271.96] | 未检出差异 | +| 内部七工具总耗时 | 3699.55 | 3660.70 | -1.05% | [-259.00, +263.60] | 未检出差异 | +| 遍历 2,048 文件 | 148.93 | 145.89 | -2.04% | [-26.36, +19.23] | 未检出差异 | +| 读取并校验 64 MiB | 114.83 | 116.22 | +1.21% | [-1.53, +2.47] | 未检出差异 | +| 写入 256 × 64 KiB | 162.10 | 131.61 | -18.81% | [-41.05, -20.17] | 更快 | +| git status | 391.26 (n=23); 759.78 (n=7) | 375.22 (n=21); 751.37 (n=9) | — | — | 分离分布,不作整体排名 | +| rg | 420.05 | 427.67 | +1.82% | [+1.09, +14.25] | 更慢 | +| 离线 Cargo 编译 | 485.05 | 490.29 | +1.08% | [+0.35, +12.54] | 更慢 | +| 离线 npm 安装 | 941.86 | 937.78 (n=22); 1661.33 (n=8) | — | — | 分离分布,不作整体排名 | + +完整任务的中位数差为 -51.50 ms,95% CI [-250.56, +271.96] ms;内部总耗时差为 -38.86 ms,区间 [-259.00, +263.60] ms。写入改进没有转化为可检出的整体收益,也不能从这些时间推断 READ 请求数量或具体锁成本。Git 两组均分簇,关闭组 npm 也分簇,相关样本保留并单独展示。 + +[完整统计 CSV](writeback-filesystem-summary.csv) · [制品、开关、预算与实验来源](writeback-provenance.csv)。原始证据在本地 `benchmark/.data/writeback-quick-20261011/`,冻结 harness、执行与验收记录在 `benchmark/pvisor/.data/writeback-ab-20261011/`。该批实际每臂 N=30,独立于 N=10 的队列快速复测和只读 DAX 实验;该历史批次的开启/关闭条件不变;当前新 VM 的默认值以所选冻结源码为准。 + +## Linux:不可变只读 share 的 DAX 配对对照 {#readonly-dax-ab} + +**DAX 在这组负载上有明显取舍,不能作为默认加速方案。** 64 MiB 双线程首次读取快 29.69%,顺序重复读取快 17.38%;顺序首次读取慢 8.72%,小文件重复读取慢 44.37%,64 MiB mmap 首次逐页访问慢 339.59%。metadata 未检出差异。测量范围是显式不可变 share,CLI 的可写 workspace 和启动、退出退化没有在此实验中复测。 + +### 对照与计时边界 {#readonly-dax-protocol} + +B-FS-ENG 使用同一个冻结的 `performance` 制品(opt-level 3、Thin LTO),两组都调用公开 API `immutable_filesystem` 创建只读设备及 128 MiB DAX 窗口,仅 guest 挂载选项 `dax=never` / `dax=inode` 不同。每次 statx 都验证预期 DAX 属性,关闭组同时保留相同的设备和窗口配置。宿主为 Ryzen 7 9700X / Linux 7.2.8,guest 为 Linux 6.12.109;相同 Btrfs backing 和固件,宿主内容缓存预热,每次新 VM 和独占 rootfs upper/work。 + +完整 campaign 及 VM 使用 CPU 0,1 的继承 affinity、两核 quota、2 GiB memory.max、swap 0、65,536 FD 上限;guest 2 vCPU/512 MiB。用户 cgroup 没有 cpuset 控制器,实际 affinity/quota 在前后核验。计时期间不启用 profiling。每组 3 次预热,再执行预先平衡随机顺序的 30 对(共 60 次正式 VM);6 次预热与 60 次正式运行全部正确、正常退出。输入与制品前后摘要一致、无 OOM、0 失败、0 剔除;组外每 0.5 秒扫描一次,共 123 次,未检测到外部构建/测试,短暂或其他宿主活动仍可能漏检。 + +同一 guest 按 metadata → 小文件 → 顺序读取 → mmap → 双线程读取执行,各数据类使用独立文件,共享窗口及其回收状态。metadata 是 2,048 路径 stat;小文件是 128×64 KiB 的 open/read/close,其 2 MiB 映射槽需求超过窗口容量。大文件各 64 MiB:read 为已打开 FD 的 64 KiB pread;mmap 首次包括建立映射并每 4 KiB 读一个字节,重复访问保留映射;parallel 含创建和回收两个读取线程。全量内容验证在各阶段计时之外。首次文件数据访问不表示磁盘冷缓存或空 DAX 窗口。 + +guest `CLOCK_MONOTONIC_RAW` 记录阶段时间。重复 read/parallel 是 4 遍的每遍均值,mmap 是 16 遍的每遍均值,其余一遍;表中为这 30 个样本的中位数。CI 用 5,000 次配对 bootstrap,对每次重采样的两组中位数计算差值及百分比;区间跨零标为未检出差异。按预先登记的相邻间隙/簇大小规则未检出分离簇,有效慢样本全部保留,不报告 P95/P99。 + +### 同批结果 {#readonly-dax-results} + +单位 ms;变化为 DAX 开相对关,正值表示更慢,95% CI 为百分比变化区间。 + +| 操作 | DAX 关 ms | DAX 开 ms | 变化 | 95% CI | 结论 | +| --- | --- | --- | --- | --- | --- | +| 2,048 路径 stat:首次 | 129.0957 | 130.0621 | +0.75% | [-8.33%, +11.32%] | 未检出差异 | +| 2,048 路径 stat:重复 | 0.9369 | 0.9690 | +3.43% | [-5.26%, +11.44%] | 未检出差异 | +| 128×64 KiB open/read/close:首次 | 40.2088 | 41.4687 | +3.13% | [-0.68%, +5.74%] | 未检出差异 | +| 128×64 KiB open/read/close:重复 | 25.3731 | 36.6306 | +44.37% | [+36.47%, +47.98%] | 更慢 | +| 64 MiB 顺序 pread:首次 | 60.6072 | 65.8917 | +8.72% | [+7.11%, +9.84%] | 更慢 | +| 64 MiB 顺序 pread:重复 | 5.5443 | 4.5805 | -17.38% | [-21.40%, -10.10%] | 更快 | +| 64 MiB mmap 逐页访问:首次 | 23.8292 | 104.7507 | +339.59% | [+328.77%, +352.40%] | 更慢 | +| 64 MiB mmap 逐页访问:重复 | 0.0964 | 0.1252 | +29.90% | [+27.00%, +33.71%] | 更慢 | +| 64 MiB 双线程 pread:首次 | 54.4338 | 38.2736 | -29.69% | [-32.16%, -28.25%] | 更快 | +| 64 MiB 双线程 pread:重复 | 4.7336 | 4.1625 | -12.07% | [-16.90%, +0.74%] | 未检出差异 | + +首次 mmap 增加约 80.92 ms,而顺序热读取每遍减少约 0.96 ms,说明减少数据 READ 请求并不足以抵消当前首次访问成本。小文件的重复 open/read/close 也出现稳定退化。首次映射、逐页 fault、窗口回收及宿主映射竞争仍需要独立诊断;这些配对时间不能将差额全部归因于某一把锁或某一步。DAX 默认关闭,保留显式 opt-in;先定位首次 mmap 和窗口压力,再决定是否接入只读工具/镜像路径。实验关闭组仍预留窗口,不能将其成本当作普通无窗口设备的精确成本。 + +[完整统计 CSV](readonly-dax-summary.csv) · [来源与校验摘要](readonly-dax-provenance.csv)。入口与复现见仓库 `benchmark.new/filesystem/README.md`;原始日志、冻结源码和制品保存在本地 `benchmark.new/filesystem/.data/dax-20261011-final/`。该工程批次独立于七工具、跨运行时和历史启动数据。 + +## Linux:直接队列服务与局部屏障,60 样本快速复测 {#direct-queue-fast} + +**调度修复呈现改善趋势,写入退化仍未解决。** 相对上轮并发版,VM 的 rg、Cargo、npm 在本批分别快约 7.54%、4.10%、4.64%;内部总耗时下降约 2.71%,但区间跨零。相对先前优化版,写入仍慢约 32.40%。本轮每格仅 N=10,用于快速判断方向,不作为 30 次配对的正式性能验收。 + +### 修改与实验设计 {#direct-queue-fast-protocol} + +队列线程直接等待和消费自己的 kick,取请求、执行和发布完成;普通请求不再由协调线程转发唤醒。stealing 只考虑重操作持续至少 2 ms 且有真实积压的普通队列,认领时在 ring 锁内复核,短 metadata 操作不会仅因 busy 被偷取。2 ms 是耗时启发式,不证明内核阻塞;控制线程每 2 ms 检查救援,队列线程空闲时无限期等待。 + +普通 I/O 在 handle map 和 admission 短锁下取得原生 Arc lease,不取得 namespace operation guard。namespace 仍串行,但只关闭相关文件、目录子树及已知对象别名的 handle admission,并等待对应 I/O;release 只等自身 handle。局部屏障保持到修改完成,失败也释放;等待时无关 handle 的新 I/O 仍能进入。快照、恢复、销毁继续使用全局 admission 屏障并排空全部 I/O。writeback 和宿主 FUSE dispatch 未改。 + +候选冻结 GNU release/gateway 制品 SHA256 为 `ae221676e4bb04231c88f2ce17c2570824f4064f8a3a63d6ef32817b0e2afa98`。同批参照为上轮并发 `build2` 和更早的 `candidate6`,后者是恢复参考,不是 `cf8bfa5` 的字面父提交。沿用原七项 workload、固件、完整工具输入与计时边界;Linux/KVM/FUSE/Btrfs,整父组 CPU 0,1、两核 quota、16 GiB、swap 0,VM 2 vCPU/16 GiB。每样本独立 namespace、pivot_root、工作区和工具缓存,热宿主缓存,新的 Host listener/VM 启动退出计入完整任务,输入和 namespace 准备不计时,profiling 关闭。 + +用户要求将 180 样本缩到 60:原批次取消并单独保留,新批次三个版本、两个后端各 3 次预热、10 个随机配对轮次,seed 20261010。60 个计时任务和 18 次预热全部通过原 workload、Run Bundle、隔离和 256 × 64 KiB upper 全字节检查,输入与制品前后校验一致,未检测到外部构建/测试干扰。0.5 s 检测可能漏掉短暂或不可见活动。有效慢样本全部保留,不报告 P95/P99;5,000 次配对 bootstrap 区间仅供探索。 + +### 快速结果与剩余问题 {#direct-queue-fast-results} + +单位 ms,每臂 N=10;前版为同批并发制品,候选为直接队列制品。95% CI 是中位数差的探索区间,全部七项、内部总耗时、Ready/Result 和正常退出分别列出。 + +| 后端 | 指标 | 前版 ms | 候选 ms | 变化 | 95% CI ms | 结论 | +| --- | --- | --- | --- | --- | --- | --- | +| staged | 首条有效输出 Ready | 218.77 | 217.92 | -0.39% | [-2.07, +2.81] | 未检出差异 | +| staged | 启动至完整结果 | 1432.62 | 1431.14 | -0.10% | [-9.49, +15.92] | 未检出差异 | +| staged | 启动至正常退出 | 1668.79 | 1667.37 | -0.09% | [-20.76, +28.91] | 未检出差异 | +| staged | 内部七工具总耗时 | 1213.94 | 1213.16 | -0.06% | [-7.08, +16.04] | 未检出差异 | +| staged | 遍历 2,048 文件 | 66.31 | 65.26 | -1.58% | [-1.32, -0.31] | 更快 | +| staged | 读取并校验 64 MiB | 68.04 | 68.75 | +1.03% | [-1.30, +4.92] | 未检出差异 | +| staged | 写入 256 × 64 KiB | 27.67 | 27.12 | -1.99% | [-1.64, +0.56] | 未检出差异 | +| staged | git status | 143.37 | 141.31 | -1.43% | [-5.07, +1.35] | 未检出差异 | +| staged | rg | 106.64 | 106.09 | -0.52% | [-1.78, +0.69] | 未检出差异 | +| staged | 离线 Cargo 编译 | 77.50 | 76.54 | -1.24% | [-1.90, +9.94] | 未检出差异 | +| staged | 离线 npm 安装 | 226.99 | 230.30 | +1.46% | [+0.62, +6.67] | 更慢 | +| VM | 首条有效输出 Ready | 691.96 | 693.36 | +0.20% | [-7.61, +5.32] | 未检出差异 | +| VM | 启动至完整结果 | 4408.21 | 4303.32 | -2.38% | [-198.58, +264.50] | 未检出差异 | +| VM | 启动至正常退出 | 4595.45 | 4484.43 | -2.42% | [-222.17, +248.70] | 未检出差异 | +| VM | 内部七工具总耗时 | 3707.29 | 3606.71 | -2.71% | [-200.10, +264.86] | 未检出差异 | +| VM | 遍历 2,048 文件 | 148.97 | 142.88 | -4.09% | [-34.00, +57.34] | 未检出差异 | +| VM | 读取并校验 64 MiB | 116.58 | 116.24 | -0.29% | [-5.80, +2.48] | 未检出差异 | +| VM | 写入 256 × 64 KiB | 153.00 | 157.08 | +2.67% | [-21.79, +28.41] | 未检出差异 | +| VM | git status | 404.51 | 387.07 | -4.31% | [-43.16, +362.83] | 未检出差异 | +| VM | rg | 453.05 | 418.87 | -7.54% | [-52.35, -19.30] | 更快 | +| VM | 离线 Cargo 编译 | 508.94 | 488.07 | -4.10% | [-30.01, -11.13] | 更快 | +| VM | 离线 npm 安装 | 975.21 | 929.92 | -4.64% | [-142.85, -34.51] | 更快 | + +相对上轮并发版,VM 内部总耗时 P50 为 3707.29 → 3606.71 ms(-2.71%),差值区间 [-200.10, 264.86] ms;完整任务为 4595.45 → 4484.43 ms(-2.42%),区间 [-222.17, 248.70] ms。二者均跨零,不能宣称整体已恢复。 + +- rg 为 453.05 → 418.87 ms(-7.54%),差值探索区间 [-52.35, -19.30] ms。 +- Cargo 为 508.94 → 488.07 ms(-4.10%),区间 [-30.01, -11.13] ms。 +- npm 为 975.21 → 929.92 ms(-4.64%),区间 [-142.85, -34.51] ms。 +- 写入为 153.00 → 157.08 ms(+2.67%),区间 [-21.79, 28.41] ms,未检出改善。metadata/read/Git 也未检出差异。 + +相对同批 `candidate6`,VM 内部总耗时为 3621.29 → 3606.71 ms(-0.40%),区间跨零;rg/npm 也未检出差异。写入为 118.64 → 157.08 ms(+32.40%),探索差值区间 [9.88, 63.74] ms,仍是未解决的重点。staged 内部总耗时为 1213.94 → 1213.16 ms,完整任务为 1668.79 → 1667.37 ms,均未检出差异;metadata/npm 的小样本变化不归因于未使用的 VM 执行改动。以上只比较本批三个版本,不与上轮中位数拼接,也不能拆分三项修复各自的贡献。 + +363 项 VM 库测试通过、8 项跳过,Clippy 全 targets 与 warnings-denied rustdoc 通过。回归验证直接 kick 与停止、短请求不偷取、长请求积压的原 ring 完成、同对象等待、无关 namespace 修改及等待期间新的无关 I/O admission;快照与生命周期测试保留。测量构建后仅将既有 `cfg(test)` device 测试移至文件末尾以满足 lint,生产实现一致。 + +[快速统计与探索区间](direct-queue-filesystem-summary.csv) · [制品、取消记录与实验来源](direct-queue-provenance.csv)。原始证据在本地 `benchmark/.data/direct-queue-quick-20261011/`;源码冻结、构建、预检、取消批次、校验与静态检查记录在 `benchmark/pvisor/.data/direct-queue-retest-20261011/`。本轮不扩样、不更新跨运行时用户表;写入还需独立定位,未覆盖冷磁盘、并发吞吐或 macOS。 + +## Linux:并发重构复测,2026-10-11 {#concurrency-retest} + +**本轮未解决全部文件系统退化。** 相对重构前制品,staged 完整七项任务下降约 5.00%,但操作内部总耗时未检出差异;VM 操作内部总耗时上升约 4.78%,写入、rg 与 npm 均有可检出的退化。多队列、专有服务线程和 handle lease 的实现不能代替性能验收。 + +### 动机与实验设计 {#concurrency-retest-protocol} + +本轮检验专用 hiprio 服务、多个普通队列及队列线程、直接执行和 pending descriptor stealing、staged guest writeback、leased I/O 与启动/退出清理改动能否恢复性能。候选是当前未提交源码的冻结 GNU release/gateway 构建 `build2`,SHA256 为 `87ebb0658f2df6a84f9e4eca29c8d3d8bc01923817f04432780272c8b1b30309`。同批另外两臂为此前 `candidate6` 和只撤销 `65e65c93`、`09bd1d59` 的回退控制;回退控制保留其余改动和默认 stage 同步,并非 `cf8bfa5` 的字面父提交。此处是版本比较,不能把差值全部归因于某一个优化。 + +Linux/KVM/FUSE/Btrfs,同一 firmware 和完整工具输入。整父组固定 CPU 0,1、两核 quota、16 GiB、swap 0;VM 为 2 vCPU、16 GiB。每臂、每后端 3 次预热、30 个随机配对轮次,seed 20261010;热宿主缓存,每样本新工作区、工具缓存、user/mount/PID namespace 和 owned pivot_root。namespace 与输入准备不计时,新的 Host listener、VM 启动与退出计入完整任务。使用原七项 reference workload 及其计时器,正式测量关闭 profiling,不是 hyperfine。 + +### 数据与分析 {#concurrency-retest-results} + +单位 ms,每臂 N=30;前版为同批重构前优化制品,候选为并发重构制品。95% CI 是中位数差;分离分布保留各簇中位数与样本数。 + +| 后端 | 指标 | 前版 ms | 候选 ms | 变化 | 95% CI ms | 结论 | +| --- | --- | --- | --- | --- | --- | --- | +| staged | 首条有效输出 Ready | 229.99 | 219.25 | -4.67% | [-11.62, -9.23] | 更快 | +| staged | 启动至完整结果 | 1452.13 | 1424.04 | -1.93% | [-38.42, -8.24] | 更快 | +| staged | 启动至正常退出 | 1755.90 | 1668.16 | -5.00% | [-99.79, -66.71] | 更快 | +| staged | 内部七工具总耗时 | 1221.64 | 1204.69 | -1.39% | [-21.77, +0.03] | 未检出差异 | +| staged | 遍历 2,048 文件 | 66.75 | 66.35 | -0.60% | [-0.71, +0.03] | 未检出差异 | +| staged | 读取并校验 64 MiB | 67.20 | 67.62 | +0.62% | [-0.82, +1.47] | 未检出差异 | +| staged | 写入 256 × 64 KiB | 27.76 | 26.67 | -3.92% | [-1.84, -0.68] | 更快 | +| staged | git status | 142.65 | 141.87 | -0.54% | [-2.02, +1.26] | 未检出差异 | +| staged | rg | 107.21 | 107.58 | +0.34% | [-0.41, +1.26] | 未检出差异 | +| staged | 离线 Cargo 编译 | 71.79 | 70.61 | -1.64% | [-2.77, +0.59] | 未检出差异 | +| staged | 离线 npm 安装 | 230.26 | 230.26 | -0.00% | [-2.28, +2.13] | 未检出差异 | +| VM | 首条有效输出 Ready | 701.29 | 693.92 | -1.05% | [-15.10, +0.22] | 未检出差异 | +| VM | 启动至完整结果 | 4278.45 | 4446.60 | +3.93% | [+65.69, +210.06] | 更慢 | +| VM | 启动至正常退出 | 4535.76 | 4635.26 | +2.19% | [-1.90, +148.13] | 未检出差异 | +| VM | 内部七工具总耗时 | 3578.18 | 3749.07 | +4.78% | [+107.48, +210.48] | 更慢 | +| VM | 遍历 2,048 文件 | 157.80 | 138.86 | -12.00% | [-35.21, +1.45] | 未检出差异 | +| VM | 读取并校验 64 MiB | 117.67 | 116.86 | -0.69% | [-3.52, +0.19] | 未检出差异 | +| VM | 写入 256 × 64 KiB | 131.05 | 163.68 | +24.90% | [+15.38, +42.35] | 更慢 | +| VM | git status | 375.05 (n=24); 749.92 (n=6) | 399.04 (n=25); 798.60 (n=5) | — | — | 分离分布,不作整体排名 | +| VM | rg | 426.74 | 450.55 | +5.58% | [+16.56, +36.92] | 更慢 | +| VM | 离线 Cargo 编译 | 501.38 | 502.77 | +0.28% | [-6.34, +10.58] | 未检出差异 | +| VM | 离线 npm 安装 | 923.22 | 973.34 | +5.43% | [+31.98, +62.59] | 更慢 | + +180 个正式任务、18 次预热均通过原任务、Run Bundle、隔离、upper 全部 256 × 64 KiB 文件的逐字节校验,完整输入清单前后相同。配对 CI 为 5,000 次同轮 bootstrap;慢有效样本全部保留,不报告 P99。相对 `candidate6`: + +- staged 完整任务 P50 为 1755.90 → 1668.16 ms,差值 -87.74 ms,95% CI [-99.79, -66.71];操作内部总耗时为 1221.64 → 1204.69 ms,差值区间 [-21.77, 0.03] 跨零。写入约快 3.92%,其余操作未检出差异。 +- VM 操作内部总耗时为 3578.18 → 3749.07 ms,差值 +170.89 ms,95% CI [107.48, 210.48]。写入为 131.05 → 163.68 ms(+24.90%),rg 为 426.74 → 450.55 ms(+5.58%),npm 为 923.22 → 973.34 ms(+5.43%),三项差值区间均未跨零;metadata、read、cargo 未检出差异。 +- VM 完整任务为 4535.76 → 4635.26 ms,差值区间 [-1.90, 148.13] 跨零,不能用退出改善掩盖内部任务变慢。Git 两臂均呈分离簇:基线 24/6 次的簇中位数为 375.05/749.92 ms,候选 25/5 次为 399.04/798.60 ms,不作整体中位数排名。 +- 相对同批回退控制,VM 内部任务仍慢 4.45%、写入慢 24.59%、rg 慢 6.17%、npm 慢 4.86%,这些差值区间均未跨零;staged 内部总耗时未检出差异。 + +首个正式批次因外部 Cargo 活动被拒绝,27 个已有样本单独保留;上面只使用新批次,0.5 s 外部构建/测试检测未检出干扰。检测可能漏掉短暂、不可见或未识别活动。初次 VM 预检还发现空队列检查与 guest 新 descriptor 发布之间的竞态,修复后重新冻结构建并通过预检;新增 5,000 次并发发布回归测试通过。相关库 633 项通过、19 项跳过,修复后 VM 库 361 项通过、8 项跳过;完整 CLI 集成测试有 Host listener/命名空间环境失败,不计作全套通过。 + +[完整统计与配对区间](concurrency-filesystem-summary.csv) · [制品、控制范围与实验来源](concurrency-retest-provenance.csv) · [启动与退出复测](vm-startup-performance-analysis.md#concurrency-retest)。原始报告与逐次证据在本地 `benchmark/.data/concurrency-formal2-20261011/`,构建、失败批次和校验脚本在 `benchmark/pvisor/.data/concurrency-retest-20261011/`。这些工程数据不替换历史跨运行时用户表,不证明冷磁盘、并发吞吐或 macOS 性能;需要单独诊断 VM 写入/工具成本与 staged 启动波动。 + +## Linux staged 的空闲请求调度 {#host-idle-dispatch} + +**同条件工程 A/B 中,空闲请求调度修复使 staged 写入耗时下降 19.27%、Git 下降 12.78%、搜索下降 12.88%,完整七项任务下降 2.86%;读取与启动未检出差异。** 结果只覆盖 Linux staged,不用于重算跨运行时用户表,也不代表 VM、macOS、冷磁盘或并发吞吐。 + +### 动机、实现与实验设计 {#host-idle-protocol} + +复制期间的响应能力与日常工具成本需要一起评估。原调度把空闲时的短请求也交给 worker,增加线程切换。修复让无需 copy preparation 的修改在修改队列空闲时直接执行;读请求仅在两条队列都空闲时直接执行。至少 64 KiB 的 READ 保留 worker 路径,维持大块读的流水处理。有已接纳请求或复制正在准备时,读请求仍进入独立有界队列;inode pin、FIFO、容量限制与关闭排空继续生效。基线指纹、journal、持久化和隔离检查保持原有语义。 + +2026-10-10,Linux x86_64 / Fedora 7.2.8-200.fc44.x86_64 / Ryzen 7 9700X / Btrfs。两份 release 制品来自同一冻结源码,候选只改变宿主 dispatch 实现、私有测试及 API/README 说明。整组 CPU 固定 0,1、quota 两核、内存 16 GiB、swap 0;热宿主缓存、每次新工作区和独立工具缓存,Python 不读取 bytecode,Node 缓存启用。七项负载与 [B-FS-TOOLS](../benchmarks/filesystem.md#reference-fs) 相同,stage 同步使用默认串行。 + +每个样本使用独立 user/mount/PID namespace 与 owned pivot_root,工作区和 stage 绑定同一 Btrfs 存储。namespace、制品复制与输入准备不计时,新的 Host listener 启动计入完整任务;因此绝对时间不与用户页的跨运行时配置拼接。每组 3 次预热、30 个随机配对轮次,seed 20261010,正式计时不开 profiling,使用原 reference task timer,并非 hyperfine。60 个正式任务及 6 个预热全部通过输出、Run Bundle、宿主未写入与 upper 全部 256 个文件的逐字节校验,输入清单前后相同。外部每 0.5 s 检查同用户构建/测试,发现干扰则拒绝整批;本组未检出干扰,该检查可能漏掉短暂、不可见或未识别的活动。 + +### 工程结果与正确性边界 {#host-idle-results} + +单位 ms,P50,每组 N=30;变化为候选相对基线,区间来自 5,000 次按同轮配对的 bootstrap。所有分布均未触发分离簇判据,不报告 P99。 + +| 指标 | 基线 ms | 修复 ms | 变化与 95% CI | +| --- | ---: | ---: | --- | +| 工具 Ready | 231.34 | 231.15 | -0.08% [-0.87%, +0.41%],未检出差异 | +| 完整七项任务与退出 | 1826.42 | 1774.15 | -2.86% [-3.57%, -1.22%] | +| 遍历 2,048 文件 | 67.69 | 66.88 | -1.20% [-1.99%, -0.81%] | +| 读取并校验 64 MiB | 72.05 | 70.12 | -2.68% [-6.31%, +4.80%],未检出差异 | +| 写入 256 文件 | 35.09 | 28.33 | -19.27% [-20.37%, -18.47%] | +| git status | 162.31 | 141.56 | -12.78% [-13.35%, -12.15%] | +| Ripgrep 搜索 | 122.74 | 106.93 | -12.88% [-13.50%, -12.17%] | +| 离线 Cargo 编译 | 79.37 | 77.06 | -2.92% [-4.21%, -1.63%] | +| 离线 npm 安装 | 231.70 | 229.50 | -0.95% [-1.74%, -0.06%] | + +完整任务中位数差为 -52.27 ms,95% CI [-65.35, -22.30] ms。读取差异区间包含零,不能宣称读取加速。68 项普通 Rust 测试、7 项真实 Linux FUSE 挂载测试、targeted Clippy 和 warnings-denied rustdoc 通过;保留复制期间 metadata/只读访问、队列饱和、FIFO、worker 与 inline 关闭排空、缓存通知和挂载并行压力覆盖。尚无该修复的 macOS 性能验收。 + +[空闲调度统计 CSV](host-idle-dispatch-summary.csv) · [源码、制品与实验来源](host-idle-dispatch-provenance.csv)。原始报告、stdout、预算与 namespace 收据在本地 `benchmark/.data/fr10fixf3/`;冻结源码、构建收据、发布校验与外部干扰记录在 `benchmark/pvisor/.data/filesystem-regression-20261010/`。原始及中间诊断样本保留,不混入正式统计。 + +## Linux VM 的 worker 调度与完成唤醒 {#vm-completion-dispatch} + +**同条件 Linux VM 工程配对中,worker 调度修复使完整七项任务与退出耗时下降 3.15%、读取并校验 64 MiB 耗时下降 3.06%、离线 Cargo 编译耗时下降 3.14%。其余指标未检出差异或呈分离分布。** 相对仅撤销两项近期改动的回退对照,遍历 2,048 文件、读取并校验 64 MiB、离线 Cargo 编译仍有可检出的成本,不能宣称全部单请求成本恢复。结果属于 B-FS-ENG,不与 staged 工程批次或跨运行时用户表合并,不外推 macOS、冷磁盘或并发吞吐。 + +### 动机、实现与实验设计 {#vm-completion-protocol} + +长读和目录请求交给线程池后,后续 metadata/hiprio 可以继续接纳,但无竞争请求的线程迁移、接收端 selector 和完成唤醒会增加工具延迟。修复在全部在途请求清空时复用固定的首个 worker,减少无竞争请求的线程迁移;已有在途请求时继续使用原共享 FIFO,保留突发负载的并行接收。专用空闲入口容量为 1,计入同一个总在途上限;排队、执行与未发布完成的总额度不变,RAM lease 仍由 owner 管理。owner 等待前先原子 arm,再检查完成 channel;worker 提交完成后原子 disarm,只有已 arm 才写唤醒。已有完成时零超时轮询,仍先检查停止事件。大 READ/目录请求保持异步,owner 独占 used ring,关闭时先排空再 join 所有 worker。 + +2026-10-11,Linux x86_64 / Fedora 7.2.8-200.fc44.x86_64 / Ryzen 7 9700X / Btrfs。三份 release 制品来自同一冻结的 10 月 10 日源码:回退对照只撤销 `65e65c93` 与 `09bd1d59`;当前基线保留这些优化和宿主空闲调度修复;候选相对当前基线只改变 VM worker 实现、私有测试与 crate README。回退对照保留其余源码和默认串行 stage 同步,与 10 月 6 日历史批次无关。 + +整父组 CPU 0,1、quota 两核、16 GiB、swap 0;VM 为 2 vCPU、16 GiB guest RAM,使用同一固件、工具与输入。热宿主缓存、每次新工作区与独立工具缓存、Python 不读写 bytecode、Node 缓存启用。每样本独立 user/mount/PID namespace、owned pivot_root、fresh VM 与 Host listener;namespace/输入准备不计时,启动与退出计入完整任务,Btrfs 工作区/stage 保持绑定。每版本 3 次预热、30 个随机配对轮次,seed 20261010,使用原 reference task timer,不是 hyperfine,正式计时关闭 profiling。 + +90 个正式任务及 9 个预热均通过输出、Run Bundle、宿主未写入、upper 全部 256 文件逐字节与输入前后清单校验。0.5 s 同用户构建/测试干扰门禁未检出活动,短暂、不可见或未识别活动可能漏检。所有有效慢样本保留;本批样本独立保存,不补入其他批次统计。 + +### 同条件结果与剩余成本 {#vm-completion-results} + +单位 ms,每版本 N=30。通常为 P50,分离簇列各簇中位数与数量且不作整体排名。区间为 5,000 次按同轮配对 bootstrap;不报告 P99。 + +| 指标 | 当前基线 ms | 修复 ms | 变化与 95% CI | +| --- | ---: | ---: | --- | +| 工具 Ready | 702.39 | 701.45 | -0.13% [-0.39%, +0.41%],未检出差异 | +| 完整七项任务与退出 | 4741.87 | 4592.36 | -3.15% [-6.14%, -1.23%] | +| 遍历 2,048 文件 | 154.58 | 159.74 | +3.34% [-11.45%, +10.03%],未检出差异 | +| 读取并校验 64 MiB | 120.47 | 116.78 | -3.06% [-3.89%, -0.96%] | +| 写入 256 文件 | 130.89 | 130.24 | -0.50% [-9.19%, +9.89%],未检出差异 | +| git status | 436.55 | 371.64 (25/30); 617.08 (5/30) | 分离分布,不作整体排名 | +| Ripgrep 搜索 | 417.92 | 424.90 | +1.67% [-1.01%, +5.40%],未检出差异 | +| 离线 Cargo 编译 | 523.22 | 506.79 | -3.14% [-4.46%, -0.53%] | +| 离线 npm 安装 | 926.99 | 923.10 | -0.42% [-1.88%, +0.63%],未检出差异 | + + +恢复程度必须使用同批回退对照,而非历史页面百分比: + +| 指标 | 回退对照 ms | 修复 ms | 变化与 95% CI | +| --- | ---: | ---: | --- | +| 完整七项任务与退出 | 4553.30 | 4592.36 | +0.86% [-1.41%, +2.43%],未检出差异 | +| 遍历 2,048 文件 | 132.99 | 159.74 | +20.12% [+1.50%, +35.87%] | +| 读取并校验 64 MiB | 114.36 | 116.78 | +2.12% [+0.80%, +3.71%] | +| 离线 Cargo 编译 | 488.23 | 506.79 | +3.80% [+2.84%, +5.71%] | + + +相对仅撤销两项近期改动的回退对照,遍历 2,048 文件、读取并校验 64 MiB、离线 Cargo 编译仍有可检出的成本,不能宣称全部单请求成本恢复。三版本对照包含近期调度与 journal 语义变化,不能把每个剩余差距全归因于一次 worker 切换。呈分离簇的指标不提供单一整体排名。359 项 VM 测试、targeted Clippy、warnings-denied rustdoc 和实际 CLI/guest 执行通过;包括空闲请求复用 worker、长请求后的 metadata 接纳、乱序完成、RAM lease 保持、就绪完成不休眠和冻结排空。macOS 性能未验收。 + +[VM 调度统计 CSV](vm-completion-dispatch-summary.csv) · [源码、制品与实验来源](vm-completion-dispatch-provenance.csv)。原始报告与各样本证据在本地 `benchmark/.data/fr10vmfixf/`,冻结源码、构建与发布校验在 `benchmark/pvisor/.data/filesystem-regression-20261010/`。这些绝对时间不与跨运行时用户页的配置拼接。 + +## 修复后的优化候选与诊断 {#optimization-candidates} + +**下一步优先减少路径解析中的重复物理检查,其次评估不可变 rootfs 的缓存契约,再优化目录请求和完成通知。** 三次插桩诊断支持这个调查顺序;尚未测得这些候选的加速比。 + +### 动机与实验设计 {#optimization-candidates-protocol} + +2026-10-11,按 B-FS-DIAG 使用上面的最终 `candidate5` 冻结 release 制品(SHA-256 `a4cfdbc6e2657f9ff6a43a4c640cd472c1c94c965c9d388924e58727dc55556d`),运行三次独立 VM 七工具任务。沿用相同输入、固件、私有 namespace/owned pivot_root、全父组 CPU 0,1 / 两核 quota / 16 GiB / swap 0 和工具缓存策略;打开 filesystem profile 与启动计时,stderr 写入普通文件。无预热,无版本对照,无外部构建/测试干扰监测;这些诊断不进入用户表,也不用于性能验收。 + +三次均通过工具输出、Run Bundle、宿主隔离和 256 个 upper 文件的完整字节检查,输入清单前后相同。每次有 13/13 个 final profile 实例,按 PID/component/instance 保留最后累计记录;同号 supervisor 与 VMM 实例分别处理。下表汇总两个有请求的 VM 实例,同一 label 的累计 span 可能跨 worker 重叠,不能推算任务耗时占比、预计加速或工具级归因。 + +### 请求成本与候选优先级 {#optimization-candidates-results} + +| 观察项 | 三次任务的范围 | 优化含义 | +| --- | ---: | --- | +| Core 路径解析 `resolve`,累计 inclusive span | 368.6–378.8 ms | 优先调查重复路径验证 | +| Core 父目录物理 stat,计数 | 186,401–187,722 | 深路径每项重复检查仍多 | +| Core 父目录身份 statx,计数 | 109,564–110,238 | 身份验证必须保留,评估同请求内复用 | +| worker 排队与完成等待,两种累计 span 合计 | 84.0–93.5 ms | 调度仍有成本,需保持长 I/O 响应性 | +| 两个 VM 实例的 used interrupt,计数 | 37,572–37,660 | 检查批处理与通知机会 | + +| 优先级 | 候选 | 需要验证的契约与实验 | +| --- | --- | --- | +| 1 | Core 与 VM backing 之间复用已验证的父目录/leaf 信息 | Core 已解析 metadata,VM native lookup 仍执行 openat/statx。评估在同一请求内传递已验证身份与有所有权的 backing handle;保留宿主替换、mount identity、硬链接拒绝和错误后的引用释放。分别测深路径 metadata、READDIRPLUS 与 Cargo。 | +| 2 | 显式不可变 rootfs 的 metadata/data cache | 只读 guest 挂载仍可能被宿主修改,需要输入生命周期或失效机制证明不可变。先减少 Core 物理查询,再评估 guest 请求数;live workspace 保持外部修改可见。分别测首次访问、热缓存与 TTL 到期。 | +| 3 | 分离普通 READDIR 与 READDIRPLUS 的服务路径 | 普通 READDIR 通常消费 OPENDIR 的名称/类型快照,PLUS 则逐项解析新属性。评估有界快照处理与批量父目录复用;旧 handle fallback、锁等待、大目录、删除、权限和 cookie/恢复行为均需保持。不能直接将所有目录请求改为 inline。 | +| 4 | worker 分发与完成/中断批处理 | 保持共享负载均衡、长读 offload、owner 发布 virtqueue、RAM lease 与 freeze drain。逐 worker 队列候选此前三次计时尝试被外部构建/测试干扰拒绝,未合入;先补混合长短请求与队列饱和正确性,再运行独立 30 轮 A/B。 | +| 5 | 指纹与持久化的重复工作 | workspace 每次有 2,157 次 content fingerprint,累计 46.3–51.9 ms。调查重复观测/批处理,保留首次修改前 preimage、journal 顺序、durability 和恢复;不可将延后记录当作低成本替代。 | + +该冻结制品的 Linux 本地读取通过 `preadv` 直接写入 guest descriptor buffer,诊断没有证明消除另一次中间拷贝能产生收益。DAX 和 writeback 现有独立开关结果分别见[只读 share 对照](#readonly-dax-ab)和[写合并对照](#writeback-ab);guest-local/block rootfs 尚无对应验收。这三次诊断没有按工具 checkpoint,不能由重叠 span 推断 metadata、read 或 Cargo 的耗时占比。 + +原始来源:`benchmark/.data/fr11explore/report.json`、冻结 `runner.py`/`child.py`/`harness/`、制品与源码回执、每任务 stderr/stdout、`diagnostic-summary.json` 与 `counter-summary.csv`。正式候选继续按 B-FS-ENG 的同源随机配对、3 warmups/30 轮、正确性与干扰门禁验收;通过后再考虑重测跨运行时用户矩阵。 + +## 宿主复制与指纹计算期间的并发访问 {#host-concurrency} + +**Apple M4 / APFS / macFUSE FSKit 的独立工程 A/B 表明:宿主调度与 journal 锁优化让其他文件的短请求与 1 GiB 复制或哈希重叠执行。** 改善的是阻塞期间的响应性;完整七项工具、Linux FUSE、VM 和冷磁盘没有对应的新性能结论。以下三组独立批次属于 B-FS-ENG,不合并样本,也不折算[跨运行时工具基线](../benchmarks/filesystem.md)。 + +### Motivation 与实验设计 {#host-concurrency-protocol} + +大文件首次写入触发 copy-up,首次读取观察触发内容指纹计算。需要同时检查其他文件访问的等待、空闲请求成本和长操作完成时间,才能判断调度改动的收益与代价。 + +测于 2026-10-10,Apple M4、24 GiB RAM、Darwin 27.0.0 / arm64、APFS、macFUSE 5.4.0 / FSKit,Rust 1.98.0 release;不固定 CPU 亲和性或全机资源限额。各 A/B 使用同一冻结源码与依赖,仅恢复/保留该项改动;copy-up 调度对照的基线文件来自 `392c9d99`,读队列与 journal 锁对照来自 `f109dd25`,后者候选源码为 `09bd1d59`。其他改动在各自 A/B 中保持一致;两次对照的候选不可当作同一批制品。 + +每格 3 次预热、30 个正式配对轮次,seed 4207 随机排列条件与 A/B 顺序;使用热缓存的 1 GiB 非稀疏确定性文件、新 upper/work 和独立路径。复制实验调用真实宿主挂载;journal 实验使用一个共享原生 Core,不经过 FUSE。复制临时文件必须确实未完成,或实际首次内容读取已发生且哈希线程仍活跃,探测才有效;没有人工延迟。读实验同时释放 OPEN、READ 和目录枚举,各自检查开始时仍在复制,避免前一条阻塞把后续探测变为空闲测量。 + +计时外校验完整 lower/upper 字节、身份、硬链接、已打开描述符、rename、临时副本清理、日志指纹和正常卸载;原生 journal 每试次校验全部 129 条观察。试次前、中途每 250 ms 和结束后检查可见构建/测试干扰;错误、未命中窗口或检出干扰使整批失败。报告中正式失败和慢样本剔除均为 0;保留所有有效慢样本。可见进程采样无法排除短暂或不可见活动。 + +表格单位 ms,每个基线/候选单元 N=30,通常为 P50;分离分布列出各簇中位数与样本数,不给整组变化排名。95% CI 使用 5,000 次配对轮次 bootstrap;P95 仅作参考,不报告 P99。正式计时没有 profile 插桩。 + +### Copy-up 调度:metadata 响应性 {#host-copy-up} + +| 日志配置 | 指标 | 基线 ms | 候选 ms | 中位数变化与 95% CI | +| --- | --- | ---: | ---: | --- | +| nojournal | 复制期间首次 stat | 802.858 | 0.681 | -99.915% [-99.919%, -99.911%] | +| nojournal | 复制期间 64 路径 stat | 836.938 | 20.999 | -97.491% [-97.545%, -97.422%] | +| nojournal | 1 GiB writable-open | 803.733 | 819.464 | +1.957% [-0.896%, +4.095%],未检出差异 | +| nojournal | 空闲 64 路径 stat | 34.558 | 34.596 | +0.108% [-8.278%, +7.639%],未检出差异 | +| compact | 复制期间首次 stat | 790.321 | 1.655 | -99.791% [-99.816%, -99.782%] | +| compact | 复制期间 64 路径 stat | 827.025 | 34.640 | -95.811% [-95.854%, -95.739%] | +| compact | 1 GiB writable-open | 1232.667 | 1231.293 | -0.111% [-1.280%, +1.470%],未检出差异 | +| compact | 空闲 64 路径 stat | 32.165 | 34.069 | +5.921% [+2.299%, +10.745%] | + +候选的 64 路径 stat 在两种配置中均为 30/30 次先于复制结束,基线首次 stat 等待复制结束。触发点在字节复制开始之后,因此不衡量之前的基线指纹等待。writable-open 包含指纹、复制、发布和协议返回,两种配置均未检出中位数差异。compact 的空闲批次增加 1.90 ms,95% CI [+0.76, +3.36] ms,不能宣称日常 metadata 全面加速。 + +### 独立读队列与 journal 指纹锁 {#host-read-journal} + +读挂载批次与上面的 metadata 批次独立。每种配置、每个候选读探测均为 30/30 次先于复制结束,基线为 0/30。 + +| 日志配置 | 指标 | 基线 ms | 候选 ms | 中位数变化与 95% CI | +| --- | --- | ---: | ---: | --- | +| nojournal | 复制期间只读 OPEN | 784.602 | 1.577 | -99.799% [-99.817%, -99.782%] | +| nojournal | 复制期间 held-fd READ,64 KiB | 784.597 | 0.521 | -99.934% [-99.936%, -99.927%] | +| nojournal | 复制期间 OPENDIR 与 64 项枚举 | 791.558 | 6.919 | -99.126% [-99.190%, -99.074%] | +| legacy | 复制期间只读 OPEN | 797.214 | 3.172 | -99.602% [-99.612%, -99.579%] | +| legacy | 复制期间 held-fd READ,64 KiB | 797.424 | 0.960 | -99.880% [-99.909%, -99.860%] | +| legacy | 复制期间 OPENDIR 与 64 项枚举 | 804.033 | 10.292 | -98.720% [-98.758%, -98.695%] | +| compact | 复制期间只读 OPEN | 800.275 (25/30); 2340.599 (5/30) | 2.219 | 分离分布,不作单一排名 | +| compact | 复制期间 held-fd READ,64 KiB | 800.486 (25/30); 2340.717 (5/30) | 0.760 | 分离分布,不作单一排名 | +| compact | 复制期间 OPENDIR 与 64 项枚举 | 807.137 (25/30); 2347.782 (5/30) | 9.737 | 分离分布,不作单一排名 | + +compact 基线读分布为 25/30 普通样本、5/30 慢样本,保留两个簇;不计算单一加速比例。这里的 OPEN 只计打开,完整 open/read/close 在 CSV 中单列,包含 FSKit 父目录 xattr 查询及只读 close 回调。 + +原生 journal 批次测 64 个其他路径的 `observe_read`,另记录哈希完成时间。读取观察保持非逐次持久化策略,最终 `sync_preimages` 在计时外执行。 + +| 日志配置 | 指标 | 基线 ms | 候选 ms | 中位数变化与 95% CI | +| --- | --- | ---: | ---: | --- | +| legacy | 首次无关读取观察 | 462.276 | 0.184 | -99.960% [-99.964%, -99.956%] | +| legacy | 64 路径读取观察 | 473.262 | 12.011 | -97.462% [-97.521%, -97.384%] | +| legacy | 完整 1 GiB 指纹计算 | 462.230 | 469.558 | +1.585% [+0.621%, +1.964%] | +| compact | 首次无关读取观察 | 450.714 | 0.045 (25/30); 0.071 (5/30) | 分离分布,不作单一排名 | +| compact | 64 路径读取观察 | 453.158 | 2.385 | -99.474% [-99.477%, -99.464%] | +| compact | 完整 1 GiB 指纹计算 | 450.739 | 452.237 | +0.332% [-0.398%, +0.783%],未检出差异 | + +legacy 的 64 路径观察等待显著减少,但完整哈希增加 7.329 ms,绝对 95% CI [+2.894, +9.066] ms;compact 哈希未检出差异。读批次的所有空闲探测与 journal 批次的空闲观察均未检出差异,不能据此证明开销相等。compact writable-open 的中位数降低 1.89%,95% CI [−3.13%, −0.45%];其余配置未检出差异。这是并发探测下的打开完成时间,不是独立存储带宽。 + +长操作尾部仍不确定:读批次无日志 writable-open 的参考 P95 从 952.6 ms 到 3166.9 ms,最大值从 1469.6 ms 到 4636.8 ms,慢样本全部保留。普通读写、目录快照与发布仍可能占用共享状态锁;独立队列不保证重叠读与修改的到达顺序,并发首次观察可能重复指纹工作。不能从短请求改善推导整个 Agent 任务或长操作均无回退。 + +### Stage 并行同步的测量状态 {#parallel-stage-sync} + +`PVISOR_STAGE_SYNC_WORKERS=2|4|8` 显式开启 stage sealing 的有界普通文件同步池;未设置或 `1` 保持串行。journal 校验和持久化先完成,文件同步全部成功且 worker 全部结束后,才执行自底向上的目录同步、父目录持久化及原子 seal 发布;失败不发布新 seal。工作负载的显式 fsync 与 journal 同步不使用该池。 + +**尚无独立串行/并行计时数据。** 上面的 A/B 没有改变 worker 数,不能用来量化该选项的收益。性能评估需同一冻结二进制、新 stage、相同 worker 以外的配置,计时包含完整持久化边界并验证 lower 不变、upper 完整字节与完成标记;不从实现存在推导加速幅度。 + +### 加工数据、来源与复现 {#host-concurrency-evidence} + +[Copy-up 统计 CSV](host-copy-up-summary.csv) · [读队列统计 CSV](host-read-summary.csv) · [Journal 锁统计 CSV](journal-lock-summary.csv)。CSV 直接复制已提交的加工结果,保留批次、样本数、分布、相对/绝对置信区间与原始报告摘要;制品摘要和源码收据见来源报告。完整空闲对照、参考尾部和极值保留在 CSV 中。 + +来源:[copy-up 工程报告](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/pvisor/HOST_COPY_UP_REPORT.md)、[读队列与 journal 锁工程报告](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/pvisor/HOST_READ_JOURNAL_REPORT.md)。原始日志、源码和制品在测量工作区的 `benchmark/pvisor/.data/host-copy-up-*`、`.data/read-*`、`.data/journal-*`,不作为站点下载发布。复现命令见[宿主 copy-up 手册](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/pvisor/README.md#host-copy-up-scheduling-ab-on-macos)与[读队列及 journal 锁手册](https://github.com/DeepLink-org/pvisor/blob/main/benchmark/pvisor/README.md#read-dispatch-and-journal-fingerprint-locks-on-macos);需要 macOS/macFUSE,不能用 Linux 上的 harness 测试代替挂载测量。 + ## 当前 Linux HOST 内核缓存能力与证据 {#host-kernel-cache} **显式所有权契约下,Linux HOST API 支持可写 `Metadata` 和只读 `MetadataAndData`;当前源码的正确性测试与独立请求计数有效,正式性能验收仍受阻。** 内核缓存与下方不可变 lower 的服务端物理元数据缓存是独立机制,均不自动改变产品执行器默认配置。 diff --git a/docs/src/zh/design/host-copy-up-summary.csv b/docs/src/zh/design/host-copy-up-summary.csv new file mode 100644 index 000000000..37941900e --- /dev/null +++ b/docs/src/zh/design/host-copy-up-summary.csv @@ -0,0 +1,9 @@ +benchmark,batch,condition,metric,unit,samples_per_arm,formal_failures,excluded_slow_samples,baseline_distribution,baseline_high_n,baseline_high_p50,baseline_low_n,baseline_low_p50,baseline_maximum,baseline_minimum,baseline_n,baseline_p50,baseline_p95_reference,candidate_distribution,candidate_high_n,candidate_high_p50,candidate_low_n,candidate_low_p50,candidate_maximum,candidate_minimum,candidate_n,candidate_p50,candidate_p95_reference,percent_change,ci95_percent_low,ci95_percent_high,median_difference_ms,ci95_difference_low_ms,ci95_difference_high_ms,report_sha256,bootstrap +B-FS-ENG,host-copy-up-formal-20261010,nojournal,first_stat_ms,ms,30,0,0,unsplit,,,,,3634.056499984581,773.0186250410043,30,802.857500500977,867.7273062290624,unsplit,,,,,1.1204580077901483,0.6125419749878347,30,0.6813124928157777,0.8123080275254324,-99.91513905115283,-99.9189855369621,-99.91109010023358,-802.1761880081613,-816.8258755176794,-794.943229004275,68c475e116a752f2029cdcb77798a747aff1e1da9147d1222ce869b1d5db15bb,5000 paired-round resamples; seed=4207 +B-FS-ENG,host-copy-up-formal-20261010,nojournal,copying_metadata_ms,ms,30,0,0,unsplit,,,,,3667.125958018005,808.33191704005,30,836.937708518235,903.5226732521551,unsplit,,,,,29.996583005413413,19.96750000398606,30,20.99912500125356,24.43401061464101,-97.4909572376143,-97.54533893196262,-97.42224995407759,-815.9385835169815,-830.5969170178287,-810.2244584879372,68c475e116a752f2029cdcb77798a747aff1e1da9147d1222ce869b1d5db15bb,5000 paired-round resamples; seed=4207 +B-FS-ENG,host-copy-up-formal-20261010,nojournal,open_ms,ms,30,0,0,unsplit,,,,,3634.9424170330167,773.8944999873638,30,803.7328124919441,868.3261270605726,unsplit,,,,,3249.2663330049254,772.2236250410788,30,819.4636040134355,2668.3761774387676,1.9572165372617745,-0.8961738589858936,4.09467614666541,15.730791521491483,-7.3310419684275985,32.76160449604504,68c475e116a752f2029cdcb77798a747aff1e1da9147d1222ce869b1d5db15bb,5000 paired-round resamples; seed=4207 +B-FS-ENG,host-copy-up-formal-20261010,nojournal,idle_metadata_ms,ms,30,0,0,unsplit,,,,,43.7037919764407,26.64250001544133,30,34.55812548054382,41.39485379564576,unsplit,,,,,44.494583969935775,27.319041022565216,30,34.59558301256038,40.166706405580044,0.10838994157147397,-8.27768514563212,7.638638248704233,0.037457532016560435,-2.914375509135425,2.479542017681524,68c475e116a752f2029cdcb77798a747aff1e1da9147d1222ce869b1d5db15bb,5000 paired-round resamples; seed=4207 +B-FS-ENG,host-copy-up-formal-20261010,compact,first_stat_ms,ms,30,0,0,unsplit,,,,,2496.950750006363,750.2186250058003,30,790.3214584803209,1632.986518763938,unsplit,,,,,2.092125010676682,1.0929999989457428,30,1.6546040133107454,1.954728623968549,-99.79064164390876,-99.81594063187839,-99.7815962367874,-788.6668544670101,-796.6828960052226,-779.6370619907975,68c475e116a752f2029cdcb77798a747aff1e1da9147d1222ce869b1d5db15bb,5000 paired-round resamples; seed=4207 +B-FS-ENG,host-copy-up-formal-20261010,compact,copying_metadata_ms,ms,30,0,0,unsplit,,,,,2532.46862499509,784.1280410066247,30,827.0250624918845,1667.264447780327,unsplit,,,,,37.64983295695856,32.7152909594588,30,34.64014548808336,37.418529522255994,-95.81147572678024,-95.85382866371097,-95.73939479771849,-792.3849170038011,-798.2175000070129,-781.1195210088044,68c475e116a752f2029cdcb77798a747aff1e1da9147d1222ce869b1d5db15bb,5000 paired-round resamples; seed=4207 +B-FS-ENG,host-copy-up-formal-20261010,compact,open_ms,ms,30,0,0,unsplit,,,,,2939.6734590409324,1189.8464169935323,30,1232.6674994837958,2075.7134746876554,unsplit,,,,,2999.5553749613464,1193.1392920087092,30,1231.2933119828813,2251.845103499364,-0.11148079279205136,-1.279514241934676,1.4696008595677323,-1.3741875009145588,-15.877063036896288,17.951396468561143,68c475e116a752f2029cdcb77798a747aff1e1da9147d1222ce869b1d5db15bb,5000 paired-round resamples; seed=4207 +B-FS-ENG,host-copy-up-formal-20261010,compact,idle_metadata_ms,ms,30,0,0,unsplit,,,,,43.17408398492262,27.599584020208567,30,32.16456301743165,39.65559376520105,unsplit,,,,,42.17404103837907,28.907125000841916,30,34.068979002768174,40.57833549159113,5.92085141745724,2.299077085046819,10.744671092543845,1.9044159853365272,0.7628744933754206,3.363728988915682,68c475e116a752f2029cdcb77798a747aff1e1da9147d1222ce869b1d5db15bb,5000 paired-round resamples; seed=4207 diff --git a/docs/src/zh/design/host-idle-dispatch-provenance.csv b/docs/src/zh/design/host-idle-dispatch-provenance.csv new file mode 100644 index 000000000..f6b087197 --- /dev/null +++ b/docs/src/zh/design/host-idle-dispatch-provenance.csv @@ -0,0 +1,22 @@ +field,value +benchmark_id,B-FS-ENG +batch,fr10fixf3 +report_sha256,3291766797d87996fcc8aa956fd90b4adbf7009c7f931a8f5eff286f346b9715 +host_kernel,7.2.8-200.fc44.x86_64 +baseline_binary_sha256,c6965986ac119544f0886e06806fcf41b814f54b54ad655978046a2449797c42 +candidate_binary_sha256,6d08e35b2f5ea9cdf1a01718d3303f7c6e06bb91216037bd48f4a175d5ae3e1d +baseline_source_manifest_sha256,a52c826c8d6c70632f4f3d8e36fda672fe8029b7f87cb5f7138a72824b6b5578 +candidate_source_manifest_sha256,e2ed6dda5e67800d803c6f2e178dfc0b5937bb5c9331b2f8ed7d0e2d31dedce9 +runner_sha256,fc06cadfb3b5dd34462ea6b1b12578f358a98bdfe410dd8cf17bf7134dbbe4cb +child_sha256,80418c8bad57bfcbd15ce01139e83de7bb2eb649a4d0785ab5680a4dded95489 +host_guard_sha256,66bfa5309994598d2f1cfc32ca1c132f02e1d48efa784b4fa60e3f11dd856e4c +launch_sha256,88b29f7000bf0bc3524547cd6213c326c63bc8abc91d363da5928ba262c705c4 +budget,"whole private parent: CPU 0,1; CPU quota 2; RAM 16 GiB; swap 0; resource observation off" +protocol,30 seeded randomly paired rounds; 3 warmups per arm; fresh workspaces and tool caches; no profiling; independent private user/mount/PID namespace and owned pivot_root per trial; native Btrfs workspace/stage bind; namespace and binary preparation outside timer; fresh Host listener included; full upper bytes and host isolation checked +path_alias,retained row paths normalize private /tmp/sample to the arm evidence directory; stdout bytes are preserved unchanged +scope,Linux staged only; not pooled with cross-runtime user cohort; no VM/macOS/cold/concurrent throughput claim +raw_location,benchmark/.data/fr10fixf3/report.json +changed_files,crates/pvisor-overlayfs/README.md;crates/pvisor-overlayfs/src/api.rs;crates/pvisor-overlayfs/src/dispatch.rs +statistics,5000 paired-round bootstrap resamples of median difference and median ratio; separated clusters have no whole-distribution ranking; no P99 +input_final_verification,passed +interference,none detected; same-UID build/test polling every 0.5 s; short/inaccessible/unrecognized activity may be missed diff --git a/docs/src/zh/design/host-idle-dispatch-summary.csv b/docs/src/zh/design/host-idle-dispatch-summary.csv new file mode 100644 index 000000000..e93377e27 --- /dev/null +++ b/docs/src/zh/design/host-idle-dispatch-summary.csv @@ -0,0 +1,10 @@ +benchmark_id,batch,backend,metric,unit,n,failures,baseline_p50_ms,candidate_p50_ms,baseline_distribution,candidate_distribution,baseline_clusters,candidate_clusters,median_difference_ms,ci95_low_ms,ci95_high_ms,change_percent,ci95_low_percent,ci95_high_percent,interpretation,report_sha256 +B-FS-ENG,fr10fixf3,pvisor-staged,ready_ms,ms,30,0,231.3429895,231.1496125,unsplit,unsplit,"{""n"": 30, ""p50"": 231.3429895, ""p95_reference"": 258.24731945, ""minimum"": 228.156094, ""maximum"": 721.567696, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 231.1496125, ""p95_reference"": 269.66952440000006, ""minimum"": 226.086159, ""maximum"": 334.906686, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.19337699999999813,-2.009503262500016,0.9543538749999989,-0.08358887399957382,-0.8668534654312147,0.41287355946471843,no detected difference,3291766797d87996fcc8aa956fd90b4adbf7009c7f931a8f5eff286f346b9715 +B-FS-ENG,fr10fixf3,pvisor-staged,completion_ms,ms,30,0,1826.4212615000001,1774.147928,unsplit,unsplit,"{""n"": 30, ""p50"": 1826.4212615000001, ""p95_reference"": 1886.6145353000002, ""minimum"": 1798.35404, ""maximum"": 2616.115118, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 1774.147928, ""p95_reference"": 2983.1745506000007, ""minimum"": 1753.962962, ""maximum"": 3944.151722, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-52.273333500000035,-65.34960165000021,-22.30310499999996,-2.862063347700472,-3.5741408369402943,-1.2217776782787793,faster,3291766797d87996fcc8aa956fd90b4adbf7009c7f931a8f5eff286f346b9715 +B-FS-ENG,fr10fixf3,pvisor-staged,metadata,ms,30,0,67.688012,66.8750695,unsplit,unsplit,"{""n"": 30, ""p50"": 67.688012, ""p95_reference"": 70.7873342, ""minimum"": 65.844761, ""maximum"": 74.339872, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 66.8750695, ""p95_reference"": 68.75847935, ""minimum"": 66.02054, ""maximum"": 70.891403, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.8129425000000055,-1.3601690000000133,-0.5477080000000001,-1.2010139993474889,-1.9937533501155458,-0.8103561838894572,faster,3291766797d87996fcc8aa956fd90b4adbf7009c7f931a8f5eff286f346b9715 +B-FS-ENG,fr10fixf3,pvisor-staged,read,ms,30,0,72.0473415,70.11834999999999,unsplit,unsplit,"{""n"": 30, ""p50"": 72.0473415, ""p95_reference"": 98.7668031, ""minimum"": 65.695082, ""maximum"": 114.985235, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 70.11834999999999, ""p95_reference"": 126.29148915000002, ""minimum"": 66.140483, ""maximum"": 179.850969, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-1.9289915000000093,-4.661307500000007,3.2695784999999944,-2.6773944185019105,-6.311997208869792,4.796779722316624,no detected difference,3291766797d87996fcc8aa956fd90b4adbf7009c7f931a8f5eff286f346b9715 +B-FS-ENG,fr10fixf3,pvisor-staged,write,ms,30,0,35.0920515,28.329621,unsplit,unsplit,"{""n"": 30, ""p50"": 35.0920515, ""p95_reference"": 36.1894698, ""minimum"": 34.582365, ""maximum"": 36.824261, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 28.329621, ""p95_reference"": 29.88571505, ""minimum"": 26.699338, ""maximum"": 31.289888, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-6.762430499999997,-7.179589499999999,-6.482372999999999,-19.270547633842373,-20.373777665160333,-18.472713563606924,faster,3291766797d87996fcc8aa956fd90b4adbf7009c7f931a8f5eff286f346b9715 +B-FS-ENG,fr10fixf3,pvisor-staged,git,ms,30,0,162.3064585,141.557841,unsplit,unsplit,"{""n"": 30, ""p50"": 162.3064585, ""p95_reference"": 177.79855020000002, ""minimum"": 160.367624, ""maximum"": 187.201031, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 141.557841, ""p95_reference"": 156.44679190000002, ""minimum"": 137.689894, ""maximum"": 163.478031, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-20.748617499999995,-21.786389050000015,-19.63463250000001,-12.783605588929781,-13.350054808993184,-12.149787988728688,faster,3291766797d87996fcc8aa956fd90b4adbf7009c7f931a8f5eff286f346b9715 +B-FS-ENG,fr10fixf3,pvisor-staged,rg,ms,30,0,122.74300450000001,106.9286245,unsplit,unsplit,"{""n"": 30, ""p50"": 122.74300450000001, ""p95_reference"": 124.06584275, ""minimum"": 120.99182, ""maximum"": 125.752858, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 106.9286245, ""p95_reference"": 109.06011240000001, ""minimum"": 105.246052, ""maximum"": 109.688326, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-15.814380000000014,-16.619792949999994,-14.843722499999998,-12.884139560067565,-13.501847285401436,-12.173678366971384,faster,3291766797d87996fcc8aa956fd90b4adbf7009c7f931a8f5eff286f346b9715 +B-FS-ENG,fr10fixf3,pvisor-staged,cargo,ms,30,0,79.374105,77.058392,unsplit,unsplit,"{""n"": 30, ""p50"": 79.374105, ""p95_reference"": 82.93333115, ""minimum"": 76.296244, ""maximum"": 86.754449, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 77.058392, ""p95_reference"": 569.1450376, ""minimum"": 74.912911, ""maximum"": 842.661286, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-2.3157130000000024,-3.378038699999999,-1.2891612874999974,-2.9174665969461944,-4.214848097245516,-1.6295569723169845,faster,3291766797d87996fcc8aa956fd90b4adbf7009c7f931a8f5eff286f346b9715 +B-FS-ENG,fr10fixf3,pvisor-staged,npm,ms,30,0,231.69645400000002,229.5019615,unsplit,unsplit,"{""n"": 30, ""p50"": 231.69645400000002, ""p95_reference"": 236.11639, ""minimum"": 226.46159, ""maximum"": 236.332118, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 229.5019615, ""p95_reference"": 235.8596702, ""minimum"": 222.832165, ""maximum"": 252.974519, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-2.194492500000024,-4.048117249999992,-0.13122049999998353,-0.9471411677280273,-1.739780613468427,-0.05722562147116861,faster,3291766797d87996fcc8aa956fd90b4adbf7009c7f931a8f5eff286f346b9715 diff --git a/docs/src/zh/design/host-read-summary.csv b/docs/src/zh/design/host-read-summary.csv new file mode 100644 index 000000000..7567ad0f1 --- /dev/null +++ b/docs/src/zh/design/host-read-summary.csv @@ -0,0 +1,28 @@ +benchmark,batch,mode,condition,metric,unit,samples_per_arm,failures,excluded_slow_samples,baseline_distribution,baseline_high_n,baseline_high_p50,baseline_low_n,baseline_low_p50,baseline_maximum,baseline_minimum,baseline_n,baseline_p50,baseline_p95_reference,candidate_distribution,candidate_high_n,candidate_high_p50,candidate_low_n,candidate_low_p50,candidate_maximum,candidate_minimum,candidate_n,candidate_p50,candidate_p95_reference,percent_change,ci95_percent_low,ci95_percent_high,median_difference_ms,ci95_difference_low_ms,ci95_difference_high_ms,report_sha256,bootstrap +B-FS-ENG,read-formal-20261010,reads,nojournal,open_ms,ms,30,0,0,unsplit,,,,,1467.6001670304686,727.9673750163056,30,784.6023124875501,950.3094731189776,unsplit,,,,,2.0554999937303364,1.167292008176446,30,1.576833485160023,1.9665085535962135,-99.79902767809074,-99.81675234968277,-99.78187322877787,-783.0254790023901,-793.8365415029693,-772.1069794788491,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,nojournal,open_complete_ms,ms,30,0,0,unsplit,,,,,1468.5537079931237,728.8003340363503,30,785.6761250004638,951.3767916854704,unsplit,,,,,3.0296669574454427,1.6788339708000422,30,2.2468749957624823,2.679954335326329,-99.71402020192976,-99.73612596711502,-99.69670926052945,-783.4292500047013,-794.0954170189798,-772.2922500106506,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,nojournal,read_ms,ms,30,0,0,unsplit,,,,,1467.5941249588504,728.0374999972992,30,784.5969375048298,950.3548499749741,unsplit,,,,,0.7355830166488886,0.3607079852372408,30,0.5213750118855387,0.72110828186851,-99.93354868124472,-99.93638603700258,-99.92703108163721,-784.0755624929443,-794.8402499896474,-770.0029794941656,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,nojournal,opendir_ms,ms,30,0,0,unsplit,,,,,1475.5269159795716,734.0120829758234,30,791.558166500181,957.3525714979041,unsplit,,,,,8.624417008832097,5.774791003204882,30,6.91893749171868,7.8491248248610646,-99.12590915177968,-99.19034255283533,-99.07428796449891,-784.6392290084623,-796.1403124791104,-769.7398550517391,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,nojournal,idle_open_ms,ms,30,0,0,unsplit,,,,,8.858042012434453,1.3563329703174531,30,1.9793754909187555,3.313037345651537,unsplit,,,,,3.896458016242832,1.2366669834591448,30,2.2158960055094212,2.803768770536408,11.949249431237586,-2.2766008540052396,20.597797709589962,0.2365205145906657,-0.0468534417450428,0.401749974116683,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,nojournal,idle_open_complete_ms,ms,30,0,0,unsplit,,,,,9.896000032313168,1.9259999971836805,30,3.064186981646344,4.434341986780055,unsplit,,,,,5.184125038795173,1.7114590154960752,30,3.363395982887596,4.02716686076019,9.764710934203213,-4.422795895504739,20.066967555659332,0.2992090012412518,-0.15222898218780756,0.5729374825023115,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,nojournal,idle_read_ms,ms,30,0,0,unsplit,,,,,0.9218340273946524,0.42150000808760524,30,0.6724790146108717,0.8842267328873277,unsplit,,,,,1.0244580334983766,0.43766701128333807,30,0.7467709947377443,0.9546792134642601,11.047479328386412,-9.681196863024189,23.15627781216896,0.07429198012687266,-0.06581255001947284,0.14406247646547854,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,nojournal,idle_opendir_ms,ms,30,0,0,unsplit,,,,,14.119667001068592,5.843000020831823,30,8.688895497471094,13.98289187636692,unsplit,,,,,14.713207958266139,5.559374985750765,30,10.010457976022735,13.021998229669407,15.209786778265233,-7.5136188921661855,26.71938430162628,1.321562478551641,-0.7691250357311219,2.1843129652552307,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,nojournal,writable_open_ms,ms,30,0,0,unsplit,,,,,1469.6194999851286,730.0400409731083,30,788.512979488587,952.5587270618416,unsplit,,,,,4636.813291988801,710.722749994602,30,789.5954585110303,3166.8974856351274,0.13728106582917565,-1.7803175611326383,2.7758275132282684,1.0824790224432945,-14.134833501884714,21.44658350152895,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,legacy,open_ms,ms,30,0,0,unsplit,,,,,4079.3276660260744,740.7681670156308,30,797.213895508321,3205.285191591247,unsplit,,,,,3.9670830010436475,2.7496250113472342,30,3.171958029270172,3.6743526259670034,-99.60211957579494,-99.6123422023328,-99.57909025695338,-794.0419374790508,-806.7137919424567,-781.1695204873104,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,legacy,open_complete_ms,ms,30,0,0,unsplit,,,,,4080.268750025425,741.74700002186,30,798.079416010296,3206.24652254628,unsplit,,,,,4.941958002746105,3.4635409829206765,30,4.113770497497171,4.700891155516729,-99.48454121043963,-99.50463951139422,-99.4540087594658,-793.9656455127988,-806.8653954542242,-781.2600409670267,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,legacy,read_ms,ms,30,0,0,unsplit,,,,,4079.449167009443,740.9073750022799,30,797.4235625006258,3205.3679455042584,unsplit,,,,,1.3484999653883278,0.6014580139890313,30,0.9599165059626102,1.2668725394178182,-99.87962275619842,-99.90923663145915,-99.86022054204186,-796.4636459946632,-809.3296039733104,-783.6247090017423,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,legacy,opendir_ms,ms,30,0,0,unsplit,,,,,4086.193875002209,748.3972500194795,30,804.0333125100005,3211.8898859334877,unsplit,,,,,11.42004202120006,9.477083978708833,30,10.292229504557326,11.231884008157067,-98.7199249901192,-98.75821953502975,-98.69517080959629,-793.7410830054432,-806.5722915052902,-780.7135004841257,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,legacy,idle_open_ms,ms,30,0,0,unsplit,,,,,5.049208994023502,1.8661249778233469,30,3.0317084747366607,4.967633690102957,unsplit,,,,,4.629125003702939,1.9340830040164292,30,2.9050830053165555,4.360628966242075,-4.176703349787092,-21.485888746623083,10.281873806672138,-0.12662546942010522,-0.7467505347449332,0.2952289942186326,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,legacy,idle_open_complete_ms,ms,30,0,0,unsplit,,,,,6.605250004213303,2.7859589899890125,30,4.162375000305474,6.209821350057609,unsplit,,,,,6.180915981531143,2.647208049893379,30,3.8770829851273447,6.136547855567187,-6.85406805386809,-20.864950837115526,8.549011515896442,-0.2852920151781291,-0.9798755054362118,0.34118699841201305,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,legacy,idle_read_ms,ms,30,0,0,unsplit,,,,,1.1060829856432974,0.5411250167526305,30,0.7092915184330195,0.9133918560110033,unsplit,,,,,1.4371249708347023,0.5126659525558352,30,0.6742085097357631,0.898106096428819,-4.946204456915337,-15.832598681641096,12.948118756594805,-0.035083008697256446,-0.11649902444332838,0.09027001215144992,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,legacy,idle_opendir_ms,ms,30,0,0,unsplit,,,,,16.912708000745624,8.036916027776897,30,11.7271039634943,14.228056548745371,unsplit,,,,,17.391290981322527,7.9338340437971056,30,10.882042028242722,15.272968751378357,-7.206058186933451,-20.18965769559886,13.147177778689656,-0.8450619352515787,-2.5633335462771356,1.3787500502076,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,legacy,writable_open_ms,ms,30,0,0,unsplit,,,,,4563.512124994304,1199.5800000149757,30,1267.0146664895583,3683.37038923637,unsplit,,,,,4630.5608749971725,1225.7922500139102,30,1267.3787495004945,1427.1339498605812,0.028735500903476208,-1.830411000342702,0.9592807960373539,0.3640830109361559,-23.540854483144358,12.134188000345603,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,compact,open_ms,ms,30,0,0,separated-clusters,5,2340.5992499901913,25,800.2748750150204,3229.997333022766,747.9514590231702,30,,2563.812739399146,unsplit,,,,,3.2600420527160168,1.7667919746600091,30,2.2190835152287036,2.848921046825126,,,,,,,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,compact,open_complete_ms,ms,30,0,0,separated-clusters,5,2341.4492500014603,25,801.4073329977691,3230.9647499932908,749.0443339920603,30,,2564.807000002475,unsplit,,,,,5.438917025458068,2.40358297014609,30,3.0509585049003363,3.6029980692546815,,,,,,,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,compact,read_ms,ms,30,0,0,separated-clusters,5,2340.716542035807,25,800.485708983615,3230.1703749690205,748.2522919890471,30,,2563.9766208012593,unsplit,,,,,1.1305000516586006,0.5438329535536468,30,0.7603749982081354,1.079644073615782,,,,,,,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,compact,opendir_ms,ms,30,0,0,separated-clusters,5,2347.781958000269,25,807.1369589888491,3236.994833976496,755.115499952808,30,,2570.192906109151,unsplit,,,,,11.764166003558785,8.47029103897512,30,9.737082989886403,10.700504359556362,,,,,,,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,compact,idle_open_ms,ms,30,0,0,unsplit,,,,,4.535374988336116,1.463791006244719,30,2.207520476076752,3.1118975195568055,unsplit,,,,,10.741165955550969,1.6459159669466317,30,2.254374965559691,3.077929187566042,2.122493992273622,-12.739470765315774,18.717609309564942,0.04685448948293924,-0.31593802850693464,0.39616605499759316,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,compact,idle_open_complete_ms,ms,30,0,0,unsplit,,,,,6.500458985101432,2.3755410220474005,30,3.3054789819289,5.548160921898671,unsplit,,,,,11.475832958240062,2.276624960359186,30,3.5406039969529957,5.324424992431887,7.113190442581163,-6.380163039835507,20.300573018660995,0.23512501502409577,-0.22566696861758828,0.6431675574276596,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,compact,idle_read_ms,ms,30,0,0,unsplit,,,,,1.8350000027567148,0.484999967738986,30,0.70477050030604,1.1332250287523493,unsplit,,,,,1.599457988049835,0.5460829706862569,30,0.6993120186962187,1.0646562412148342,-0.7745048363192009,-11.945557375284055,16.193849762775624,-0.00545848160982132,-0.09214604506269097,0.10735500836744905,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,compact,idle_opendir_ms,ms,30,0,0,unsplit,,,,,13.834165991283953,7.464417023584247,30,10.807042010128498,13.161814393242821,unsplit,,,,,13.926875020843,7.543832994997501,30,10.611874982714653,12.461610906757414,-1.805924574281581,-15.271794499301706,12.826513400154372,-0.19516702741384506,-1.6715420060791075,1.2616039894055575,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 +B-FS-ENG,read-formal-20261010,reads,compact,writable_open_ms,ms,30,0,0,unsplit,,,,,3701.6902919858694,1206.1046250164509,30,1275.5105630203616,3025.0988392363074,unsplit,,,,,1450.9638330200687,1200.4248749581166,30,1251.4398540079128,1362.5303499953588,-1.8871430555189028,-3.126423601885342,-0.4510966465714472,-24.070709012448788,-40.43987503973767,-5.648437014315277,e572ec69552e15688b3307d0d745d62fab17cb602c61243d4c5f1dcc8d175ec0,5000 paired-round resamples; seed=4207 diff --git a/docs/src/zh/design/index.md b/docs/src/zh/design/index.md index 0e00826b3..7b322d402 100644 --- a/docs/src/zh/design/index.md +++ b/docs/src/zh/design/index.md @@ -218,6 +218,8 @@ workspace checkpoint 只保存文件分支;Agent trajectory 保存模型上下 固定任务、输入、输出检查、版本和总预算,分别记录正确完成量、CPU、内存峰值、memory-time 与恢复延迟。监督成本还需要实际审查、拒绝和返工数据,不能用机器运行时间代替人的注意力。[研究方向](research/index.md)维护这些假设和接入边界;现有数据在[基准测试](../benchmarks/index.md)中保留各自测量范围。 +实验能力在[实验特性](experimental/index.md)中按平台、profile 和实施阶段登记。vCPU 空闲观测目前只交付 M0 观测原型,自动卸载仍待验证;guest 文件系统缓存处于 RFC 阶段,拟将元数据和普通只读 open 移到 guest 本地,同时保留宿主 stage 与权限边界。两条路线都需要用完整生命周期和资源预算检验净收益。 + ## 11. 回到整个系统 {#results} 现在可以把示例任务完整串起来:准入固定策略;Session 准备 VM、文件与网络;guest 经 virtio-fs 读取 lower,HTTPS 经受控出口建立连接;文件修改 copy-up 到 upper;执行结束后同步 stage、清理资源、保存事实;开发者最后通过 preimage 检查,把选中的候选文件发布到 target。 diff --git a/docs/src/zh/design/journal-lock-summary.csv b/docs/src/zh/design/journal-lock-summary.csv new file mode 100644 index 000000000..5c2503dc8 --- /dev/null +++ b/docs/src/zh/design/journal-lock-summary.csv @@ -0,0 +1,9 @@ +benchmark,batch,mode,condition,metric,unit,samples_per_arm,failures,excluded_slow_samples,baseline_distribution,baseline_high_n,baseline_high_p50,baseline_low_n,baseline_low_p50,baseline_maximum,baseline_minimum,baseline_n,baseline_p50,baseline_p95_reference,candidate_distribution,candidate_high_n,candidate_high_p50,candidate_low_n,candidate_low_p50,candidate_maximum,candidate_minimum,candidate_n,candidate_p50,candidate_p95_reference,percent_change,ci95_percent_low,ci95_percent_high,median_difference_ms,ci95_difference_low_ms,ci95_difference_high_ms,report_sha256,bootstrap +B-FS-ENG,journal-formal-20261010,journal,legacy,first_journal_ms,ms,30,0,0,unsplit,,,,,568.324916,458.69720800000005,30,462.2756875,495.60153545000003,unsplit,,,,,0.38699999999999996,0.124583,30,0.18412499999999998,0.35779360000000004,-99.96016987157691,-99.96382274198461,-99.95586138813522,-462.0915625,-466.22510399999993,-461.00487499999997,912a04d76e0dcf79f5d5800837ee913cebe4d8fc0fdffc7794fa6d555f34b7a0,5000 paired-round resamples; seed=4207 +B-FS-ENG,journal-formal-20261010,journal,legacy,journal_batch_ms,ms,30,0,0,unsplit,,,,,582.0345,468.937416,30,473.2618545,506.18555,unsplit,,,,,15.090667,11.177792,30,12.0108955,13.70676875,-97.46210361435331,-97.52085173909593,-97.3835427004595,-461.250959,-465.0704995,-459.908458,912a04d76e0dcf79f5d5800837ee913cebe4d8fc0fdffc7794fa6d555f34b7a0,5000 paired-round resamples; seed=4207 +B-FS-ENG,journal-formal-20261010,journal,legacy,idle_journal_ms,ms,30,0,0,unsplit,,,,,14.546417,10.37175,30,11.5576875,13.209233300000001,unsplit,,,,,14.919208,10.267833000000001,30,11.575124500000001,13.05685405,0.15086928072767236,-1.5641657096678085,1.7679201005688094,0.017437000000001035,-0.1838754999999992,0.2026249999999994,912a04d76e0dcf79f5d5800837ee913cebe4d8fc0fdffc7794fa6d555f34b7a0,5000 paired-round resamples; seed=4207 +B-FS-ENG,journal-formal-20261010,journal,legacy,hash_ms,ms,30,0,0,unsplit,,,,,568.166333,458.621959,30,462.22968749999995,495.5203,unsplit,,,,,477.01720800000004,465.995583,30,469.558313,473.19524605,1.5854943328364346,0.621484196309452,1.9643508230387408,7.328625500000044,2.8939379999999915,9.066229999999962,912a04d76e0dcf79f5d5800837ee913cebe4d8fc0fdffc7794fa6d555f34b7a0,5000 paired-round resamples; seed=4207 +B-FS-ENG,journal-formal-20261010,journal,compact,first_journal_ms,ms,30,0,0,unsplit,,,,,464.882458,447.15683299999995,30,450.7143125,460.11643925,separated-clusters,5,0.070541,25,0.045292,0.16804100000000002,0.043125,30,,0.07129725,,,,,,,912a04d76e0dcf79f5d5800837ee913cebe4d8fc0fdffc7794fa6d555f34b7a0,5000 paired-round resamples; seed=4207 +B-FS-ENG,journal-formal-20261010,journal,compact,journal_batch_ms,ms,30,0,0,unsplit,,,,,467.385042,449.662916,30,453.1578335,462.5730788,unsplit,,,,,2.736958,2.331709,30,2.3846875,2.71766265,-99.47376226918959,-99.47651529707625,-99.46397417187268,-450.773146,-452.92120900000003,-449.49152050000004,912a04d76e0dcf79f5d5800837ee913cebe4d8fc0fdffc7794fa6d555f34b7a0,5000 paired-round resamples; seed=4207 +B-FS-ENG,journal-formal-20261010,journal,compact,idle_journal_ms,ms,30,0,0,unsplit,,,,,2.917375,2.3904579999999997,30,2.4861875,2.742283,unsplit,,,,,3.22125,2.359,30,2.504521,2.9772981,0.7374142135297523,-1.5067998043199138,5.203211239337668,0.01833349999999978,-0.03820900000000016,0.1288545000000001,912a04d76e0dcf79f5d5800837ee913cebe4d8fc0fdffc7794fa6d555f34b7a0,5000 paired-round resamples; seed=4207 +B-FS-ENG,journal-formal-20261010,journal,compact,hash_ms,ms,30,0,0,unsplit,,,,,464.912333,447.199083,30,450.73900000000003,460.14889755,unsplit,,,,,466.439833,447.136333,30,452.237021,462.23250625,0.3323477666676178,-0.39781265752761863,0.7834892479944067,1.4980209999999943,-1.7978335000000243,3.518750000000068,912a04d76e0dcf79f5d5800837ee913cebe4d8fc0fdffc7794fa6d555f34b7a0,5000 paired-round resamples; seed=4207 diff --git a/docs/src/zh/design/readonly-dax-provenance.csv b/docs/src/zh/design/readonly-dax-provenance.csv new file mode 100644 index 000000000..b56fd8098 --- /dev/null +++ b/docs/src/zh/design/readonly-dax-provenance.csv @@ -0,0 +1,51 @@ +field,value +benchmark_id,B-FS-ENG +role,engineering A/B +batch,dax-20261011-final +raw_evidence,benchmark.new/filesystem/.data/dax-20261011-final +clock,guest CLOCK_MONOTONIC_RAW; phase only; verification excluded +cache,host-hot Btrfs; new guest; same-guest repeats +host,Linux-7.2.8-200.fc44.x86_64-x86_64-with-glibc2.43 +host_cpu,AMD Ryzen 7 9700X +guest_kernel,6.12.109 +subject_profile,performance; opt-level=3; Thin LTO +affinity,"0,1; inherited by full VM group" +cpu_quota,200000 100000 +cpuset_controller,not delegated; affinity and quota verified +memory_max_bytes,2147483648 +memory_swap_max_bytes,0 +guest_cpus,2 +guest_memory_mib,512 +nofile_limit,65536 +dax_window_bytes,134217728 +only_variable,guest mount dax=never vs dax=inode; same read-only DAX-capable subject +order,"metadata,small,sequential,mmap,parallel; independent backing files; shared window/reclamation" +warmups_per_arm,3 +formal_pairs,30 +formal_vm_runs,60 +failures,0 +excluded,0 +interference_scan_ticks,123 +detected_build_test_interference,0 +bootstrap_samples,5000 +bootstrap_estimator,paired resampling; median(on)-median(off) and median(on)/median(off)-1 +seed,20261011 +distribution_rule,"sorted gap >=1.5x, >=3 samples on each side, cluster medians >=1.7x => separated; no overall ranking" +read_parallel_repeated_passes,4; per-pass mean +mmap_repeated_passes,16; per-pass mean +correctness,"all 66 runs: statx selection, full bytes, namespace, normal zero exit; input/artifact inventories match; no OOM; stages reaped" +coverage_limit,immutable share only; no CLI writable workspace/startup/exit or cold storage result +bin/subject_sha256,dcf9d8cc6e52b032fa7d0fdc20616489c7bd3b34ae8ff957bf6a95183d750657 +bin/libkrunfw.so_sha256,b61f68dac3ef20a88e1ee387733e4baed2f7c02edac940c8882e0b549dac95e4 +immutable/workload_sha256,04f484ba1f035acd2484f6d0ca76dc504505279433b1f8e4dfa706f75e468da4 +harness/dax_ab.py_sha256,281960fb18a29c8d41fca6f588c12cd3930d5b1b64f8c47a5cc85d957b797044 +harness/dax_driver.rs_sha256,f24dc1b9c89e2f0d10a5e6df9ff5845219c6f6c07ae8b2b59920e1ab5aec6c1e +harness/dax_workload.c_sha256,fd712782fee61404746544a1c51ea6a2aa8b0cd025ec75b4ae036b27367fc158 +driver/Cargo.toml_sha256,f3d57fbe2d7027335a5b78926d8ac79b60fc6b16feefb77f85f3b10ea4fa6020 +driver/Cargo.lock_sha256,5aef3f760fb491e621434fb44160e986cd2ea1d327567288ff9b8e6e63a9d41f +protocol.json_sha256,de2c025a793ba54eb6b6a770b8062649431f1cf9c57258e996ed0446c934ad9d +records.json_sha256,6e530f00b59f17dd84c896c74de8da1d54a6d85aaf75aa63be63ba233ccf6e1d +summary.json_sha256,1b486c1a2048ad4cf9827b20dd241c15a7ef9de2f0024cf2e17b74adf9c00b4b +in.bench.json_sha256,240215cc199e49e707406b6ca2ffa505d8864991eeacbe54db3a25d2fb3dd9b2 +audit.json_sha256,acaa4792f8807d777410d3aec726045845133d97eaf113424174be4a197df8d9 +source_inventory_entries,1126 diff --git a/docs/src/zh/design/readonly-dax-summary.csv b/docs/src/zh/design/readonly-dax-summary.csv new file mode 100644 index 000000000..29a0daf50 --- /dev/null +++ b/docs/src/zh/design/readonly-dax-summary.csv @@ -0,0 +1,11 @@ +batch,metric,pairs,off_ms,on_ms,delta_percent,ci95_low_percent,ci95_high_percent,verdict +dax-20261011-final,metadata_first,30,129.09571449999999,130.062067,0.7485550575732081,-8.331332908565948,11.320616416738938,not detected +dax-20261011-final,metadata_repeat,30,0.9368765,0.968997,3.4284668256701956,-5.263369735224099,11.443024177112582,not detected +dax-20261011-final,small_first,30,40.208754,41.468747,3.133628562576196,-0.6843687800041409,5.740488924683174,not detected +dax-20261011-final,small_repeat,30,25.373077000000002,36.6305735,44.36788056884071,36.46610040529619,47.98132060479314,slower +dax-20261011-final,read_first,30,60.607162,65.8917175,8.719358118104914,7.1137647656107195,9.84490311050028,slower +dax-20261011-final,read_repeat,30,5.544313,4.580477,-17.384227766361672,-21.395220652225067,-10.101683557067531,faster +dax-20261011-final,mmap_first,30,23.829177,104.75073,339.5902132918816,328.766260843115,352.398630591051,slower +dax-20261011-final,mmap_repeat,30,0.0964175,0.1252455,29.899136567531826,27.003940158837647,33.7086975848274,slower +dax-20261011-final,parallel_first,30,54.43383,38.2735545,-29.687926607405725,-32.155415115930666,-28.250685731462653,faster +dax-20261011-final,parallel_repeat,30,4.733574,4.162454,-12.065302031826263,-16.90299974114886,0.744292090068277,not detected diff --git a/docs/src/zh/design/vm-completion-dispatch-provenance.csv b/docs/src/zh/design/vm-completion-dispatch-provenance.csv new file mode 100644 index 000000000..5fa253d52 --- /dev/null +++ b/docs/src/zh/design/vm-completion-dispatch-provenance.csv @@ -0,0 +1,25 @@ +field,value +benchmark_id,B-FS-ENG +batch,fr10vmfixf +report_sha256,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +host_kernel,7.2.8-200.fc44.x86_64 +before_binary_sha256,b074594368e5655c0e6949dcd45e6eb958816f3cdfca53011911f6d059fcbfe8 +baseline_binary_sha256,6d08e35b2f5ea9cdf1a01718d3303f7c6e06bb91216037bd48f4a175d5ae3e1d +candidate_binary_sha256,a4cfdbc6e2657f9ff6a43a4c640cd472c1c94c965c9d388924e58727dc55556d +before_source_manifest_sha256,1068608e3b4e611871e0e00a755b8ddb4e9d9c1a5e48b4b46597d353937b603e +before_scope,same frozen source; reverses only 65e65c93 copy-up/dispatch and 09bd1d59 read-dispatch/journal changes; stage sync default unchanged +baseline_source_manifest_sha256,e2ed6dda5e67800d803c6f2e178dfc0b5937bb5c9331b2f8ed7d0e2d31dedce9 +candidate_source_manifest_sha256,b4c43ac9ddc7a12ff644f4dc940efba3e963a37fbce252f27b9a099164066f03 +runner_sha256,fc06cadfb3b5dd34462ea6b1b12578f358a98bdfe410dd8cf17bf7134dbbe4cb +child_sha256,80418c8bad57bfcbd15ce01139e83de7bb2eb649a4d0785ab5680a4dded95489 +host_guard_sha256,55ea8ee64ccd792d6f203da38cfe516f4e383ba9caf91bb06fe46576f2fc3701 +launch_sha256,cd3b3459e4bcdbbf137200054e284c8dc85017e1ce59944d284ab06b3b7aa05d +budget,"whole private parent: CPU 0,1; CPU quota 2; RAM 16 GiB; swap 0; resource observation off" +protocol,30 seeded randomly paired rounds; 3 warmups per arm; fresh workspaces and tool caches; no profiling; independent private user/mount/PID namespace and owned pivot_root per trial; native Btrfs workspace/stage bind; namespace and binary preparation outside timer; fresh VM launch and Host listener included; full upper bytes and host isolation checked +path_alias,retained row paths normalize private /tmp/sample to the arm evidence directory; stdout bytes are preserved unchanged +scope,Linux VM only; three-arm same-cohort comparisons; not pooled with staged or cross-runtime user cohorts; no macOS/cold/concurrent throughput claim +raw_location,benchmark/.data/fr10vmfixf/report.json +changed_files,crates/pvisor-vm/README.md;crates/pvisor-vm/src/devices/virtio/fs/worker.rs +statistics,5000 paired-round bootstrap resamples of median difference and median ratio; separated clusters have no whole-distribution ranking; no P99 +input_final_verification,passed +interference,none detected; same-UID build/test polling every 0.5 s; short/inaccessible/unrecognized activity may be missed diff --git a/docs/src/zh/design/vm-completion-dispatch-summary.csv b/docs/src/zh/design/vm-completion-dispatch-summary.csv new file mode 100644 index 000000000..62f363d2d --- /dev/null +++ b/docs/src/zh/design/vm-completion-dispatch-summary.csv @@ -0,0 +1,19 @@ +benchmark_id,reference,batch,backend,metric,unit,n,failures,baseline_p50_ms,candidate_p50_ms,baseline_distribution,candidate_distribution,baseline_clusters,candidate_clusters,median_difference_ms,ci95_low_ms,ci95_high_ms,change_percent,ci95_low_percent,ci95_high_percent,interpretation,report_sha256 +B-FS-ENG,baseline,fr10vmfixf,pvisor-vm,ready_ms,ms,30,0,702.3939865,701.4516415,unsplit,unsplit,"{""n"": 30, ""p50"": 702.3939865, ""p95_reference"": 808.14949285, ""minimum"": 687.619476, ""maximum"": 868.72758, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 701.4516415, ""p95_reference"": 804.2475959000001, ""minimum"": 691.019323, ""maximum"": 997.421687, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.942344999999932,-2.752516987500073,2.8529320625001042,-0.13416188323245315,-0.391114148694679,0.40816561589901446,no detected difference,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +B-FS-ENG,baseline,fr10vmfixf,pvisor-vm,completion_ms,ms,30,0,4741.868579,4592.3552565,unsplit,unsplit,"{""n"": 30, ""p50"": 4741.868579, ""p95_reference"": 5820.2319456000005, ""minimum"": 4497.116143, ""maximum"": 5981.512409, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 4592.3552565, ""p95_reference"": 5326.98575385, ""minimum"": 4377.759236, ""maximum"": 6562.385439, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-149.51332249999996,-297.75259800000094,-57.810381499999494,-3.153046526049663,-6.139649356853594,-1.2345936261486057,faster,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +B-FS-ENG,baseline,fr10vmfixf,pvisor-vm,metadata,ms,30,0,154.5752905,159.74082900000002,unsplit,unsplit,"{""n"": 30, ""p50"": 154.5752905, ""p95_reference"": 196.7367547, ""minimum"": 111.713649, ""maximum"": 197.391937, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 159.74082900000002, ""p95_reference"": 197.9649697, ""minimum"": 111.054683, ""maximum"": 229.822146, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",5.165538500000025,-18.704709500000007,14.823612712499976,3.3417621168889466,-11.451575593893605,10.026152135336996,no detected difference,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +B-FS-ENG,baseline,fr10vmfixf,pvisor-vm,read,ms,30,0,120.4666895,116.776761,unsplit,unsplit,"{""n"": 30, ""p50"": 120.4666895, ""p95_reference"": 137.1536455, ""minimum"": 115.152226, ""maximum"": 449.899657, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 116.776761, ""p95_reference"": 121.74813105, ""minimum"": 113.592056, ""maximum"": 140.507933, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-3.6899285000000077,-4.713184999999996,-1.1335625000000107,-3.0630280580591585,-3.8940370007429097,-0.9621710154964713,faster,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +B-FS-ENG,baseline,fr10vmfixf,pvisor-vm,write,ms,30,0,130.8912965,130.237845,unsplit,unsplit,"{""n"": 30, ""p50"": 130.8912965, ""p95_reference"": 509.23759750000005, ""minimum"": 96.253082, ""maximum"": 549.579803, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 130.237845, ""p95_reference"": 147.1340695, ""minimum"": 96.222624, ""maximum"": 151.850029, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.653451500000017,-12.501612999999992,11.757977000000011,-0.49923220066814267,-9.186748271715182,9.887533079905088,no detected difference,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +B-FS-ENG,baseline,fr10vmfixf,pvisor-vm,git,ms,30,0,436.545769,,unsplit,separated-clusters,"{""n"": 30, ""p50"": 436.545769, ""p95_reference"": 757.9528677, ""minimum"": 362.876494, ""maximum"": 781.502844, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": """", ""p95_reference"": 662.80974025, ""minimum"": 348.549234, ""maximum"": 730.702844, ""distribution"": ""separated-clusters"", ""low_n"": 25, ""low_p50"": 371.637144, ""high_n"": 5, ""high_p50"": 617.078816}",,,,,,,separated clusters; no whole-distribution ranking,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +B-FS-ENG,baseline,fr10vmfixf,pvisor-vm,rg,ms,30,0,417.9235195,424.8981695,unsplit,unsplit,"{""n"": 30, ""p50"": 417.9235195, ""p95_reference"": 438.58633055, ""minimum"": 372.132895, ""maximum"": 446.147432, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 424.8981695, ""p95_reference"": 442.5985308, ""minimum"": 391.163356, ""maximum"": 445.670106, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",6.974649999999997,-4.3014853499999735,21.828084637500062,1.6688819065135085,-1.0065899682415047,5.403448458678617,no detected difference,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +B-FS-ENG,baseline,fr10vmfixf,pvisor-vm,cargo,ms,30,0,523.217155,506.78623500000003,unsplit,unsplit,"{""n"": 30, ""p50"": 523.217155, ""p95_reference"": 558.5827725500001, ""minimum"": 505.388571, ""maximum"": 639.279189, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 506.78623500000003, ""p95_reference"": 546.5293922, ""minimum"": 489.259377, ""maximum"": 588.752572, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-16.430920000000015,-23.509490500000084,-2.757717174999903,-3.1403633927102437,-4.455254371495688,-0.5327595802016086,faster,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +B-FS-ENG,baseline,fr10vmfixf,pvisor-vm,npm,ms,30,0,926.9887335000001,923.0999919999999,unsplit,unsplit,"{""n"": 30, ""p50"": 926.9887335000001, ""p95_reference"": 994.1152894, ""minimum"": 891.468702, ""maximum"": 997.64239, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 923.0999919999999, ""p95_reference"": 957.64733845, ""minimum"": 847.70355, ""maximum"": 2157.706799, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-3.888741500000151,-17.679189000000065,5.820267000000058,-0.4195025634580851,-1.882554003609349,0.6313804659402678,no detected difference,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +B-FS-ENG,before,fr10vmfixf,pvisor-vm,ready_ms,ms,30,0,702.549837,701.4516415,unsplit,unsplit,"{""n"": 30, ""p50"": 702.549837, ""p95_reference"": 725.27700655, ""minimum"": 690.48279, ""maximum"": 745.307488, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 701.4516415, ""p95_reference"": 804.2475959000001, ""minimum"": 691.019323, ""maximum"": 997.421687, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-1.0981954999999743,-4.383062999999993,3.034041012500024,-0.15631567216489728,-0.6212667717275133,0.4344802511269897,no detected difference,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +B-FS-ENG,before,fr10vmfixf,pvisor-vm,completion_ms,ms,30,0,4553.2998769999995,4592.3552565,unsplit,unsplit,"{""n"": 30, ""p50"": 4553.2998769999995, ""p95_reference"": 5171.8396289, ""minimum"": 4375.251084, ""maximum"": 6064.830518, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 4592.3552565, ""p95_reference"": 5326.98575385, ""minimum"": 4377.759236, ""maximum"": 6562.385439, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",39.05537950000053,-65.00957449999987,109.30449549999958,0.8577379165664034,-1.4145246877866557,2.425865142059336,no detected difference,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +B-FS-ENG,before,fr10vmfixf,pvisor-vm,metadata,ms,30,0,132.986561,159.74082900000002,unsplit,unsplit,"{""n"": 30, ""p50"": 132.986561, ""p95_reference"": 194.86099745, ""minimum"": 109.04524, ""maximum"": 197.681179, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 159.74082900000002, ""p95_reference"": 197.9649697, ""minimum"": 111.054683, ""maximum"": 229.822146, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",26.754268000000025,2.2465518750000006,42.72792774999997,20.118023805428,1.5034244846752791,35.86653826558702,slower,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +B-FS-ENG,before,fr10vmfixf,pvisor-vm,read,ms,30,0,114.356567,116.776761,unsplit,unsplit,"{""n"": 30, ""p50"": 114.356567, ""p95_reference"": 121.2560414, ""minimum"": 110.975274, ""maximum"": 128.126917, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 116.776761, ""p95_reference"": 121.74813105, ""minimum"": 113.592056, ""maximum"": 140.507933, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",2.420193999999995,0.9247463875000029,4.215384499999999,2.1163576902409,0.8010390682132118,3.7146177573702754,slower,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +B-FS-ENG,before,fr10vmfixf,pvisor-vm,write,ms,30,0,128.7334605,130.237845,unsplit,unsplit,"{""n"": 30, ""p50"": 128.7334605, ""p95_reference"": 141.3955217, ""minimum"": 95.923001, ""maximum"": 158.291904, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 130.237845, ""p95_reference"": 147.1340695, ""minimum"": 96.222624, ""maximum"": 151.850029, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",1.5043844999999862,-8.28170449999999,8.014729000000017,1.1686041019614901,-6.226101820650198,6.475990285338895,no detected difference,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +B-FS-ENG,before,fr10vmfixf,pvisor-vm,git,ms,30,0,,,separated-clusters,separated-clusters,"{""n"": 30, ""p50"": """", ""p95_reference"": 743.2506656, ""minimum"": 347.876071, ""maximum"": 757.800732, ""distribution"": ""separated-clusters"", ""low_n"": 24, ""low_p50"": 371.36215749999997, ""high_n"": 6, ""high_p50"": 730.0854245}","{""n"": 30, ""p50"": """", ""p95_reference"": 662.80974025, ""minimum"": 348.549234, ""maximum"": 730.702844, ""distribution"": ""separated-clusters"", ""low_n"": 25, ""low_p50"": 371.637144, ""high_n"": 5, ""high_p50"": 617.078816}",,,,,,,separated clusters; no whole-distribution ranking,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +B-FS-ENG,before,fr10vmfixf,pvisor-vm,rg,ms,30,0,426.5778535,424.8981695,unsplit,unsplit,"{""n"": 30, ""p50"": 426.5778535, ""p95_reference"": 442.32714699999997, ""minimum"": 396.98717, ""maximum"": 445.517949, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 424.8981695, ""p95_reference"": 442.5985308, ""minimum"": 391.163356, ""maximum"": 445.670106, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-1.6796840000000088,-6.513258175000037,3.5822351250000133,-0.3937579005141689,-1.521132228535439,0.8491631897326141,no detected difference,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +B-FS-ENG,before,fr10vmfixf,pvisor-vm,cargo,ms,30,0,488.230866,506.78623500000003,unsplit,unsplit,"{""n"": 30, ""p50"": 488.230866, ""p95_reference"": 508.37289334999997, ""minimum"": 470.29451, ""maximum"": 535.109574, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 506.78623500000003, ""p95_reference"": 546.5293922, ""minimum"": 489.259377, ""maximum"": 588.752572, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",18.55536900000004,13.895840999999962,27.86026868750004,3.8005317345094047,2.8358804935130766,5.707160165494329,slower,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 +B-FS-ENG,before,fr10vmfixf,pvisor-vm,npm,ms,30,0,923.1478515,923.0999919999999,unsplit,unsplit,"{""n"": 30, ""p50"": 923.1478515, ""p95_reference"": 1368.2353354000004, ""minimum"": 761.534452, ""maximum"": 2025.01419, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 923.0999919999999, ""p95_reference"": 957.64733845, ""minimum"": 847.70355, ""maximum"": 2157.706799, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-0.04785950000007233,-17.101800000000026,12.256040999999982,-0.005184380803391342,-1.8228309470598059,1.3448264683470823,no detected difference,f982ffef15b17f3bf804b69d9a46753ef97ebeaa73168bb64eadbfe8c7815818 diff --git a/docs/src/zh/design/vm-startup-performance-analysis.md b/docs/src/zh/design/vm-startup-performance-analysis.md index ba8df3885..c14182089 100644 --- a/docs/src/zh/design/vm-startup-performance-analysis.md +++ b/docs/src/zh/design/vm-startup-performance-analysis.md @@ -1,5 +1,27 @@ # VM 启动:技术分析与实验记录 +## Linux:启动与退出修复复测,2026-10-11 {#concurrency-retest} + +**退出清理优化有明确收益,但启动退化尚未全部解决。** 相对 `cf8bfa5` GNU 制品,host Ready 减少约 9.11 ms、Exit 减少约 75.00 ms;VM Exit 减少约 48.26 ms,Ready 未检出改善。新版 staged Ready 出现两个明显分布簇,不能宣称已恢复。 + +### 动机与实验设计 {#concurrency-retest-protocol} + +本轮检验重复二进制读取/哈希、重复进程树清理和固定等待修复,并包含新的文件系统执行模型。三个控制版本为历史 `cfe36981` GNU release/gateway 制品、`cf8bfa5fe8ad3438a096a2293bcc411176d388c8` GNU release/gateway 制品和当前冻结 `build2`。所有 VM 使用同一动态 firmware,避免混用 GNU 与 musl;候选完整 SHA256 为 `87ebb0658f2df6a84f9e4eca29c8d3d8bc01923817f04432780272c8b1b30309`。两个历史版本与当前之间还有其他源码改动,不能只归因于哈希或清理。 + +B-STARTUP-REVISION-ENG,Linux/KVM/FUSE;每后端、每版本 3 次预热、60 个 seed 20261011 随机配对轮次,依次测 host、staged、VM,不与文件系统采样并行。整父组 CPU 0,1、两核 quota、16 GiB、swap 0,VM 为 2 vCPU、128 MiB。各版本持有独立 user/mount/PID namespace、owned pivot_root 与预热的 Host listener;输入准备、namespace 和服务首次启动在计时外。原 startup workload 从 Popen 计到精确 Ready 行及正常零退出;校验输出、Run Bundle、隔离和完整输入前后清单。正式计时不开 profiling,使用原 Python perf_counter_ns observer,不是 hyperfine。 + +### 数据与分析 {#concurrency-retest-results} + +540 次正式运行、27 次预热全部通过,未检测到外部构建/测试干扰,制品前后摘要一致。5,000 次同轮配对 bootstrap,保留所有有效慢样本;分离簇不作整体中位数排名,不报告 P99。相对 `cf8bfa5`: + +- host Ready 为 29.14 → 20.03 ms,差值 -9.11 ms,95% CI [-9.51, -8.72];Exit 为 99.16 → 24.16 ms,差值 -75.00 ms,CI [-75.24, -74.59]。相对历史 `cfe36981`,Ready 仍多 6.82 ms,CI [6.16, 7.27];Exit 仍多 0.25 ms,CI [0.02, 0.63]。退出主要退化已消除,但不能称全部指标恢复。 +- staged 基线 Ready 为 41.23 ms;候选为 19 次/41 次的两个簇,各簇中位数 32.17/121.95 ms,不给整体降幅。Exit 为 118.41 → 118.67 ms,差值区间 [-31.54, 16.60] 跨零,未检出改善;历史控制的 Exit 也呈分离簇,分别为 53 次/7 次、34.12/74.43 ms。 +- VM Ready 为 119.41 → 115.25 ms,差值 -4.16 ms,CI [-7.08, 0.77] 跨零;Exit 为 218.71 → 170.45 ms,差值 -48.26 ms,CI [-67.43, -33.36]。历史 `cfe36981` 同批 Ready/Exit 为 174.06/211.83 ms,当前更快;版本差异较多,不能据此归因于单项优化。 + +**整轮性能验收仍不通过:** staged 启动存在明显波动,且[同制品文件系统复测](filesystem-performance-analysis.md#concurrency-retest)显示 VM 写入和工具成本仍退化。需要独立诊断,不能拿 host 的退出收益替代其他后端或任务内部性能结论。 + +[完整统计、分布与配对区间](concurrency-startup-summary.csv) · [制品与实验来源](concurrency-retest-provenance.csv)。原始数据、每次 stdout/时序、Run Bundle、输入与构建收据在本地 `benchmark.new/startup/.data/concurrency-retest2-20261011/`。这些工程版本比较不重写用户页的历史跨运行时表,不覆盖冷缓存、并发负载、macOS 或服务首次启动;0.5 s 干扰检测可能漏掉短暂、不可见或未识别活动。 + ## 当前通用与裁剪内核的能力检查 {#kernel-capabilities} **裁剪配置通过了工作区暂存、开发工具、IPv4 HTTP 和执行快照恢复的这些检查。** 使用当前 CLI、相同 Linux 6.12.109 源码、补丁和 compact 打包实现,两种配置分别重建固件。冻结源码的 79 个输入文件和两份实际构建输入在前后通过完整摘要校验;CLI 的 1,063 个产品源码和依赖文件与当前工作树相同。测于 2026-10-06,Linux/KVM、CPU 0,1、配置 2 vCPU,工具预检采样资源;这些能力检查不作为正式计时。 diff --git a/docs/src/zh/design/writeback-filesystem-summary.csv b/docs/src/zh/design/writeback-filesystem-summary.csv new file mode 100644 index 000000000..6ea87bca7 --- /dev/null +++ b/docs/src/zh/design/writeback-filesystem-summary.csv @@ -0,0 +1,12 @@ +benchmark_id,batch,backend,metric,reference,condition,unit,n,failures,reference_p50_ms,candidate_p50_ms,reference_distribution,candidate_distribution,reference_clusters,candidate_clusters,median_difference_ms,ci95_low_ms,ci95_high_ms,change_percent,ci95_low_percent,ci95_high_percent,interpretation +B-FS-ENG,writeback-quick-20261011,pvisor-vm,ready_ms,enabled,disabled,ms,30,0,696.9494565,694.986362,unsplit,unsplit,"{""n"": 30, ""p50"": 696.9494565, ""p95_reference"": 712.4009224, ""minimum"": 683.565333, ""maximum"": 717.745967, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 694.986362, ""p95_reference"": 716.7755949499999, ""minimum"": 686.484271, ""maximum"": 719.396438, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-1.963094500000011,-4.96355296250002,2.394365699999976,-0.28166956465658366,-0.711456305702351,0.3442330024193646,no detected difference +B-FS-ENG,writeback-quick-20261011,pvisor-vm,result_ms,enabled,disabled,ms,30,0,4409.188655,4363.77111,unsplit,unsplit,"{""n"": 30, ""p50"": 4409.188655, ""p95_reference"": 4940.9638421, ""minimum"": 4029.662268, ""maximum"": 5265.56947, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 4363.77111, ""p95_reference"": 5159.0962155, ""minimum"": 4091.938518, ""maximum"": 5461.139328, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-45.417545000000246,-247.19498550000026,262.0467350125003,-1.030065813774994,-5.437974504970356,6.046697594907724,no detected difference +B-FS-ENG,writeback-quick-20261011,pvisor-vm,completion_ms,enabled,disabled,ms,30,0,4616.3745155,4564.8741415,unsplit,unsplit,"{""n"": 30, ""p50"": 4616.3745155, ""p95_reference"": 5171.99739015, ""minimum"": 4222.747388, ""maximum"": 5491.126034, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 4564.8741415, ""p95_reference"": 5374.2518042, ""minimum"": 4282.715599, ""maximum"": 5611.732468, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-51.50037399999928,-250.55795301249947,271.9566279999999,-1.1156021641459346,-5.257892084083037,6.0241089839853545,no detected difference +B-FS-ENG,writeback-quick-20261011,pvisor-vm,worker_ms,enabled,disabled,ms,30,0,3699.5548980000003,3660.6983525,unsplit,unsplit,"{""n"": 30, ""p50"": 3699.5548980000003, ""p95_reference"": 4242.751085, ""minimum"": 3334.736781, ""maximum"": 4550.259803, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 3660.6983525, ""p95_reference"": 4449.1540682, ""minimum"": 3398.779741, ""maximum"": 4768.843474, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-38.856545500000266,-258.99918149999985,263.6009412499999,-1.050303254616014,-6.6853570373825555,7.235655063411996,no detected difference +B-FS-ENG,writeback-quick-20261011,pvisor-vm,metadata,enabled,disabled,ms,30,0,148.926601,145.8935565,unsplit,unsplit,"{""n"": 30, ""p50"": 148.926601, ""p95_reference"": 183.48406035000002, ""minimum"": 108.516341, ""maximum"": 191.798087, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 145.8935565, ""p95_reference"": 190.69478625, ""minimum"": 108.840289, ""maximum"": 190.742598, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-3.033044500000017,-26.35526191249999,19.229239000000007,-2.0366035883676803,-17.44780390399727,13.625263081824613,no detected difference +B-FS-ENG,writeback-quick-20261011,pvisor-vm,read,enabled,disabled,ms,30,0,114.829478,116.2222105,unsplit,unsplit,"{""n"": 30, ""p50"": 114.829478, ""p95_reference"": 136.8899074, ""minimum"": 111.745819, ""maximum"": 143.110932, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 116.2222105, ""p95_reference"": 122.09985255000001, ""minimum"": 111.780323, ""maximum"": 133.880558, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",1.3927325000000081,-1.525919800000009,2.470849000000001,1.2128701830378397,-1.3180313620600361,2.161548355282683,no detected difference +B-FS-ENG,writeback-quick-20261011,pvisor-vm,write,enabled,disabled,ms,30,0,162.10058700000002,131.6145675,unsplit,unsplit,"{""n"": 30, ""p50"": 162.10058700000002, ""p95_reference"": 176.42817875, ""minimum"": 100.124991, ""maximum"": 193.086273, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 131.6145675, ""p95_reference"": 150.63688915, ""minimum"": 96.603164, ""maximum"": 155.468492, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",-30.486019500000026,-41.04980449999998,-20.17293277499999,-18.80685324106817,-25.058474230843046,-12.86551705502134,faster +B-FS-ENG,writeback-quick-20261011,pvisor-vm,git,enabled,disabled,ms,30,0,,,separated-clusters,separated-clusters,"{""n"": 30, ""p50"": """", ""p95_reference"": 770.411194, ""minimum"": 356.547428, ""maximum"": 772.120794, ""distribution"": ""separated-clusters"", ""low_n"": 23, ""low_p50"": 391.260942, ""high_n"": 7, ""high_p50"": 759.777391}","{""n"": 30, ""p50"": """", ""p95_reference"": 763.62356215, ""minimum"": 351.748717, ""maximum"": 768.235727, ""distribution"": ""separated-clusters"", ""low_n"": 21, ""low_p50"": 375.215455, ""high_n"": 9, ""high_p50"": 751.366224}",,,,,,,separated clusters; no whole-distribution ranking +B-FS-ENG,writeback-quick-20261011,pvisor-vm,rg,enabled,disabled,ms,30,0,420.04630199999997,427.67075850000003,unsplit,unsplit,"{""n"": 30, ""p50"": 420.04630199999997, ""p95_reference"": 441.54148365000003, ""minimum"": 399.870682, ""maximum"": 445.790778, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 427.67075850000003, ""p95_reference"": 445.77900589999996, ""minimum"": 406.710051, ""maximum"": 449.623597, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",7.624456500000065,1.0899594999999636,14.249818000000005,1.815146678758306,0.26007782187420503,3.4109237718902357,slower +B-FS-ENG,writeback-quick-20261011,pvisor-vm,cargo,enabled,disabled,ms,30,0,485.0502085,490.2938245,unsplit,unsplit,"{""n"": 30, ""p50"": 485.0502085, ""p95_reference"": 564.250258, ""minimum"": 468.755493, ""maximum"": 811.936019, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": 490.2938245, ""p95_reference"": 504.8452994, ""minimum"": 466.780379, ""maximum"": 506.569854, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}",5.243616000000031,0.3492803000000435,12.541900412500015,1.0810460253621423,0.07130699507785941,2.613647008284152,slower +B-FS-ENG,writeback-quick-20261011,pvisor-vm,npm,enabled,disabled,ms,30,0,941.861598,,unsplit,separated-clusters,"{""n"": 30, ""p50"": 941.861598, ""p95_reference"": 1450.4631543000005, ""minimum"": 759.464465, ""maximum"": 1787.042376, ""distribution"": ""unsplit"", ""low_n"": """", ""low_p50"": """", ""high_n"": """", ""high_p50"": """"}","{""n"": 30, ""p50"": """", ""p95_reference"": 1746.6509477500001, ""minimum"": 779.404457, ""maximum"": 1791.564318, ""distribution"": ""separated-clusters"", ""low_n"": 22, ""low_p50"": 937.7803074999999, ""high_n"": 8, ""high_p50"": 1661.332023}",,,,,,,separated clusters; no whole-distribution ranking diff --git a/docs/src/zh/design/writeback-provenance.csv b/docs/src/zh/design/writeback-provenance.csv new file mode 100644 index 000000000..10b66c013 --- /dev/null +++ b/docs/src/zh/design/writeback-provenance.csv @@ -0,0 +1,15 @@ +field,value +report_sha256,44a7e0ba8fd7e36f66bfd749db4a29c10f722230a589a5cc6750895cf2d109ea +raw,benchmark/.data/writeback-quick-20261011/report.json +launch_sha256,2026de69ba8814fa6cd5cb77a86fe61a56b367ea91d90208e05704f690d58e4a +guard_sha256,462b4a5ce08df697628c1acd0e5cee14b914cfc7ec8f981cd58245d6d721e0ff +protocol,"30 seeded shuffled paired VM rounds; 3 warmups per arm; same frozen binary, inputs, seven-operation workload and private namespace per trial; full written-byte and Run Bundle checks; profiles off" +binary_sha256,ae221676e4bb04231c88f2ce17c2570824f4064f8a3a63d6ef32817b0e2afa98 +source_manifest_sha256,f5d1759332f5393bf9ff56569018ccb443d889f4a028ee6df4563569329a5590 +runner_sha256,95df22d6c110834d3e84abb459bbb2b02a141bbbf92ea0f42412dbe68518c6de +child_sha256,1bbde1e30507e8d9e4038d0ccdb3d24997c9183915e3be4929a9d72988f90b79 +switch,PVISOR_VM_FS_WRITEBACK=1 versus 0; originating environment restored in dedicated execution worker; only disabled arm changes the flag; production defaults unchanged +budget,"whole owned parent CPUs 0,1; CPUQuota 200%; RAM 16 GiB; swap 0; VM 2 vCPU/16 GiB" +statistics,5000 paired-round bootstrap resamples; difference and ratio of group medians CI95; separated clusters prevent global ranking; no P95/P99 +scope,"B-FS-ENG Linux KVM/Btrfs warm caches, sequential task workload; no cold-cache, macOS or concurrent-throughput claim" +publication_script_sha256,7831bc3559630703ae23a2ae2f9d421da8d68fa01493513357bccba75e5b6f58 diff --git a/docs/translations.json b/docs/translations.json index b95d35cd4..5b9cbdbf2 100644 --- a/docs/translations.json +++ b/docs/translations.json @@ -1,18 +1,18 @@ { - "benchmarks/agent-tasks.md": "b4c7a078af759e2005cf26e8ee2e4c31dce6c7098d7c1e22ca4860795262e43d", - "benchmarks/cluster-scalability.md": "c43fb3328f86e4dd927badada43353ae2ff3e8a0d61c3e7c2201c46c52b809b6", - "benchmarks/compare-rl-infra.md": "08b500a4394ead21ce8bf2df288161165d9dac99d0583816e7286808d1a2d861", - "benchmarks/density.md": "9ac2774ca0c2d3dcb081cddaa4b21446c1e67a5e102a26e363f68ec58519c1eb", - "benchmarks/filesystem.md": "dbedbcb8cc039b78d097d98893a43b7fb9eccc7dd1bf295478eae7906f1e4bc3", - "benchmarks/index.md": "eaefeacd78cbc0256e1c1836eed888b0bf3d75ce3b9ebd2d4a4efaec2ba38ee8", - "benchmarks/isolation-tests.md": "c456843e5865985cdc16fea822fa9b48563e3af8d8675a54a3c7d1f0e952f096", - "benchmarks/lazy-image-startup.md": "7db9a0d7ddef94758227d5b6e6464e966c289bfe5a3f3ea75681287e15702edd", - "benchmarks/methodology.md": "f937b1fe796df6b4ab7ff1258909663b0ac98b2cc6f9c1e5be51ac5dee4c4e8c", - "benchmarks/network.md": "e9dd86a18a32d43f7cbb08b7981ba86eed143f08e768349de5f689a58c7bc823", - "benchmarks/replay-fidelity.md": "3a67f6ba2abfa4f7c014ca661ac2cc6a3a08296594abe29a97f3761a295f27f1", - "benchmarks/startup.md": "9cb01a1b6df92aad277342e97bead6d30e0336171c16e943d311ddbf2556fee0", - "benchmarks/supervision-cost.md": "b491c32520377c62d6f07fe503ccbd7f1c99563fa9ce917420a0a4f7ab1a534f", - "benchmarks/vm-memory/index.md": "593647e079c2baa9f8f966297a86b4238588ea7b24736945667b4ed3a5ad89c7", + "benchmarks/agent-tasks.md": "14bf4ddd0d58aea2db8b79fdef0a54af95bf2cde0a879b08164d1faec57e8f33", + "benchmarks/cluster-scalability.md": "5bf70d1c30586e3a084c0e5c8932383cf6b366a528fb9e1a43eabe2078cab2f6", + "benchmarks/compare-rl-infra.md": "28af7db93ff8621f16aa9fb612d52182fa1cee905910040ec5a0473f9d15e9f3", + "benchmarks/density.md": "f831a7957aca1201f1c0512e67342763a486b4f423f5b38daed6981a019a4302", + "benchmarks/filesystem.md": "9b4f786e57792ed71ac93cd2da7c1134d625f88b1e2e49119466a177dd2833e9", + "benchmarks/index.md": "f4e6a7a3302c40c14e26a8369075cc67ae520437e034716474050678f36eb699", + "benchmarks/isolation-tests.md": "11b2e4d5af6547b45539d8df37f5b77a8964a21b87b9374baaa85fcda0088892", + "benchmarks/lazy-image-startup.md": "943c04bb6d05f9223b002471430c43d7a08fd3a83d63a0ff37641d1727070fac", + "benchmarks/methodology.md": "0f7cb1ebb112978fa387e82faf6d2d7b41dc1a32baee5e42abb1b89d4477a39e", + "benchmarks/network.md": "ad4c2a14cc224f8e20e46f31b27245432ed49acc819784e314a926bf5b47821c", + "benchmarks/replay-fidelity.md": "e9499e98dd6a1d4065890b1388e6baeb186f677029e7e535b78fe5c0741d797d", + "benchmarks/startup.md": "c3a4b10a5b28faff07eb78b60264bb86b5e4fae786b0ba9e3a9e953f868bc187", + "benchmarks/supervision-cost.md": "2c2cc7fc0f3087a97615e21d2ae6777d18e2878d1adc3a5abd81e71ca999b9db", + "benchmarks/vm-memory/index.md": "de2d1a192c2d2e42ff6d506da603baf7b5f04ae59943c8ae19be7b630baf1efd", "cases/01-first-job.md": "706bb522c10a4bc17324a69b0ef025f53362b7450ad4dfc6be02865be3305c70", "cases/02-review-and-decide.md": "4cac11c747cfb43841f28e8daa0a71d13653f823d4718d6501842580af94b204", "cases/03-checkpoint-and-fork.md": "49281a3745d63381b47e0a9f9903536a1733b59df67bc58f9db53cf97d6a7b5a", @@ -62,10 +62,13 @@ "design/decisions/index.md": "a5235ec70a55b84c37035a5cae4004bbffdd3b191fcda14cca7a7987050a112c", "design/environment-snapshot.md": "3172b7848f558b06fc2e35c097a41f7fea6dd4c2c14587b2bb7a3233024a9666", "design/execution-model.md": "8182aecd82ab87fc16dadd02f1849fed9202f5bdf2dc6269d7c24e87a845b577", + "design/experimental/guest-stage-cache/index.md": "7bd617207e58e2f7bb8a140e3b4a1162db1a511b921defcd25538eee5867f69c", + "design/experimental/index.md": "8d680068189f3e9056e83dd6fe21920a400bbe880be02ee5f6cd91595362a740", + "design/experimental/vcpu-idle-offload/index.md": "30249391d0d37b438847f670bd9c6eb589b754d72b86b3a9f319d0ca0a79ac93", "design/failure-semantics.md": "91bad87a97df5d87efc6c11e8cfda22ee3946fb12e09186f028c6bb1d1a26822", - "design/filesystem-performance-analysis.md": "97f0a985819ccd88ef593747d77800119a1f4848b9d067aef5200991c0fdd7fe", + "design/filesystem-performance-analysis.md": "6eb4ad12bd30b8bc73339c36f5b80e78372acf7df3d6f8be14edba0920069ca2", "design/gateway.md": "bb0c97fd0e0fb14820a34db539ebb2875b0095bf189764d690b20d1898b5a889", - "design/index.md": "caa3b80ca71b97bada57947ba3a120640699d8559004d721967efa12945a1e4f", + "design/index.md": "719eb926a3993f0aa726bd99ddbba7b6dfe179c809b95354090fb41cb55adbae", "design/isolation.md": "8f47ebb7db484a5e8cc4d6bc2d99caac2ca039c437fc704bb7221f9c9321b957", "design/job-checkpoint-cli.md": "c304d251091c546c5f752b7279213ff2250c68b538e71d63cff387e386c7e04d", "design/journal.md": "7b3607cf18b9e5752bd5cd5e76eaa97718682bcca88129d423fa6bdaaa6c9a28", @@ -94,7 +97,7 @@ "design/shared-image-cache-storage.md": "792957de74a27a61d5b37f5e3f845ef40573515187c45d79a168eee689424175", "design/vm-memory-performance-analysis.md": "1e313d25bd208879447363605063624f705470ed25f3368f465407fbe4ce9a74", "design/vm-runtime.md": "a1e921cbeef5062b84a0f5eb0f8a3774139425e15f438c8e8641253e5bd72e57", - "design/vm-startup-performance-analysis.md": "50f4fcd68287d688c356eb2a93afb67da56aa04757fe079a3a0b3807fab30ae6", + "design/vm-startup-performance-analysis.md": "4b41dca9236a4747dd8491099438c03f178b7c21aa8827f3be2aedc8b6d6827c", "guides/agents/aider.md": "66d85777bbf9e54330d96e1654d9d4b111e6d3115a5e8910f9dc98144d1b9019", "guides/agents/claude-code.md": "fef3dcb668ea039347418423eeb1cfe79b3ebf8c7e96e3db0a865b5e0b2934b7", "guides/agents/codex.md": "3bf9e8c7fd6dba4b5cb216f5a8e5cbc394f6647f12d14e41aa13ecdfd52e57ac", diff --git a/docs/zensical.en.toml b/docs/zensical.en.toml index fd39c9a3b..41de3697a 100644 --- a/docs/zensical.en.toml +++ b/docs/zensical.en.toml @@ -4,6 +4,7 @@ site_name = "PolicyVisor" site_description = "Scaling autonomous agent execution for Agents, scripts, and automation." site_url = "https://deeplink-org.github.io/pvisor/en/" docs_dir = "src/en" +watch = ["src/en/benchmarks", "semspec_tables.py"] site_dir = "site/en" cache_dir = ".cache/en" dev_addr = "127.0.0.1:3001" @@ -185,6 +186,11 @@ nav = [ { "Records and version matrix" = "design/records-and-versions.md" }, { "Failure semantics and retries" = "design/failure-semantics.md" }, ] }, + { "Experimental features" = [ + { "Stages and scope" = "design/experimental/index.md" }, + { "EXP-001 (observation prototype): vCPU idle observation and automatic RAM offload" = "design/experimental/vcpu-idle-offload/index.md" }, + { "EXP-002 (design stage): Guest kernel filesystem cache" = "design/experimental/guest-stage-cache/index.md" }, + ] }, { "Research directions" = [ { "Research directions" = "design/research/index.md" }, { "External orchestration and single-node execution" = "design/research/cluster-execution.md" }, @@ -270,6 +276,8 @@ alternate_style = true [project.markdown_extensions."tables"] +[project.markdown_extensions."docs.semspec_tables"] + [project.markdown_extensions."toc"] permalink = true diff --git a/docs/zensical.zh.toml b/docs/zensical.zh.toml index fde4a44c1..732d88753 100644 --- a/docs/zensical.zh.toml +++ b/docs/zensical.zh.toml @@ -4,6 +4,7 @@ site_name = "PolicyVisor" site_description = "Scaling autonomous agent execution for Agents, scripts, and automation." site_url = "https://deeplink-org.github.io/pvisor/zh/" docs_dir = "src/zh" +watch = ["src/zh/benchmarks", "semspec_tables.py"] site_dir = "site/zh" cache_dir = ".cache/zh" dev_addr = "127.0.0.1:3000" @@ -185,6 +186,11 @@ nav = [ { "记录与版本矩阵" = "design/records-and-versions.md" }, { "失败语义与重试" = "design/failure-semantics.md" }, ] }, + { "实验特性" = [ + { "阶段与范围" = "design/experimental/index.md" }, + { "EXP-001(观测原型):vCPU 空闲观测与自动内存卸载" = "design/experimental/vcpu-idle-offload/index.md" }, + { "EXP-002(设计阶段):Guest 内核文件系统缓存" = "design/experimental/guest-stage-cache/index.md" }, + ] }, { "研究方向" = [ { "研究方向" = "design/research/index.md" }, { "外部编排与单机执行" = "design/research/cluster-execution.md" }, @@ -270,6 +276,8 @@ alternate_style = true [project.markdown_extensions."tables"] +[project.markdown_extensions."docs.semspec_tables"] + [project.markdown_extensions."toc"] permalink = true diff --git a/experimental/vcpu-idle-offload/README.md b/experimental/vcpu-idle-offload/README.md deleted file mode 100644 index 4bbb8ad7f..000000000 --- a/experimental/vcpu-idle-offload/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# EXP-001:vCPU 空闲观测与自动内存卸载 - -| 项目 | 当前状态 | -|---|---| -| 阶段 | **M0:观测原型;尚未通过全部退出门槛** | -| 核心目标 | 利用 VM 等待窗口降低驻留内存,宿主事件到来时自动恢复 | -| 默认行为 | 保持现有行为;尚无自动 offload 开关 | -| 首选路径 | pVisor vCPU/宿主观测,不修改 Agent 或 guest 内核 | -| HVF | 已接入 wait/timeout 观测与全 vCPU 聚合;未实际编译/运行验收 | -| KVM | 真实 VM API 与实验通过;KVM_RUN 内 Unknown,尚无可靠 idle 信号 | -| 现有基础 | pause/resume、RAM offload、device memory gate、Host backing commit | -| 主要新增 | 等待窗口观测、全 vCPU 聚合、策略 owner、wake/deadline 与 lost-wakeup 防护 | -| 尚未完成 | 完整 idle 来源、wake/deadline、自动 offload、HVF 验收与卸载收益实验 | - -## 用户价值 - -Agent 等待模型响应、输入或外部工具时,VM可能不需要执行,但仍保留大量驻留RAM。短等待让CPU自然休眠;有足够长的等待窗口时,评估自动offload,减少内存时间成本。唤醒后继续同一个live Attempt,不将其冒充持久checkpoint或重新启动。 - -选择是否卸载由宿主决定,依据窗口长度、内存压力、设备安全、恢复预算和支持范围;不对每次线程阻塞立即卸载。 - -## 阅读顺序 - -1. [设计](design.md):host-driven路线、后端差异、状态机、wake和安全边界。 -2. [实施计划](implementation-plan.md):从观察器到受限PoC、目标代码归属、验收矩阵与证据门。 -3. [第一版实现与验证](validation.md):实际 API、平台状态、真实实验及后续门槛。 - -## 当前约束 - -- 第一版不要求guest内核通知;仅保留PV通知作为将来精度增强。 -- 自动策略的观察是机会信号,不是“所有Linux进程都阻塞”的证明;安全卸载仍依赖CPU/设备quiescence与backing提交。 -- 初期只支持明确的普通file-backed RAM;现有cold pager/pool与whole-VM offload互斥,private restored COW不支持该discard路径。 -- deadline/wake支持不完整的平台或设备,只观察,不自动卸载。 -- 不解除人工pause、snapshot freeze或失败停驻,不授予guest Host控制权限。 - -现有手动offload数据只支持基础成本分析,不能宣称自动策略已经提升生产密度。当前观测结果不能授权自动卸载,设计不是验收结果。 diff --git a/experimental/vcpu-idle-offload/design.md b/experimental/vcpu-idle-offload/design.md deleted file mode 100644 index 1ae042a1f..000000000 --- a/experimental/vcpu-idle-offload/design.md +++ /dev/null @@ -1,88 +0,0 @@ -# EXP-001 设计:Host-driven idle offload - -## 1. 目标与非目标 - -不修改Agent与guest内核,从pVisor vCPU和宿主事件侧发现等待窗口,自动卸载RAM并在需要执行时恢复。观测回答“何时值得卸载”;现有CPU/device quiescence和backing commit回答“如何安全卸载”。 - -不证明所有Linux进程阻塞,不做通用checkpoint/migration,不冻结guest时间,不支持任意设备或所有内存机制叠加,不承诺生产密度倍率。 - -## 2. 后端观测 - -| 后端 | 现有接点 | 拟议方案 | 限制 | -|---|---|---|---| -| HVF | `vmm/macos/vstate.rs`的WaitForEvent/Timeout及等待channel;`hvf/mod.rs`读取虚拟timer | 等待进入/离开序列、可用deadline,独立owner聚合全部vCPU | WFE可能是同步等待;需验证其它timer/IRQ来源,不能只凭一个寄存器声称完整 | -| KVM | vCPU线程与`vmm/linux/vstate.rs`运行循环 | 先观察CPU时间/调度等候选窗口;核验可选KVM宿主事件来源 | KVM_RUN未返回可能正在执行;低CPU也可能宿主抢占;当前Hlt分支是Stopped,禁止直接改语义 | - -KVM tracepoint/eBPF或用户态halt处理只是候选路线,具体内核接口、权限、开销与兼容性未核验。第一阶段不改irqchip、不要求全局sysctl或特权追踪。无法可靠区分时标Unknown并拒绝自动操作。 - -M0 已定义观测状态:Executing、HandlingExit、WaitingForEvent、HostDescheduled、ManualPaused、Stopped、Unknown。只有全online vCPU等待且满足策略时产生候选。禁止把HostDescheduled归为guest idle;等待状态是机会,不是Linux runqueue真值。 - -## 3. 职责与接入 - -```mermaid -flowchart TD - Backend[KVM / HVF vCPU观测] --> Aggregate[全vCPU窗口与epoch聚合] - Events[宿主网络 / 控制 / deadline] --> Wake[独立wake锁存] - Aggregate --> Owner[Host自动策略owner] - Wake --> Owner - Owner --> Control[现有pause/offload/resume控制] - Control --> Boundary[CPU停驻 / device gate排空] - Boundary --> Commit[RAM writeback与Host backing提交] - Commit --> Park[Offloaded] - Park --> Owner -``` - -- `pvisor-vm`归属后端观测及CPU/device控制,新增公共契约必须集中于`api.rs`,不为实验暴露私有VMM模块。 -- `pvisor`归属策略、Host backing commit、Attempt生命周期、配置和可解释事件。 -- OverlayNet等事件源在guest RAM访问或可能阻塞的channel之前通知wake。 -- 单一control owner串行仲裁自动策略与手动pause/resume/checkpoint/cancel,不另造completion owner。 - -观测状态、快照与 `VcpuObservationControl` 已在 `api.rs` 实现,详见 [验证记录](validation.md)。本文 Host 自动策略 owner、wake/deadline 与自动 offload 状态机仍为设计,尚未实现。 - -## 4. Wake与deadline - -至少需要Attempt身份、CPU topology generation、观测sequence、idle epoch、pending wake sequence、停驻原因、可用deadline和clock domain。 - -wake在Candidate/Quiescing/Offloading/Offloaded阶段均latch;已有pending事件不得忽略。进入停止边界后重验观测和事件sequence。Host backing提交期间收到wake先排队,提交完成后立即恢复,不能提前打开writers。 - -网络数据、EOF、错误、连接完成优先作为语义wake,不用socket writable/普通ACK无条件触发。wake先于virtqueue/memory gate访问,由独立owner处理,设备线程不得持queue/proxy锁同步resume。恢复后重查挂起事件/ring,不仅依赖新EPOLLET边沿。 - -最早有效timer deadline需要覆盖目标profile,明确guest/host时间转换与提前恢复margin;未知或太短就跳过。watchdog只能作为PoC容错,不能代替精确timer支持。当前pause保留wall-time,不通过冻结时间隐藏过期timer。 - -## 5. 状态与失败 - -```mermaid -stateDiagram-v2 - Observing --> Candidate: 全vCPU等待窗口 - Candidate --> Observing: 过期或策略拒绝 - Candidate --> Quiescing: wake已登记且profile支持 - Quiescing --> Offloading: 安全边界重验通过 - Offloading --> Offloaded: runner与Host提交完成 - Offloading --> Resuming: 提交后处理锁存wake - Offloaded --> Resuming: 事件或deadline - Resuming --> Observing: gate与CPU恢复 - Quiescing --> Failed: 实际transition失败 - Offloading --> Failed: writeback或提交未知 - Resuming --> Failed: 恢复失败 -``` - -busy/unsupported/short-window是破坏性transition之前的正常拒绝。现有drain超时会fail_control,不能把poisoned runner当普通拒绝后继续执行。正常取消一个已开始的transition必须证明状态恢复,不能直接跳回Observing。 - -自动wake只解除该owner/epoch的自动停驻;人工pause、snapshot freeze、failed状态保持原语义。guest不能伪造Host权限或绕过资源限额。观察误判主要影响性能,但丢wake、timer缺失或绕过commit会破坏执行正确性。 - -## 6. 初期profile - -- 普通writable shared file-backed RAM、raw offload、同live Attempt。 -- 先单vCPU,SMP聚合/epoch验收后扩展。 -- 网络wake优先现有OverlayNet IPv4 TCP;明确timer与控制wake支持。 -- 无在途virtio-fs请求;慢I/O跳过,不对已有RAM lease强行discard。 -- 排除cold pager/pool、restored private COW、未守护设备、未知wake来源。 -- 后端deadline/wake未验收时,保持observe-only;不因HVF接点更明确就宣传macOS全平台已支持。 - -## 7. 策略与收益 - -先固定可解释的grace、deadline guard、最低再停驻间隔和thrash抑制,再探索pressure-aware策略。参数由实验决定,不从单次手动offload数据设置生产默认。 - -主要目标是内存时间积分下降,同时满足任务QoS;包括完整cgroup/PSS/cache/helper、CPU/IO、峰值、恢复延迟、吞吐、失败和有效结果。短阻塞只CPU休眠,长窗口才回收RAM,更长停驻再研究压缩backing。 - -先证明host-only观测足够有用;guest PV可选增强留待后续,不因追求“精确全部进程状态”无限扩大首版范围。 diff --git a/experimental/vcpu-idle-offload/implementation-plan.md b/experimental/vcpu-idle-offload/implementation-plan.md deleted file mode 100644 index a7864478e..000000000 --- a/experimental/vcpu-idle-offload/implementation-plan.md +++ /dev/null @@ -1,89 +0,0 @@ -# EXP-001 实施计划与验收 - -> 已实现 M0 观测 API、后端接点和实验;自动 idle/offload 尚未实现。已完成项与未验收项分开,详见 [验证记录](validation.md),不承诺固定排期。 - -## M0:基线与Observe-only - -- [ ] 核对当前后端idle、IRQ、timer、offload、device gate和Host commit链。 -- [x] 定义跨后端观测契约与Unknown语义,遵守VM公共API边界。 -- [x] 注册vCPU身份/topology generation、enter/exit sequence、等待窗口与拒绝原因。 -- [ ] HVF从现有wait/timeout路径采集;KVM先验证低成本候选观测,区分guest等待与宿主抢占。 -- [x] 接入单一 VM-local collector,只记录、不调用pause/offload;Host策略owner尚未实现。 -- [ ] 完成跨平台采集开销、退出清理与容量验收;KVM已完成有界采样与工程A/B,HVF尚未验收。 - -退出条件:能解释真实任务的等待窗口,不因无KVM exit、低CPU或人工pause误报精确idle;平台未提供可靠信息时Unknown。 - -## M1:可控wake闭环 - -- [ ] 增加独立wake latch,覆盖候选/排空/卸载/停驻全部窗口。 -- [ ] 网络数据/EOF/错误/连接完成在RAM访问与有界发送等待前登记。 -- [ ] 建立deadline clock domain、提前恢复margin与timer取消/重编程规则。 -- [ ] 将wake交现有Host控制owner,保证runner ACK后的backing commit顺序。 -- [ ] 仲裁自动、人工、snapshot、cancel与失败状态。 -- [ ] 验证resume后重查queue/backend,不丢已消费边沿。 - -退出条件:故障注入和可控交错证明lost wake不出现;deadline未知profile拒绝自动深停驻。mock通过后仍需真实VM验证。 - -## M2:单vCPU受限raw offload PoC - -- [ ] 默认关闭;拟议enable配置经实现评审后才命名、接CLI。 -- [ ] 普通file-backed RAM支持检查,排除pager/pool/private COW等。 -- [ ] 在途FS/未守护设备提前拒绝,不以真实drain失败作为常规策略。 -- [ ] 初期grace、deadline guard、重入冷却、wake频率抑制。 -- [ ] 真实delayed network、timer、控制wake下恢复同Attempt并校验全部内容。 -- [ ] offload/store/恢复失败沿用fail-stop与unknown,终态不伪造。 - -退出条件:正确性、权限与生命周期负对照通过;仅对验收的平台/profile标记受限PoC。 - -## M3:SMP与更多事件 - -- [ ] 全online vCPU聚合、跨CPUwake/IPI、迁移/topology变化竞态。 -- [ ] 一个CPU运行时不卸载;退出/恢复旧epoch不复用。 -- [ ] 扩展vsock、终端、signal等事件,不在proxy/queue锁内同步切换。 -- [ ] 多连接、streaming、backpressure、恢复风暴与deadline竞争。 - -退出条件:平台支持矩阵逐格记录,不以SMP mock替真实guest验收。 - -## M4:收益实验与是否继续 - -- [ ] 按`benchmark/README.md`先登记自动策略的问题、角色、runner与统计协议。 -- [ ] 同任务/硬件/预算/制品,自动策略on/off随机配对,多轮;手动策略可独立对照。 -- [ ] waiting/compute/短阻塞/streaming/timer-heavy/恢复风暴分开。 -- [ ] memory-time integral、cgroup/PSS/cache、全组peak、CPU/IO、恢复及完整任务延迟、失败和有效吞吐。 -- [ ] 计入观察者/全局扫描器等组外成本的说明,不用低PSS或配置容量估生产密度。 -- [ ] 正确性失败不算收益;保留失败、干扰与完整来源,不挑样本。 - -晋级条件:在明确用户负载上有净资源收益且满足QoS,测试可复现、操作可解释。若thrash、CPU/IO或恢复代价抵消收益,就调整策略或退役,不为保留功能强行上线。 - -## 代码归属建议 - -| 归属 | 职责 | 验证 | -|---|---|---| -| `crates/pvisor-vm/src/api.rs`与私有后端 | 观测契约、wait/deadline、CPU控制与device gate | API boundary、状态/epoch、timer与RAM正确性 | -| `crates/pvisor/src/executor/vm/` | 观测/控制通道、backing提交、支持profile | 排空/提交/wake并发、错误fail-stop | -| `crates/pvisor/src/runtime/` | Attempt仲裁、自动策略与事件 | manual/auto/snapshot/cancel不越权 | -| `crates/pvisor-overlaynet/src/vm.rs`及后续设备事件源 | wake-before-RAM与外部socket生命周期 | 数据/EOF/错误/背压/已pending事件 | -| 原crate测试与产品集成测试 | mock交错与真实VM验证 | 分平台记录通过/失败/跳过 | -| `benchmark/` | 已登记策略实验 | 独立数据/收据/样本,不放本目录 | - -这张表规定职责,不预先规定新文件名或引入依赖;实施时按已有代码结构收敛。 - -## 必测负对照 - -| 场景 | 预期 | -|---|---| -| compute busy、宿主CPU拥塞 | 不将低CPU误判为确定guest等待;策略误判也不能破坏安全边界 | -| sleep/futex deadline、timer取消 | 不丢timer,不冻结时间;未知deadline拒绝 | -| 任意卸载窗口到来的wake | 锁存并最终交付,不需guest自己重新发事件 | -| 慢FS持RAM lease | 正常提前跳过;真实transition失败必须fail-stop | -| 手动pause或snapshot freeze | 网络/自动deadline不能擅自解除 | -| backing commit中网络到达 | 等提交完成后恢复,RAM writer不早开 | -| KVM Hlt/guest shutdown | 保留shutdown语义,不变成自动idle | -| 已offloaded时cancel/exit | 清理与终态正确,不保留永远无法唤醒的owner | -| 不支持的pager/pool/COW | 明确拒绝且不改变运行状态 | - -## 验证记录模板 - -每次推进填写:日期、平台、stage/profile、源码SHA+dirty manifest、binary/firmware/rootfs身份、命令、结果、失败/跳过、未覆盖项、证据路径、结论。实际记录见 [第一版实现与验证](validation.md) 和 [KVM 工程实验报告](../../benchmark/pvisor/vcpu_idle_report.md)。 - -默认使用目标包的`just test PACKAGE`;真实VM/特殊runner按已有契约运行。语义审批与PASS分开,不由AI操作真实ledger。实验实现成功后再同步用户文档和正式支持矩阵。 diff --git a/experimental/vcpu-idle-offload/validation.md b/experimental/vcpu-idle-offload/validation.md deleted file mode 100644 index 56572e317..000000000 --- a/experimental/vcpu-idle-offload/validation.md +++ /dev/null @@ -1,67 +0,0 @@ -# EXP-001 第一版实现与验证 - -## 当前结论 - -**已交付 M0 observe-only 观测原型和真实 VM 实验,不是自动 offload 第一版闭环。** KVM 实测证明当前用户态接点不足以识别等待:sleep/busy/短 timer/SMP 条件几乎都为 Unknown。不能为了完成自动策略而把低 CPU、未返回的 KVM_RUN 或 shutdown HLT 改作 idle。 - -HVF 等待路径已接入,但本 Linux 工作区没有实际 macOS 编译或运行验收。M0 未完成全部退出门槛;M1/M2 未实施,不新增自动 offload CLI 开关。 - -## 实际代码与 API - -- `crates/pvisor-vm/src/api.rs`:`VcpuObservationControl`、状态/拒绝原因、逐 CPU 记录与聚合快照,公共契约集中于该文件。 -- `crates/pvisor-vm/src/vcpu_observation.rs`:默认关闭的单 VM collector,无后台线程和事件队列,内存 O(vCPU 数),session/sequence/topology generation/idle epoch、等待累计与拒绝原因。 -- `crates/pvisor-vm/src/vmm/{builder,mod}.rs`:collector 生命周期与 CPU 注入。 -- `crates/pvisor-vm/src/vmm/{linux,macos}/vstate.rs`:后端观测,人工停驻与停止保持原控制语义。 -- `crates/pvisor/examples/vm_vcpu_observe.rs`:真实 SDK VM、有界采样和 guest 正确性负载。 - -```rust -use pvisor_vm::api::VcpuObservationControl; - -handle.set_vcpu_observation(true)?; -let snapshot = handle.vcpu_observation()?; -handle.set_vcpu_observation(false)?; -``` - -此 API 不调用 pause/resume/offload。中途启用不 kick CPU,状态保守 Unknown,直到下一接点。KVM 的 Executing 是执行入口接点,KVM_RUN 内 Unknown,返回后 HandlingExit;HLT 仍为停止。HVF WaitingForEvent 表示 wait/select 接点窗口,可能立即消费已就绪事件,不等价于精确睡眠或 Linux runqueue 事实。 - -全配置 CPU 的 online 指后端生命周期,不证明 guest Linux CPU-online。停止/尚未启动的槽不冒充全 CPU idle。即使全等待成立,仍以 WakeDeadlineUnavailable 拒绝自动操作。 - -## 实验与结果 - -注册 **B-VCPU-IDLE-ENG**,工程 A/B,不进入用户 benchmark 正文。完整[协议](../../benchmark/pvisor/vcpu_idle_plan.md)、[报告](../../benchmark/pvisor/vcpu_idle_report.md)及[复现入口](../../benchmark/pvisor/README.md#vcpu-observation-m0)。 - -矩阵:sleep、busy、2 ms 短 timer 分别使用 1/2 vCPU;另设 2 vCPU 仅 CPU 0 busy。每格 fresh 256 MiB VM、3 秒 workload、10 ms 采样,observer off/on seeded 随机配对,每 case 5 pairs,共 70 VMs。guest multiprocessing 独立进程与亲和性验证防止 GIL 串行化,逐 worker 校验 SHA-256、工作区间、退出与输出。 - -| 项目 | 实际结果 | -|---|---| -| 原始预检 | 0/14,通过失败均保留;Python 3.14 forkserver bootstrap 错误 | -| 显式 fork 修复后独立预检 | 14/14 有效,0 失败 | -| 正式批 | 70/70 有效,0 失败、0 timeout,35 完整 pairs | -| 周期采样 | 10,613 snapshots,16,666 条 CPU 状态 | -| Unknown | 16,657 条,99.946% | -| WaitingForEvent / 全等待 epoch | 0 / 0 | -| 配对任务 wall 差异 | 全部 case 的 95% CI 包含 0,未检出差异 | -| 配对 VM CPU 差异中位数 | +48.253~+88.676 ms;2-vCPU busy CI 包含 0,其余为正 | -| snapshot + JSON Value 构造时间 | 描述性中位数 24.566 µs,不包含 JSONL 写入 | - -开销包含 collector、sampler 和证据 I/O,非纯接点成本。GNU debug、单宿主、每 case n=5,无 cgroup 限额或连续宿主干扰检测,不推导 release、P95/P99、生产 density 或整机节省。未调用 offload,没有内存回收效果或恢复延迟结果。 - -## 验证与制品边界 - -- 最终 `just test pvisor-vm`:345 passed、8 skipped;含真实 KVM busy-loop/OUT/HLT 特殊测试、SMP/session/并发采样/退出/容量/统计一致性回归。 -- Python runner:16 个单测通过,包含 Python 默认 forkserver 下的显式 fork 回归、超时、来源、拓扑和输出拒绝。 -- example debug 构建、修改文件格式检查通过;Linux VM 库 Clippy 通过。tests Clippy 被依赖的现有 pvisor 告警阻断,未改无关代码。 -- macOS/HVF 编译与真实运行未验收;无 lost-wakeup、完整 timer/deadline 或自动恢复测试。 - -正式测量绑定冻结 binary SHA-256 `af6830774dfd9fef042893c7491c38077377cf7032afeb1add3af433ca83f239`;report SHA-256 `036094ed87fcc5ffd7d7c73d28cd932350bbd16499fae6b8ae28f4655e5bf339`。来源与资产完整摘要在报告,原始证据在 `benchmark/pvisor/.data/vcpu-m0-fork-pairs5-20261007/`。 - -**实测后修复了快照时间戳锁内一致性,以及 disable 截断逐 CPU 等待记账两项问题。** 最终单测包含修复,但冻结 binary 未替换、真实 A/B 未重跑;上述实验只属于修复前冻结版本。工作区同时有外部开发,旧 receipt 会拒绝当前源码;下一次测量需 NEW build/cohort,不修改历史结果。 - -## 下一版实施门槛与实验 - -1. **KVM 信号问题优先**:验证宿主 KVM halt/wake 观测接口是否能按 VM/vCPU 关联、区分抢占并控制权限/开销。不先修改 irqchip、HLT shutdown 或全局 sysctl;无可靠来源则保持 Unknown。 -2. **HVF 实机验收**:同矩阵核实 wait 接点、短 timer、单 CPU busy SMP 与 pending event。补 host 拥塞负对照及更低开销的 aggregate-only 采样,不把接点窗口当未来 deadline。 -3. **M1 正确性实验**:wake-before-RAM、候选/排空/提交/停驻四阶段注入事件;timer 重编程/取消、人工 pause、cancel、失败,证明不丢事件、不越权恢复。 -4. **M2/M4 回收 A/B**:上述闭环通过后,在普通 file-backed raw、单 vCPU、明确网络/timer profile 对比 off/on。独立登记/补全协议,测等待 0.1/1/5/30/60 秒、计算/streaming/短 timer/恢复风暴,重复/随机工作集;固定组预算、完整 cgroup/PSS/cache/helper、memory-time integral、CPU/IO/peak、完整任务延迟和有效结果。当前均未测,不预设节约倍率。 - -这次实验的价值是及时暴露 Linux 路线的观测缺口,而非给尚不存在的自动卸载策略提供漂亮收益数字。 diff --git a/justfile b/justfile index d2645750b..ea58e0eaa 100644 --- a/justfile +++ b/justfile @@ -3,7 +3,7 @@ set positional-arguments repo := justfile_directory() target_dir := absolute_path(env("CARGO_TARGET_DIR", repo / "target")) -python_paths := "pvisor tests examples benchmark/pvisor/test_*.py" +python_paths := "pvisor tests examples benchmark/pvisor/test_*.py benchmark.new" zensical_version := "0.0.67" import "tests/justfile" @@ -199,7 +199,7 @@ test-rust *packages: cargo nextest run --locked "${args[@]}" "$@" # 标准库 unittest;支持文件、测试名、-k/-v,或 discover 参数 -# 无参数时覆盖 tests/ 和 benchmark/pvisor/,目录选择用 discover -s DIR +# 无参数时覆盖 tests/、历史 benchmark 测试和 hyperfine 测试 [group("06. 测试与冒烟")] test-py *args: #!/usr/bin/env bash @@ -218,6 +218,7 @@ test-py *args: if [[ "$selected" == 0 ]]; then uv run --no-project --with 'setuptools>=77' python -m unittest discover -s tests "$@" uv run --no-project --with 'setuptools>=77' python -m unittest discover -s benchmark/pvisor "$@" + uv run --no-project --with 'setuptools>=77' python -m unittest discover -s benchmark.new/tests "$@" else uv run --no-project --with 'setuptools>=77' python -m unittest "$@" fi @@ -335,22 +336,22 @@ test-vm-snapshot-state: (vm-probe-build "pvisor-vm" "threaded_cold_restore_case" # 09. benchmark # ============================================================ -# 不传参数运行 smoke;传参数选择一个或多个已注册套件(B-PROCESS: smoke/nightly) +# 不传参数运行 smoke;其他参数指定 benchmark.new 中的 JSON 表格。 [group("09. benchmark")] -benchmark *suites: +benchmark *args: #!/usr/bin/env bash set -euo pipefail if [[ $# -eq 0 ]]; then set -- smoke; fi - for suite in "$@"; do - case "$suite" in - smoke|nightly) ;; - *) echo "unknown benchmark suite: $suite (expected smoke or nightly)" >&2; exit 2 ;; - esac - done - benchmark_repo="${PVISOR_BENCHMARK_REPO:-{{ repo }}}" - for suite in "$@"; do - python3 "$benchmark_repo/benchmark/pvisor/bench.py" run --suite "$suite" --repo "$benchmark_repo" --output "${PVISOR_BENCHMARK_OUTPUT:-benchmark/pvisor/.data/process-current}/$suite" --target-dir "${PVISOR_BENCHMARK_BUILD_DIR:-{{ target_dir }}/pvisor-benchmark-build}" - done + process_build="$(python3 "{{ repo }}/benchmark.new/run.py" --check "$@")" + if [[ "$process_build" == process ]]; then + benchmark_repo="${PVISOR_BENCHMARK_REPO:-{{ repo }}}" + benchmark_build="${PVISOR_BENCHMARK_BUILD_DIR:-{{ target_dir }}/pvisor-benchmark-build}" + CARGO_TARGET_DIR="$benchmark_build" just --justfile "$benchmark_repo/justfile" --working-directory "$benchmark_repo" build release + set -- --binary "$benchmark_build/release/pvisor" "$@" + fi + cargo build --quiet --locked --manifest-path "{{ repo }}/tools/semspec/Cargo.toml" --target-dir "{{ target_dir }}/semspec" + export SEMSPEC_BIN="{{ target_dir }}/semspec/debug/semspec" + python3 "{{ repo }}/benchmark.new/run.py" "$@" # ============================================================ # 10. 文档 @@ -359,11 +360,13 @@ benchmark *suites: # 构建并检查双语文档;支持 --require-recorded [group("10. 文档")] docs-build *check_args: + python3 benchmark.new/docs.py check python3 scripts/check-reference.py python3 scripts/check-docs.py --preflight-build + cargo build --quiet --locked --manifest-path tools/semspec/Cargo.toml --target-dir "{{ target_dir }}/semspec" rm -rf docs/site - uv run --no-project --with zensical=={{ zensical_version }} zensical build --strict -f docs/zensical.zh.toml - uv run --no-project --with zensical=={{ zensical_version }} zensical build --strict -f docs/zensical.en.toml + PVISOR_DOCS_LIVE=0 PYTHONPATH="{{ repo }}${PYTHONPATH:+:$PYTHONPATH}" SEMSPEC_BIN="{{ target_dir }}/semspec/debug/semspec" uv run --no-project --with zensical=={{ zensical_version }} zensical build --clean --strict -f docs/zensical.zh.toml + PVISOR_DOCS_LIVE=0 PYTHONPATH="{{ repo }}${PYTHONPATH:+:$PYTHONPATH}" SEMSPEC_BIN="{{ target_dir }}/semspec/debug/semspec" uv run --no-project --with zensical=={{ zensical_version }} zensical build --clean --strict -f docs/zensical.en.toml cp docs/index.html docs/site/index.html python3 scripts/check-docs.py "$@" @@ -377,7 +380,15 @@ docs-serve locale="zh" *args: *) echo "expected zh or en, got: $1" >&2; exit 2 ;; esac shift - python3 scripts/check-docs.py --preflight-build + if ! python3 scripts/check-docs.py --preflight-build; then + echo "warning: documentation preflight failed; continuing live preview" >&2 + fi + if ! cargo build --quiet --locked --manifest-path tools/semspec/Cargo.toml --target-dir "{{ target_dir }}/semspec"; then + echo "warning: semspec build failed; continuing live preview with the existing renderer when available" >&2 + fi + export PVISOR_DOCS_LIVE=1 + export PYTHONPATH="{{ repo }}${PYTHONPATH:+:$PYTHONPATH}" + export SEMSPEC_BIN="{{ target_dir }}/semspec/debug/semspec" exec uv run --no-project --with zensical=={{ zensical_version }} zensical serve -f "$config" "$@" # ============================================================ @@ -546,14 +557,6 @@ vm-case-driver: codesign --verify --strict "$driver" fi -[private] -benchmark-startup *args: - python3 benchmark/pvisor/run_all.py "$@" - -[private] -benchmark-startup-raw *args: - python3 benchmark/pvisor/startup.py --output benchmark/pvisor/.data/startup "$@" - [private] test-semspec *args: cargo nextest run --manifest-path tools/semspec/Cargo.toml --locked "$@" @@ -563,8 +566,8 @@ semspec *args: cargo run --quiet --manifest-path tools/semspec/Cargo.toml --locked -- "$@" [private] -benchmark-compare candidate baseline="" output="benchmark/pvisor/.data/comparison" threshold="15": - python3 benchmark/pvisor/bench.py compare --repo "{{ repo }}" --candidate "$1" --baseline "$2" --output "$3" --regression-threshold "$4" +benchmark-compare candidate baseline="" output="benchmark.new/process/.data/comparison" threshold="15": + python3 benchmark.new/process/compare.py --candidate "$1" --baseline "$2" --output "$3" --threshold "$4" [private] set-nightly-version *args: diff --git a/tests/justfile b/tests/justfile index 7f7257f89..d588e1195 100644 --- a/tests/justfile +++ b/tests/justfile @@ -11,6 +11,7 @@ test-task-routing: trap 'echo "FAIL: $BASH_COMMAND" >&2; cat "$work/stdout" "$work/stderr" >&2 2>/dev/null || true' ERR trap 'rm -rf "$work"' EXIT export JUST_TEST_LOG="$work/commands" CARGO_TARGET_DIR="$work/target with spaces" CARGO_INSTALL_ROOT="$work/install" + export JUST_TEST_PYTHON="$(python3 -c 'import sys; print(sys.executable)')" mkdir "$work/bin" "$work/tests" cp "{{ repo }}/justfile" "$work/justfile" cp "{{ repo }}/tests/justfile" "$work/tests/justfile" @@ -21,6 +22,10 @@ test-task-routing: printf '%q\t' "$name" "$@" >> "$JUST_TEST_LOG" printf '\n' >> "$JUST_TEST_LOG" args=("$@") + if [[ "$name" == python3 && "${2:-}" == --check ]]; then + exec "$JUST_TEST_PYTHON" "$@" + fi + if [[ "$name" == python3 && "$1" == scripts/check-docs.py && "${JUST_TEST_BAD_DOCS_PREFLIGHT:-0}" == 1 ]]; then exit 1; fi if [[ "$name" == python3 && "$1" == scripts/packaging/stage_wheel_binaries.py ]]; then profile=debug for ((i=0; i<$#; i++)); do @@ -55,6 +60,8 @@ test-task-routing: fi fi if [[ "$name" == uv || "$name" == uvx ]]; then + if [[ " $* " == *" zensical serve "* ]]; then test "${PVISOR_DOCS_LIVE:-0}" == 1; fi + if [[ " $* " == *" zensical build "* ]]; then test "${PVISOR_DOCS_LIVE:-0}" == 0; fi for ((i=0; i<$#; i++)); do if [[ "${args[$i]}" == --out-dir || "${args[$i]}" == --output-dir ]]; then staging=${args[$((i+1))]} @@ -117,6 +124,7 @@ test-task-routing: run test-py check_line uv run --no-project --with 'setuptools>=77' python -m unittest discover -s tests check_line uv run --no-project --with 'setuptools>=77' python -m unittest discover -s benchmark/pvisor + check_line uv run --no-project --with 'setuptools>=77' python -m unittest discover -s benchmark.new/tests for flags in selected leading filter; do case "$flags" in selected) run test-py tests/test_docs.py -k reference -v; check_line uv run --no-project --with 'setuptools>=77' python -m unittest tests/test_docs.py -k reference -v ;; @@ -187,14 +195,49 @@ test-task-routing: expect_failure ci "check;touch $work/injected" build test ! -e "$work/injected" test ! -s "$JUST_TEST_LOG" + ln -s "{{ repo }}/benchmark.new" "$work/benchmark.new" for recipe in fw cases benchmark doctor; do case "$recipe" in cases) expect_failure cases --suite unknown ;; benchmark) expect_failure benchmark smoke unknown ;; *) expect_failure "$recipe" unknown ;; esac - test ! -s "$JUST_TEST_LOG" + if [[ "$recipe" == benchmark ]]; then + command_line python3 "$work/benchmark.new/run.py" --check smoke unknown > "$work/expected" + diff -u "$work/expected" "$JUST_TEST_LOG" + else + test ! -s "$JUST_TEST_LOG" + fi done + run benchmark startup/timing 'pvisor vm' + check_line python3 "$work/benchmark.new/run.py" --check startup/timing 'pvisor vm' + check_line python3 "$work/benchmark.new/run.py" startup/timing 'pvisor vm' + run benchmark filesystem/tools native-read + check_line python3 "$work/benchmark.new/run.py" filesystem/tools native-read + expect_failure benchmark filesystem/tools 'pvisor vm' + command_line python3 "$work/benchmark.new/run.py" --check filesystem/tools 'pvisor vm' > "$work/expected" + diff -u "$work/expected" "$JUST_TEST_LOG" + grep -Fq 'available: native-metadata, native-read' "$work/stderr" + run benchmark smoke --binary /already-built/pvisor + { + command_line python3 "$work/benchmark.new/run.py" --check smoke --binary /already-built/pvisor + command_line cargo build --quiet --locked --manifest-path "$work/tools/semspec/Cargo.toml" --target-dir "$CARGO_TARGET_DIR/semspec" + command_line python3 "$work/benchmark.new/run.py" smoke --binary /already-built/pvisor + } > "$work/expected" + diff -u "$work/expected" "$JUST_TEST_LOG" + run benchmark smoke + check_line python3 "$work/benchmark.new/run.py" --binary "$CARGO_TARGET_DIR/pvisor-benchmark-build/release/pvisor" smoke + check_line python3 scripts/packaging/stage_wheel_binaries.py --profile release --target-dir "$CARGO_TARGET_DIR/pvisor-benchmark-build" + JUST_TEST_BAD_DOCS_PREFLIGHT=1 JUST_TEST_BAD_BUILD=1 run docs-serve en -a 127.0.0.1:3002 + check_line uv run --no-project --with zensical==0.0.67 zensical serve -f docs/zensical.en.toml -a 127.0.0.1:3002 + grep -Fq 'continuing live preview' "$work/stderr" + JUST_TEST_BAD_DOCS_PREFLIGHT=1 expect_failure docs-build + { + command_line python3 benchmark.new/docs.py check + command_line python3 scripts/check-reference.py + command_line python3 scripts/check-docs.py --preflight-build + } > "$work/expected" + diff -u "$work/expected" "$JUST_TEST_LOG" selection="S-DOC-001,\$(touch $work/injected)" run cases --case "$selection" --keep --require-reviewed check_line cargo run --quiet --manifest-path tools/semspec/Cargo.toml --locked -- run docs/src/zh/reference --domain DOC --subject-bin "$CARGO_TARGET_DIR/release/pvisor" --format json --output "$CARGO_TARGET_DIR/pvisor-case-report.json" --case "$selection" --keep --require-reviewed diff --git a/tests/test_docs.py b/tests/test_docs.py index fc771db25..958e01117 100644 --- a/tests/test_docs.py +++ b/tests/test_docs.py @@ -1,8 +1,12 @@ """Bilingual docs cannot silently lose a page or drift in examples/revisions.""" +import os import runpy +import subprocess +import sys import unittest from pathlib import Path +from types import SimpleNamespace from unittest import mock from tests.fixtures import workspace @@ -12,6 +16,90 @@ reference = runpy.run_path(str(ROOT / "scripts/check-reference.py")) +def table_preprocessor(): + """Load the process adapter without requiring the site's Python dependencies.""" + + class Preprocessor: + def __init__(self, md): + self.md = md + + context = mock.Mock() + context.from_markdown.return_value = SimpleNamespace( + config={"root_dir": "/workspace/docs", "docs_dir": "src/zh"}, + page=SimpleNamespace(path="page.md"), + ) + modules = { + "markdown": SimpleNamespace(Extension=object), + "markdown.preprocessors": SimpleNamespace(Preprocessor=Preprocessor), + "zensical.extensions.context": SimpleNamespace(ContextPreprocessor=context), + } + with mock.patch.dict(sys.modules, modules): + extension = runpy.run_path(str(ROOT / "docs/semspec_tables.py")) + return extension["TablePreprocessor"](None) + + +class LiveTableTests(unittest.TestCase): + def test_live_mode_selects_best_effort_and_forwards_warnings(self): + lines = ['{{ table("missing.json") }}'] + preprocessor = table_preprocessor() + with ( + mock.patch.dict(os.environ, {"PVISOR_DOCS_LIVE": "1", "SEMSPEC_BIN": "/semspec"}), + mock.patch( + "subprocess.run", + return_value=SimpleNamespace( + returncode=0, + stdout="warning placeholder\n| valid | 2 |\n", + stderr="missing table: missing.json", + ), + ) as run, + self.assertLogs("", level="WARNING") as warnings, + ): + result = preprocessor.run(lines) + self.assertEqual(result[:2], ["warning placeholder", "| valid | 2 |"]) + self.assertIn("missing.json", warnings.output[0]) + self.assertEqual( + run.call_args.args[0], + ["/semspec", "render", "/workspace/docs/src/zh/page.md", "--stdin", "--best-effort"], + ) + + def test_renderer_failures_leave_page_text_in_live_mode_but_fail_strict_mode(self): + lines = ["# Page", "", '{{ table("missing.json") }}', "", "Body"] + preprocessor = table_preprocessor() + errors = [FileNotFoundError(""), subprocess.TimeoutExpired("semspec", 30)] + for live in ("0", "1"): + for error in errors: + with ( + self.subTest(live=live, error=type(error).__name__), + mock.patch.dict(os.environ, {"PVISOR_DOCS_LIVE": live}), + mock.patch("subprocess.run", side_effect=error), + ): + if live == "1": + with self.assertLogs("", level="WARNING"): + rendered = preprocessor.run(lines) + self.assertEqual(rendered[-len(lines) :], lines) + self.assertIn("!!! warning", rendered[0]) + self.assertNotIn("", "\n".join(rendered)) + else: + with self.assertRaises(type(error)): + preprocessor.run(lines) + for live in ("0", "1"): + with ( + self.subTest(live=live, error="render"), + mock.patch.dict(os.environ, {"PVISOR_DOCS_LIVE": live}), + mock.patch( + "subprocess.run", + return_value=SimpleNamespace(returncode=2, stdout="", stderr="invalid table"), + ) as run, + ): + if live == "1": + with self.assertLogs("", level="WARNING"): + self.assertEqual(preprocessor.run(lines)[-len(lines) :], lines) + else: + with self.assertRaisesRegex(RuntimeError, "invalid table"): + preprocessor.run(lines) + self.assertNotIn("--best-effort", run.call_args.args[0]) + + def write_locale_configs(docs): for locale in ("en", "zh"): (docs / f"zensical.{locale}.toml").write_text( diff --git a/tests/test_semspec_tables.py b/tests/test_semspec_tables.py new file mode 100644 index 000000000..62034c37f --- /dev/null +++ b/tests/test_semspec_tables.py @@ -0,0 +1,73 @@ +"""The native documentation preprocessor reads results through semspec.""" + +import importlib.util +import os +import subprocess +import unittest +from pathlib import Path +from types import SimpleNamespace +from unittest import mock + +from tests.fixtures import workspace + +ROOT = Path(__file__).resolve().parents[1] + + +class SemspecTableTests(unittest.TestCase): + def load_extension(self, resources): + # The ordinary Python suite doesn't install the documentation generator. + # Supply only its extension API here; the native build validates rendering. + context = mock.Mock() + modules = { + "markdown": SimpleNamespace(Extension=object), + "markdown.preprocessors": SimpleNamespace(Preprocessor=object), + "zensical.extensions.context": SimpleNamespace(ContextPreprocessor=context), + } + resources.enter_context(mock.patch.dict("sys.modules", modules)) + spec = importlib.util.spec_from_file_location( + "semspec_tables_test", ROOT / "docs/semspec_tables.py" + ) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module, context + + def test_source_path_is_relative_to_locale_and_current_markdown_is_stdin(self): + with workspace() as (root, resources): + extension, context = self.load_extension(resources) + context.from_markdown.return_value = SimpleNamespace( + config={"root_dir": str(root), "docs_dir": "src/zh"}, + page=SimpleNamespace(path="benchmarks/startup.md"), + ) + run = resources.enter_context(mock.patch.object(extension.subprocess, "run")) + run.return_value = subprocess.CompletedProcess([], 0, "| | ms |\n| --- | --- |\n", "") + resources.enter_context(mock.patch.dict(os.environ, {"SEMSPEC_BIN": "/test/semspec"})) + processor = extension.TablePreprocessor() + processor.md = object() + source = ["# Results", "", '{{ table("startup.render.json") }}'] + result = processor.run(source) + self.assertEqual(result[0], "| | ms |") + self.assertEqual( + run.call_args.args[0], + ["/test/semspec", "render", str(root / "src/zh/benchmarks/startup.md"), "--stdin"], + ) + self.assertEqual(run.call_args.kwargs["input"], "\n".join(source)) + + def test_plain_pages_do_not_launch_processes_and_missing_results_fail_build(self): + with workspace() as (root, resources): + extension, context = self.load_extension(resources) + context.from_markdown.return_value = SimpleNamespace( + config={"root_dir": str(root), "docs_dir": "src/en"}, + page=SimpleNamespace(path="page.md"), + ) + run = resources.enter_context(mock.patch.object(extension.subprocess, "run")) + processor = extension.TablePreprocessor() + processor.md = object() + self.assertEqual(processor.run(["# Plain"]), ["# Plain"]) + run.assert_not_called() + run.return_value = subprocess.CompletedProcess([], 2, "", "missing table") + with self.assertRaisesRegex(RuntimeError, "missing table"): + processor.run(['{{ table("missing.json") }}']) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/semspec/Cargo.lock b/tools/semspec/Cargo.lock index 8970288e8..4f4ce058e 100644 --- a/tools/semspec/Cargo.lock +++ b/tools/semspec/Cargo.lock @@ -525,6 +525,7 @@ version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" dependencies = [ + "indexmap", "itoa", "memchr", "serde", diff --git a/tools/semspec/Cargo.toml b/tools/semspec/Cargo.toml index 1a7d652e1..67be7d883 100644 --- a/tools/semspec/Cargo.toml +++ b/tools/semspec/Cargo.toml @@ -15,7 +15,7 @@ clap = { version = "4", features = ["derive", "env"] } libc = "0.2" pulldown-cmark = "0.13" serde = { version = "1", features = ["derive"] } -serde_json = "1" +serde_json = { version = "1", features = ["preserve_order", "float_roundtrip"] } sha2 = "0.11" shell-words = "1" similar = "2" diff --git a/tools/semspec/DESIGN.md b/tools/semspec/DESIGN.md index 97a33ef3e..54815b723 100644 --- a/tools/semspec/DESIGN.md +++ b/tools/semspec/DESIGN.md @@ -253,3 +253,40 @@ AI 不得执行 approve 或编辑真实 REVIEWED.toml、遗留 .approved 文件 固定核对 74 条原有产品检查的内容摘要,不代替人工审核。引擎语义升级为 6,已有 审核须人工重审;迁移不改写真实台账或快照。`@vocab:` 和 JSON 的 `vocab_review` 保留为审核记录的字段名,其内容现在指向准备文档。 + +## 15. Benchmark 表格与文档预处理 + +`semspec bench in.json` 将「行名 → 列名 → 标量」JSON 模板中的 `$ ` 命令单元格 +执行并填回结果,输出 `in.render.json`。同一模板中的相同命令只执行一次。 +直接 hyperfine 命令按 argv 启动;默认提取 `/results/0/median` 并乘 1000, +`--pointer`、`--scale` 可选择其他字段与单位。每格只测一个命令,样本退出码须全为 0。 +单元格可用 `| json /results/0/summary/time_wall_clock/mean * 1000` 覆盖提取字段和缩放; +该后缀不是 shell 管道。同一 hyperfine argv 的不同统计列共享一次实验。 +`--cwd` 指定执行目录;每命令日志保留在 `in.bench.logs/`,hooks 通过 +`SEMSPEC_BENCH_LOG` 获取日志路径。原始导出、命令及单元格对应关系保存在 `in.bench.json`。 +其他单元格 Bash 命令须输出单个 JSON 标量;由命令自己明确执行提取或计算。 +行值可直接写 `$ hyperfine ...`:执行一次实验,固定展开 N、Median ms、Mean ms、SD ms、 +Min ms、Max ms,全部来自同一原始导出;kind=hyperfine-row 与行列映射保留在证据中。 +整行单位固定为 ms,不应用 pointer/scale,也不允许标量提取后缀。 +行值也可直接写 `$ python3 native_read.py`:脚本 stdout 输出一个非空 JSON 对象, +键是列名,值是已完成的标量数据,整个对象替换该行。同一命令只执行一次,可与普通行、 +单元格命令混用;一条脚本命令不能同时作为行与标量单元格。列顺序保留,所有完成行须 +具有相同列。整行结果不做 pointer 提取或 scale 缩放,单位由脚本定义。stderr 保留诊断; +非零退出、非法/空对象、嵌套数据、未执行命令和列不一致均阻止发布,不递归执行返回值。 +原始对象、日志和 kind=script-row 保留在 commands,rows 记录行到命令及列的关联。 +脚本结果不自动认证为 hyperfine 统计值;完整命令实验保持 benchmark.new 的严格 hyperfine 发布门禁;注册的 pVisor VM Ready / Exit +脚本在同一次运行中记录两个时刻,另行审核逐次单调时钟、有效输出与正常退出,以 Python 时钟口径独立发布。 +渲染未展开的行命令报错,不执行。 + +`semspec render xxx.md` 读取独立一行的 `{{ table("in.render.json") }}`, +按 Markdown 源文件的目录解析 JSON 路径,输出普通 Markdown 表格。 +代码块和行内代码保持字面内容;缺失结果或未执行的命令单元格报错。 +可选第二参数为 JSON 对象:`row_names: false` 隐藏行键列;`markdown: true` +显式信任仓库内的链接、代码与强调标记;默认仍转义纯文本。`align` 对每个输出列指定 +left/right/center/default;`headers` 可覆盖显示表头,支持重复显示标签。所有选项严格 +校验类型、未知键和列数;管道符及换行保持表格安全。 +默认输出到 stdout,`--output` 可写另一个文件;`--stdin` 供文档预处理扩展传入正文。 + +这两个子命令独立于 case runner、verdict、digest 与人工审核,不读取或更新台账。 +实验显式运行,文档构建只进行读取与替换。Zensical 的薄 Preprocessor 将替换后的 +Markdown 在内存中交给原有解析器,原始语义规格与源文件保持原样。 diff --git a/tools/semspec/README.md b/tools/semspec/README.md index 647cd14d3..64c1eae45 100644 --- a/tools/semspec/README.md +++ b/tools/semspec/README.md @@ -111,3 +111,152 @@ just semspec lint tools/semspec/semantics Self-specifications in semantics/review.md are drafts. AI must not execute S-REVIEW-004. Conventional tests cover Markdown inputs, parsing, digest golden values, review transitions, verdicts, timeout cleanup and exact content/tree helpers. + +## Executable benchmark tables + +`bench` and `render` work independently of semantic cases and human review. +Define a JSON object whose keys are row names; each row maps column names to +scalar values, or a `$ ` script command that returns the entire row. All resolved rows must have the same columns. JSON row order and the first +row's column order are preserved. Numbers, strings, booleans and null are literal +data; only strings beginning with `$ ` are commands. + +```json +{ + "Native": { + "Median ms": "$ hyperfine --shell=none --warmup 3 --runs 60 '/bin/true'", + "N": 60 + } +} +``` + +```sh +semspec bench in.json # creates in.render.json and in.bench.json +semspec bench --pointer /results/0/mean --scale 1000 in.json +semspec render article.md # expanded Markdown on stdout +semspec render article.md --output article.render.md +``` + +Direct hyperfine commands are parsed as argv, without an outer shell. Hyperfine +alone measures its subject; semspec startup and JSON processing are outside that +interval. Each cell must measure exactly one subject. The default extraction is +`/results/0/median` multiplied by 1000 (milliseconds); `--pointer` is RFC 6901 JSON +Pointer, and `--scale` applies to every hyperfine cell in that invocation. +`--ignore-failure` is rejected, and sample exit codes must all be zero. +Hyperfine 1.x and 2.x exports are supported. In 2.x, the familiar time pointers +`/results/0/{mean,median,stddev,min,max}` select `summary.time_wall_clock`; native +2.x pointers also work. For older exports without sample exit codes, successful +hyperfine execution establishes success because ignoring failures is disabled. + +Commands run in the template's directory, or the directory selected by `--cwd`, +and inherit the caller's environment. A cell may override extraction with +`$ hyperfine ... | json /results/0/summary/time_wall_clock/mean * 1000`. +The suffix is semspec syntax, not a shell pipeline. Identical base hyperfine argv +execute once per invocation; different selectors share that raw experiment. +Use `| json /results/0/summary/time_wall_clock/count * 1` for sample counts. +Each command writes a fresh log in `in.bench.logs/`; hooks receive its path via +`SEMSPEC_BENCH_LOG`. Logs remain available after failure. +Hyperfine always writes a fresh temporary JSON export. If the command specifies +`--export-json native.json`, semspec also saves the successful export to that path. +Otherwise raw samples are still preserved in `in.bench.json`, together with the +original commands, extraction parameters and row/column mappings. A failed +execution removes the old resolved table and evidence file; invalid templates +are rejected before execution and leave existing results unchanged. The template +is never overwritten, and completed tables are published atomically. + +Other trusted cell commands execute in Bash with `-euo pipefail` and must print exactly +one JSON scalar; these values are inserted directly without pointer extraction +or scaling. This supports explicit calculations or other JSON-producing tools. +Command cells are executable code, like semantic specification Bash blocks. + +In Markdown, put a table reference on its own line: + +~~~~markdown +## Results + +{{ table("in.render.json") }} +~~~~ + +Paths resolve relative to the Markdown file. `render` replaces references with +ordinary Markdown tables, using an unnamed first column for row names. It escapes +cell text and pipes, renders newlines as `
` and null as `—`. Fenced/indented +code blocks and inline examples remain literal. Missing/malformed JSON or +unresolved command cells fail; `render` never executes commands or edits its +source. `--stdin` accepts preprocessed content while the input filename supplies +the base path, for documentation integrations. + +`render --best-effort` is intended for live previews: a missing, malformed or +unresolved table becomes a visible warning, its source location and error are +printed to stderr, and other tables and prose continue to render. It does not +execute unresolved commands or change inputs. A later render reads repaired +files normally. Strict rendering remains the default for published builds. + +pVisor's native Zensical configurations register `docs.semspec_tables`, a thin +Python-Markdown preprocessor invoking `semspec render --stdin`. `just docs-build` +and `just docs-serve` build the CLI and provide its path. Benchmark execution +remains an explicit `semspec bench` operation before documentation rendering. +`docs-serve` opts into `--best-effort`; `docs-build` keeps strict rendering. + +A direct hyperfine row command also expands all six statistics in one experiment: + +```json +{"Native": "$ hyperfine --shell=none --runs 60 --warmup 3 --command-name native /bin/true"} +``` + +The row contains `N`, `Median ms`, `Mean ms`, `SD ms`, `Min ms` and `Max ms`, +all selected from the same export. Raw exports and the `hyperfine-row` kind are +retained in evidence, with the row/column mapping. Hyperfine 1.x and 2.x are +supported; counts must match samples. Row units are fixed to milliseconds; +`--pointer` and `--scale` affect scalar cells only. A scalar `| json` suffix on +an entire hyperfine row is rejected before execution. + +A script can also produce all columns of a row in one execution: + +```json +{ + "native-read": "$ python3 native_read.py" +} +``` + +The script's stdout must contain one nonempty JSON object, with column names +as keys and resolved scalar data as values, for example: + +```json +{"N": 30, "Median ms": 1.25, "Mean ms": 1.5, "SD ms": 0.1, "Min ms": 1.0, "Max ms": 2.0} +``` + +`semspec bench in.json` fills this object into `native-read` in +`in.render.json`; ordinary rows and scalar command cells can coexist. Identical +script command strings run once, even when referenced by multiple rows. Row +and column insertion order are retained. Row results receive no pointer +extraction or scaling: the script defines the values and units. Scripts run in +the same template directory/`--cwd` as cell commands. Write diagnostics to stderr; +if the script invokes hyperfine, redirect its progress away from stdout and +retain its raw export separately. + +Arrays, empty objects, nested cells, unresolved `$ ` values, nonzero exits and +inconsistent columns fail without publishing results. Returned commands are +never recursively executed. Known template errors are checked before execution; +columns determined by scripts are checked after execution. A script command +cannot be used simultaneously as a row and a scalar cell. Rendering an unresolved +row fails without running it. + +Evidence includes each script's raw returned object and log, `kind: script-row`, +and a `rows` mapping with its row name, expression and columns. Script numbers +are script-provided data; semspec does not time the script or certify its internal +measurement/statistics as hyperfine results. Direct hyperfine evidence remains +separate. This feature does not change pVisor's strict hyperfine-only preparation +and publication checks in `benchmark.new`. + +Table options preserve existing article layouts when moving literal data to JSON: + +```markdown +{{ table("data.render.json", {"row_names": false, "markdown": true, "align": ["left", "right"]}) }} +``` + +`row_names` defaults to true; false displays only the named data columns. +`markdown` defaults to false (escaped text); true explicitly trusts repository +markup such as links, code and emphasis. Pipes and newlines remain table-safe. +`align` is optional and lists `left`, `right`, `center` or `default` for every +rendered column, including row names when enabled. Optional `headers` supplies +one displayed label per rendered column, allowing repeated labels while JSON +keys remain unique. Unknown options, invalid types and incorrect counts fail. diff --git a/tools/semspec/src/helpers.rs b/tools/semspec/src/helpers.rs index 05b401bc2..29404fb31 100644 --- a/tools/semspec/src/helpers.rs +++ b/tools/semspec/src/helpers.rs @@ -117,7 +117,9 @@ pub fn json_get(path: &Path, pointer: &str) -> Result { } let value: serde_json::Value = serde_json::from_reader(fs::File::open(path)?)?; let value = value.pointer(pointer).context("JSON pointer is absent")?; - Ok(serde_json::to_string(value)?) + let mut value = value.clone(); + value.sort_all_objects(); + Ok(serde_json::to_string(&value)?) } pub fn file_diff(a: &Path, b: &Path) -> Result<(String, i32)> { fn read(path: &Path) -> Result { diff --git a/tools/semspec/src/lib.rs b/tools/semspec/src/lib.rs index 943eaa1b3..2944aecb6 100644 --- a/tools/semspec/src/lib.rs +++ b/tools/semspec/src/lib.rs @@ -7,3 +7,4 @@ pub mod parse; pub mod project; pub mod runner; pub mod seal; +pub mod table; diff --git a/tools/semspec/src/main.rs b/tools/semspec/src/main.rs index 00877c01f..2e0eac175 100644 --- a/tools/semspec/src/main.rs +++ b/tools/semspec/src/main.rs @@ -27,6 +27,32 @@ struct Cli { } #[derive(Subcommand)] enum Commands { + /// Execute command cells or rows in a JSON table; write INPUT.render.json and INPUT.bench.json. + Bench { + input: PathBuf, + /// Working directory for commands; defaults to the template directory. + #[arg(long)] + cwd: Option, + /// RFC 6901 pointer into each hyperfine export (default: first command's median). + #[arg(long, default_value = "/results/0/median")] + pointer: String, + /// Multiply the selected hyperfine value (seconds to milliseconds by default). + #[arg(long, default_value = "1000")] + scale: f64, + }, + /// Replace standalone {{ table("file.render.json") }} placeholders; print Markdown. + Render { + input: PathBuf, + /// Optional output file; otherwise write to stdout. The source cannot be overwritten. + #[arg(long)] + output: Option, + /// Read Markdown from stdin, using INPUT only as the source path for relative tables. + #[arg(long)] + stdin: bool, + /// Continue past table errors with visible warnings; intended for live previews. + #[arg(long)] + best_effort: bool, + }, Init { directory: PathBuf, }, @@ -121,6 +147,45 @@ fn main() { } fn dispatch(cli: Cli) -> Result { match cli.command { + Commands::Bench { + input, + cwd, + pointer, + scale, + } => { + ensure!(cli.spec_dir.is_empty(), "bench does not accept --spec-dir"); + let output = semspec::table::bench_at(&input, &pointer, scale, cwd.as_deref())?; + println!("{}", output.display()); + return Ok(0); + } + Commands::Render { + input, + output, + stdin, + best_effort, + } => { + ensure!(cli.spec_dir.is_empty(), "render does not accept --spec-dir"); + let source = if stdin { + std::io::read_to_string(io::stdin())? + } else { + fs::read_to_string(&input)? + }; + let text = semspec::table::render(&source, &input, best_effort)?; + if let Some(output) = output { + let output = absolute(&output)?; + ensure!( + output != absolute(&input)? + && (!output.exists() + || !input.exists() + || output.canonicalize()? != input.canonicalize()?), + "render output must not overwrite its Markdown source" + ); + atomic_write(&output, text.as_bytes())?; + } else { + print!("{text}"); + } + return Ok(0); + } Commands::Helper { command } => { return match command { Helper::TreeState { directory } => { @@ -319,7 +384,10 @@ fn dispatch(cli: Cli) -> Result { } Ok(0) } - Commands::Helper { .. } | Commands::Init { .. } => unreachable!(), + Commands::Helper { .. } + | Commands::Init { .. } + | Commands::Bench { .. } + | Commands::Render { .. } => unreachable!(), } } fn absolute(path: &Path) -> Result { diff --git a/tools/semspec/src/table.rs b/tools/semspec/src/table.rs new file mode 100644 index 000000000..480589757 --- /dev/null +++ b/tools/semspec/src/table.rs @@ -0,0 +1,738 @@ +//! Executable JSON tables and read-only Markdown substitution. These commands +//! are independent of semantic cases, verdicts, review ledgers and digests. +use anyhow::{Context, Result, ensure}; +use pulldown_cmark::{Event, Parser, Tag, TagEnd}; +use serde::Deserialize; +use serde_json::{Map, Value, json}; +use std::{ + collections::HashMap, + fs, + path::{Path, PathBuf}, + process::{Command, Stdio}, +}; + +use crate::project::atomic_write; + +/// Resolve a row-name → column-name → scalar JSON template. `$ ` cells execute +/// once per distinct command in the template's directory. Direct hyperfine +/// commands use a fresh export and select `pointer`, multiplied by `scale`; +/// other trusted Bash cells must print one JSON scalar. A `$ ` row command +/// prints a nonempty object of scalar columns and replaces that entire row. +/// Returned command strings are rejected, never recursively executed. Results are published +/// atomically only after all commands succeed; a failed run leaves no old result. +/// Raw exports and cell-to-command mappings are preserved in INPUT.bench.json. +pub fn bench(input: &Path, pointer: &str, scale: f64) -> Result { + bench_at(input, pointer, scale, None) +} + +/// Resolve a template with an optional explicit command working directory. +/// Hyperfine command cells may end in `| json POINTER [* SCALE]`; selections +/// share one execution of the same command. Per-command logs are retained in +/// INPUT.bench.logs and exposed to hooks as SEMSPEC_BENCH_LOG. +pub fn bench_at(input: &Path, pointer: &str, scale: f64, cwd: Option<&Path>) -> Result { + ensure!(scale.is_finite(), "scale must be finite"); + validate_pointer(pointer)?; + let input = input.canonicalize().context("read benchmark template")?; + ensure!( + input.extension().is_some_and(|ext| ext == "json"), + "bench input must be a .json file" + ); + let output = input.with_extension("render.json"); + let evidence = input.with_extension("bench.json"); + let mut table: Value = serde_json::from_slice(&fs::read(&input)?)?; + validate_table(&table, true)?; + let cwd = cwd + .unwrap_or(input.parent().unwrap()) + .canonicalize() + .context("command cwd must exist")?; + ensure!(cwd.is_dir(), "command cwd must be a directory"); + let protected = [&input, &output, &evidence]; + let mut plans = HashMap::new(); + let mut row_shapes = HashMap::new(); + let mut export_paths = Vec::new(); + // Validate every command before clearing an existing result or running code. + for row in table.as_object().unwrap().values() { + let values = if let Some(row) = row.as_object() { + row.values().collect::>() + } else { + vec![row] + }; + for cell in values { + if let Some(command) = cell.as_str().and_then(|s| s.strip_prefix("$ ")) { + let is_row = !row.is_object(); + if let Some(previous) = row_shapes.insert(command.to_owned(), is_row) { + ensure!( + previous == is_row, + "one script command cannot produce both a row object and a scalar cell" + ); + } + if plans.contains_key(command) { + continue; + } + let plan = plan(command, &cwd, &protected)?; + ensure!( + row.is_object() || plan.pointer.is_none() && plan.scale.is_none(), + "hyperfine row commands cannot select a scalar pointer or scale" + ); + if let Some(path) = &plan.export { + ensure!( + !export_paths + .iter() + .any(|(existing, key)| existing == path && key != &plan.key), + "different commands share export file {}", + path.display() + ); + export_paths.push((path.clone(), plan.key.clone())); + } + plans.insert(command.to_owned(), plan); + } + } + } + for path in [&output, &evidence] { + if path.exists() { + fs::remove_file(path)?; + } + } + let mut cache = HashMap::::new(); + let log_dir = input.with_extension("bench.logs"); + fs::create_dir_all(&log_dir)?; + let mut records = Vec::new(); + let mut cells = Vec::new(); + let mut rows = Vec::new(); + for (row_name, row) in table.as_object_mut().unwrap() { + if let Some(command) = row + .as_str() + .and_then(|s| s.strip_prefix("$ ")) + .map(str::to_owned) + { + let plan = &plans[&command]; + let raw = if let Some(raw) = cache.get(&plan.key) { + raw.clone() + } else { + eprintln!("bench: {row_name}: {}", plan.key); + let log = log_dir.join(format!("{:04}.log", records.len() + 1)); + let raw = execute(plan, &cwd, &log) + .with_context(|| format!("benchmark row {row_name:?}"))?; + let value = row_value(&raw, plan)?; + records.push(json!({"command": plan.key, "kind": if plan.shell.is_some() {"script-row"} else {"hyperfine-row"}, "raw": raw, "value": value, "log": log})); + cache.insert(plan.key.clone(), raw.clone()); + raw + }; + let raw = row_value(&raw, plan)?; + // Validate as data before insertion; emitted commands are not plans. + validate_table(&json!({row_name: raw}), false) + .with_context(|| format!("script output for row {row_name:?}"))?; + rows.push( + json!({"row": row_name, "command": plan.key, "expression": command, + "columns": raw.as_object().unwrap().keys().collect::>()}), + ); + *row = raw; + continue; + } + for (column, cell) in row.as_object_mut().unwrap() { + let Some(command) = cell + .as_str() + .and_then(|s| s.strip_prefix("$ ")) + .map(str::to_owned) + else { + continue; + }; + let plan = &plans[&command]; + let raw = if let Some(raw) = cache.get(&plan.key) { + raw.clone() + } else { + eprintln!("bench: {row_name}: {}", plan.key); + let log = log_dir.join(format!("{:04}.log", records.len() + 1)); + let raw = execute(plan, &cwd, &log) + .with_context(|| format!("benchmark cell {row_name:?} / {column:?}"))?; + let value = select(&raw, plan, pointer, scale)?; + records.push(json!({"command": plan.key, "kind": if plan.shell.is_some() {"script-cell"} else {"hyperfine"}, "raw": raw, "value": value, "log": log})); + cache.insert(plan.key.clone(), raw.clone()); + raw + }; + *cell = select(&raw, plan, pointer, scale)?; + cells.push( + json!({"row": row_name, "column": column, "command": plan.key, + "expression": command, "pointer": plan.pointer.as_deref().unwrap_or(pointer), + "scale": plan.scale.unwrap_or(scale)}), + ); + } + } + // Script rows may determine the schema only at runtime. Reject mismatches + // before either result or evidence is published. + validate_table(&table, false)?; + atomic_write( + &evidence, + &json_bytes(&json!({ + "format": 1, "template": input, "cwd": cwd, "pointer": pointer, "scale": scale, + "commands": records, "cells": cells, "rows": rows + }))?, + )?; + atomic_write(&output, &json_bytes(&table)?)?; + Ok(output) +} + +fn row_value(raw: &Value, plan: &Plan) -> Result { + if plan.shell.is_some() { + return Ok(raw.clone()); + } + validate_export(raw, "")?; + let result = &raw["results"][0]; + let v2 = raw["schema_version"].as_u64() == Some(2); + let count = if v2 { + let summary = &result["summary"]["time_wall_clock"]; + ensure!( + summary["unit"] == "second", + "hyperfine wall time must be in seconds" + ); + let count = summary["count"].as_u64().context("missing sample count")?; + ensure!( + Some(count as usize) == result["measurements"].as_array().map(Vec::len), + "hyperfine summary count differs from samples" + ); + count + } else { + result["times"].as_array().context("missing times")?.len() as u64 + }; + let mut columns = Map::new(); + columns.insert("N".into(), json!(count)); + for (column, field) in [ + ("Median ms", "median"), + ("Mean ms", "mean"), + ("SD ms", "stddev"), + ("Min ms", "min"), + ("Max ms", "max"), + ] { + let value = if v2 { + &result["summary"]["time_wall_clock"][field] + } else { + &result[field] + }; + let ms = value + .as_f64() + .with_context(|| format!("missing numeric hyperfine {field}"))? + * 1000.0; + ensure!(ms.is_finite() && ms >= 0.0, "invalid hyperfine statistic"); + columns.insert(column.into(), json!(ms)); + } + Ok(Value::Object(columns)) +} + +fn validate_pointer(pointer: &str) -> Result<()> { + ensure!( + pointer.is_empty() || pointer.starts_with('/'), + "pointer must be an RFC 6901 JSON Pointer" + ); + for token in pointer.split('/').skip(1) { + let mut chars = token.chars(); + while let Some(c) = chars.next() { + if c == '~' { + ensure!( + matches!(chars.next(), Some('0' | '1')), + "invalid JSON Pointer escape" + ); + } + } + } + Ok(()) +} + +struct Plan { + key: String, + argv: Vec, + shell: Option, + export: Option, + pointer: Option, + scale: Option, +} + +fn plan(command: &str, cwd: &Path, protected: &[&PathBuf]) -> Result { + ensure!(!command.trim().is_empty(), "empty command cell"); + let mut argv = shell_words::split(command).context("invalid command quoting")?; + let hyperfine = argv.first().is_some_and(|s| { + Path::new(s) + .file_name() + .is_some_and(|name| name == "hyperfine") + }); + if !hyperfine { + return Ok(Plan { + key: command.to_owned(), + argv: Vec::new(), + shell: Some(command.to_owned()), + export: None, + pointer: None, + scale: None, + }); + } + let mut pointer = None; + let mut scale = None; + if let Some(index) = argv.iter().position(|arg| arg == "|") { + let suffix = argv.split_off(index); + ensure!( + matches!(suffix.len(), 3 | 5) && suffix[1] == "json", + "expected | json POINTER [* SCALE]" + ); + let selection = &suffix[2]; + validate_pointer(selection)?; + pointer = Some(selection.clone()); + if suffix.len() == 5 { + ensure!(suffix[3] == "*", "expected | json POINTER * SCALE"); + let factor: f64 = suffix[4].parse().context("invalid cell scale")?; + ensure!(factor.is_finite(), "cell scale must be finite"); + scale = Some(factor); + } + } + let key = shell_words::join(&argv); + // Shell operators outside the measured command are not argv parameters. + ensure!( + !argv + .iter() + .any(|a| matches!(a.as_str(), "|" | "&&" | ";" | ">" | "2>")), + "use a direct hyperfine command; shell pipelines can be placed inside an explicit Bash command cell" + ); + ensure!( + !argv + .iter() + .any(|a| a == "--ignore-failure" || a.starts_with("--ignore-failure=") || a == "-i"), + "hyperfine --ignore-failure is not allowed" + ); + let mut export = None; + let mut index = 1; + while index < argv.len() { + let path = if argv[index] == "--export-json" { + ensure!(index + 1 < argv.len(), "--export-json needs a path"); + let path = argv.remove(index + 1); + argv.remove(index); + Some(path) + } else if let Some(path) = argv[index].strip_prefix("--export-json=") { + let path = path.to_owned(); + argv.remove(index); + Some(path) + } else { + index += 1; + None + }; + if let Some(path) = path { + ensure!(export.is_none(), "only one --export-json is allowed"); + let path = cwd.join(path); + let parent = path + .parent() + .context("export file requires a parent directory")? + .canonicalize() + .context("export parent must exist")?; + let path = parent.join(path.file_name().context("export requires a filename")?); + ensure!( + !protected.iter().any(|p| path == **p), + "export must not overwrite template or generated results" + ); + ensure!( + !path.is_symlink() && (!path.exists() || path.is_file()), + "export must be a regular file" + ); + export = Some(path); + } + } + Ok(Plan { + key, + argv, + shell: None, + export, + pointer, + scale, + }) +} + +fn execute(plan: &Plan, cwd: &Path, log: &Path) -> Result { + let temporary = tempfile::tempdir()?; + let raw_path = temporary.path().join("hyperfine.json"); + let mut process = if let Some(shell) = &plan.shell { + let mut command = Command::new("bash"); + command.args(["-euo", "pipefail", "-c", shell]); + command + } else { + let mut command = Command::new(&plan.argv[0]); + command + .arg("--export-json") + .arg(&raw_path) + .args(&plan.argv[1..]); + command + }; + process + .current_dir(cwd) + .stdin(Stdio::null()) + .env("SEMSPEC_BENCH_LOG", log); + if plan.shell.is_some() { + let output = process.output().context("launch command")?; + fs::write( + log, + [output.stdout.as_slice(), output.stderr.as_slice()].concat(), + )?; + ensure!( + output.status.success(), + "command exited {}; see {}", + output.status, + log.display() + ); + let value: Value = serde_json::from_slice(&output.stdout) + .context("command stdout must be one JSON value")?; + return Ok(value); + } + let stream = fs::File::create(log)?; + let status = process + .stdout(stream.try_clone()?) + .stderr(stream) + .status() + .context("launch hyperfine")?; + ensure!( + status.success(), + "hyperfine exited {status}; see {}", + log.display() + ); + let raw: Value = serde_json::from_slice( + &fs::read(&raw_path).context("hyperfine did not write its JSON export")?, + )?; + let results = raw["results"] + .as_array() + .context("hyperfine export has no results")?; + ensure!( + results.len() == 1, + "one benchmark cell must measure exactly one hyperfine command" + ); + validate_export(&raw, "")?; + if let Some(path) = &plan.export { + atomic_write(path, &json_bytes(&raw)?)?; + } + Ok(raw) +} + +fn select(raw: &Value, plan: &Plan, pointer: &str, scale: f64) -> Result { + if plan.shell.is_some() { + ensure!( + is_scalar(raw) && !raw.as_str().is_some_and(|s| s.starts_with("$ ")), + "cell command must produce a resolved JSON scalar" + ); + return Ok(raw.clone()); + } + let pointer = plan.pointer.as_deref().unwrap_or(pointer); + let scale = plan.scale.unwrap_or(scale); + let effective_pointer = validate_export(raw, pointer)?; + let selected = raw + .pointer(&effective_pointer) + .with_context(|| format!("missing hyperfine pointer {pointer}"))?; + let value = selected + .as_f64() + .context("selected hyperfine field must be numeric")?; + let value = value * scale; + ensure!(value.is_finite(), "scaled hyperfine result must be finite"); + Ok(if scale == 1.0 { + selected.clone() + } else { + json!(value) + }) +} + +fn validate_export(raw: &Value, pointer: &str) -> Result { + let result = &raw["results"][0]; + if raw + .get("schema_version") + .is_some_and(|v| v.as_u64() == Some(2)) + { + let measurements = result["measurements"] + .as_array() + .context("hyperfine v2 export has no measurements")?; + ensure!(!measurements.is_empty(), "hyperfine export has no samples"); + ensure!( + measurements + .iter() + .all(|m| m["exit_code"].as_i64() == Some(0)), + "hyperfine contains failed or incomplete samples" + ); + if let Some(field) = pointer.strip_prefix("/results/0/") + && matches!(field, "mean" | "median" | "stddev" | "min" | "max") + { + ensure!( + result["summary"]["time_wall_clock"]["unit"] == "second", + "hyperfine wall time must be in seconds" + ); + return Ok(format!("/results/0/summary/time_wall_clock/{field}")); + } + } else { + ensure!( + raw.get("schema_version") + .is_none_or(|v| v.as_u64() == Some(1)), + "unsupported hyperfine JSON schema version" + ); + let times = result["times"] + .as_array() + .context("hyperfine export has no times")?; + ensure!(!times.is_empty(), "hyperfine export has no samples"); + // Hyperfine 1.x may omit exit_codes; successful hyperfine without + // --ignore-failure then establishes successful subjects. + if let Some(exits) = result.get("exit_codes") { + let exits = exits + .as_array() + .context("hyperfine exit_codes must be an array")?; + ensure!( + exits.len() == times.len() && exits.iter().all(|code| code.as_i64() == Some(0)), + "hyperfine contains failed or incomplete samples" + ); + } + } + Ok(pointer.to_owned()) +} + +fn is_scalar(value: &Value) -> bool { + !value.is_object() && !value.is_array() +} + +fn validate_table(table: &Value, commands: bool) -> Result<()> { + let rows = table + .as_object() + .context("table must be an object of named rows")?; + ensure!(!rows.is_empty(), "table must have at least one row"); + let columns = rows.values().find_map(Value::as_object); + for (name, row) in rows { + if row.as_str().is_some_and(|s| s.starts_with("$ ")) { + ensure!( + commands, + "unresolved row command at {name:?}; run semspec bench first" + ); + continue; + } + let row = row + .as_object() + .with_context(|| format!("row {name:?} must be an object"))?; + ensure!(!row.is_empty(), "table must have at least one column"); + let columns = columns.unwrap(); + ensure!( + row.len() == columns.len() && columns.keys().all(|key| row.contains_key(key)), + "row {name:?} has different columns" + ); + for (column, value) in row { + ensure!( + is_scalar(value), + "cell {name:?} / {column:?} must be a scalar" + ); + ensure!( + commands || !value.as_str().is_some_and(|s| s.starts_with("$ ")), + "unresolved command at {name:?} / {column:?}; run semspec bench first" + ); + } + } + Ok(()) +} + +fn json_bytes(value: &Value) -> Result> { + let mut bytes = serde_json::to_vec_pretty(value)?; + bytes.push(b'\n'); + Ok(bytes) +} + +/// Substitute standalone `{{ table("relative.json") }}` lines with Markdown +/// tables. JSON paths are relative to `input`; code blocks and inline examples +/// remain literal. This function only reads files and never executes commands. +/// Rows and columns retain JSON insertion order. In strict mode, table errors +/// fail the render. With `best_effort`, each failed placeholder becomes a visible +/// warning, diagnostics go to stderr and other tables continue to render. +pub fn render(source: &str, input: &Path, best_effort: bool) -> Result { + let mut protected = Vec::new(); + let mut start = None; + for (event, range) in Parser::new(source).into_offset_iter() { + match event { + Event::Start(Tag::CodeBlock(_)) => start = Some(range.start), + Event::End(TagEnd::CodeBlock) => { + if let Some(start) = start.take() { + protected.push(start..range.end); + } + } + Event::Code(_) => protected.push(range), + _ => {} + } + } + let mut text = String::new(); + let mut offset = 0; + for (index, line) in source.split_inclusive('\n').enumerate() { + let trimmed = line.trim(); + if !protected + .iter() + .any(|r| r.start < offset + line.len() && offset < r.end) + && trimmed.starts_with("{{ table(") + { + let rendered = match render_placeholder(trimmed, input) { + Ok(rendered) => rendered, + Err(error) if best_effort => { + eprintln!( + "semspec: warning: {}:{}: {error:#}", + input.display(), + index + 1 + ); + format!( + "!!! warning \"Table unavailable during live preview\"\n\n {}", + escape(&format!("{error:#}")) + ) + } + Err(error) => return Err(error), + }; + // Blank lines separate a table from adjacent paragraphs. + if !text.is_empty() && !text.ends_with("\n\n") { + text.push('\n'); + } + let indent = &line[..line.len() - line.trim_start().len()]; + for line in rendered.lines() { + text.push_str(indent); + text.push_str(line); + text.push('\n'); + } + text.push('\n'); + } else { + text.push_str(line); + } + offset += line.len(); + } + Ok(text) +} + +fn render_placeholder(placeholder: &str, input: &Path) -> Result { + let path = placeholder + .strip_prefix("{{ table(") + .unwrap() + .strip_suffix(") }}") + .context("table placeholder must be {{ table(\"file.json\") }} on its own line")?; + let args: Vec = serde_json::from_str(&format!("[{path}]")) + .context("expected a JSON filename and optional table options")?; + ensure!( + matches!(args.len(), 1 | 2), + "expected filename and optional table options" + ); + let path = args[0] + .as_str() + .context("table path must be a JSON string")?; + let options: TableOptions = if args.len() == 2 { + serde_json::from_value(args[1].clone()).context("invalid table options")? + } else { + TableOptions::default() + }; + let file = input.parent().unwrap_or(Path::new(".")).join(path); + let table: Value = serde_json::from_slice( + &fs::read(&file).with_context(|| format!("read table {}", file.display()))?, + )?; + validate_table(&table, false).with_context(|| format!("table {}", file.display()))?; + markdown_table(table.as_object().unwrap(), &options) +} + +fn escape(text: &str) -> String { + text.replace('&', "&") + .replace('<', "<") + .replace('>', ">") + .replace('\\', "\") + .replace('|', "|") + .replace('`', "`") + .replace('*', "*") + .replace('_', "_") + .replace('[', "[") + .replace(']', "]") + .replace("\r\n", "\n") + .replace(['\n', '\r'], "
") +} + +#[derive(Deserialize)] +#[serde(default, deny_unknown_fields)] +struct TableOptions { + row_names: bool, + markdown: bool, + align: Option>, + headers: Option>, +} + +impl Default for TableOptions { + fn default() -> Self { + Self { + row_names: true, + markdown: false, + align: None, + headers: None, + } + } +} + +#[derive(Deserialize)] +#[serde(rename_all = "lowercase")] +enum Alignment { + Left, + Right, + Center, + Default, +} + +fn table_text(text: &str, markdown: bool) -> String { + if !markdown { + return escape(text); + } + // Trusted article markup is opt-in. Escape table delimiters even inside + // code spans; preserve existing escaped pipes and all other Markdown. + let mut output = String::new(); + let mut slashes = 0; + for c in text.replace("\r\n", "\n").chars() { + if c == '|' && slashes % 2 == 0 { + output.push('\\'); + } + match c { + '\n' | '\r' => output.push_str("
"), + _ => output.push(c), + } + slashes = if c == '\\' { slashes + 1 } else { 0 }; + } + output +} + +fn markdown_table(rows: &Map, options: &TableOptions) -> Result { + let columns = rows.values().next().unwrap().as_object().unwrap(); + let count = columns.len() + usize::from(options.row_names); + if let Some(align) = &options.align { + ensure!( + align.len() == count, + "alignment count must match rendered columns" + ); + } + let mut headers = Vec::new(); + if options.row_names { + headers.push(String::new()); + } + headers.extend(columns.keys().map(|key| table_text(key, options.markdown))); + if let Some(labels) = &options.headers { + ensure!( + labels.len() == count, + "header count must match rendered columns" + ); + headers = labels + .iter() + .map(|label| table_text(label, options.markdown)) + .collect(); + } + let separators: Vec<_> = (0..count) + .map(|i| match options.align.as_ref().map(|a| &a[i]) { + Some(Alignment::Left) => ":---", + Some(Alignment::Right) => "---:", + Some(Alignment::Center) => ":---:", + _ => "---", + }) + .collect(); + let mut text = format!( + "| {} |\n| {} |\n", + headers.join(" | "), + separators.join(" | ") + ); + for (name, row) in rows { + let mut cells = Vec::new(); + if options.row_names { + cells.push(table_text(name, options.markdown)); + } + cells.extend(columns.keys().map(|key| match &row[key] { + Value::String(s) => table_text(s, options.markdown), + Value::Null => "—".into(), + value => value.to_string(), + })); + text.push_str(&format!("| {} |\n", cells.join(" | "))); + } + Ok(text) +} diff --git a/tools/semspec/tests/tables.rs b/tools/semspec/tests/tables.rs new file mode 100644 index 000000000..b2b01640c --- /dev/null +++ b/tools/semspec/tests/tables.rs @@ -0,0 +1,678 @@ +use serde_json::{Value, json}; +use std::{ + fs, + os::unix::fs::PermissionsExt, + path::Path, + process::{Command, Output}, +}; + +fn cli(root: &Path, args: &[&str]) -> Output { + Command::new(env!("CARGO_BIN_EXE_semspec")) + .current_dir(root) + .args(args) + .output() + .unwrap() +} + +fn write(path: &Path, value: &Value) { + fs::write(path, serde_json::to_vec_pretty(value).unwrap()).unwrap(); +} + +fn fake_hyperfine(root: &Path) -> String { + let path = root.join("hyperfine"); + fs::write(&path, r#"#!/bin/bash +set -euo pipefail +test -f fixture +printf 'run\n' >> calls +test "$1" = --export-json +export_file=$2 +shift 2 +if [[ -f fail ]]; then exit 9; fi +if [[ -f schema.json ]]; then + cat schema.json > "$export_file" +else + printf '%s' '{"results":[{"command":"/bin/true","median":0.002,"mean":0.003,"stddev":0.001,"min":0.001,"max":0.004,"times":[0.002,0.002],"exit_codes":[0,0]}]}' > "$export_file" +fi +printf 'hyperfine progress is not JSON\n' +"#).unwrap(); + fs::set_permissions(&path, fs::Permissions::from_mode(0o755)).unwrap(); + shell_words::quote(path.to_str().unwrap()).into_owned() +} + +#[test] +fn bench_maps_cells_to_one_fresh_hyperfine_export_and_keeps_order() { + let tmp = tempfile::tempdir().unwrap(); + let root = tmp.path(); + fs::create_dir(root.join("data")).unwrap(); + let data = root.join("data"); + fs::write(data.join("fixture"), "ready").unwrap(); + let command = format!( + "$ {} --shell=none --runs 2 --export-json raw.json '/bin/true'", + fake_hyperfine(&data) + ); + fs::write(data.join("in.json"), format!(r#"{{"Z native":{{"median ms":{command:?},"N":2}},"A native":{{"median ms":{command:?},"N":2}}}}"#)).unwrap(); + fs::write(data.join("raw.json"), "stale").unwrap(); + let output = cli(root, &["bench", "data/in.json"]); + assert!( + output.status.success(), + "{}", + String::from_utf8_lossy(&output.stderr) + ); + let rendered: Value = + serde_json::from_slice(&fs::read(data.join("in.render.json")).unwrap()).unwrap(); + assert_eq!(rendered["Z native"]["median ms"], 2.0); + assert_eq!(rendered["A native"]["N"], 2); + assert_eq!( + rendered.as_object().unwrap().keys().collect::>(), + ["Z native", "A native"] + ); + assert_eq!(fs::read_to_string(data.join("calls")).unwrap(), "run\n"); + let evidence: Value = + serde_json::from_slice(&fs::read(data.join("in.bench.json")).unwrap()).unwrap(); + assert_eq!(evidence["commands"].as_array().unwrap().len(), 1); + assert_eq!(evidence["cells"].as_array().unwrap().len(), 2); + assert_eq!( + evidence["commands"][0]["raw"]["results"][0]["times"], + json!([0.002, 0.002]) + ); + assert!( + fs::read_to_string(data.join("raw.json")) + .unwrap() + .trim() + .starts_with('{') + ); + let output = cli( + root, + &[ + "bench", + "data/in.json", + "--pointer", + "/results/0/mean", + "--scale", + "1", + ], + ); + assert!(output.status.success()); + let rendered: Value = + serde_json::from_slice(&fs::read(data.join("in.render.json")).unwrap()).unwrap(); + assert_eq!(rendered["Z native"]["median ms"], 0.003); + fs::write(data.join("fail"), "").unwrap(); + assert_eq!(cli(root, &["bench", "data/in.json"]).status.code(), Some(2)); + assert!(!data.join("in.render.json").exists()); + assert!(!data.join("in.bench.json").exists()); +} + +#[test] +fn bench_supports_json_scalar_commands_and_rejects_invalid_templates_before_execution() { + let tmp = tempfile::tempdir().unwrap(); + let root = tmp.path(); + write( + &root.join("in.json"), + &json!({"native": {"value": "$ printf '12.5'", "note": "literal"}}), + ); + assert!(cli(root, &["bench", "in.json"]).status.success()); + let rendered: Value = + serde_json::from_slice(&fs::read(root.join("in.render.json")).unwrap()).unwrap(); + assert_eq!(rendered["native"]["value"], 12.5); + write( + &root.join("in.json"), + &json!({"native": {"value": "$ touch executed; printf 1"}, "other": {"wrong": 1}}), + ); + assert_eq!(cli(root, &["bench", "in.json"]).status.code(), Some(2)); + assert!(!root.join("executed").exists()); + assert!(root.join("in.render.json").exists()); + for command in [ + "$ printf not-json", + "$ false", + "$ printf '[1]'", + "$ printf '\"$ unresolved\"'", + "$ printf 1; false", + ] { + write( + &root.join("in.json"), + &json!({"native": {"value": command}}), + ); + assert_eq!( + cli(root, &["bench", "in.json"]).status.code(), + Some(2), + "{command}" + ); + assert!(!root.join("in.render.json").exists()); + } +} + +#[test] +fn hyperfine_cannot_overwrite_templates_or_ignore_subject_failure() { + let tmp = tempfile::tempdir().unwrap(); + let root = tmp.path(); + for args in [ + "--export-json in.json", + "--export-json in.render.json", + "--ignore-failure", + "--export-json", + "--export-json in.bench.json", + ] { + write( + &root.join("in.json"), + &json!({"native": {"value": format!("$ hyperfine {args} '/bin/true'")}}), + ); + let source = fs::read(root.join("in.json")).unwrap(); + assert_eq!(cli(root, &["bench", "in.json"]).status.code(), Some(2)); + assert_eq!(fs::read(root.join("in.json")).unwrap(), source); + } +} + +#[test] +fn render_is_read_only_resolves_relative_json_and_preserves_code_examples() { + let tmp = tempfile::tempdir().unwrap(); + let root = tmp.path(); + fs::create_dir(root.join("data")).unwrap(); + write( + &root.join("data/in.render.json"), + &json!({"Z|native": {"median ms": 2.13, "notes": "\na|b"}, "A": {"median ms": Value::Null, "notes": "plain"}}), + ); + let source = "# Data\n\n{{ table(\"in.render.json\") }}\n\n```markdown\n{{ table(\"missing.json\") }}\n```\n\n {{ table(\"missing.json\") }}\n\n`{{ table(\"missing.json\") }}`\n"; + fs::write(root.join("data/page.md"), source).unwrap(); + let output = cli(root, &["render", "data/page.md"]); + assert!( + output.status.success(), + "{}", + String::from_utf8_lossy(&output.stderr) + ); + let rendered = String::from_utf8(output.stdout).unwrap(); + assert!( + rendered.contains("| Z|native | 2.13 | <tag>
a|b |"), + "{rendered}" + ); + assert!(rendered.contains("| A | — | plain |")); + assert!(rendered.contains("```markdown\n{{ table(\"missing.json\") }}\n```")); + assert!(rendered.contains(" {{ table(\"missing.json\") }}")); + assert_eq!( + fs::read_to_string(root.join("data/page.md")).unwrap(), + source + ); + assert_eq!( + cli( + root, + &["render", "data/page.md", "--output", "data/page.md"] + ) + .status + .code(), + Some(2) + ); + assert!( + cli(root, &["render", "data/page.md", "--output", "result.md"]) + .status + .success() + ); + assert_eq!( + fs::read_to_string(root.join("result.md")).unwrap(), + rendered + ); +} + +#[test] +fn render_refuses_unresolved_commands_and_missing_or_malformed_tables() { + let tmp = tempfile::tempdir().unwrap(); + let root = tmp.path(); + fs::write(root.join("page.md"), "{{ table(\"in.json\") }}\n").unwrap(); + assert_eq!(cli(root, &["render", "page.md"]).status.code(), Some(2)); + write( + &root.join("in.json"), + &json!({"row": {"value": "$ touch executed; echo 1"}}), + ); + assert_eq!(cli(root, &["render", "page.md"]).status.code(), Some(2)); + assert!(!root.join("executed").exists()); + for value in [ + json!({}), + json!([]), + json!({"row": {"x": [1]}}), + json!({"row": {"x": 1}, "other": {"y": 2}}), + ] { + write(&root.join("in.json"), &value); + assert_eq!(cli(root, &["render", "page.md"]).status.code(), Some(2)); + } +} + +#[test] +fn live_render_keeps_valid_tables_and_recovers_after_a_missing_table_is_created() { + let tmp = tempfile::tempdir().unwrap(); + let root = tmp.path(); + write(&root.join("good.json"), &json!({"native": {"ms": 2}})); + fs::write(root.join("bad.json"), "not json").unwrap(); + write( + &root.join("command.json"), + &json!({"row": {"x": "$ touch executed"}}), + ); + let source = "# Preview\n\n{{ table(\"missing.json\") }}\n\n{{ table(\"good.json\") }}\n\n{{ table(\"bad.json\") }}\n\n{{ table(\"good.json\", {\"wrong\":true}) }}\n\n{{ table(\"command.json\") }}\n\n{{ table(invalid) }}\n\n```markdown\n{{ table(\"example.json\") }}\n```\n\nTail\n"; + fs::write(root.join("page.md"), source).unwrap(); + assert_eq!(cli(root, &["render", "page.md"]).status.code(), Some(2)); + let preview = cli(root, &["render", "page.md", "--best-effort"]); + assert!(preview.status.success()); + let text = String::from_utf8(preview.stdout).unwrap(); + let errors = String::from_utf8(preview.stderr).unwrap(); + assert_eq!(text.matches("!!! warning").count(), 5); + assert!(text.contains("| native | 2 |")); + assert!(text.contains("```markdown\n{{ table(\"example.json\") }}\n```")); + assert!(text.ends_with("Tail\n")); + assert!(errors.contains("page.md:3:")); + assert!(errors.contains("missing.json")); + assert!(!root.join("executed").exists()); + assert_eq!(fs::read_to_string(root.join("page.md")).unwrap(), source); + write(&root.join("missing.json"), &json!({"repaired": {"ms": 3}})); + let preview = cli(root, &["render", "page.md", "--best-effort"]); + let text = String::from_utf8(preview.stdout).unwrap(); + assert!(text.contains("| repaired | 3 |")); + assert_eq!(text.matches("!!! warning").count(), 4); +} + +#[test] +fn canonical_json_helper_keeps_sorted_nested_objects() { + let tmp = tempfile::tempdir().unwrap(); + fs::write(tmp.path().join("data.json"), r#"{"z":0,"a":{"z":1,"a":2}}"#).unwrap(); + let output = cli(tmp.path(), &["helper", "json-get", "data.json", ""]); + assert!(output.status.success()); + assert_eq!( + String::from_utf8(output.stdout).unwrap(), + "{\"a\":{\"a\":2,\"z\":1},\"z\":0}\n" + ); +} + +#[test] +fn per_cell_selections_share_execution_and_explicit_cwd_has_fresh_logs() { + let tmp = tempfile::tempdir().unwrap(); + let root = tmp.path(); + let work = root.join("working directory"); + fs::create_dir(&work).unwrap(); + fs::write(work.join("fixture"), "ready").unwrap(); + let modern = json!({"schema_version": 2, "results": [{ + "measurements": [{"exit_code": 0}, {"exit_code": 0}], + "summary": {"time_wall_clock": {"unit": "second", "count": 2, "median": 0.002, "mean": 0.003}} + }]}); + write(&work.join("schema.json"), &modern); + let command = format!("$ {} --shell=none /bin/true", fake_hyperfine(&work)); + write( + &root.join("in.json"), + &json!({"native": { + "N": format!("{command} | json /results/0/summary/time_wall_clock/count * 1"), + "median": format!("{command} | json /results/0/median * 1000"), + "mean": format!("{command} | json /results/0/mean * 1000") + }}), + ); + let output = cli(root, &["bench", "in.json", "--cwd", work.to_str().unwrap()]); + assert!( + output.status.success(), + "{}", + String::from_utf8_lossy(&output.stderr) + ); + let table: Value = + serde_json::from_slice(&fs::read(root.join("in.render.json")).unwrap()).unwrap(); + assert_eq!(table["native"], json!({"N": 2, "median": 2.0, "mean": 3.0})); + assert_eq!(fs::read_to_string(work.join("calls")).unwrap(), "run\n"); + let evidence: Value = + serde_json::from_slice(&fs::read(root.join("in.bench.json")).unwrap()).unwrap(); + assert_eq!(evidence["commands"].as_array().unwrap().len(), 1); + assert_eq!(evidence["cells"].as_array().unwrap().len(), 3); + let log = Path::new(evidence["commands"][0]["log"].as_str().unwrap()); + assert!( + fs::read_to_string(log) + .unwrap() + .contains("hyperfine progress") + ); + assert!( + cli(root, &["bench", "in.json", "--cwd", work.to_str().unwrap()]) + .status + .success() + ); + assert_eq!( + fs::read_to_string(log) + .unwrap() + .matches("hyperfine progress") + .count(), + 1 + ); +} + +#[test] +fn hyperfine_schema_versions_and_failed_exports_are_checked() { + let tmp = tempfile::tempdir().unwrap(); + let root = tmp.path(); + fs::write(root.join("fixture"), "ready").unwrap(); + write( + &root.join("in.json"), + &json!({"native": {"ms": format!("$ {} --shell=none /bin/true", fake_hyperfine(root))}}), + ); + let modern = json!({"schema_version": 2, "results": [{ + "measurements": [{"exit_code": 0}], + "summary": {"time_wall_clock": {"unit": "second", "median": 0.0123}} + }]}); + write(&root.join("schema.json"), &modern); + assert!(cli(root, &["bench", "in.json"]).status.success()); + let rendered: Value = + serde_json::from_slice(&fs::read(root.join("in.render.json")).unwrap()).unwrap(); + assert_eq!(rendered["native"]["ms"], 12.3); + assert!( + cli( + root, + &[ + "bench", + "in.json", + "--pointer", + "/results/0/summary/time_wall_clock/median" + ] + ) + .status + .success() + ); + write( + &root.join("schema.json"), + &json!({"results": [{"median": 0.01, "times": [0.01]}]}), + ); + assert!(cli(root, &["bench", "in.json"]).status.success()); + for schema in [ + json!({"schema_version": 9, "results": [{"median": 0.01, "times": [0.01]}]}), + json!({"results": [{"median": 0.01, "times": [0.01], "exit_codes": [1]}]}), + json!({"results": [{"median": 0.01, "times": []}]}), + json!({"results": [{"median": 0.01, "times": [0.01]}, {"median": 0.02, "times": [0.02]}]}), + json!({"schema_version": 2, "results": [{"measurements": [{"exit_code": 1}], "summary": {"time_wall_clock": {"unit": "second", "median": 0.01}}}]}), + ] { + write(&root.join("schema.json"), &schema); + assert_eq!(cli(root, &["bench", "in.json"]).status.code(), Some(2)); + assert!(!root.join("in.render.json").exists()); + } +} + +#[test] +fn invalid_cell_selection_preserves_previous_results_without_execution() { + let tmp = tempfile::tempdir().unwrap(); + let root = tmp.path(); + fs::write(root.join("fixture"), "ready").unwrap(); + let executable = fake_hyperfine(root); + for selection in ["/results/~2median", "results/0/median"] { + write( + &root.join("in.json"), + &json!({"Native": {"ms": format!( + "$ {executable} --shell=none /bin/true | json {selection} * 1000" + )}}), + ); + fs::write(root.join("in.render.json"), "previous result").unwrap(); + assert_eq!(cli(root, &["bench", "in.json"]).status.code(), Some(2)); + assert!(!root.join("calls").exists()); + assert_eq!( + fs::read_to_string(root.join("in.render.json")).unwrap(), + "previous result" + ); + } +} + +#[test] +fn render_options_preserve_trusted_article_markup_and_duplicate_labels() { + let tmp = tempfile::tempdir().unwrap(); + let root = tmp.path(); + write( + &root.join("data.json"), + &json!({"first": {"name": "[link](source.csv)", "value": "`a|b` **yes**
ok", "value2": "x\\|y\nnext"}}), + ); + fs::write(root.join("page.md"), "{{ table(\"data.json\", {\"row_names\":false,\"markdown\":true,\"align\":[\"left\",\"right\",\"center\"],\"headers\":[\"Name\",\"Value\",\"Value\"]}) }}\n").unwrap(); + let result = cli(root, &["render", "page.md"]); + assert!( + result.status.success(), + "{}", + String::from_utf8_lossy(&result.stderr) + ); + let text = String::from_utf8(result.stdout).unwrap(); + assert!(text.contains("| Name | Value | Value |\n| :--- | ---: | :---: |")); + assert!(text.contains("| [link](source.csv) | `a\\|b` **yes**
ok | x\\|y
next |")); + for options in [ + r#"{"row_names":false,"align":["left"]}"#, + r#"{"headers":["one"]}"#, + r#"{"markdown":"yes"}"#, + r#"{"unknown":true}"#, + r#"{"align":["invalid"]}"#, + ] { + fs::write( + root.join("page.md"), + format!("{{{{ table(\"data.json\", {options}) }}}}\n"), + ) + .unwrap(); + assert_eq!(cli(root, &["render", "page.md"]).status.code(), Some(2)); + } +} + +#[test] +fn script_expands_a_complete_row_once_and_preserves_result_and_provenance() { + let tmp = tempfile::tempdir().unwrap(); + let root = tmp.path(); + fs::create_dir(root.join("data")).unwrap(); + fs::create_dir(root.join("work")).unwrap(); + fs::write(root.join("work/fixture"), "ready").unwrap(); + // Stdout is the row; diagnostics go to stderr. Cwd and the hook log path + // have the same contracts as existing scalar command cells. + fs::write(root.join("work/measure.py"), "import json, os, sys\nfrom pathlib import Path\nassert Path('fixture').read_text() == 'ready'\nassert os.environ['SEMSPEC_BENCH_LOG']\nwith open('calls', 'a') as f: f.write('run\\n')\nprint('diagnostic', file=sys.stderr)\nprint(json.dumps({'N': 30, 'Median ms': 1.25, 'Mean ms': 1.5, 'SD ms': .1, 'Min ms': 1, 'Max ms': 2}))\n").unwrap(); + write( + &root.join("data/in.json"), + &json!({"native-read": "$ python3 measure.py", "same-script": "$ python3 measure.py", "literal": {"N": 30, "Median ms": 3, "Mean ms": 3, "SD ms": 0, "Min ms": 3, "Max ms": 3}}), + ); + let result = cli( + root, + &["bench", "data/in.json", "--cwd", "work", "--scale", "99"], + ); + assert!( + result.status.success(), + "{}", + String::from_utf8_lossy(&result.stderr) + ); + let table: Value = + serde_json::from_slice(&fs::read(root.join("data/in.render.json")).unwrap()).unwrap(); + assert_eq!(table["native-read"]["Median ms"], 1.25); + assert_eq!(table["same-script"], table["native-read"]); + assert_eq!(table["literal"]["N"], 30); + assert_eq!( + table.as_object().unwrap().keys().collect::>(), + ["native-read", "same-script", "literal"] + ); + assert_eq!( + table["native-read"] + .as_object() + .unwrap() + .keys() + .collect::>(), + ["N", "Median ms", "Mean ms", "SD ms", "Min ms", "Max ms"] + ); + assert_eq!( + fs::read_to_string(root.join("work/calls")).unwrap(), + "run\n" + ); + let evidence: Value = + serde_json::from_slice(&fs::read(root.join("data/in.bench.json")).unwrap()).unwrap(); + assert_eq!(evidence["commands"].as_array().unwrap().len(), 1); + assert_eq!(evidence["commands"][0]["kind"], "script-row"); + assert_eq!(evidence["commands"][0]["raw"], table["native-read"]); + assert_eq!(evidence["rows"].as_array().unwrap().len(), 2); + assert_eq!(evidence["rows"][0]["row"], "native-read"); + assert_eq!(evidence["rows"][0]["expression"], "python3 measure.py"); + let log = evidence["commands"][0]["log"].as_str().unwrap(); + assert!(fs::read_to_string(log).unwrap().contains("diagnostic")); + fs::write( + root.join("data/page.md"), + "{{ table(\"in.render.json\") }}\n", + ) + .unwrap(); + let result = cli(root, &["render", "data/page.md"]); + assert!(result.status.success()); + assert!( + String::from_utf8(result.stdout) + .unwrap() + .contains("| native-read | 30 | 1.25 | 1.5 | 0.1 | 1 | 2 |") + ); +} + +#[test] +fn script_rows_and_cell_commands_can_share_a_table_but_not_return_commands() { + let tmp = tempfile::tempdir().unwrap(); + let root = tmp.path(); + write( + &root.join("in.json"), + &json!({"script": "$ printf '%s' '{\"value\":2}'", "cell": {"value": "$ printf 3"}}), + ); + assert!(cli(root, &["bench", "in.json"]).status.success()); + let table: Value = + serde_json::from_slice(&fs::read(root.join("in.render.json")).unwrap()).unwrap(); + assert_eq!(table, json!({"script": {"value": 2}, "cell": {"value": 3}})); + for output in [ + "null", + "[]", + "7", + "{}", + "{\"value\":[]}", + "{\"value\":{}}", + "{\"value\":\"$ touch executed\"}", + "{\"wrong\":2}", + "not-json", + "{} {}", + ] { + write( + &root.join("in.json"), + &json!({"script": format!("$ printf '%s' '{output}'"), "cell": {"value": 3}}), + ); + fs::write(root.join("in.render.json"), "stale").unwrap(); + fs::write(root.join("in.bench.json"), "stale").unwrap(); + assert_eq!( + cli(root, &["bench", "in.json"]).status.code(), + Some(2), + "{output}" + ); + assert!(!root.join("in.render.json").exists()); + assert!(!root.join("in.bench.json").exists()); + assert!(!root.join("executed").exists()); + assert!(root.join("in.bench.logs/0001.log").exists()); + } + write( + &root.join("in.json"), + &json!({"script": "$ printf '{\"value\":1}'; exit 9"}), + ); + assert_eq!(cli(root, &["bench", "in.json"]).status.code(), Some(2)); + // Rendering a template must reject the unresolved row without running it. + write( + &root.join("in.json"), + &json!({"script": "$ touch executed; printf '{\"value\":1}'"}), + ); + fs::write(root.join("page.md"), "{{ table(\"in.json\") }}\n").unwrap(); + assert_eq!(cli(root, &["render", "page.md"]).status.code(), Some(2)); + assert!(!root.join("executed").exists()); +} + +#[test] +fn invalid_static_columns_and_row_command_plans_preserve_previous_results() { + let tmp = tempfile::tempdir().unwrap(); + let root = tmp.path(); + for table in [ + json!({"script": "$ touch executed; printf '{}'", "a": {"value": 1}, "b": {"wrong": 2}}), + json!({"script": "$ hyperfine --runs 2 '/bin/true' | json /results/0/median * 1000"}), + json!({"script": "$ printf '{\"value\":1}'", "cell": {"value": "$ printf '{\"value\":1}'"}}), + json!({"script": "$ 'unmatched"}), + ] { + write(&root.join("in.json"), &table); + fs::write(root.join("in.render.json"), "previous result").unwrap(); + fs::write(root.join("in.bench.json"), "previous evidence").unwrap(); + assert_eq!(cli(root, &["bench", "in.json"]).status.code(), Some(2)); + assert_eq!( + fs::read_to_string(root.join("in.render.json")).unwrap(), + "previous result" + ); + assert_eq!( + fs::read_to_string(root.join("in.bench.json")).unwrap(), + "previous evidence" + ); + assert!(!root.join("executed").exists()); + } +} + +#[test] +fn script_row_preserves_full_float_precision() { + let tmp = tempfile::tempdir().unwrap(); + let root = tmp.path(); + let input = root.join("in.json"); + fs::write( + &input, + r#"{"VM":"$ printf '%s' '{\"mean\":196.85512733333334}'"}"#, + ) + .unwrap(); + let status = cli(root, &["bench", "in.json"]); + assert!( + status.status.success(), + "{}", + String::from_utf8_lossy(&status.stderr) + ); + let text = fs::read_to_string(root.join("in.render.json")).unwrap(); + assert!(text.contains("196.85512733333334"), "{text}"); +} + +#[test] +fn hyperfine_row_generates_all_statistics_from_one_export() { + let tmp = tempfile::tempdir().unwrap(); + let root = tmp.path(); + fs::write(root.join("fixture"), "ready").unwrap(); + let command = format!("$ {} --shell=none --runs 2 /bin/true", fake_hyperfine(root)); + write( + &root.join("in.json"), + &json!({"Native": command, "alias": command}), + ); + let result = cli(root, &["bench", "in.json", "--scale", "99"]); + assert!( + result.status.success(), + "{}", + String::from_utf8_lossy(&result.stderr) + ); + let table: Value = + serde_json::from_slice(&fs::read(root.join("in.render.json")).unwrap()).unwrap(); + assert_eq!( + table["Native"], + json!({"N":2,"Median ms":2.0,"Mean ms":3.0,"SD ms":1.0,"Min ms":1.0,"Max ms":4.0}) + ); + assert_eq!(table["Native"], table["alias"]); + assert_eq!(fs::read_to_string(root.join("calls")).unwrap(), "run\n"); + let evidence: Value = + serde_json::from_slice(&fs::read(root.join("in.bench.json")).unwrap()).unwrap(); + assert_eq!(evidence["commands"].as_array().unwrap().len(), 1); + assert_eq!(evidence["commands"][0]["kind"], "hyperfine-row"); + assert_eq!(evidence["commands"][0]["value"], table["Native"]); + assert_eq!(evidence["rows"].as_array().unwrap().len(), 2); + assert!(evidence["cells"].as_array().unwrap().is_empty()); +} + +#[test] +fn hyperfine_rows_validate_v2_counts_units_and_failure_before_publication() { + let tmp = tempfile::tempdir().unwrap(); + let root = tmp.path(); + fs::write(root.join("fixture"), "ready").unwrap(); + let executable = fake_hyperfine(root); + let valid = json!({"schema_version":2,"results":[{"command":"/bin/true","measurements":[{"exit_code":0},{"exit_code":0}],"summary":{"time_wall_clock":{"unit":"second","count":2,"median":0.002,"mean":0.003,"stddev":0.001,"min":0.001,"max":0.004}}}]}); + write(&root.join("schema.json"), &valid); + write( + &root.join("in.json"), + &json!({"VM":format!("$ {executable} --runs 2 /bin/true")}), + ); + assert!(cli(root, &["bench", "in.json"]).status.success()); + let result: Value = + serde_json::from_slice(&fs::read(root.join("in.render.json")).unwrap()).unwrap(); + assert_eq!(result["VM"]["N"], 2); + assert_eq!(result["VM"]["Median ms"], 2.0); + for (pointer, value) in [ + ("/results/0/summary/time_wall_clock/count", json!(3)), + ( + "/results/0/summary/time_wall_clock/unit", + json!("millisecond"), + ), + ("/results/0/measurements/0/exit_code", json!(1)), + ("/results/0/summary/time_wall_clock/stddev", Value::Null), + ] { + let mut bad = valid.clone(); + *bad.pointer_mut(pointer).unwrap() = value; + write(&root.join("schema.json"), &bad); + fs::write(root.join("in.render.json"), "stale").unwrap(); + assert_eq!(cli(root, &["bench", "in.json"]).status.code(), Some(2)); + assert!(!root.join("in.render.json").exists()); + assert!(!root.join("in.bench.json").exists()); + } +}