Wazero: The zero dependency WebAssembly runtime for Go developers
github.com
github.com
[0] https://github.com/jvm-profiling-tools/perf-map-agent
[1] https://github.com/torvalds/linux/blob/master/tools/perf/Doc...
Wasmtime also has this page on debugging with supported debuggers when the webassembly was compiled with debug information: https://docs.wasmtime.dev/examples-debugging.html
https://developer.chrome.com/blog/new-in-devtools-91/#error-...
See it in action:
1. DWARF support - this requires those compiling to Wasm to configure to emit that. It is also murky because there's no canonical mapping in Wasm (most rely on LLVM underneath, but wazero is zero dependencies so we can't do that). Anyway, this is still on the table. [2]
2. Observability hooks - wazero is uniquely go-native which means we can do things like context propagation, allowing telemetry hooks including distributed tracing. While it may not seem intuitive to use that in Wasm, it can allow you to re-use tools that can show a trace graph. We have an experimental listener now, which has an example of how to make some strace looking logs. [3] Later, we'll convert this to an interceptor approach so that you can do metrics and stuff nicer. [4]
Regardless of this, debugging is easier because everything is in go. Apart from the DWARF thing which is totally important, the runtime being in Go completely means you debug everything going on, down to what is allocating memory, just by putting break-points in Go code. This is a very neat thing, and has helped some folks develop better code. Ex. https://github.com/inkeliz/karmem
Hope these help. Indeed the road to wasm is littered with a lot of runtimes, mostly abandoned or archived. We're hoping to buck the trend debugability, so watch the issues below if interested, and feel free to tell us anything we aren't doing right, too.
[1]: https://github.com/tetratelabs/wazero/issues/484 [2]: https://github.com/tetratelabs/wazero/issues/58 [3]: https://github.com/tetratelabs/wazero/blob/main/experimental... [4]: https://github.com/tetratelabs/wazero/issues/451
waPC [1] (used by some things like kubewarden) is an example of a plugin system, and its go integration [2] is one of the few things currently using wazero. It also integrates with alternatives such as wasmer and wasmtime, so you can look at the code to see how things are different.
We were intentionally hush about the project as we finish version 1.0, so weren't pushing for a lot of integrations, rather serving early adopters. Hence, wapc-go is currently the main victim of our api ;)
hope this helps!
Anyway, if you use `wazero.NewRuntime()` magically it redirects to `wazero.NewRuntimeCompiler()`[1]. When the compiler is in use, all the native stuff happens automatically and you don't need to change your binary based on the wasm you use.
This is actually neater than it seems as embedded compiler is goroutine safe, meaning you can compile modules concurrently.
https://pkg.go.dev/github.com/tetratelabs/wazero#NewRuntimeC...
When explaining it, it might be a good idea to focus on a little more on what this technology can do for you: "How to add cross-platform plugin support to a Go program using WebAssembly."
I haven’t worked with WASM before, but it seems like you should be able to sniff the CLI definition and expose the arguments and flags as basic web components / widgets.
TL;DR;
> whats the compiled size?
wazero adds less than 2MB to your binary with no strings attached.
> Any performance numbers?
yes, but unlike most projects we don't add numbers to our README
> How is it easier to access functions and types from WASM to Go, and vice versa?
Take a look at our examples, and let us know how easy it is? [5]
Long:
> whats the compiled size?
wazero adds less than 2MB to your binary with no strings attached.
Because wazero doesn't rely on CGO, you have the simplicity of counting only the size of the binary. In other words, there are no potentially large shared libraries you need to count, and certainly no glibc needed.
I made this example [1], which is the a basic calculater, and it makes a 3.3MB binary (go 1.18.2) slightly larger in Docker (3.4MB via FROM scratch). It used to be 3.1MB until we added WebAssembly 2.0 support which is really quite big. Meanwhile I think the smallest Go binary would be 1.7MB or so.
> Any performance numbers?
yes, but unlike most projects we don't add numbers to our README.
README numbers are instantly out of date. We might publish on cron or something, since people seem really interested.
We run benchmarks on every commit[2], and the results vary, but not in surprising ways. The most accurate benchmark is allocation [3], though most people seem to only benchmark math functions. allocation is more like what your real code would do, ex an envoy filter changing a header. I saved results from a recent run to save you some clicking [4]. Sometimes wasmer beats us in one of the benchmarks, and that's with their default config.
Besides speed of calling, we pay attention to both the performance and safety of instantiating a wasm module. Instantiation is when you create a new sandbox. Granted, this is a new project and speed wise we have work to do, but at least concurrency works. So you can safely use the same runtime and create a new sandbox even per request. If your wasm is small that could happen in <1ms.
> How is it easier to access functions and types from WASM to Go, and vice versa?
Take a look at our examples, and let us know how easy it is? [5]
We've worked on this quite a bit, and hope that we are doing ok on it. Basically wasm has "export" functions (sometimes called externs), and we have an api.Function[6] interface for calling regardless of who defined it. We have a ModuleBuilder[7] which is how you define functions in go. These are explained by our examples [5]. For us, we mainly designed based on how we felt things could be in Go, feedback from users and also comparing against how others do it. Particularly, we looked at wapc-go [8] to ensure our API felt the leanest possible to achieve the same goal.
[1]: https://gist.github.com/codefromthecrypt/edb33284354d592dc60... [2]: https://github.com/tetratelabs/wazero/blob/main/.github/work... [3]: https://github.com/tetratelabs/wazero/blob/main/internal/int... [4]: https://gist.github.com/codefromthecrypt/48877abc4b3ba5ceca1... [5]: https://github.com/tetratelabs/wazero/tree/main/examples [6]: https://pkg.go.dev/github.com/tetratelabs/wazero/api#Functio... [7]: https://pkg.go.dev/github.com/tetratelabs/wazero#ModuleBuild... [8]: https://github.com/wapc/wapc-go/tree/master/engines
Here's an example:
I recently finished building https://subzo.com.au which allows customizing and ordering 3D models. The way the model's cost, volume and other attributes are calculated needs to be done both on the frontend (for speed) and on the backend (to validate). Backend is in Go and we can't practically run Go in the browser. So instead, I wrote the calculation snippets in JavaScript (which runs natively in the browser) and ran them on the backend on a JavaScript VM library written in Go [1].
[1] https://github.com/tetratelabs/wazero/tree/main/internal/int... [2] https://github.com/tetratelabs/wazero/runs/6472382559?check_...
What have Google got to do with it?
C/C++ is a solid choice too thanks to EMSCRIPEN, but it is very slow to compile, and is not very clean solution
dotnet is just too big for WASM
rust is too complicated slow to program with
I'm not sure if they're actually working on it or blocked for some reason
[1]: https://github.com/WebAssembly/proposals [2]: https://github.com/WebAssembly/meetings/blob/main/process/ph... [3]: https://webassembly.org/roadmap/ [4]: https://github.com/WebAssembly/gc
[1] https://github.com/tetratelabs/wazero/tree/main/examples/all...
Emscripten is amazing if you need a full POSIX environment in a web browser. Everything is emulated through Javascript calls.
If you don't need that, or just need the WASI subset, you can simply use Zig to compile C/C++ code to WebAssembly ("zig cc -target wasm32-wasi"). Compilation is as fast as other targets.