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.
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)