GopherCon 2019 - Get Going with WebAssembly
Alex Boten for the GopherCon Liveblog
Presenter: Johan Brandhorst
Liveblogger: Alex Boten
Curious about WebAssembly and how we can use it with Go? In this session, Johan introduces the technology, shows how to get started with WebAssembly and Go, discusses what is possible today and what will be possible tomorrow.
What is WebAssembly?
WebAssembly is a web standard binary instruction format. It's developed by the W3C and it grew out of the asm.js and Google's Native Client projects. It aims to provide portable targets to allow code to be compiled once and run across many platforms. Currently runtimes for WASM include:
- Browsers: Chrome, Firefox, Safari, Edge and others since late 2017
- Native runtimes: wasmer, wasmtime, wagon, life, lucent, WAVM, more
Many of these runtimes have appeared fairly recently, meaning that WebAssembly is no longer just a runtime for the browser but can be used for code that can run anywhere, including bare metal.
WebAssembly in Go
As of Go 1.13, there is experimental support for WebAssembly using the JavaScript interface but as it is only experimental, using it in production is not recommended. Support for the WASI interface is not currently available but has been planned and may be available as early as Go 1.14.
It's quite easy to get started with WebAssembly in Go. Let's start with a simple "Hello World". The following code will allow us to compile a Go program that can be loaded in a browser. Note the build tag at the top.
We then compile the program specifying the wasm
architecture and js
GOOS environment variables. Additionally to get the setup up and running, we'll need to copy some files that will load the binary into the browser and execute it.
Lastly, we'll need a webserver to ensure the right content type is served.
The examples in Johan's repo has some nice Makefile targets to simplify the process, as well as many other examples and a webserver that's ready to use. Once the server is up and running, launch a browser and bring up the console to see the message being printed out. By default, standard output will go to the console.
If you look at the size of the file, you can start seeing some of the challenges. This is a very large file for a simple "Hello World".
JavaScript interface
The next example in the tutorial makes use of the JavaScript interface syscall/js
, based on the GopherJS interface. An important thing to note is that any data that is passed to JavaScript must be copied as there is no shared memory yet. Data transfers should be minimized for greater performance. The following code writes an element to the DOM instead of the console. The file size of this binary is "only 1.37MB", as we're no longer importing the fmt
package. Large packages compile to much larger binaries, something to be conscious of when building for WASM.
net/http
client
It's possible to use some of the standard libraries in WebAssembly, including the net/http
library, which is built on top of the browser's Fetch API. The next example shows us how to do just that by making a request and printing its output to the DOM.
Go test
You might think it would be hard to run tests, but there are already a couple of options for WebAssempbly test runners. The standard library has go_wasm_js_exec for node and wasmbrowser supports testing the code using the browser.
Web frameworks
Vecty
The first web framework example shown is using vecty which is very React-like. It was originally written for GopherJS and was later modified to support WebAssembly. Set the title of the window and specify the function to render which defines how the struct should be rendered in a browser. Syntax much like inline html that react uses, but with the power of Go. This is really powerful and expressive.
Vugu
The last example shown is Vugu. It is a Vue-like library and uses a relatively new custom file format, which is a super set of html with code generators. Vugu provides a generator vugugen
which takes .vugu
files and generates the Go code, which you should never have to modify. For handling asynchronous functions, goroutines are used, and Vugu offers locking functions Lock
and UnlockRender
. The embedded code is Go.
You can try it in the playground https://play.vugu.org.
Challenges
We've seen a few demos but what about the file sizes? An easy solution to reducing the size of the files is to use compression like gzip. The demo webserver in the repo has support for gzipping content via a command line flag, which when demo'd, reduced the size of the content being served by roughly 75%.
Another option would be to use a Content Delivery Network (CDN), to distribute the content closer to the users and provide compression. The wasmgo project can be used to deploy binaries to CDNs easily.
TinyGo
It's still very early days, but a very exciting project to bring Go to microcontrollers. Tinygo is built on a completely different compiler stack, different runtime with its own garbage collector. It doesn't have a standard implementation for some libraries like http for example, but it does support WebAssembly. The example available in the repo uses a Docker container to run tiny locally and reduced the size of the binary created from 2.58MB down to 33.40KB.
Looking ahead
Though it's currently usable, there are still many areas where work needs to be done for WebAssembly in Go. Some areas of improvement include:
- removing the need to copy when using the APIs
- adding support for threading
- reduce the size of the binaries being compiled, could be much smaller if we didnt have to compile the garbage collector
- providing typesafe APIs, gowebapi is in the works
- support for WASI
Conclusion
WebAssembly in Go is here and ready to try! Although the landscape is evolving really quickly, the opportunity is huge. The ability to deliver truly portable system binaries could potentially replace JavaScript in the browser. WebAssembly has the potential to finally realize the goal of being platform agnostic without having to rely on a JVM.
Join the Gophers Slack #webassembly, #tinygo channels. The examples are all available in the repo here: https://github.com/johanbrandhorst/wasm-experiments