Site Reliability Engineer, Software Engineer, coffee addicted, traveler

WebAssembly in 2024: Finding Prime Numbers With Go And Wasm

January 22, 2024


Share it

What Is WebAssembly?

It's been a while since last time I did something with WebAssembly, somewhere back then in 2022. If this word is new to you don't worry. Putting in simple terms, it is a binary instruction format designed as a portable compilation target for high-level programming languages. It allows code written in languages like C, C++, Rust and Go to be executed in web browsers at near-native speed. WebAssembly is designed to be a low-level, efficient, and secure virtual machine that can be embedded in web pages, providing a platform-independent execution environment for web applications.

Why Play With It Now?

From time to time I see people claiming that WebAssembly is close to become a normal tool in our day to day work. With insomnia as my unlikely companion and nothing better to do I decided to check the state of tooling specifically for golang and I intend to show my findings in this one :)

A Goal For this Endeavour

The goal of this project was building a simple app using Go and Wasm to implement the Sieve of Eratosthenes Algorithm. This algorithm is very useful for finding prime numbers up to a given limit. For instance, if you ask for all the prime numbers up to 12 you'll have 2, 3, 5, 7 and 11.

The algorithm's idea is quite simple: (1) we create a list of numbers with the size of the given limit + 1 (just to be able to represent the numbers using indexes); (2) we assume that all numbers in such a list are truly prime numbers; (3) as we know that 0 and 1 are not prime numbers we mark them as false; (4) then, we iterate from 2 up to the square root of the given limit checking if the current number can divide the limit, if so we mark all multiples of the current number as false; (5) and then we repeat this process.

I chose this objective because it involves not only transferring data from the "JavaScript part" to the "Go part" but also the reverse process.

The basic flow is: the user provides a number representing the end of the interval to search for prime numbers. This number is then passed as a parameter to a Go function responsible for identifying prime numbers within the specified range. Subsequently, the discovered prime numbers are returned to the UI layer and displayed as a list.

Binding Go and JavaScript

As mentioned earlier, this project has two main parts, let's call them the Go part and JavaScript part. The Go part is a simple program that makes a function implementing the Sieve of Eratosthenes available to be called by JavaScript when running in a WebAssembly environment. The very app entry point is bellow one:

App entry point.

The line js.Global().Set("findPrimes", findPrimes()) serves as the bridge between Go and JavaScript, allowing the function findPrimes be callable from JavaScript. Additionally, the line <-make(chan bool) just defines a simple channel to prevent the program from exiting immediately. This is typical in WebAssembly programs where the Go runtime does not automatically wait for asynchronous tasks.

To build the go part we can run:

GOARCH=wasm GOOS=js go build -o public/main.wasm cmd/wasm/*.go  

This is basically setting the target architecture for the Go compiler to WebAssembly by using GOARCH=wasm and GOOS=js just targets operating system to JavaScript. The created file main.wasm will be placed at the public directory.

And another important file is the wasm_exec.js, already provided by Go, thus we just need to copy it. It can be done by running:

cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" public/wasm_exec.js

With files wasm_exec.js and main.wasm available we can load them on the JavaScript client to call the function findPrimes. And that's all for the Go part :)

You can see the full JavaScript part on the project repository, but the important chunk is bellow one:

Chunk of the JavaScript part.

Besides the input validation, it fetches the WebAssembly binary (main.wasm), instantiates it, and passes the import object from the go instance, this is done by WebAssembly.instantiateStreaming(fetch(wasmFile), go.importObject).then((result) => {});. Besides of it, the chunk go.run(result.instance); executes the Go program inside the WebAssembly instance, making the Go function findPrimes callable.

Deploying To Vercel

Vercel now supports Go functions, and due to the easy integration with Github projects I decided to deploy it there and you can see the project running live here. In case you want to check the code and run it locally you can find the project on my Github

My Conclusion So Far

As I could see, indeed there are good learning resources out there, like this one, but they require you dig deep into it in order to mine useful resources for what you need. For sure this is true for any dev tool, but at least, for instance, one can easily find a plenty of tutorials on "how to build a project XYZ using NestJS, Spring Boot, Echo, etc.", the same is not true for WebAssembly yet.

But I really enjoyed it, and I won't stop with this one. I'll probably experiment it using C due to the tool I found during this project called Emscripten.