DEV Community

Daniel Budden
Daniel Budden

Posted on

Running WebAssembly outside of the browser

This is the third article in a series of posts on Rust, WebAssembly and Genetic Algorithms. If you haven't read the first or second articles in this series it's definitely worth going back to them before continuing. In this article I will talk about Wasmtime, a runtime for executing WebAssembly outside of the browser. I'll take the algorithm developed in the first article in this series, build some extra features for improved command line usage, compile to WebAssembly and run the WebAssembly module from the command line with Wasmtime.

The complete code for this post can be found here.

Abstract image of green and white swirls
Photo by Darío Méndez on Unsplash

The first improvement I've made to the Rust code from the first post in this series is to take the algorithm parameters as command line arguments. To enable this I've added the structopt crate, which builds on the popular clap crate, and allows the definition of command line arguments using a struct. To define the CLI arguments a struct is defined and used with a macro and custom derive from structopt.

#[derive(StructOpt)]
#[structopt()]
struct Opt {
    #[structopt(name = "iterations")]
    iterations: usize,
    #[structopt(name = "pop_size")]
    population_size: usize,
    #[structopt(name = "crossover_rate")]
    crossover_rate: f64,
    #[structopt(name = "mutation_rate")]
    mutation_rate: f64,
    #[structopt(name = "survival_rate")]
    survival_rate: f64,
    #[structopt(name = "csv", parse(from_os_str))]
    csv: PathBuf,
}

fn main() {
    let opts = Opt::from_args();
    ...
}

The second change is to parse the list of cities for the travelling salesman problem from a CSV file. The CSV will have two columns, the first being the x co-ordinate and the second being the y co-ordinate for each city. The first row of the CSV file will contain a header with the column names (x and y) and this will allow the rows to be deserialised into the City struct by the csv crate when they are read in.

#[derive(Deserialize)]
pub struct City {
    x: f64,
    y: f64,
}

fn main() {
    ...
    let mut reader = Reader::from_path(opts.csv).unwrap();
    let cities: Vec<City> = reader.deserialize()
        .map(|r| {
            let result: City = r.unwrap();
            result
        })
        .collect();

    ...
}

To get started with Wasmtime you will need to compile it from source. The full details on getting setup can be found here, but for MacOS this involved:

brew install cmake llvm

git clone --recurse-submodules https://github.com/CraneStation/wasmtime.git

cd wasmtime

cargo build

Once Wasmtime is compiled the next step is to ensure the necessary compile target for WebAssembly is available. Wasmtime uses the WebAssembly System Interface (WASI), which provides WebAssembly code with access to operating system features such as the filesystem. Adding the right compile target and compiling the Rust code to WebAssembly is done using:

rustup target add wasm32-wasi

cargo build --target wasm32-wasi --release

And finally running the WebAssembly Genetic Algorithm on the command line with Wasmtime. WebAssembly's security model involves sandboxing, this means that to enable the program to access files from the operating system the Wasmtime runtime needs a list of directories that the program will should be allowed to access. In this case the current directory . is provided so that the WebAssembly module can access the cities.csv file there.

../wasmtime/target/release/wasmtime --dir=. target/wasm32-wasi/release/wasi-genetic.wasm 5000 500 0.4 0.001 0.3 cities.csv

Thanks for reading this series of posts on Rust, WebAssembly and Genetic Algorithm. Any questions, feedback or comments are welcome here or on the repositories listed. The complete code for this post can be found here.

Top comments (0)