Optimizing your code for performance: A guide to using Rust and WebAssembly

Stefan Kupresak
4 min readFeb 3, 2023

--

JavaScript is a powerful language for building web applications, but as applications grow in complexity, performance can become an issue. When it comes to optimizing your code, one promising solution is to use Rust and WebAssembly.

Rust is a systems programming language that is known for its performance and safety, while WebAssembly is a binary format for running code on the web that is designed to be fast and efficient. When combined, Rust and WebAssembly can offer a significant boost to the performance of your JavaScript code.

In this blog post, we’ll explore how to use Rust and WebAssembly to optimize a JavaScript application. We’ll start by looking at a simple example of JavaScript code that calculates the Fibonacci sequence, and we’ll show how to use Rust and WebAssembly to create a faster and more efficient version of the code. Along the way, we’ll discuss the benefits of using Rust and WebAssembly together, and we’ll offer tips and advice for getting started with this exciting and powerful combination of technologies.

Creating a cargo wasm project

Getting started with Rust and WebAssembly involves setting up a Rust project and configuring it to compile to WebAssembly. Here are the basic steps you’ll need to follow:

  • Install the Rust toolchain. You can easly do that by going to https://rustup.rs/
  • Using the cargo command-line utility create a project:
$ cargo new --lib fib-wasm
  • Configure the project to target WebAssembly and add wasm-bindgen to dependecies
[package]
name = "fib-wasm"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[lib]
crate-type = ["cdylib"]

[dependencies]
wasm-bindgen = "0.2.74"

Now we will test our setup by creating a simple greet function that’s a wrapper around alert from our browsers:

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern "C" {
pub fn alert(s: &str);
}

#[wasm_bindgen]
pub fn greet(name: &str) {
alert(&format!("Hello, {}!", name));
}

Let’s compile it:

$ wasm-pack build 

This will create a pkg directory in our project with a .js module file as well as our compiled rust code to .wasm

Testing it with HTML

The simplest way to run our example is to make an index.html file inside the root of our cargo project.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script type="module">
import init, { greet } from "./pkg/fib_wasm.js";

init().then(() => {
greet("It works!");
});
</script>
</body>
</html>

Note that running this shows us an alert in the browser. Note, we’re leveraging the new type=module which is a new browser feature so we don’t have to deal with bundlers for our little experiment.

Use case: a slow fib function

We will start by making a function that logs execution time.

function logExecutionTime(fn) {
console.time("Start time");
fn();
console.timeEnd("End time");
}

And now define out slow JS fib(n) function:

function fib(n) {
if (n <= 1) {
return n;
}
return fib(n - 1) + fib(n - 2);
}

Running it like so we can see the test results:

logExecutionTime(fib.bind(null, 40));
// logs timer: 2466ms - timer ended

Now, let’s go to our rust code and write a better version of this function:

use wasm_bindgen::prelude::*;

#[no_mangle]
#[wasm_bindgen]
pub extern "C" fn fib(n: i32) -> i32 {
if n <= 1 {
return 1;
}

let mut a = 0;
let mut b = 1;
let mut c = 0;
for _i in 1..n {
c = a + b;
a = b;
b = c;
}
c
}

This code uses an iterative approach instead of a recursive one, which is much faster and more efficient.

Let’s see how it performs:

import init, { rust_fib } from "./pkg/fib_wasm.js";
// ...

init().then(() => {
logExecutionTime(rust_fib.bind(null, 40));
// Logs 8ms
});

The performance boost is quite significant. In fact, the optimized code using Rust and WebAssembly runs almost 10 times faster than the original JavaScript code.

This demonstrates the power of Rust and WebAssembly in improving the performance of your code, and it highlights why it’s worth considering this combination of technologies when optimizing your JavaScript applications. The improved performance will provide a better user experience for your users, and it will also allow your application to handle more demanding tasks with ease.

Conclusion

In conclusion, Rust and WebAssembly are powerful technologies that can help you optimize your JavaScript code and achieve significant performance gains. Whether you’re a seasoned web developer or just starting out, there’s never been a better time to explore the possibilities of Rust and WebAssembly. So why not give it a try? You might just be surprised at what you can achieve.

Happy coding! | If you’d like, you can support my content here. :)

--

--

Stefan Kupresak

Hello. I’m a full-stack developer specializing in Elixir/Phoenix and React, and I love going into lots of details in any problem.