Gasm Versions Save

wazero: the zero dependency WebAssembly runtime for Go developers

v1.7.1

3 weeks ago

Hiya folks, time to celebrate the new wazero release again! The last release 1.7.0 was such a blast so this new 1.7.1 is a little bit boring compared to it, but it still has some niceties!

This patch release has basically two major things: various bug fixes and experimental memory allocation API!

Bug fixes

Since the release of 1.7.0, several community members (@jerbob92, @anuraaga and @davidmdm) tried the new optimizing compiler and reported some bugs. @mathetake and @evacchi worked on the fix and all the reported bugs were removed. Notably, the arm64 compiler has become more robust against huge binaries like the ones produced by the Go official compiler, in addition to a corner case in the bounds check elimination optimization pass applied to both arm64 and amd64.

Experimental Memory Allocator API

The experimental Memory Allocator API has been added to our experimental friends by @ncruces in collaboration with @achille-roussel. This allows you to control how to allocate the linear memory of Wasm instance. For instance, you can use a memory mapped buffer as a Wasm linear memory. This is highly advanced feature hence requires a lot of detailed knowledge on how Wasm module works. If this is something interesting to you, we highly recommend to talk to @ncruces who is an expert in here ;)

Other Contributions

  • it is now possible to successfully build wazero with TinyGo compiler thanks to the contributions by @deadprogram and @orsinium
  • @ncruces helped clean up the dead codes and experimental packages.

v1.7.0

1 month ago

Welcome to wazero 1.7: the release that upgrades like a minor, but feels like a major!

It's finally time for the long-awaited, final release of our brand new optimizing compiler. This is such a big deal that we are celebrating it at Wasm I/O 2024 with another round of wonderful lightning talks from wazero users like we did in 2023. In fact, even this release is being tagged during the event! Stay tuned on our usual channels to see the recording:

wazero optimizes machine code compiled from wasm

Translating Wasm bytecode into machine code can take multiple forms. An optimizing compiler performs multiple nontrivial transformations on the input code, ultimately producing more efficient ("optimized") code.

In 1.7 we replaced our internal wasm compiler with an optimizing one. This means it is a drop-in: you don’t need to do anything to use it. If interested in compiler design, please read the docs, contributed by @evacchi.

As for performance improvements, we have come to expect a run-time boost ranging from 10% to even 60%, with 30-40% being the average. Notably:

While a major improvement, we decided against calling this version 2.0. If we did, we would cause library dependency lockups due to go imports needing a ‘/v2’. We take backwards compatibility seriously, so couldn’t do that to you!

As usual, @mathetake owns the lionshare of the contributions, with @evacchi helping along the way, especially on the new amd64 backend. Notably, @achille-roussel also contributed a performance improvement to the compiler (#2026) and @ncruces helped in many ways with testing and verifying the implementation, validating (among other things) against his library go-sqlite3.

Note: an optimizing compiler does more work, so it takes longer. Production use of wazero should always compile wasm during initialization to avoid slowing down function runtime.

Experimental: Wasm Threads Spec

The Wasm Threads spec introduces instructions to explicitly share memory between modules, and new operations for atomic memory access. Compilers may use this feature for concurrent threads of execution. For instance @anuraaga’s Go ports of protoc plugins needed atomic instruction support to compile to Wasm.

1.7 concludes a long journey started with @anuraaga's first PR #1899 and continued with @mathetake occasionally tag-teaming, especially to support in the new compiler; @ncruces assisted with reviewing.

You can enable Wasm Threads support by setting the corresponding flag:

     // Threads support must be enabled explicitly in addition to standard V2 features.
     cfg := wazero.NewRuntimeConfig().
           WithCoreFeatures(api.CoreFeaturesV2 | experimental.CoreFeaturesThreads)

For a usage example see features_example_test.go

Experimental: Snapshot/Restore

The Snapshot/Restore experimental feature saves the state of a wasm function and restores it later. This feature has been contributed by @anuraaga to implement exception handling in wasilibs/go-pgquery

You can enable snapshot/restore by setting the context flag:

     // Enable experimental snapshotting functionality by setting it to context. We use this
     // context when invoking functions, indicating to wazero to enable it.
     ctx = context.WithValue(ctx, experimental.EnableSnapshotterKey{}, struct{}{})

For a detailed example see experimental/checkpoint_example_test.go and for further details read PR #1808.

Notably, the Thread Spec and Snapshot/Restore were originally developed as part of the wazerox friendly fork to support wasilibs. With both features making it into this release, @anuraaga could proceed with archiving wazerox: another win from wazero 1.7!

Other changes

  • Experimental support for GOOS=gojs has been dropped (@mathetake, #2027)
  • @orsinium contributed a fix to a test (TestErrorBuilderGoRuntimeError, #2142)
  • @karelbilek has started to extensively test our VFS implementation, contributed a tiny fix in #2100 and reported a few documentation issues.

Thanks everyone for making wazero awesomer and awesomer!

v1.7.0-pre.1

2 months ago

This is a pre-release for wazero 1.7.0. You can watch Releases for the final 1.7.0

watch-wazero-releases

v1.6.0

4 months ago

Hey, gophers! Do you know what time it is? It is that time of the year again! That time when we all gather together to share our time with friends and family to celebrate ANOTHER AWESOME WAZERO RELEASE!

Notably, this release includes the first public experimental release of the long-awaited, highly-requested multi-pass optimizing compiler! Keep reading for more details!

A huge thanks to all the contributors, old and new, and always remember that all the developers hang out at the Gophers Slack in the #wazero channel; and if you haven’t already you can star our repo. You know, Santa is making a list, and checking it twice.

It’s been a while since v1.5.0, but we promise v1.6.0 was worth the wait! Fun facts about v1.6.0:

  • This is the best release since the last one.
  • This release is 100% richer in holiday cheer. ❄️🎄
  • Mulled wine is a fantastic developer productivity boost.

The Optimizing Compiler

Jokes aside, we have a lot to cheer about! The lion share of this release is obviously our brand new optimizing compiler (codename “wazevo”) now available as an experimental feature for arm64 (#1496)!

@mathetake led the design and implementation. Work started this summer, and it has evolved in a few iterations over the last months. Initial focus has been on general abstract infrastructure and support to the arm64 instruction set as our first compilation target. @evacchi contributed to the arm64 backend with special focus on the SIMD instructions, and @achille-roussel helped improve the initial register allocator (this was then further evolved and eventually overhauled again by @mathetake). @ncruces contributed with suggestions and testing.

We held off a few releases to polish this new compiler and gain more confidence in the implementation. In this first public release, the optimizing compiler is only available for arm64. The journey to supporting amd64 is set to begin soon with @evacchi leading the effort.

The new compiler has been, as usual, extensively tested against the Wasm spec, our own unit tests, and against the standard libraries of TinyGo, Go and Zig; and of course it has also been hardened through hours-long fuzzing.

You can enable the optimizing compiler by replacing wazero.NewRuntimeConfigCompiler() or wazero.NewRuntimeConfig() with the new experimental API as follows:

-   	c := wazero.NewRuntimeConfigCompiler()
+   	c := opt.NewRuntimeConfigOptimizingCompiler()
-   	c := wazero.NewRuntimeConfig()
+   	c := opt.NewRuntimeConfigOptimizingCompiler()

The CLI is now also exposing an experimental flag -optimizing-compiler:

wazero run -optimizing-compiler myapp.wasm

The optimizing compiler is an experimental feature so we welcome your input. It is also a work in progress: we implemented only a few optimization passes, guiding our choices through testing and benchmarking.

It is worth noting that WebAssembly is an interesting beast: since it is a compilation target, most compilers generate pre-optimized output; therefore, some traditional optimization passes may surprisingly only add build-time overhead and produce no observable improvement. However, our work there is far from being done: more optimization passes can be added; we invite you to do your experiments and bring your own suggestions. For instance, among others, we currently implement forms of dead-code elimination, and bounds-checking eliminations.

In your experiments, you should also expect the CompileModule phase to take a while longer than the old compiler: the difference may be noticeable with large modules; but you can still cache the result, so you can pay this cost only once. The good news is that, in our tests, the run-time should always visibly improve. Interestingly enough, there are also some cases where both compile-time and run-time have improved: this might be the case when the input module is not pre-optimized, and the dead-code elimination procedures kick in.

For instance, the Zig standard library is about 2x quicker to compile and 4x faster to run than the old compiler. However, a pre-optimized test binary (e.g. pre-processed using Binaryen’s wasm-opt) will be much faster to build on the old compiler, but the new compiler will still produce 2x faster code. This is fully expected because the old compiler does a straightforward translation from input Wasm to native code: therefore, processing time tends to be low; but if the input is large, the generated output will be large. The new compiler is smarter, in that it is able to drop all the irrelevant code sections; in fact, processing time is about the same on both an optimized and unoptimized binary.

The bottom line is: if you control the Wasm binary, run it through wasm-opt and compare the result for your workload!

Deprecations

  • @mathetake removed the Parameters API from StackIterator (#1716) as this feature hasn't been used in practice, and it is also difficult to implement in the optimizing compiler.
  • @Danlock contributed (#1782) some deprecation warnings to emscripten.Instantiate and related functions in favor of emscripten.InstantiateForModule. Examples have been updated accordingly. This is also @Danlock’s first contribution! Cheers!

Other Contributions

Speaking of first-timers, let’s welcome them all:

  • @Danlock’s contributed the emscripten deprecation warnings mentioned above.
  • @valpackett contributed performance and reliability improvements, solving a potential memory leak and a crash when caching is enabled (#1815, #1816).
  • @yagehu contributed a few important fixes to our WASI implementation, especially regarding parameter types and error values (#1863, #1866, #1871).

Cheers to all of you! You are helping make wazero awesome!

Now onto the veterans!

  • @anuraaga contributed a fix to our CI (#1711), but he is also continuing his work improving wazero on his friendly fork wazerox. Keep an eye on it for some bleeding edge features that might eventually end up upstream!
  • @ncruces made sure that wazero builds successfully when GOOS=aix (#1723) for all you mainframe lovers
  • Zig is now pinned to version 0.11 and we are testing against TinyGo 0.30 on CI (@mathetake, #1729 and #1745)
  • golangci-lint has been updated to v1.55.2 for compatibility with the latest Go point release (@evacchi, #1879)

Finally, we also welcome to the community pages:

  • @jerbob92 adding wazero-emscripten-embind (#1790)
  • @ckaznocha adding Impart Security to the users page (#1688)
  • @nilslice adding Extism (#1841)

v1.5.0

8 months ago

Hey, do you know what time is it? ⏰ Yes! Time for another wazero release! 🎉

Notably, as previously announced, this release drops support to Go 1.18. But you must be here for the goodies! Don't you worry, we have you covered: there's a whole lot of updates especially for you Emscripten fanatics!

Time flies when you are having fun: WasmCon 2023 is only a few days away (5-6 September). Remember, our very own @evacchi will be there to talk about wazero, so if you are going to be there, don't be shy and say hi!

Improved Emscripten support

Contributor @jerbob92 together with @mathetake brought quite some improvements to our Emscripten support.

What is Embind? In @jerbob92's words:

Embind allows developers to write C++ code and directly interact with that code from Javascript in the browser. It also allows to call Javascript methods directly from C++. [...] [The Wazero implementation] is trying to be a 1-on-1 implementation of the JS version in Emscripten so that the same codebase can be used for both Web and WASI WebAssembly builds.

Embind support lives in its own module and you can check it out at jerbob92/wazero-emscripten-embind!

Go Version Upgrade

As you may already know, we follow the Go support policy: i.e., we support each major Go release until there are two newer major releases: in v1.4.0 we added support to Go 1.21, while keeping support to Go 1.18 for a little while more. The time has come to say goodbye to Go 1.18: it's been an honor and a privilege, sir. 🫡

@evacchi (https://github.com/tetratelabs/wazero/pull/1622) and @ncruces (https://github.com/tetratelabs/wazero/pull/1620) upgraded the code paths that were relying on old workarounds or simply pointed to an older version.

Other Contributions

v1.4.0

9 months ago

This release is hot 🔥 like a torrid summer sun! 🏖️

Let us all rejoyce: Go 1.21 is out!! We are now promoting the existing CI tests against the Go development branch to tests against the 1.21 stable release (#1616).

As you know may already know, we follow the Go support policy: i.e., we support each major Go release until there are two newer major releases: this means starting with the next release we will drop support for 1.18!

In case you missed it, WasmCon 2023 is taking place in Bellevue, 5-6 September so if you happen to be there, you might bump into @evacchi, who will be there to deliver a talk on your favorite zero-dependency Wasm runtime for Go (yes, that's wazero).

If you won't be there, don't forget we always hang out in the gophers slack #wazero channel. Note: You may need an invite to join gophers. Regardless, if you like what we are doing, remember to star our repo because that's what the cool kids do 😎

In the meantime, let's take a look at what is new with this release. We have the usual influx of fixes and improvements and a major feature in experimental state: enter the Virtual File System API!

Virtual File System API

Thanks to a final spike before the release @codefromthecrypt, with a lil' help from @evacchi, completed this long-awaited feature, closing issue #1013.

It is now possible to configure a virtual file system using a lower-level API under experimental/sysfs, experimental/sys. In order to configure a custom file system you can type:


import(
	"github.com/tetratelabs/wazero/experimental/sys"
	"github.com/tetratelabs/wazero/experimental/sysfs"
)

...

	cfg := wazero.NewModuleConfig().
		WithFSConfig(wazero.NewFSConfig().(sysfs.FSConfig).
			WithSysFSMount(<experimentalsys.FS>, <guestPath>))

This lower-level API gives a higher degree of control over lower-level FS operations, as compared to fs.FS, and ensure cross-platform portability. It handles experimentalsys.File instances (instead fs.File or os.File) and returns experimentalsys.Errno in case of error.

For instance, sysfs.DirFS is equivalent to os.DirFS and may be instantiated with:

      fs := sysfs.DirFS("/some/path")

AdaptFS adapts an fs.FS:

		adapted = &sysfs.AdaptFS{FS: fs}

You can also embed most of these types in your struct to provide default behavior to your own implemementation; for instance ReadFS provides a read-only view over a file system; there is also an experimentalsys.UnimplementedFS (returning experimentalsys.ENOSYS for most operations) that should be always embedded in your own implementation for forward compatibility. You can find examples under experimental/sysfs.

Notably, the Virtual File System implementation does not currently expose methods to override blocking/nonblocking behavior; during this release @evacchi and @codefromthecrypt collaborated on these internals (#1596, #1597, #1599, #1604, #1612, #1613), including the wasip1 poll_oneoff implementation, which was improved and simplified. Related File APIs may eventually be exposed to the VFS layer as well.

Cross-Compilation Improvements

It is now possible to build wazero successfully on GOOS=plan9 (@codefromthecrypt #1603), incidentally, this also makes it possible to build on GOOS=js (@mathetake, #1614) and GOOS=wasip1: yes we heard you like wazero, so now you can put wazero in your wazero so YOU CAN WASM WHILE YOU WASM.

Other Contributions

  • @panchen66 discovered a memory leak (#1600) happening when repeatedly re-instantiating a module. @ncruces and @achille-roussel tag-teamed (#1608, #1609) a simplified reproducer and a solution. Huge thanks!

  • @anuraaga contributed a fix to the amd64 compiler in case of error, and some doc corrections (#1593, #1593)

  • Other fixes to the documentation

    • TinyGo docs (@codefromthecrypt, #1590)
    • Updated specs.md with sock_* (@evacchi, #1598)
    • Fixed paths in README.md (sho-hata, #1591)

v1.3.1

9 months ago

Aren't you excited for the imminent release of Go 1.21 with GOOS=wasip1 GOARCH=wasm? We are!

wazero 1.3.1 is a minor release with important changes. No public APIs have been affected in any way, but we improved portability and further moved forward support to nonblocking I/O, especially on Windows!

Remember to star wazero, and check our community for other cool wazero users like you!

Improve Support to Cross-Compilation

@SlashScreen noticed (#1578) that builds for GOOS=plan9 were failing due to some missing constant definitions; this is somewhat related to an earlier issue reported by @supermigo (#1526) that prevents building wazero against the new GOOS=wasip1 GOARCH=wasm target pair with Go 1.21.

@codefromthecrypt has led a spike (#1582, #1584, #1586, #1588) of refactorings to decouple our core from the direct dependency on the syscall package, which is obviously highly platform-specific; for instance, some error codes or flags may only exist on some platforms: referring to such constants in shared code paths prevents a successful cross-platform build. #1582 introduces sys.Errno as an abstraction over platform-specific error codes, and #1586 defines fsapi.Oflag to abstract over file open flags.

If you are interested in that kind of oddity, support to GOOS=wasip1 will allow you to run a wasm wazero on a native wazero because self-hosting is the ultimate test for a runtime, but also because... why not? 🔥

Improve Nonblocking I/O

This release is also improving support for nonblocking I/O, first by fixing a bug (#1538) that was originally addressed with #1542, but ultimately was not completely resolved. @johanbrandhorst reported that the Go CI was still producing corrupted output when writing to a redirected stdout. @evacchi uncovered the root cause of the bug rather quickly; in fact, most time was spent writing an automated test case that reproduced the conditions of the Go CI (i.e. spawning a stand-alone wazero process and hooking that stdout to a buffer). This was fixed with #1581.

Notably, @evacchi moved issue #1500 another step closer to resolving, i.e., nonblocking I/O on Windows, by emulating select using a few different Windows APIs. Details on how this has been realized are described in #1579 and are summarized in /RATIONALE.md. Further work is still needed to close the gap in poll_oneoff.

Minor changes

  • @abraithwaite contributed the RunReveal project to community page (#1577)
  • @codefromthecrypt bumped the CI version of Go to the latest Go 1.21rc3 (#1589)

v1.3.0

9 months ago

wazero 1.3.0 is ready for next month's release of Go 1.21.

The new GOOS=wasip1 GOARCH=wasm will be very popular: It is the first WebAssembly platform for non-browser use. A %.wasm file compiled with Go 1.21 runs fine in wazero. This is thanks to efforts on both sides, CI with gotip and the latest release candidate (1.21rc2).

Go 1.21 is already bringing new developers to WebAssembly: We suggest you unconditionally upgrade in anticipation of demand. Besides this and bug fixes, you may also be interested in our new sys.Stat_t type used for fs.FS integration.

Don't forget to star wazero and any project relevant to you, made by our community. Let's get into the details of what's new!

Go 1.21 ready!

wazero has two relationships to Go 1.21:

  • Can wazero be a dependency of a project compiled with Go 1.21?
  • Can wazero run wasm compiled with Go 1.21's GOOS=wasip1 GOARCH=wasm?

We have made significant progress on both these points. We now run tests on every change with the latest Go 1.21 release candidate and gotip (last commit refreshed weekly).

wazero tests now run with go1.21rc2

wazero includes logic conditional on Go versions, notably to dodge known problems on Windows with Go 1.18. This logic was made more flexible to be forwards compatible with any subsequent version. There was also a platform difference in setting file times, fixed by @evacchi. wazero now runs tests with the latest release candidate on every change, as well as everything it was testing before.

Go no longer skips wasip1 tests if the runtime is wazero

Before, there were a couple of standard library tests skipped on wazero. Notably, these were around non-blocking I/O (os.Stdout and io.Pipe()) and pre-opened sockets. @chriso and @evacchi collaborated to both fix these and also remove the special casing in golang/go. Edo even went beyond to improve code not tested upstream such as named pipes on Windows!

Go and TinyGo share the same heuristics when reading directories

Many compilers use wasi-libc directly to implement functions such as readdir. Go doesn't rely on this, so all logic in GOOS=wasip1 is implemented directly. TinyGo's -target=wasi is a hybrid where some features are implemented in Go and others imported from wasi-libc.

@GeorgeMac noticed some inconsistency when wrapping file systems due to this, where files without inodes were filtered out. Details such as this are not defined in wasip1: while POSIX has something to say, there is murky water. After a long and detailed investigation between @achille-roussel and @codefromthecrypt, three things happened:

  • wazero improved its /RATIONALE.md dramatically around inodes.
  • @achille-roussel championed consistent behavior ending in merged pull requests both to Go and TinyGo.
  • wazero added a new way to control stat info, discussed below.

Custom file stat

fs.FileInfo returned from Stat can optionally include raw info from its Sys() method. In Unix systems, this returns *syscall.Stat_t, allowing access to more timestamps and the file's inode.

However, *syscall.Stat_t is not platform agnostic: its field lengths vary, and the type doesn't exist in Windows or virtual filesystems such as go:embed.

Our first answer to this problem is to define and process a new type, *sys.Stat_t, if returned from info.Sys(). This allows developers to intercept *os.File's Readdir or any fs.File.Stat functions to backfill inode information. For example, if a Readdir returns file info with this, it can set a non-zero inode. This prevents a performance penalty caused by wasi-libc, which would otherwise fan out with a Stat call for each directory entry.

It is understood this first feature is not for most integrators. However, it is an important milestone for filesystem performance and customization. wazero 1.4 will promote our internal writeable API to experimental, allowing full control over filesystem features used by wasip1.

Minor changes

There were numerous small changes in 1.3, thanks to folks who brought them to our attention. Notable call out to @abraithwaite, @ncruces, @leighmcculloch who helped us understand edge cases on resource cleanup and close state concerns.

  • fixed a performance regression on fd_readdir since 1.2.0
  • adds integration tests for TinyGo and GOOS=wasip1 covering custom file systems.
  • added IsClosed() to api.Module for better error handling when a module exits during initialization (_start).
  • numerous improvements in trap code by @ncruces.
  • compiler memory leak mitigation by @inkeliz.
  • experimental: Adds CloseNotifier for custom cleanup tasks.
  • experimental: Removes HTTP and filesystem features only supported in GOOS=js.

v1.2.1

10 months ago

wazero 1.2.1 is an easy upgrade decision for everyone: There is no public API change of any kind, yet updating gives you a more portable and faster wasm runtime.

This release was helped and motivated by friendly projects in our ecosystem, please star their repos!

  • go needs non-blocking I/O for the new GOOS=wasip1, for example, running an HTTP server with middleware, using only one thread.
  • go-sqlite3 wants smaller and faster machine code to make queries more performant.
  • wasi-go collaborated on a different approach to non-blocking I/O.

As usual, we have a hefty amount of background for you to enjoy, even if you aren't a wazero user, yet. Please star our repo if you like it!

Progress in non-blocking I/O

WebAssembly does not yet support true parallelism; it lacks support for multiple threads, atomics, and memory barriers. This may be addressed portably in the future, when the threads proposal standardizes and common concerns like garbage collection employ it. For now, the only way to safely use wasm is sequentially, like how GOMAXPROCS=1 works in Go.

This isn't a problem in a lot of cases, but in I/O it can be. Network services often need to accept new connections while processing existing ones. Interleaving of this isn't viable with blocking I/O. The typical way to address this is using non-blocking I/O, with a loop that looks for channels which are ready and processes accordingly.

As mentioned in the 1.2.0 release, non-blocking I/O is a work in progress, and it is improved in 1.2.1. This is improved so much that @chriso was able to remove skips for HTTP and file-based non-blocking tests in the emerging GOOS=wasip1 in Go 1.21.

@evacchi focused on the problem of non-blocking I/O, both network sockets and files. He both invented some solutions and leveraged existing approaches from wasi-go to fix issues such as inability to use HTTP middleware in Go source compiled to wasm. To make sure it worked, he landed new tests, for example C code compiled with wasi-libc and go(tip) compiled with GOOS=wasip1.

The changes are all transparent to end users, and while Edo led this effort, @achille-roussel and @chriso deserve a large thank you for support and the some prior art in wasi-go.

One final note is the battle is not over. We still have work to do in windows and surely there will be more edge cases. Please track issue 1500 and add comments if you noticed any more glitches.

Significant execution time savings

While developing go-sqlite3, @ncruces noticed some opportunities both to save size in machine code and performance. This focused on "traps" which are unresolvable execution errors that happen in cases such as divide by zero or invalid bounds conditions. The basic idea was to centralize the concern, so that any instruction that could trap uses the same way out of machine code.

While developing this, Nuno found a glitch which is fast-pathing these cases can interfere with source code mapping. For example, if you were using a debugger or using a DWARF-enabled trace, the line numbers could be wrong. After discussing with others, a pragmatic way out was chosen: optimize when there is either no debug information (usually the case in release builds) or if RuntimeConfiguration.WithDebugInfoEnabled is set to false. The latter is helpful, because it can be difficult to prevent a compiler from obfuscating function names, so some may use debug builds always. This is handy in the case of a rare bug such as a nil pointer, because you can still identify original source function by name.

While led by @ncruces, others were involved in review and design. This was made easier because Nuno kept excellent notes and comments, as well made speedtest1 to test results. @achille-roussel and @mathetake contributed feedback and @evacchi ported the implementation over to arm64 a lot easier due to the rigor involved.

The end results are really quite excellent, especially as debug info is rarely used in release builds. For example, an unrelated project kube-scheduler-wasm-extension performance improved with real data, up to 6.5%, with no other changes except upgrading to the latest patch.

Note on TinyGo v0.28.1

We updated our TinyGo examples to v0.28.1, which supports more features (such as ReadDir and json), and more idiomatic wasm import signatures. A lot went into this release, so please thank the TinyGo team with a star!

- //go:wasm-module env
- //export log
+ //go:wasmimport env log
func _log(ptr, size uint32)

v1.2.0

11 months ago

wazero 1.2.0 includes 33 days of valiant effort towards performance, interop and debug goals, shared not only in wazero, but WebAssembly in general. We appreciate folks waiting a couple more days than usual and expect you'll enjoy what you see below.

While we haven't set a formal post 1.0 release cadence, you can expect another patch or minor within a month. Meanwhile, this is our most performant and best tested release yet. As always, star all the projects that interest you, and say thanks when you can.

Performance

Performance is something we aim to always improve, release to release. This includes looking at memory usage as well as latency. While there were multiple people involved in efficiency work, @achille-roussel and @lburgazzoli deserve special call outs for leading efforts, and @ncruces deserves a big pat on the back for contributing reviews, cleanups and advice.

@achille-roussel made many changes internal to our compiler, as well linux-only specializations such as using huge pages for the memory mapped regions under wasm functions. These were all profile and benchmark guided changes and proposed in top rigor.

@lburgazzoli tracked down best practice in TinyGo, consolidating advice from various experts, such as the primary developer of TinyGo @aykevl. He worked with @ncruces to make sure our allocation example is not just a code snippet, but an exemplar of good practice, without risk of memory leaks and performance validated with benchmarks.

The combination of backend work (e.g. runtime improvements) and frontend work (e.g. changes to our TinyGo example) combined in a notable holistic gain across the board. This was true teamwork and a job well done!

$ benchstat v1.1.0.txt v1.2.0.txt 
goos: darwin
goarch: arm64
pkg: github.com/tetratelabs/wazero/internal/integration_test/vs/compiler
                          │  v1.1.0.txt  │             v1.2.0.txt             │
                          │    sec/op    │   sec/op     vs base               │
Allocation/Compile-12       3.365m ±  1%   3.174m ± 1%   -5.66% (p=0.002 n=6)
Allocation/Instantiate-12   149.1µ ± 26%   120.4µ ± 7%  -19.23% (p=0.002 n=6)
Allocation/Call-12          1.404µ ±  2%   1.297µ ± 2%   -7.66% (p=0.002 n=6)
geomean                     88.97µ         79.13µ       -11.05%

                          │  v1.1.0.txt  │              v1.2.0.txt               │
                          │     B/op     │     B/op      vs base                 │
Allocation/Compile-12       2.404Mi ± 0%   1.292Mi ± 0%  -46.24% (p=0.002 n=6)
Allocation/Instantiate-12   319.4Ki ± 0%   230.5Ki ± 0%  -27.84% (p=0.002 n=6)
Allocation/Call-12            48.00 ± 0%     48.00 ± 0%        ~ (p=1.000 n=6) ¹
geomean                     33.28Ki        24.27Ki       -27.07%
¹ all samples are equal

                          │ v1.1.0.txt  │              v1.2.0.txt              │
                          │  allocs/op  │  allocs/op   vs base                 │
Allocation/Compile-12       1.830k ± 0%   1.595k ± 0%  -12.84% (p=0.002 n=6)
Allocation/Instantiate-12    803.0 ± 0%    508.0 ± 0%  -36.74% (p=0.002 n=6)
Allocation/Call-12           5.000 ± 0%    5.000 ± 0%        ~ (p=1.000 n=6) ¹
geomean                      194.4         159.4       -18.00%
¹ all samples are equal

Interop

Compatibility is a moving target as both specifications change as well understanding of specifications. For example, the WebAssembly Core Specification 2.0 remains in a draft state, and expectations of the VM change as it changes. Also the d'facto WASI version preview1 (a.k.a. wasip1) had no tests, nor detailed documentation for the first several years of its existence. This left interop as more a quorum of implementation practice vs a spec. While new initiatives such as the wasi-testsuite and wasix aim to stabilize this, WASI compatibility remains a source of work from wazero maintainers and compiler developers. We really appreciate the efforts spent here to keep as many users unaware of these glitches as possible.

On the WebAssembly Core (VM) side, we appreciate @mathetake updating our code and spec suite to pass latest changes there. Also, we appreciate an attempt by @anuraaga with support by @ncruces on the Threads proposal, despite us ending up parking the idea until the proposal finishes.

On the WASI side, we appreciate a lot of work driven by the team working on Go. Specifically, the upcoming GOOS=wasip1 planned for 1.21 helped reveal a number of grey areas that required work to support in Go without breaking other languages. Championing these came from various team members including @Pryz, @achille-roussel and @evacchi on various file rights and non-blocking related glitches, some fixing other language runtimes such as python.

We're also excited that @evacchi began an experiment to support sockets, currently working for blocking requests. As wasm only has one thread to use, libraries often need non-blocking functionality to do anything notable. We'll report more on sockets once non-blocking glitches sort out.

Meanwhile, those using sockets know that the preview1 version of WASI is extremely limited. There are other ABI such as wasmedge_wasi_socket, wasi-sockets and most recently wasix. All of these go beyond the simple TCP sock accept, read/write in wasip1. If you'd like the bleeding edge socket support, please try wasi-go and request the features you want to experiment with. This project is a layer over wazero with an alternate syscall layer. wasi-go can move faster due to less constraints than upstream, such as Windows or virtual files. This makes it a lower risk and ideal playground to develop evolving host function (ABI) specifications. As these functions mature, what makes sense to build-in will land upstream in wazero.

Debug

We are very excited to power the only known out-of-browser CPU and memory profiler for WebAssembly, wzprof. wzprof brings the power of pprof to wasm, regardless of if the source language is Go or not.

wzprof has already served a lot of benefits in its short life so far. For example, the kube-scheduler-wasm-extension used it to isolate a garbage collection problem endemic in large protobuf decoders compiled to wasm.

Implementing this required significant work in wazero, which we are happy went upstream! @pelletier added the source offset to experimental.StackIterator which allows source-mapping in debugging use cases. @achille-roussel polished the experimental.FunctionListener to be more performant, including optimizing its API around errors and removing context propagation. @chriso and @mathetake helped fix some glitches along the way. Finally, @achille-roussel made it easier to develop 3rd party listeners by adding experimental.FunctionListenerFactory to supply them and wazerotest.Module to test them.

While we don't expect a lot of people to implement listeners, it was a great team effort to get the substrate together to the point you can build a profiler on top of it. Kudos especially to the wzprof team on finally giving wasm developers a decent profiler!

What's next?

In the short term, we'll try to close the gaps on non-blocking I/O inside WASI. We still aim to have a fully pluggable filesystem soon, including an in-memory option for those needing to provide something like tmpfs from Go. Of course compatibility issues and user demands will take priority as they always do.

Longer term, @mathetake is taking on the task of an optimizing compiler affectionately named wazevo. This will likely take a year to mature, and will narrow the performance gap on certain libraries like libsodium without adding any platform dependencies whatsoever.

Meanwhile, if you want updates you can always contact the community and ask, or just wait for the next release. Until next time!