Skip to content

feat: restore snapshots with less scratch - #1947

Open
danbugs wants to merge 3 commits into
mainfrom
scratch-resize
Open

danbugs wants to merge 3 commits into
mainfrom
scratch-resize

Conversation

@danbugs

@danbugs danbugs commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

Sandbox::snapshot_with_scratch_size takes a snapshot that restores with a smaller scratch region. A guest boots with the scratch its initialisation needs and runs guest function calls from a snapshot with only what they need, so each restore resets less memory. Follows the discussion on #1901.

  • The transport arena sits at a fixed address below the reserved scratch-top pages, with one free page between them. Page tables start at the scratch base. Snapshot ABI 7, goldens v7.0.
  • snapshot_with_scratch_size rejects a size that is larger, too small for the transport, or not whole host pages, before the guest is entered. The snapshot loader refuses a scratch size the host cannot map.
  • Growing is refused. A follow-up can allow it with a check that no guest mapping sits below the current scratch window.
  • hyperlight-unikraft, python restore plus guest function call, 256 MiB → 8 MiB: MSHV 32.6 → 1.07 ms, WHP 11.6 → 3.80 ms, KVM 2.30 → 1.84 ms.

The guest builds its transport context once and keeps the ring
addresses. For scratch to change size across a snapshot, the transport
arena (virtqueue rings, pools and the checkpoint mailbox) needs an
address that does not depend on the scratch size.

The arena ends one free page below the reserved top pages. The page
tables start at the scratch base. The free page takes a small overrun of
the exception stack above it. The guest's page allocator stops at the
arena GPA the host publishes, and aborts if that is not between the
scratch base and the end of the transport range. The guest's transport
memory access is bound to the arena.

scratch_allocator_limit_gpa is renamed scratch_reserved_top_gpa, so a
guest that stops its allocator at the old limit fails to compile. The
minimum scratch size grows by the free page.

This changes the snapshot layout: snapshot ABI 7, goldens v7.0. Existing
snapshots must be regenerated, and guests rebuilt.

Signed-off-by: danbugs <danilochiarlone@gmail.com>
Sandbox::snapshot_with_scratch_size takes a snapshot whose layout has a
smaller scratch size. Its page tables are built for that size, walking
the live tables with the sandbox's own layout. Its virtqueue rings carry
over unchanged, since the transport arena's address does not depend on
the scratch size. Restore replaces scratch when the size differs, and
restoring a larger snapshot grows it back.

A size larger than the current scratch, too small for the transport, or
not a whole number of host pages is rejected before the guest is
entered. Room for the page tables is checked when they are built. The
snapshot loader refuses a scratch size this host cannot map.

A guest can boot with the scratch its initialisation needs and run guest
function calls from a snapshot with only the scratch they need, so
restores that zero or replace all of scratch cost less.

Signed-off-by: danbugs <danilochiarlone@gmail.com>
Copilot AI balanced review requested due to automatic review settings October 16, 2026 23:32
@danbugs danbugs added kind/enhancement For PRs adding features, improving functionality, docs, tests, etc. regen-goldens Regenerate snapshot golden fixtures labels Oct 16, 2026

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

Memory-layout and ABI changes need cross-platform human validation, and a host-page-dependent test failure remains unresolved.

1 open issue
What changed in this PR

Enables snapshots with smaller scratch regions to reduce restore costs while preserving transport addresses.

Changes:

  • Adds scratch-size selection and validation for snapshots.
  • Fixes transport placement and bounds guest allocation below it.
  • Updates snapshot ABI, tests, and documentation.
File Description
src/​hyperlight_host/​tests/​snapshot_goldens/​goldens_version.rs Selects v7.0 goldens.
src/​hyperlight_host/​src/​sandbox/​snapshot/​tripwires.rs Pins ABI 7.
src/​hyperlight_host/​src/​sandbox/​snapshot/​mod.rs Builds snapshots with target layouts.
src/​hyperlight_host/​src/​sandbox/​snapshot/​file/​mod.rs Validates host-page alignment.
src/​hyperlight_host/​src/​sandbox/​snapshot/​file/​media_types.rs Sets ABI 7.
src/​hyperlight_host/​src/​sandbox/​snapshot/​file_tests.rs Tests alignment and ABI rejection.
src/​hyperlight_host/​src/​sandbox/​initialized.rs Adds resizing API and runtime tests.
src/​hyperlight_host/​src/​mem/​virtq/​tests.rs Updates transport address tests.
src/​hyperlight_host/​src/​mem/​virtq/​mod.rs Supports resized transport snapshots.
src/​hyperlight_host/​src/​mem/​mgr.rs Passes target layouts and mailbox offsets.
src/​hyperlight_host/​src/​mem/​layout.rs Defines fixed transport placement and sizing.
src/​hyperlight_guest/​src/​transport/​mem.rs Bounds memory access to transport ranges.
src/​hyperlight_guest/​src/​transport/​context.rs Uses scratch-independent transport bounds.
src/​hyperlight_guest/​src/​layout.rs Defines the allocator’s transport boundary.
src/​hyperlight_guest/​src/​arch/​amd64/​prim_alloc.rs Applies the allocator boundary.
src/​hyperlight_guest/​src/​arch/​aarch64/​prim_alloc.rs Applies the allocator boundary.
src/​hyperlight_common/​src/​layout.rs Defines transport placement helpers.
docs/​virtio-host-guest-communication.md Documents transport placement.
docs/​snapshot-versioning.md Documents ABI 7 compatibility.
docs/​paging-development-notes.md Explains resizing and allocation limits.
CHANGELOG.md Records the API and breaking changes.

Review effort: Balanced

Comment thread src/hyperlight_host/src/sandbox/snapshot/file/mod.rs
Signed-off-by: danbugs <danilochiarlone@gmail.com>
@hyperlight-gh-bot

Copy link
Copy Markdown

Benchmark Results

Measured commit: 0e239724adc5
Baseline commit: dbd00a6dd893

kvm / amd (Linux) (➖ stable)

No benchmark improved or regressed.

Benchmark Results

function_call_codec

encode_control decode_vec_bytes_copy
byte_chunks 744.18 ns (➖ 1.04x faster)
vec_bytes 583.54 ns (➖ 1.00x slower)
384.87 µs (➖ 1.03x slower)

payload_allocation

slot_pool_segmented
262144 523.12 ns (➖ 1.00x faster)
65536 143.13 ns (➖ 1.03x faster)

sandboxes

create_initialized_and_drop
medium 81.14 ms (➖ 1.00x slower)

slot_pool

alloc_dealloc_1500 alloc_dealloc_4096 alloc_dealloc_128
7.72 ns (➖ 1.01x faster) 7.72 ns (➖ 1.21x faster) 7.73 ns (➖ 1.00x faster)

snapshot_files

load_snapshot_unverified
small 92.89 µs (➖ 1.00x faster)

virtq_readonly

slot_pool_segmented_fragmented slot_pool_segmented
262144 7.27 µs (➖ 1.01x slower) 6.99 µs (➖ 1.00x slower)
65536 2.08 µs (➖ 1.07x slower) 1.93 µs (➖ 1.09x faster)

virtq_readwrite

slot_pool_segmented_fragmented slot_pool_segmented
65536 6.34 µs (➖ 1.02x faster) 6.27 µs (➖ 1.03x faster)
8192 1.08 µs (➖ 1.03x slower) 1.10 µs (➖ 1.05x slower)
262144 27.35 µs (➖ 1.02x faster)
kvm / intel (Linux) (➖ stable)

No benchmark improved or regressed.

Benchmark Results

function_call_codec

encode_control decode_vec_bytes_copy
byte_chunks 670.53 ns (➖ 1.01x faster)
vec_bytes 515.08 ns (➖ 1.00x faster)
667.57 µs (➖ 1.02x slower)

payload_allocation

slot_pool_segmented
262144 496.04 ns (➖ 1.00x slower)
65536 134.12 ns (➖ 1.00x faster)

sandboxes

create_initialized_and_drop
medium 76.06 ms (➖ 1.01x faster)

slot_pool

alloc_dealloc_1500 alloc_dealloc_4096 alloc_dealloc_128
7.09 ns (➖ 1.02x slower) 6.91 ns (➖ 1.01x slower) 6.92 ns (➖ 1.00x faster)

snapshot_files

load_snapshot_unverified
small 44.87 µs (➖ 1.02x slower)

virtq_readonly

slot_pool_segmented_fragmented slot_pool_segmented
262144 7.59 µs (➖ 1.01x slower) 7.60 µs (➖ 1.01x slower)
65536 2.15 µs (➖ 1.01x slower) 2.15 µs (➖ 1.01x slower)

virtq_readwrite

slot_pool_segmented_fragmented slot_pool_segmented
65536 7.19 µs (➖ 1.00x faster) 7.09 µs (➖ 1.01x faster)
8192 777.59 ns (➖ 1.01x slower) 747.98 ns (➖ 1.01x faster)
262144 30.08 µs (➖ 1.01x slower)
mshv3 / amd (Linux) (❌ 2.28x)

Top regressions

  • payload_allocation/slot_pool_segmented/262144 — ❌ 2.28x slower

Benchmark Results

function_call_codec

encode_control decode_vec_bytes_copy
byte_chunks 986.15 ns (➖ 1.04x slower)
vec_bytes 703.56 ns (➖ 1.02x faster)
273.57 µs (➖ 1.00x faster)

payload_allocation

slot_pool_segmented
262144 1.63 µs (❌ 2.28x slower)
65536 190.96 ns (➖ 1.00x slower)

sandboxes

create_initialized_and_drop
medium 61.66 ms (➖ 1.02x faster)

slot_pool

alloc_dealloc_1500 alloc_dealloc_4096 alloc_dealloc_128
9.55 ns (➖ 1.01x faster) 9.40 ns (➖ 1.00x slower) 9.45 ns (➖ 1.00x faster)

snapshot_files

load_snapshot_unverified
small 84.45 µs (➖ 1.01x slower)

virtq_readonly

slot_pool_segmented_fragmented slot_pool_segmented
262144 10.85 µs (➖ 1.08x slower) 9.47 µs (➖ 1.04x faster)
65536 2.44 µs (➖ 1.01x faster) 2.28 µs (➖ 1.02x slower)

virtq_readwrite

slot_pool_segmented_fragmented slot_pool_segmented
65536 8.34 µs (➖ 1.13x faster) 8.44 µs (➖ 1.12x faster)
8192 1.36 µs (➖ 1.04x slower) 1.29 µs (➖ 1.03x slower)
262144 36.22 µs (➖ 1.05x faster)
mshv3 / intel (Linux) (➖ stable)

No benchmark improved or regressed.

Benchmark Results

function_call_codec

encode_control decode_vec_bytes_copy
byte_chunks 929.53 ns (➖ 1.02x faster)
vec_bytes 669.70 ns (➖ 1.02x slower)
830.94 µs (➖ 1.22x slower)

payload_allocation

slot_pool_segmented
262144 651.48 ns (➖ 1.01x faster)
65536 174.38 ns (➖ 1.00x slower)

sandboxes

create_initialized_and_drop
medium 63.60 ms (➖ 1.05x faster)

slot_pool

alloc_dealloc_1500 alloc_dealloc_4096 alloc_dealloc_128
9.57 ns (➖ 1.01x slower) 9.57 ns (➖ 1.01x faster) 9.37 ns (➖ 1.00x slower)

snapshot_files

load_snapshot_unverified
small 52.34 µs (➖ 1.14x slower)

virtq_readonly

slot_pool_segmented_fragmented slot_pool_segmented
262144 8.00 µs (➖ 1.00x faster) 8.00 µs (➖ 1.01x slower)
65536 2.27 µs (➖ 1.01x slower) 2.28 µs (➖ 1.02x slower)

virtq_readwrite

slot_pool_segmented_fragmented slot_pool_segmented
65536 7.62 µs (➖ 1.04x faster) 7.58 µs (➖ 1.01x faster)
8192 930.72 ns (➖ 1.02x faster) 936.61 ns (➖ 1.02x slower)
262144 38.59 µs (➖ 1.02x slower)
hyperv-ws2025 / amd (Windows) (➖ stable)

No benchmark improved or regressed.

Benchmark Results

function_call_codec

encode_control decode_vec_bytes_copy
byte_chunks 1.15 µs (➖ 1.05x faster)
vec_bytes 816.41 ns (➖ 1.04x slower)
1.91 ms (➖ 1.02x faster)

payload_allocation

slot_pool_segmented
262144 783.15 ns (➖ 1.00x faster)
65536 237.19 ns (➖ 1.03x faster)

sandboxes

create_initialized_and_drop
medium 92.79 ms (➖ 1.04x faster)

slot_pool

alloc_dealloc_1500 alloc_dealloc_4096 alloc_dealloc_128
10.44 ns (➖ 1.09x faster) 10.20 ns (➖ 1.01x faster) 10.38 ns (➖ 1.14x faster)

snapshot_files

load_snapshot_unverified
small 677.17 µs (➖ 1.14x faster)

virtq_readonly

slot_pool_segmented_fragmented slot_pool_segmented
262144 9.15 µs (➖ 1.02x faster) 9.01 µs (➖ 1.01x faster)
65536 2.41 µs (➖ 1.03x slower) 2.36 µs (➖ 1.01x slower)

virtq_readwrite

slot_pool_segmented_fragmented slot_pool_segmented
65536 10.07 µs (➖ 1.13x slower) 9.17 µs (➖ 1.02x slower)
8192 1.37 µs (➖ 1.01x slower) 1.32 µs (➖ 1.05x slower)
262144 41.68 µs (➖ 1.05x slower)
hyperv-ws2025 / intel (Windows) (➖ stable)

No benchmark improved or regressed.

Benchmark Results

function_call_codec

encode_control decode_vec_bytes_copy
byte_chunks 1.19 µs (➖ 1.02x faster)
vec_bytes 800.56 ns (➖ 1.02x slower)
2.98 ms (➖ 1.04x faster)

payload_allocation

slot_pool_segmented
262144 728.65 ns (➖ 1.03x faster)
65536 209.40 ns (➖ 1.01x faster)

sandboxes

create_initialized_and_drop
medium 105.19 ms (➖ 1.10x faster)

slot_pool

alloc_dealloc_1500 alloc_dealloc_4096 alloc_dealloc_128
10.78 ns (➖ 1.07x slower) 9.98 ns (➖ 1.01x faster) 9.96 ns (➖ 1.00x faster)

snapshot_files

load_snapshot_unverified
small 635.55 µs (➖ 1.01x slower)

virtq_readonly

slot_pool_segmented_fragmented slot_pool_segmented
262144 7.78 µs (➖ 1.01x slower) 7.80 µs (➖ 1.00x faster)
65536 2.37 µs (➖ 1.01x slower) 2.39 µs (➖ 1.01x slower)

virtq_readwrite

slot_pool_segmented_fragmented slot_pool_segmented
65536 8.44 µs (➖ 1.07x faster) 8.52 µs (➖ 1.01x faster)
8192 1.11 µs (➖ 1.14x faster) 1.12 µs (➖ 1.03x slower)
262144 40.62 µs (➖ 1.14x faster)

Reported by cargo ci bench-report --candidate run:38983416070 --config-file bench_report.toml.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind/enhancement For PRs adding features, improving functionality, docs, tests, etc. regen-goldens Regenerate snapshot golden fixtures

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants