
Wasmer: the fastest Go library for executing WebAssembly code
- Transfer
WebAssembly (wasm) is a portable binary instruction format. The same code wasm code can be executed in any environment. In order to support this statement, every language, platform, and system must be able to execute such code, making it as fast and safe as possible. Wasmer is a wasm runtime written in Rust . Obviously, wasmer can be used in any Rust application. The author of the material, the translation of which we publish today, says that he and other participants in the Wasmer project successfully implemented this wasm-code runtime in other languages:

Here we will talk about a new project - go-ext-wasm , which is a library for Go, designed to execute binary wasm-code. As it turned out, the go-ext-wasm project is much faster than other similar solutions. But let's not get ahead of ourselves. Let's start with a story about how to work with him.
To get started, install wasmer in a Go environment (with cgo support).
The go-ext-wasm project is a regular Go library. When working with this library, a construction is used
Now let's get to practice. We will write a simple program that compiles in wasm. We will use for this, for example, Rust:
We will name the file with the program
The following program, written in Go, performs a function
Here, a program written in Go calls a function from a wasm file that was obtained by compiling code written in Rust.
So, the experiment was a success, we successfully executed the WebAssembly code in Go. It should be noted that data type conversion is automated. Those Go values that are passed to the wasm code are cast to WebAssembly types. What the wasm function returns is cast to Go types. As a result, working with functions from wasm files in Go looks the same as working with regular Go functions.
As we saw in the previous example, WebAssembly modules are able to export functions that can be called from outside. This is the mechanism that allows wasm code to be executed in various environments.
At the same time, WebAssembly modules themselves can work with imported functions. Consider the following program written in Rust.
Let's name the file with it
The exported function
We implement the function
Let's parse this code:
The WebAssembly instance has linear memory. Let's talk about how to read data from it. Let's start, as usual, with the Rust code, which we will name
The result of compiling this code is in the file
The function
Now go to the Go side:
The function
This function returns the memory slice of the WebAssembly instance. You can use it like any Go slice.
Fortunately, we know the length of the line that we want to read, so to read the necessary information, it is enough to use the construction
Here's an example that shows more advanced memory mechanisms when using Go's WebAssembly code.
The go-ext-wasm project, as we have just seen, has a convenient API. Now it's time to talk about its performance.
Unlike PHP or Ruby, the Go world already has solutions for working with wasm code. In particular, we are talking about the following projects:
The material on the php-ext-wasm project used the n-body algorithm to study performance . There are many other algorithms suitable for examining the performance of code execution environments. For example, this is the Fibonacci algorithm (recursive version) and the Pollard ρ-algorithm used in Life. This is the Snappy compression algorithm. The latter works successfully with go-ext-wasm, but not with Life or Wagon. As a result, he was removed from the test set. Test code can be found here .
During the tests, the latest versions of the research projects were used. Namely, these are Life 20190521143330-57f3819c2df0 and Wagon 0.4.0.
The numbers shown on the chart reflect the average values obtained after 10 starts of the test. The study used a 2016 MacBook Pro 15 "with 2.9 GHz Intel Core i7 processor and 16 GB of memory.
The test results are grouped along the X axis according to the types of tests. The Y axis shows the time in milliseconds required to complete the test. The lower the indicator - . better

performance comparing Wasmer, Wagon and Life by implementing various algorithms
Platform for Life and Wagon, on average, give similar results Wasmer same, on average, 72 times faster..
it is important to note that Wasmer supports three backend: SinglePass , Cranelift and LLVM. The default backend in the Go library is Cranelift ( here you can find out more about it). Using LLVM will give performance close to native, but it was decided to start with Cranelift, since this backend gives the best ratio between compilation time and program execution time.
Here you can read about different backends, their pros and cons, and in which situations it is better to use them.
The open source project go-ext-wasm is a new Go library designed to execute binary wasm code. It includes a Wasmer runtime . Its first version includes APIs, the need for which arises most often.
Performance tests showed that Wasmer, on average, is 72 times faster than Life and Wagon.
Dear readers! Do you plan to use the ability to run wasm code in Go using go-ext-wasm?


- In C and C ++, this is implemented through bindings.
- In PHP, it is an extension of php-ext-wasm .
- In Python, this is a wasmer package published in PyPi that is being worked on in the python-ext-wasm repository .
- In Ruby, this is the wasmer gem that can be found on RubyGems. Its code is in the ruby-ext-wasm repository .
Here we will talk about a new project - go-ext-wasm , which is a library for Go, designed to execute binary wasm-code. As it turned out, the go-ext-wasm project is much faster than other similar solutions. But let's not get ahead of ourselves. Let's start with a story about how to work with him.
Calling wasm functions from Go
To get started, install wasmer in a Go environment (with cgo support).
export CGO_ENABLED=1; export CC=gcc; go install github.com/wasmerio/go-ext-wasm/wasmer
The go-ext-wasm project is a regular Go library. When working with this library, a construction is used
import "github.com/wasmerio/go-ext-wasm/wasmer"
. Now let's get to practice. We will write a simple program that compiles in wasm. We will use for this, for example, Rust:
#[no_mangle]
pub extern fn sum(x: i32, y: i32) -> i32 {
x + y
}
We will name the file with the program
simple.rs
, as a result of compilation of this program we get the file simple.wasm . The following program, written in Go, performs a function
sum
from the wasm file, passing it the numbers 5 and 37 as arguments:package main
import (
"fmt"
wasm "github.com/wasmerio/go-ext-wasm/wasmer"
)
func main() {
// Чтение модуля WebAssembly.
bytes, _ := wasm.ReadBytes("simple.wasm")
// Создание экземпляра модуля WebAssembly.
instance, _ := wasm.NewInstance(bytes)
defer instance.Close()
// Получение экспортированной функции `sum` из экземпляра WebAssembly.
sum := instance.Exports["sum"]
// Вызов экспортированной функции с использовании стандартных значений Go.
// Преобразование типов данных, передаваемых функции и получаемых из неё, выполняется автоматически.
result, _ := sum(5, 37)
fmt.Println(result) // 42!
}
Here, a program written in Go calls a function from a wasm file that was obtained by compiling code written in Rust.
So, the experiment was a success, we successfully executed the WebAssembly code in Go. It should be noted that data type conversion is automated. Those Go values that are passed to the wasm code are cast to WebAssembly types. What the wasm function returns is cast to Go types. As a result, working with functions from wasm files in Go looks the same as working with regular Go functions.
Call Go Functions from WebAssembly Code
As we saw in the previous example, WebAssembly modules are able to export functions that can be called from outside. This is the mechanism that allows wasm code to be executed in various environments.
At the same time, WebAssembly modules themselves can work with imported functions. Consider the following program written in Rust.
extern {
fn sum(x: i32, y: i32) -> i32;
}
#[no_mangle]
pub extern fn add1(x: i32, y: i32) -> i32 {
unsafe { sum(x, y) } + 1
}
Let's name the file with it
import.rs
. Compiling it into WebAssembly will result in code that can be found here . The exported function
add1
calls the function sum
. There is no implementation of this function, only its signature is defined in the file. This is the so-called extern function. For WebAssembly, this is an imported function. Its implementation must be imported. We implement the function
sum
using Go. For this we need to use cgo . Here is the resulting code. Some comments, which are descriptions of the main code fragments, are numbered. Below we will talk about them in more detail.package main
// // 1. Объявляем сигнатуру функции `sum` (обратите внимание на cgo).
//
// #include
//
// extern int32_t sum(void *context, int32_t x, int32_t y);
import "C"
import (
"fmt"
wasm "github.com/wasmerio/go-ext-wasm/wasmer"
"unsafe"
)
// 2. Пишем реализацию функции `sum` и экспортируем её (для cgo).
//export sum
func sum(context unsafe.Pointer, x int32, y int32) int32 {
return x + y
}
func main() {
// Чтение модуля WebAssembly.
bytes, _ := wasm.ReadBytes("import.wasm")
// 3. Объявление импортированной функции для WebAssembly.
imports, _ := wasm.NewImports().Append("sum", sum, C.sum)
// 4. Создание экземпляра модуля WebAssembly с импортами.
instance, _ := wasm.NewInstanceWithImports(bytes, imports)
// Позже закроем экземпляр WebAssembly.
defer instance.Close()
// Получение экспортированной функции `add1` из экземпляра WebAssembly.
add1 := instance.Exports["add1"]
// Вызов экспортированной функции.
result, _ := add1(1, 2)
fmt.Println(result)
// add1(1, 2)
// = sum(1 + 2) + 1
// = 1 + 2 + 1
// = 4
// QED
}
Let's parse this code:
- The signature of the function is
sum
defined in C (see the comment above the commandimport "C"
). - The implementation of the function is
sum
defined in Go (pay attention to the line//export
- cgo uses this mechanism to establish the connection of code written in Go with code written in C). NewImports
Is the API used to create WebAssembly imports. In this code"sum"
, this is the name of the function imported by WebAssembly,sum
is the pointer to the Go function, andC.sum
is the pointer to the cgo function.- And finally,
NewInstanceWithImports
this is a constructor designed to initialize a WebAssembly module with imports.
Reading data from memory
The WebAssembly instance has linear memory. Let's talk about how to read data from it. Let's start, as usual, with the Rust code, which we will name
memory.rs
.#[no_mangle]
pub extern fn return_hello() -> *const u8 {
b"Hello, World!\0".as_ptr()
}
The result of compiling this code is in the file
memory.wasm
that is used below. The function
return_hello
returns a pointer to a string. The line ends, as in C, with a null character. Now go to the Go side:
bytes, _ := wasm.ReadBytes("memory.wasm")
instance, _ := wasm.NewInstance(bytes)
defer instance.Close()
// Вызов экспортированной функции `return_hello`.
// Эта функция возвращает указатель на строку.
result, _ := instance.Exports["return_hello"]()
// Значение указателя рассматривается как целое число.
pointer := result.ToI32()
// Чтение данных из памяти.
memory := instance.Memory.Data()
fmt.Println(string(memory[pointer : pointer+13])) // Hello, World!
The function
return_hello
returns a pointer as a value i32
. We get this value by calling ToI32
. Then we get the data from memory with instance.Memory.Data()
. This function returns the memory slice of the WebAssembly instance. You can use it like any Go slice.
Fortunately, we know the length of the line that we want to read, so to read the necessary information, it is enough to use the construction
memory[pointer : pointer+13]
. Then the read data is converted to a string. Here's an example that shows more advanced memory mechanisms when using Go's WebAssembly code.
Benchmarks
The go-ext-wasm project, as we have just seen, has a convenient API. Now it's time to talk about its performance.
Unlike PHP or Ruby, the Go world already has solutions for working with wasm code. In particular, we are talking about the following projects:
- Life from Perlin Network - WebAssembly interpreter.
- Go Interpreter's Wagon is a WebAssembly interpreter and toolkit.
The material on the php-ext-wasm project used the n-body algorithm to study performance . There are many other algorithms suitable for examining the performance of code execution environments. For example, this is the Fibonacci algorithm (recursive version) and the Pollard ρ-algorithm used in Life. This is the Snappy compression algorithm. The latter works successfully with go-ext-wasm, but not with Life or Wagon. As a result, he was removed from the test set. Test code can be found here .
During the tests, the latest versions of the research projects were used. Namely, these are Life 20190521143330-57f3819c2df0 and Wagon 0.4.0.
The numbers shown on the chart reflect the average values obtained after 10 starts of the test. The study used a 2016 MacBook Pro 15 "with 2.9 GHz Intel Core i7 processor and 16 GB of memory.
The test results are grouped along the X axis according to the types of tests. The Y axis shows the time in milliseconds required to complete the test. The lower the indicator - . better

performance comparing Wasmer, Wagon and Life by implementing various algorithms
Platform for Life and Wagon, on average, give similar results Wasmer same, on average, 72 times faster..
it is important to note that Wasmer supports three backend: SinglePass , Cranelift and LLVM. The default backend in the Go library is Cranelift ( here you can find out more about it). Using LLVM will give performance close to native, but it was decided to start with Cranelift, since this backend gives the best ratio between compilation time and program execution time.
Here you can read about different backends, their pros and cons, and in which situations it is better to use them.
Summary
The open source project go-ext-wasm is a new Go library designed to execute binary wasm code. It includes a Wasmer runtime . Its first version includes APIs, the need for which arises most often.
Performance tests showed that Wasmer, on average, is 72 times faster than Life and Wagon.
Dear readers! Do you plan to use the ability to run wasm code in Go using go-ext-wasm?
