Skip to content

openvmm: write each page of a new microVM RAM file once at capture - #797

Open
Pedro Henrique Penna (ppenna) wants to merge 1 commit into
devfrom
agents/issue-169-investigation-fix-validation
Open

Pedro Henrique Penna (ppenna) wants to merge 1 commit into
devfrom
agents/issue-169-investigation-fix-validation

Conversation

@ppenna

@ppenna Pedro Henrique Penna (ppenna) commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Pins microsoft/openvmm#4734 at 8a0d945edca13b301a5152f225702057337b2a56. With it, a Windows microVM capture writes each page of its new guest RAM file once, in one ascending pass. Refs #169: this removes a source of variance in capture.mapped_memory_flush, not the external storage trigger, so the issue stays open.

#788 merged into dev at 11:03Z on 2026-10-23. This PR is now one commit, aaf30ed88, directly on dev at 17f84bb60. It is 7e65d9038, the commit originally stacked on #788's head, rebased without change: git range-diff reports an identical patch, and no dev commit since #788 touches openvmm, doc/benchmarks.md, or doc/openvmm-upstream-roadmap.md.

Change

File Change
openvmm 79a501b63 → 8a0d945ed: the single commit of microsoft/openvmm#4734 on feature/microvm/dev
doc/benchmarks.md A 128 MiB capture writes at most 128 MiB, in one pass, instead of about 150 MiB
doc/openvmm-upstream-roadmap.md Records the commit as position 338, a PR02 fixup to fold into position 010 (microsoft/openvmm#4558), and moves the NVX pin to it

Why

On Windows, NTFS zero-fills a file below any write that starts past its valid data length. The RAM backing that OpenVMM creates for a microVM capture is new. OpenVMM flushed it with up to eight concurrent FlushViewOfFile calls over disjoint ranges (the bounded-concurrency flush that #171 brought in), so ranges that NTFS had zero-filled below a high write were written again. A 128 MiB guest wrote about 135–180 MiB per capture, a different amount each time. That spread capture.mapped_memory_flush within a lifecycle series even on a steady scratch volume. Fragmented dirty pages turned the flush into seconds of small writes: 7.7–25.8 s for alternating pages in a new 128 MiB file.

OpenVMM now marks every page of the writable RAM view modified, without changing its contents, and flushes it in one ascending pass. Linux hosts are unaffected; the change is in sparse_mmap's Windows module.

Evidence

I took azure-windows-1, -3, and -4 out of the pool temporarily and ran CI's Windows acceptance benchmark on each at CI's settings: a 128 MiB guest, 1 vCPU, one warmup and ten samples, F: scratch. The runs interleaved CI's build, a local build of the same source, and the fix:

Runner (CPU) Snapshot generation p50: CI build Local build, same source Fix Fix: p50 over p25
azure-windows-1 (8370C) 1188 / 1105 ms 1091 / 1108 ms 836 / 847 ms 1.2% / 0.4%
azure-windows-3 (8573C) 1137 / 1089 ms 1112 / 1128 ms 844 / 844 ms 0.0% / 0.1%
azure-windows-4 (8573C) 1110 / 1056 ms 1111 / 1124 ms 877 / 878 ms 0.4% / 0.2%
  • The flush wrote 112–128 MiB per capture, against about 135–180 MiB before.
  • Cold boot moved by under 1%, and restore p50 by −0.5 to +6.2 ms, within one standard deviation of the samples.
  • On bare-metal WHP, the capture median fell from 561 ms (σ 202 ms) to 348 ms (σ 24 ms).
  • The fix does not make capture immune to the scratch volume's throughput. With a competing writer on F: from the seventh capture on, the validator rejected all 12 series, six per build, on the three runners. Like the natural episode that I caught on azure-windows-3 (one 2.9 s flush at 50 MiB/s), that trigger is outside OpenVMM.

Equivalence and validation

  • The runners measured ae65298b4, the same change on 7ff31ed7a, the v2.2 pin. It has the same patch-id as 8a0d945ed (a22aec434) and the same blobs for both changed files. That is patch equivalence, not tree identity, because the bases differ.
  • OpenVMM gates on both bases, for sparse_mmap: cargo check --all-targets, cargo clippy --all-targets, cargo doc --no-deps, and cargo nextest run --profile agent (21 passed, 2 skipped). cargo xtask fmt --fix made no changes.
  • NVX: python scripts\nvx.py verify and scripts/check_markdown_links.py pass at this head.
  • CI at 7e65d9038: run 39940877632, attempt 2, passed every job. Attempt 1 failed only because a powershell.exe launch in NVX CLI / Windows exceeded its 30 s test timeout on the hosted runner, which also skipped the WHP jobs.
  • CI at aaf30ed88, against dev with ci: shard the WHP OpenVMM VMM tests across two runners #803's sharded WHP VMM tests: run 39990548819 passed every job on its first attempt, including both OpenVMM vmm-tests / Windows / WHP shards.

Exact NVX head submitted to CI: aaf30ed88d0c091ae955a660956055a66bff7452.

Performance history

At aaf30ed88, the WHP platform job on the benchmark runner, azure-windows-5, accepted its first lifecycle measurement. Snapshot generation had a p50 of 783.3 ms (732–868 ms), and every flush took 670–805 ms. That is 23% below the 10-point base median of 1021.1 ms (965–1108 ms) in data/windows-whp-virtual-machine-microvm-v2-1vcpu.csv. Cold start (p50 550.5 ms) and restore (150.5 ms) stayed at their base medians of 549.6 and 155.0 ms. At 7e65d9038, the same job measured a snapshot-generation p50 of 755.6 ms. The gate treats lower values as improvements, so the history needs no reset.

Copilot AI balanced review requested due to automatic review settings October 23, 2026 10:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The supplied evidence leaves source review and exact-pin Windows/WHP runtime validation pending for the memory-writeback change.

No open issues 🎉

What changed in this PR

This PR integrates OpenVMM’s Windows RAM-flush optimization into NVX, aiming to reduce snapshot-capture write amplification and timing variance. It builds on #788 and does not resolve the external storage stalls tracked by #169.

Changes:

  • Documents the ascending flush pass and updated write-volume estimate for a 128 MiB guest.
  • Records the new OpenVMM pin at position 338 and its planned fold into the existing sparse-memory upstream work.
File Description
doc/​openvmm-upstream-roadmap.md Records the new pin and position-338 fixup atop the v2.3 roadmap.
doc/​benchmarks.md Explains the reduced Windows capture writes and continued dependence on scratch-volume throughput.

Review effort: Balanced

Pin microsoft/openvmm#4734 at 8a0d945edca13b301a5152f225702057337b2a56,
one commit on 79a501b63, the head of feature/microvm/dev and
feature/microvm/release/v2.3, which #788 pins.

A Windows microVM capture flushes guest RAM to the backing file that
OpenVMM creates at launch. NTFS zero-fills such a new file below any
write past its valid data length, and OpenVMM flushed it with up to
eight concurrent FlushViewOfFile calls over disjoint ranges, so ranges
were written twice: a 128 MiB guest wrote about 135 to 180 MiB per
capture, a different amount each time. That spread
capture.mapped_memory_flush within a lifecycle series even on a steady
scratch volume and left the stability check of #169 less margin for
the volume's throughput. OpenVMM now marks every page of the RAM view
modified and flushes it in one ascending pass, so a capture writes at
most 128 MiB, and fragmented dirty pages no longer turn the flush into
seconds of small writes.

The runners measured the identical patch on 7ff31ed7a, the v2.2 pin.
At CI's Windows acceptance settings with F: scratch, on
azure-windows-1, -3, and -4, snapshot generation fell from a p50 of
1056 to 1188 ms to 836 to 878 ms, with every series' p50 within 1.2% of
its p25, and cold boot and restore stayed within their run-to-run
variation. Linux hosts are unaffected. The change does not remove the
external trigger of #169: when another writer halves the scratch
volume's throughput mid-series, the series of both builds still split
and are rejected.

doc/benchmarks.md said that a 128 MiB capture writes about 150 MiB; it
now describes the single pass. The roadmap records the commit as
position 338, a PR02 fixup to fold into position 010.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@ppenna
Pedro Henrique Penna (ppenna) force-pushed the agents/issue-169-investigation-fix-validation branch from 7e65d90 to aaf30ed Compare October 23, 2026 16:53
Copilot AI balanced review requested due to automatic review settings October 23, 2026 16:53

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The promoted unsafe Windows mapped-memory operations warrant human source review despite reported successful exact-head CI.

No open issues 🎉

Review effort: Balanced

Pedro Henrique Penna (ppenna) added a commit that referenced this pull request Oct 23, 2026
Pin microsoft/openvmm#4735 at 2b681564dd63483f2f9c9f9bba3b98a4c00e9680,
two commits on 8a0d945ed, the head of microsoft/openvmm#4734 that #797
pins, which is one commit on 79a501b63, the head of feature/microvm/dev.

The first commit lets a platform snapshot opt into restore-time vCPU
activation. Restore-time memory expansion already worked on every tier,
but a platform capture booted with maxcpus=B failed to publish, because
the platform tier's command-line allowlist did not accept maxcpus=, and
one captured without it refused --restore-processors at every restore.
OpenVMM now accepts one maxcpus= token on a platform command line, the
canonical form of the recorded boot-online count, as it accepts
nr_cpus=, so each clone of one platform capture can bring its own vCPU
prefix online at restore.

The second stops the control-console broker from timing a host's
authentication while host input is gated. A host that connected as soon
as it launched a gated restore, and authenticated right away, was
detached whenever the guest's repair outlasted
--microvm-control-auth-timeout-ms, although the broker had not read its
authentication yet. The timeout is now suspended while input is gated
and restarts when the input is released.

Part of #796

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants