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(())
}