From 14d18ac5055b14a42ffad10f5786958ec82180aa Mon Sep 17 00:00:00 2001 From: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com> Date: Fri, 9 Oct 2026 10:50:02 -0700 Subject: [PATCH 1/2] Use talc as the guest heap allocator buddy_system_allocator writes a free-list pointer at the start of every power-of-two heap block during guest init. talc writes only the start and end of the heap. The host populates snapshot memory on first guest touch, so fewer written pages make sandbox creation faster. Criterion sandboxes/create_initialized: * MSHV: 25% to 37% faster for small to large heaps * KVM: 9% to 11% faster for small and large heaps Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com> --- CHANGELOG.md | 2 ++ Cargo.lock | 39 ++++++++++++++------------- src/hyperlight_guest_bin/Cargo.toml | 2 +- src/hyperlight_guest_bin/src/lib.rs | 17 +++++++----- src/tests/rust_guests/Cargo.lock | 41 +++++++++++++++-------------- 5 files changed, 54 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab1c248753..6f2c768d8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). * A new sandbox writes only the guest code, PEB and init data into its memory. The host populates other pages on first guest touch, which speeds up sandbox creation. +* Use `talc` as the guest heap allocator. Guest init writes only the start and + end of the heap, which speeds up sandbox creation. ### Removed diff --git a/Cargo.lock b/Cargo.lock index a87bf33835..d3b04c1307 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -42,6 +42,12 @@ dependencies = [ "cc", ] +[[package]] +name = "allocator-api2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c880a97d28a3681c0267bd29cff89621202715b065127cd445fa0f0fe0aa2880" + [[package]] name = "android_system_properties" version = "0.1.6" @@ -258,15 +264,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "buddy_system_allocator" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1af5a01adeeade54c9f5060300227a8ee64c05b6376f28e9b8ee3b72dd2056f" -dependencies = [ - "spin 0.10.1", -] - [[package]] name = "built" version = "0.8.1" @@ -1649,7 +1646,7 @@ dependencies = [ "quickcheck", "rand 0.10.3", "smallvec", - "spin 0.12.3", + "spin", "thiserror", "tracing", "tracing-core", @@ -1711,7 +1708,6 @@ dependencies = [ name = "hyperlight-guest-bin" version = "0.18.0" dependencies = [ - "buddy_system_allocator", "flatbuffers", "hyperlight-common", "hyperlight-guest", @@ -1720,7 +1716,8 @@ dependencies = [ "hyperlight-libc", "linkme", "log", - "spin 0.12.3", + "spin", + "talc", "tracing", ] @@ -1739,7 +1736,7 @@ name = "hyperlight-guest-tracing" version = "0.18.0" dependencies = [ "hyperlight-common", - "spin 0.12.3", + "spin", "tracing", "tracing-core", ] @@ -3706,12 +3703,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "spin" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "023a211cb3138dbc438680b32560ad89f699977624c9f8dbb95a47d5b4c07dd3" - [[package]] name = "spin" version = "0.12.3" @@ -3815,6 +3806,16 @@ dependencies = [ "version-compare", ] +[[package]] +name = "talc" +version = "5.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53f8ad14fa1c1b8635114abf9721076fe1e869572a5f26a52690f407292c80dc" +dependencies = [ + "allocator-api2", + "lock_api", +] + [[package]] name = "target-lexicon" version = "0.13.5" diff --git a/src/hyperlight_guest_bin/Cargo.toml b/src/hyperlight_guest_bin/Cargo.toml index 6c6f86efe1..0b9fe21966 100644 --- a/src/hyperlight_guest_bin/Cargo.toml +++ b/src/hyperlight_guest_bin/Cargo.toml @@ -25,7 +25,7 @@ hyperlight-common = { workspace = true, default-features = false } hyperlight-guest-tracing = { workspace = true, default-features = false } hyperlight-guest-macro = { workspace = true, default-features = false, optional = true } hyperlight-libc = { workspace = true, default-features = false, optional = true } -buddy_system_allocator = "0.13.0" +talc = { version = "5.1.1", default-features = false } log = { version = "0.4", default-features = false } linkme = { version = "0.3.36", optional = true } spin = "0.12.0" diff --git a/src/hyperlight_guest_bin/src/lib.rs b/src/hyperlight_guest_bin/src/lib.rs index 7df64cefc6..3cf85417ea 100644 --- a/src/hyperlight_guest_bin/src/lib.rs +++ b/src/hyperlight_guest_bin/src/lib.rs @@ -8,7 +8,6 @@ extern crate alloc; use core::fmt::Write; use arch::dispatch::dispatch_function; -use buddy_system_allocator::LockedHeap; use guest_function::register::GuestFunctionRegister; use guest_logger::init_logger; use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode; @@ -18,6 +17,8 @@ use hyperlight_common::mem::HyperlightPEB; use hyperlight_common::outb::OutBAction; use hyperlight_guest::exit::write_abort; use hyperlight_guest::guest_handle::handle::GuestHandle; +use talc::TalcLock; +use talc::source::Manual; // === Modules === #[cfg_attr(target_arch = "x86_64", path = "arch/amd64/mod.rs")] @@ -50,10 +51,12 @@ mod init; pub use hyperlight_libc as libc; // Globals +type GuestHeap = TalcLock, Manual>; + #[cfg(all(feature = "mem_profile", target_arch = "x86_64"))] -struct ProfiledLockedHeap(LockedHeap); +struct ProfiledLockedHeap(GuestHeap); #[cfg(all(feature = "mem_profile", target_arch = "x86_64"))] -unsafe impl alloc::alloc::GlobalAlloc for ProfiledLockedHeap { +unsafe impl alloc::alloc::GlobalAlloc for ProfiledLockedHeap { unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 { let addr = unsafe { self.0.alloc(layout) }; unsafe { @@ -107,11 +110,10 @@ unsafe impl alloc::alloc::GlobalAlloc for ProfiledLockedHeap // === Globals === #[cfg(not(all(feature = "mem_profile", target_arch = "x86_64")))] #[global_allocator] -pub(crate) static HEAP_ALLOCATOR: LockedHeap<32> = LockedHeap::<32>::empty(); +pub(crate) static HEAP_ALLOCATOR: GuestHeap = GuestHeap::new(Manual); #[cfg(all(feature = "mem_profile", target_arch = "x86_64"))] #[global_allocator] -pub(crate) static HEAP_ALLOCATOR: ProfiledLockedHeap<32> = - ProfiledLockedHeap(LockedHeap::<32>::empty()); +pub(crate) static HEAP_ALLOCATOR: ProfiledLockedHeap = ProfiledLockedHeap(GuestHeap::new(Manual)); pub static mut GUEST_HANDLE: GuestHandle = GuestHandle::new(); pub(crate) static mut REGISTERED_GUEST_FUNCTIONS: GuestFunctionRegister = @@ -271,7 +273,8 @@ pub(crate) extern "C" fn generic_init( heap_allocator .try_lock() .expect("Failed to access HEAP_ALLOCATOR") - .init(heap_start, heap_size); + .claim(heap_start as *mut u8, heap_size) + .expect("Failed to claim guest heap"); peb_ptr }; diff --git a/src/tests/rust_guests/Cargo.lock b/src/tests/rust_guests/Cargo.lock index 14c0698272..4d2223cb74 100644 --- a/src/tests/rust_guests/Cargo.lock +++ b/src/tests/rust_guests/Cargo.lock @@ -11,6 +11,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c880a97d28a3681c0267bd29cff89621202715b065127cd445fa0f0fe0aa2880" + [[package]] name = "anstream" version = "1.0.0" @@ -79,15 +85,6 @@ version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" -[[package]] -name = "buddy_system_allocator" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1af5a01adeeade54c9f5060300227a8ee64c05b6376f28e9b8ee3b72dd2056f" -dependencies = [ - "spin 0.10.1", -] - [[package]] name = "bumpalo" version = "3.20.3" @@ -245,7 +242,7 @@ dependencies = [ "flatbuffers", "log", "smallvec", - "spin 0.12.3", + "spin", "thiserror", "tracing-core", ] @@ -296,7 +293,6 @@ dependencies = [ name = "hyperlight-guest-bin" version = "0.18.0" dependencies = [ - "buddy_system_allocator", "flatbuffers", "hyperlight-common", "hyperlight-guest", @@ -304,7 +300,8 @@ dependencies = [ "hyperlight-guest-tracing", "linkme", "log", - "spin 0.12.3", + "spin", + "talc", "tracing", ] @@ -323,7 +320,7 @@ name = "hyperlight-guest-tracing" version = "0.18.0" dependencies = [ "hyperlight-common", - "spin 0.12.3", + "spin", "tracing", "tracing-core", ] @@ -630,12 +627,6 @@ version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" -[[package]] -name = "spin" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "023a211cb3138dbc438680b32560ad89f699977624c9f8dbb95a47d5b4c07dd3" - [[package]] name = "spin" version = "0.12.3" @@ -667,6 +658,16 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "talc" +version = "5.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53f8ad14fa1c1b8635114abf9721076fe1e869572a5f26a52690f407292c80dc" +dependencies = [ + "allocator-api2", + "lock_api", +] + [[package]] name = "thiserror" version = "2.0.20" @@ -915,7 +916,7 @@ dependencies = [ "hyperlight-component-macro", "hyperlight-guest", "hyperlight-guest-bin", - "spin 0.12.3", + "spin", ] [[package]] From b55b4ad9d7faa5b206314cad0c956c4c2667e5a7 Mon Sep 17 00:00:00 2001 From: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com> Date: Thu, 15 Oct 2026 14:50:57 -0700 Subject: [PATCH 2/2] PR feedback Add a SAFETY comment to the guest heap claim. Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com> --- src/hyperlight_guest_bin/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/hyperlight_guest_bin/src/lib.rs b/src/hyperlight_guest_bin/src/lib.rs index 3cf85417ea..c253fac20b 100644 --- a/src/hyperlight_guest_bin/src/lib.rs +++ b/src/hyperlight_guest_bin/src/lib.rs @@ -270,6 +270,8 @@ pub(crate) extern "C" fn generic_init( let heap_allocator = &HEAP_ALLOCATOR; #[cfg(all(feature = "mem_profile", target_arch = "x86_64"))] let heap_allocator = &HEAP_ALLOCATOR.0; + // SAFETY: the host reserves the PEB heap range for the guest heap, and + // init claims it once. Only this allocator writes to it. heap_allocator .try_lock() .expect("Failed to access HEAP_ALLOCATOR")