Snapshots
Deno's runtime supports the use of memory snapshots to skip the lengthy process of spinning up a new runtime and loading all the necessary code. This can give gains of ~10x in startup time, but comes with some caveats:
- The snapshot must be generated on the same system it will be used on
- The extensions in the snapshot builder runtime must be the same, and the same order as the runtime that will use the snapshot
- The
snapshot_builder
feature must be enabled on rustyscript - This feature DOES preserve the integrity of the sandbox
Generating a snapshot
The snapshot must be provided to the runtime via the startup_snapshot
field of RuntimeOptions
, which as a type of &'static [u8]
.
Therefore the snapshot will typically be created in build.rs
(short of using Box::leak
, lazy_static, or similar)
The SnapshotBuilder
struct is used to generate the snapshot, and can be used to pre-load modules into the snapshot. It has methods similar to the normal Runtime
struct.
Below is a sample that generates a snapshot with a pre-loaded module:
You could also do this in build.rs
and write to concat!(env!("OUT_DIR"), "/snapshot.bin")
use rustyscript::{Error, Module, SnapshotBuilder};
use std::fs;
static SNAPSHOT_PATH: &str = "examples/snapshot.bin";
fn main() -> Result<(), Error> {
// A module we want pre-loaded into the snapshot
let module = Module::new(
"my_module.js",
"globalThis.importantFunction = function() {
return 42;
}",
);
let snapshot = SnapshotBuilder::new(Default::default())?
.with_module(&module)?
.finish();
fs::write(SNAPSHOT_PATH, snapshot)?;
Ok(())
}
Then, in order to use the snapshot:
use rustyscript::{Runtime, RuntimeOptions};
static SNAPSHOT: &[u8] = include_bytes!("example_snapshot.bin");
fn main() -> Result<(), rustyscript::Error> {
let mut runtime = Runtime::new(RuntimeOptions {
startup_snapshot: Some(SNAPSHOT),
..Default::default()
})?;
let important_value: u32 = runtime.eval("importantFunction()")?;
assert_eq!(important_value, 42);
Ok(())
}
The startup time of the runtime will fall from ~50ms, to less than 1 with the snapshot.
You could also generate the snapshot at runtime, in a lazy_static block, or similar:
use rustyscript::{Error, Module, Runtime, RuntimeOptions, SnapshotBuilder};
use std::sync::OnceLock;
static SNAPSHOT: OnceLock<Box<[u8]>> = OnceLock::new();
fn get_snapshot() -> &'static [u8] {
SNAPSHOT.get_or_init(|| {
let module = Module::new(
"my_module.js",
"globalThis.importantFunction = function() {
return 42;
}",
);
SnapshotBuilder::new(Default::default())
.unwrap()
.with_module(&module)
.unwrap()
.finish()
})
}
fn main() -> Result<(), Error> {
let mut runtime = Runtime::new(RuntimeOptions {
startup_snapshot: Some(get_snapshot()),
..Default::default()
})?;
runtime.eval::<()>("1 + 1")?;
Ok(())
}