Skip to content

Commit 659125c

Browse files
committed
Remove Boxing and shrink the wasm default sizes on Linux
Signed-off-by: James Sturtevant <jsturtevant@gmail.com>
1 parent 4a7b42c commit 659125c

22 files changed

Lines changed: 170 additions & 167 deletions

File tree

docs/end-user-overview-slides.md

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -261,18 +261,19 @@ resp = http_get("https://other.com/admin")
261261

262262
Measured from `benchmark.py` (release build) — 5 cold / 10 warm rounds, averages shown.
263263

264+
==========================================================================================
264265
Step Wasm + Python Wasm + JavaScript HyperlightJS
265266
-----------------------------------------------------------------------------------------
266-
Cold start (create + first run) 420.8 ms 409.2 ms 386.8 ms
267-
Warm run (no restore) 0.2 ms 0.4 ms 0.1 ms
268-
Cold start + tool dispatch 370.7 ms 384.1 ms 387.7 ms
269-
Warm tool dispatch (no restore) 0.4 ms 0.4 ms 0.1 ms
270-
Cold start + file I/O 451.9 ms 443.5 ms 373.7 ms
271-
Warm file I/O (no restore) 0.3 ms 0.5 ms 0.1 ms
272-
Snapshot 239.6 ms 215.3 ms 168.9 ms
273-
Restore 46.7 ms 43.5 ms 41.9 ms
274-
Restore + run 2.6 ms 3.3 ms 1.5 ms
275-
Restore + tool dispatch 2.7 ms 3.2 ms 1.3 ms
267+
Cold start (create + first run) 143.9 ms 119.9 ms 96.7 ms
268+
Warm run (no restore) 0.2 ms 0.3 ms 0.1 ms
269+
Cold start + tool dispatch 138.1 ms 114.5 ms 105.4 ms
270+
Warm tool dispatch (no restore) 0.4 ms 0.3 ms 0.1 ms
271+
Cold start + file I/O 125.7 ms 119.4 ms 101.8 ms
272+
Warm file I/O (no restore) 0.3 ms 0.4 ms 0.1 ms
273+
Snapshot 59.1 ms 59.7 ms 20.5 ms
274+
Restore 11.4 ms 11.1 ms 11.5 ms
275+
Restore + run 1.5 ms 1.3 ms 0.7 ms
276+
Restore + tool dispatch 1.3 ms 1.2 ms 0.6 ms
276277

277278

278279
---

src/hyperlight_sandbox/src/lib.rs

Lines changed: 44 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ pub mod runtime;
1010
pub mod test_utils;
1111
pub mod tools;
1212

13-
use std::any::Any;
1413
use std::path::{Path, PathBuf};
1514

1615
use anyhow::Result;
@@ -30,13 +29,13 @@ pub use tools::{ArgType, ToolRegistry, ToolSchema};
3029
#[cfg(windows)]
3130
pub const DEFAULT_HEAP_SIZE: u64 = 400 * 1024 * 1024;
3231
#[cfg(not(windows))]
33-
pub const DEFAULT_HEAP_SIZE: u64 = 200 * 1024 * 1024;
32+
pub const DEFAULT_HEAP_SIZE: u64 = 25 * 1024 * 1024;
3433

3534
/// Default guest stack / scratch size in bytes (platform-dependent).
3635
#[cfg(windows)]
3736
pub const DEFAULT_STACK_SIZE: u64 = 200 * 1024 * 1024;
3837
#[cfg(not(windows))]
39-
pub const DEFAULT_STACK_SIZE: u64 = 100 * 1024 * 1024;
38+
pub const DEFAULT_STACK_SIZE: u64 = 35 * 1024 * 1024;
4039

4140
/// Configuration for building a sandbox guest.
4241
#[derive(Debug, Clone)]
@@ -75,42 +74,31 @@ pub struct ExecutionResult {
7574
// Snapshot
7675
// ---------------------------------------------------------------------------
7776

78-
#[derive(Clone)]
79-
pub struct Snapshot {
77+
pub struct Snapshot<T> {
8078
kind: &'static str,
81-
runtime: std::sync::Arc<dyn Any + Send + Sync>,
79+
snapshot: std::sync::Arc<T>,
8280
}
8381

84-
impl Snapshot {
82+
impl<T> Clone for Snapshot<T> {
83+
fn clone(&self) -> Self {
84+
Self {
85+
kind: self.kind,
86+
snapshot: self.snapshot.clone(),
87+
}
88+
}
89+
}
90+
91+
impl<T> Snapshot<T> {
8592
pub fn kind(&self) -> &'static str {
8693
self.kind
8794
}
8895

89-
pub fn new<T>(kind: &'static str, runtime: std::sync::Arc<T>) -> Self
90-
where
91-
T: Any + Send + Sync + 'static,
92-
{
93-
Self { kind, runtime }
96+
pub fn new(kind: &'static str, snapshot: std::sync::Arc<T>) -> Self {
97+
Self { kind, snapshot }
9498
}
9599

96-
pub fn restore<T>(
97-
&self,
98-
restore_runtime: impl FnOnce(std::sync::Arc<T>) -> Result<()>,
99-
fs: &std::sync::Arc<std::sync::Mutex<CapFs>>,
100-
) -> Result<()>
101-
where
102-
T: Any + Send + Sync + 'static,
103-
{
104-
let runtime = self
105-
.runtime
106-
.clone()
107-
.downcast::<T>()
108-
.map_err(|_| anyhow::anyhow!("snapshot type mismatch (kind: {})", self.kind))?;
109-
restore_runtime(runtime)?;
110-
fs.lock()
111-
.map_err(|_| anyhow::anyhow!("filesystem mutex poisoned during snapshot restore"))?
112-
.clear_output_files();
113-
Ok(())
100+
pub fn snapshot(&self) -> &std::sync::Arc<T> {
101+
&self.snapshot
114102
}
115103
}
116104

@@ -119,48 +107,50 @@ impl Snapshot {
119107
// ---------------------------------------------------------------------------
120108

121109
pub trait Guest: Sized {
110+
type Sandbox: GuestSandbox;
122111
fn build(
123112
self,
124113
config: SandboxConfig,
125114
tools: ToolRegistry,
126115
network: std::sync::Arc<std::sync::Mutex<NetworkPermissions>>,
127116
fs: std::sync::Arc<std::sync::Mutex<CapFs>>,
128-
) -> Result<Box<dyn GuestSandbox>>;
117+
) -> Result<Self::Sandbox>;
129118
}
130119

131120
pub trait GuestSandbox: Send {
121+
type SnapshotData: Send + Sync + 'static;
132122
/// Execute guest code.
133123
///
134124
/// Output files under `/output` are wiped before each execution.
135125
/// Input files are read-only and managed by the host.
136126
fn run(&mut self, code: &str) -> Result<ExecutionResult>;
137127
/// Capture a snapshot of the guest runtime state.
138-
fn snapshot(&mut self) -> Result<Snapshot>;
128+
fn snapshot(&mut self) -> Result<Snapshot<Self::SnapshotData>>;
139129
/// Restore a previously captured guest runtime state.
140-
fn restore(&mut self, snapshot: &Snapshot) -> Result<()>;
130+
fn restore(&mut self, snapshot: &Snapshot<Self::SnapshotData>) -> Result<()>;
141131
}
142132

143133
// ---------------------------------------------------------------------------
144134
// Sandbox
145135
// ---------------------------------------------------------------------------
146136

147-
pub struct Sandbox {
148-
inner: Box<dyn GuestSandbox>,
137+
pub struct Sandbox<G: Guest> {
138+
inner: G::Sandbox,
149139
network: std::sync::Arc<std::sync::Mutex<NetworkPermissions>>,
150140
fs: std::sync::Arc<std::sync::Mutex<CapFs>>,
151141
}
152142

153-
impl Sandbox {
143+
impl<G: Guest> Sandbox<G> {
154144
/// Create a sandbox without filesystem access.
155-
pub fn new<G: Guest>(guest: G, config: SandboxConfig, tools: ToolRegistry) -> Result<Self> {
145+
pub fn new(guest: G, config: SandboxConfig, tools: ToolRegistry) -> Result<Self> {
156146
let network = std::sync::Arc::new(std::sync::Mutex::new(NetworkPermissions::new()));
157147
let fs = std::sync::Arc::new(std::sync::Mutex::new(CapFs::new()));
158148
let inner = guest.build(config, tools, network.clone(), fs.clone())?;
159149
Ok(Self { inner, network, fs })
160150
}
161151

162152
/// Create a sandbox with a read-only input directory.
163-
pub fn with_input<G: Guest>(
153+
pub fn with_input(
164154
guest: G,
165155
config: SandboxConfig,
166156
tools: ToolRegistry,
@@ -173,10 +163,6 @@ impl Sandbox {
173163
Ok(Self { inner, network, fs })
174164
}
175165

176-
pub fn builder() -> SandboxBuilder<NoGuest> {
177-
SandboxBuilder::default()
178-
}
179-
180166
/// Execute guest code.
181167
///
182168
/// Output files under `/output` are cleared before each run. Input files
@@ -185,12 +171,17 @@ impl Sandbox {
185171
self.inner.run(code)
186172
}
187173

188-
pub fn snapshot(&mut self) -> Result<Snapshot> {
174+
pub fn snapshot(&mut self) -> Result<Snapshot<<G::Sandbox as GuestSandbox>::SnapshotData>> {
189175
self.inner.snapshot()
190176
}
191177

192-
pub fn restore(&mut self, snapshot: &Snapshot) -> Result<()> {
193-
self.inner.restore(snapshot)
178+
pub fn restore(&mut self, snapshot: &Snapshot<<G::Sandbox as GuestSandbox>::SnapshotData>) -> Result<()> {
179+
self.inner.restore(snapshot)?;
180+
self.fs
181+
.lock()
182+
.map_err(|_| anyhow::anyhow!("filesystem mutex poisoned during snapshot restore"))?
183+
.clear_output_files();
184+
Ok(())
194185
}
195186

196187
/// List filenames in the output directory (without reading contents).
@@ -231,7 +222,7 @@ pub struct NoGuest;
231222
/// Builder for constructing a [`Sandbox`].
232223
///
233224
/// ```rust,ignore
234-
/// let sandbox = Sandbox::builder()
225+
/// let sandbox = SandboxBuilder::new()
235226
/// .module_path("guest.aot")
236227
/// .output_dir("/tmp/sandbox-out")
237228
/// .guest(Wasm)
@@ -246,6 +237,12 @@ pub struct SandboxBuilder<G = NoGuest> {
246237
temp_output: bool,
247238
}
248239

240+
impl SandboxBuilder<NoGuest> {
241+
pub fn new() -> Self {
242+
Self::default()
243+
}
244+
}
245+
249246
impl Default for SandboxBuilder<NoGuest> {
250247
fn default() -> Self {
251248
Self {
@@ -336,7 +333,7 @@ impl<G> SandboxBuilder<G>
336333
where
337334
G: Guest,
338335
{
339-
pub fn build(self) -> Result<Sandbox> {
336+
pub fn build(self) -> Result<Sandbox<G>> {
340337
let network = std::sync::Arc::new(std::sync::Mutex::new(NetworkPermissions::new()));
341338
let mut vfs = CapFs::new();
342339
if let Some(input_dir) = &self.input_dir {

src/javascript_sandbox/examples/basics.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
//! Network tests live in network_demo.rs.
88
99
use hyperlight_javascript_sandbox::HyperlightJs;
10-
use hyperlight_sandbox::{Sandbox, ToolRegistry};
10+
use hyperlight_sandbox::{Sandbox, SandboxBuilder, ToolRegistry};
1111
use serde::Deserialize;
1212

1313
fn separator(title: &str) {
@@ -54,7 +54,7 @@ fn main() {
5454
Ok(serde_json::json!(val))
5555
});
5656

57-
let mut sandbox = Sandbox::builder()
57+
let mut sandbox = SandboxBuilder::new()
5858
.guest(HyperlightJs)
5959
.with_tools(tools)
6060
.build()

src/javascript_sandbox/examples/filesystem_demo.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Filesystem capabilities demo for the JavaScript sandbox.
22
33
use hyperlight_javascript_sandbox::HyperlightJs;
4-
use hyperlight_sandbox::{DirPerms, FilePerms, Sandbox};
4+
use hyperlight_sandbox::{DirPerms, FilePerms, Sandbox, SandboxBuilder};
55

66
fn separator(label: &str) {
77
println!("\n── {label} ──");
@@ -10,7 +10,7 @@ fn separator(label: &str) {
1010
fn main() {
1111
// ── 1: No filesystem ────────────────────────────────────────────
1212
separator("Test 1: No filesystem");
13-
let mut sandbox = Sandbox::builder()
13+
let mut sandbox = SandboxBuilder::new()
1414
.guest(HyperlightJs)
1515
.build()
1616
.expect("failed to create sandbox");
@@ -31,7 +31,7 @@ fn main() {
3131
let input_tmp = tempfile::tempdir().unwrap();
3232
std::fs::write(input_tmp.path().join("hello.txt"), b"hello from host").unwrap();
3333

34-
let mut sandbox = Sandbox::builder()
34+
let mut sandbox = SandboxBuilder::new()
3535
.guest(HyperlightJs)
3636
.input_dir(input_tmp.path())
3737
.build()
@@ -50,7 +50,7 @@ fn main() {
5050

5151
// ── 3: Temp output only ─────────────────────────────────────────
5252
separator("Test 3: Temp output only");
53-
let mut sandbox = Sandbox::builder()
53+
let mut sandbox = SandboxBuilder::new()
5454
.guest(HyperlightJs)
5555
.temp_output()
5656
.build()
@@ -74,7 +74,7 @@ fn main() {
7474
let input_tmp = tempfile::tempdir().unwrap();
7575
std::fs::write(input_tmp.path().join("data.json"), br#"{"n": 21}"#).unwrap();
7676

77-
let mut sandbox = Sandbox::builder()
77+
let mut sandbox = SandboxBuilder::new()
7878
.guest(HyperlightJs)
7979
.input_dir(input_tmp.path())
8080
.temp_output()
@@ -112,7 +112,7 @@ console.log('doubled: ' + data.n * 2);
112112
let output_tmp = tempfile::tempdir().unwrap();
113113
std::fs::write(input_tmp.path().join("msg.txt"), b"shout").unwrap();
114114

115-
let mut sandbox = Sandbox::builder()
115+
let mut sandbox = SandboxBuilder::new()
116116
.guest(HyperlightJs)
117117
.input_dir(input_tmp.path())
118118
.output_dir(
@@ -139,7 +139,7 @@ console.log('done');
139139

140140
// ── 6: Output wiped between runs ────────────────────────────────
141141
separator("Test 6: Output is ephemeral");
142-
let mut sandbox = Sandbox::builder()
142+
let mut sandbox = SandboxBuilder::new()
143143
.guest(HyperlightJs)
144144
.temp_output()
145145
.build()
@@ -168,7 +168,7 @@ console.log('done');
168168
let input_tmp = tempfile::tempdir().unwrap();
169169
std::fs::write(input_tmp.path().join("readonly.txt"), b"do not modify").unwrap();
170170

171-
let mut sandbox = Sandbox::builder()
171+
let mut sandbox = SandboxBuilder::new()
172172
.guest(HyperlightJs)
173173
.input_dir(input_tmp.path())
174174
.build()

src/javascript_sandbox/examples/network_demo.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Network access demo for the HyperlightJS sandbox backend.
22
33
use hyperlight_javascript_sandbox::HyperlightJs;
4-
use hyperlight_sandbox::Sandbox;
4+
use hyperlight_sandbox::{Sandbox, SandboxBuilder};
55

66
fn separator(title: &str) {
77
println!("\n{}", "═".repeat(60));
@@ -10,7 +10,7 @@ fn separator(title: &str) {
1010
}
1111

1212
fn main() {
13-
let mut sandbox = Sandbox::builder()
13+
let mut sandbox = SandboxBuilder::new()
1414
.guest(HyperlightJs)
1515
.build()
1616
.expect("failed to create JS sandbox");

0 commit comments

Comments
 (0)