Back

Extensible WASM Applications with Go

229 points20 daysgo.dev
stpedgwdgfhgdd20 days ago

A problem is that a Go produced WASM binary is really large. TinyGo overcomes this, but compiles too slow and you need to be really careful which libs to import (reflect). To overcome both, you need a lot of patience.

If you want to tryout Go WASM on Cloudflare workers you need to buy a subscription due to the size of the binary. Last time I tried you could run hello-world, but anything more substantial exceeded the size threshold.

Such a pity…

jbreckmckye20 days ago

I guess my question is, why is Go compelling as a source language for WASM?

If I want to do high performance programming I don't want to worry about garbage collection.

If I can withstand GC then I know enough about JavaScript to keep V8's optimiser happy. Which will always be the best DX for writing code in the browser. (edit: though this post is specifically about WASI)

Go has never focused on binary size and probably never will unless it actually becomes an issue for Google. That's the disadvantage of having a corporate BDFL I guess.

jerf20 days ago

"why is Go compelling as a source language for WASM?"

As near as I can tell, the answer is that it is supported, and has been working now for many versions.

See the short list of "production" languages here: https://github.com/appcypher/awesome-wasm-langs And read the sections carefully; you might think your $FAVORITE_LANG must be in the "stable for production usage" section given how big it is but it's full of a lot of languages with small communities. The really big names are mostly in the "unstable" category.

I can verify that I've toyed with some WASM support in a few languages now, and it is often not even functional, or takes vast effort to set up far above and beyond the official tutorials. You'll see a steady stream of stories on HN over the years about how this language or that language supports it, but in many cases those should be understood not so much as a commitment of support, but as a snapshot, that in that moment, it worked, but it may not work next week.

It's still true that Go produces big binaries. Official WASM garbage collection support may shrink that at some point though it'll still be larger than other languages for the goroutine runtime support. Not only is a bare minimum "Hello World" a couple of megabytes off the top, the Go WASM executables grow quickly as you add libraries. It rapidly becomes usable only for situations where you are guaranteed very high bandwidth for all users. Such situations exist, but is certainly a major limitation. (Though at least it does cache well, if you are even slightly careful with caching.)

So, to summarize, I'd say that it's not that Go is awesome and amazing, but that vast swathes of the competition are still quite bad.

martinald19 days ago

Not true. I've built multiple production apps with Blazor WASM. Works great. Only drawback is the continually delayed multi threading support, but given native JS multi threading is far from ideal it's not a huge drawback.

bloppe20 days ago

I thought the runtime was essentially a fixed cost, and that a compiled Go binary would be similar in size to the native binary for the same source tree. Are you saying it grows faster?

+1
jerf19 days ago
pr353n747-0n8319 days ago

The most basic understanding of programming language runtimes is sufficient to guess the entire Production-tier list. There isn't any need to talk down to us about $FAVORITE_LANG

jerf19 days ago

I strongly disagree. I'd expect it to me much larger by now, at least, based on my expectations and not my experience.

And once you clear the small-community languages out of the "stable" list I think it's underpopulated there as well; it's basically just .Net, .Net through C#, and .Net through F# (which I'm sure is more the .Net than the F#, no offense intended to the F# team). Way, way more of the languages I can name off the top of my head in "unstable" than I'd expect.

And while I don't intend this to be offensive to anyone either, WASM is just generally moving way slower than I expect. Given how many years that has been true at this point I must conclude it is being quite underinvested in, or, to put it another way, I'm blaming Google and peers for underinvesting, not the people working on it. More power to the people working on it, their skill is evident.

wjfuasdf19 days ago
jerf19 days ago

How do you think?

bborud20 days ago

> I guess my question is, why is Go compelling as a source language for WASM?

For me it is for three reasons: 1) I mostly write things in Go, 2) My main use-cases for WASM are backend applications, 3) being able to run sandboxed code (in backends) would be really, really useful.

Sadly, right now it is rather fiddly to make use of WASM in Go.

> If I want to do high performance programming I don't want to > worry about garbage collection

Then don't worry.

For most applications performance isn't going to be lost due to GC, but due to poor design choices and lack of effort. My observation is that most programmers tend to overestimate their own ability to routinely produce high performance code and under-estimate the cost of producing high performance code. The latter is more important. Writing high performance code (regardless of language/runtime) is time-consuming and tends to require a lot of skill to achieve consistently.

That being said, "high performance programming" is ill defined. For the term to have any real meaning one has to be more specific about what one tries to achieve.

> Go has never focused on binary size and probably never will unless > it actually becomes an issue for Google.

Probably correct. The problems of those who put in the work developing Go is probably going to receive priority. Binary size isn't a huge problem for the things people tend to use Go for.

As an aside; I've been writing software for an embedded Linux platform lately where binary size can become a challenge. I observed that the binaries produced by roughly equivalent C++ programs are about the same size as the Go binaries. Bigger if you take into account that the C++ programs were dynamically linked and the Go programs were statically linked. So your mileage may vary.

It may be that it is possible to introduce tooling and libraries that would allow you to generate WASM output that has a significantly smaller footprint.

> That's the disadvantage of having a corporate BDFL I guess.

No, I don't think it is tied to Google's ownership of Go. As with almost all large open source projects it is about developers and priorities. And a lot of open source gets developed on company time. If you want to help the Go project produce smaller WASM binaries then I don't see why Google would discourage you from contributing. If companies and people do not want to contribute it isn't going to get done.

jbreckmckye20 days ago

What primarily brings you to using backend WASM rather than deploying Go natively?

    Then don't worry [...] high performance programming" is ill defined. For the term to have any real meaning one has to be more specific about what one tries to achieve.
My main "high performance" experience is of writing WebVR applications. VR can't tolerate framerate drops because it makes users physically sick.

I don't know what the GC story is like for Go in WASM but it would make me very wary.

+1
bborud20 days ago
bborud20 days ago

Well, now you have defined what you mean (realtime'ish execution low/no latency variance). The next step would be to formulate a test and see if it is actually a problem and then make technology choices. Just assuming isn't really professional.

You can always find use-cases where GC'ed languages might be challenging to use. With emphasis on "might", because you kind of have to try it out before you can determine if it is a problem.

There are a lot of high performance applications where they're just fine. For instance if you care more about throughput than latency variance. This is why "high performance" isn't a useful term. You have to be specific about how you measure "performance".

Most of the code for most developers isn't terribly performance critical. Correctness is usually far more important. Most performance tends to be lost due to lack of design talent rather than the language.

boomskats20 days ago

This is a great take, I agree 100%.

I'm curious, would you mind elaborating on your use-cases for sandboxed backend application code? This is clearly the direction in which it's all headed, yet is still mentioned relatively rarely in WASM-related discussions on here.

cogman1019 days ago

> I'm curious, would you mind elaborating on your use-cases for sandboxed backend application code?

When working with backend systems you generally have 2 choices, a less secure containerized environment that does a very good job of distributing resources or a bulky VM which has a fixed memory cost that you might not need.

WASM sits right in the middle of the two. It has a durable sandbox that prevents bugs in the application logic from exposing the whole machine to exploit (and it itself can be further isolated using the same containerization security practices) but also has the benefits of single app like resource requirements.

Another prime benefit of WASM (not related to the sandboxing) is that WASM is a write once run anywhere standard. That means you don't need a different x86 and ARM wasm deployable. Similar to Java's JAR.

+3
bborud20 days ago
twoodfin20 days ago

Database stored procedures / user-defined functions written in arbitrary languages is a live use case on several platforms today.

coder54320 days ago

> Which will always be the best DX for writing code in the browser.

The article we're all commenting on is not about running WASM in the browser.

WASI in particular may never be supported by browsers.

jbreckmckye20 days ago

I'm aware of the article's context but am asking the broader question.

+1
boomskats20 days ago
pjmlp20 days ago

And on that regard, there are more mature options out there, than reinventing the wheel with WebAssembly.

+1
coder54320 days ago
+3
apitman20 days ago
9rx20 days ago

> Go has never focused on binary size and probably never will unless it actually becomes an issue for Google.

The Go project moved to being community directed quite a few years ago. The community hasn't shown it desperately needs smaller binaries either, though. It'd take them, I'm sure, but there are more important concerns.

> That's the disadvantage of having a corporate BDFL I guess.

Even if we, for the sake of discussion, assumed that Google still only specs the language for Google needs, there shouldn't be anything in the specification that necessitates large binaries. tinygo has shown that they can be small. And now that Microsoft has its own Go compiler, Google isn't the only deep pockets involved either. But so long as it is not seen as a pressing concern there isn't apt to be anyone to step up and put in the work to close the gaps.

verst19 days ago

The Microsoft Go distribution is a special build for FIPS 140-2 compliance (replacing some TLS / crypto libraries and such). I do not believe that there are plans to make any other changes here.

4ad20 days ago

> why is Go compelling as a source language for WASM?

"Compelling" has nothing to do with it. People write Go code (or have already written it) and want to run in Wasm, just like they want to run it in any other environment. It's as simple as that.

Your question is equivalent to "why do people want to write Go?".

blog_nikajon_es20 days ago

> I guess my question is, why is Go compelling as a source language for WASM?

I'm using WASM as a proper plugin architecture for some of my go applications. I provide an interface that allows external developers to create plugins (in whatever language supports WASM as a bonus). Then my application can then use these plugins to execute some extra functionality. It slows the application startup a little but runs pretty fast when in use, but I don't have plugins in any hot paths (yet).

hit8run20 days ago

Sounds interesting. What context? How can I imagine this?

sleepybrett20 days ago
ratorx20 days ago

I can think of a few (nothing super compelling though):

* “batteries-included” std - some of this is platform APIs, so only really the pure stuff. You can use all of this in the wasm extension, whereas you’d need to pull in deps for JS.

* Non-browser use cases (dynamic extensions, cross-platform “binaries”, cloud functions etc)

* probably the same reasons that JS/NodeJS became popular on the backend, but with roles reversed.

tylerflick20 days ago

I used this in a previous role. The answer is, we had a server side solution that we wanted to make available on clients that had no internet connection. The logic was complex, and this allowed us to ship much faster.

wjfuasdf19 days ago

> I guess my question is, why is Go compelling as a source language for WASM?

It's not currently: https://github.com/golang/go/issues/71134

Thaxll20 days ago

Go binary size is not an issue, especially where the discussions is not related to browsers.

LtWorf20 days ago

The use case is: "people who will waste days trying to not learn anything new".

The entire idea o nodejs is that.

eknkc20 days ago

Go is also a lot slower in wasm than non-gc languages due to its runtime requirements. I believe the wasm gc spec does not really help Go either so there won't be significant performance gains. It's a shame but it is what it is.

I have been playing with different languages in wasm and I settled on zig which does wasm pretty fine.

GrumpyCat4220 days ago

In practice, I've very rarely seen a downside: I compare it to something like Vite, where it uses esbuild in development and rollup in production, because the two libraries are for different things (esbuild doesn't bundle, rollup does, etc).

In development, I compile it with Go. In production, I compile it with TinyGo. Yes, you need to be careful about imports, and it does add complexity, but the benefits of not managing two different completely different languages (JS and whatever you're compiling to WASM) far outweigh that cost.

As for Cloudflare, I don't think their service's benefits outweigh the complexity, but I've had luck in the past with the dual setup on Workers.

jitl20 days ago

ESBuild is a bundler. The subtitle on the website is literally “An extremely fast bundler for the web”. To bundle, pass —-bundle flag.

https://esbuild.github.io/

GrumpyCat4219 days ago

Yep! You're right, it does have the ability to bundle -- that's my bad. Rollup is just much better at bundling for production, just like TinyGo. Similarly, esbuild is better for development, just like Go.

A good rundown (rolldown) is available here: https://rolldown.rs/about

scosman20 days ago

Agreed this is a big downside. It rules out go for websites (except maybe the biggest web “apps”) where load time isn’t important.

But being able to deploy a Go app to Cloudflare or other wasi runtimes is still really nice to have.

nullocator19 days ago

You can run the Go compiled wasm binary through wasm-opt (https://github.com/WebAssembly/binaryen) and it will decrease the size and give back some of the performance lost by using the standard go compiler over TinyGo. Fair warning it's pretty slow, and fun fact tinygo passes it's wasm files through wasm-opt which is why the tinygo compilation feels especially slow, I believe this can possibly be disabled via flag.

boomskats20 days ago

This is awesome. Something to keep in mind[0]:

> Remember that all the webassembly work in Go has been designed and implemented by volunteers, not the Go team, so timelines are dependent on availability of volunteers.

[0]: https://github.com/golang/go/issues/65333#issuecomment-22336...

donatj20 days ago

Seems to me like it would have been more "Go" to simply export all functions starting with a capital letter in the main package, as exporting works normally in the language, only resorting to the compiler directive when you need to specifically name something starting with lowercase.

Apparently this is just copying the existing way cgo exports work. Fair enough I guess, prior art. The ergonomics are just a little outside the language as usual.

euroderf20 days ago

No mention of working with the WASM component model.

phickey20 days ago

This addition unlocks the ability to use Go with the component model. The component model layers on top of Wasm Modules - a module which imports and exports functions with the component model ABI can be turned into a component using e.g. `wasm-tools component new`, and that transformation can include adapting wasip1 interfaces to wasip2. Without support for declaring export functions besides `main`, Go could only target the wasip2 CLI command world, but this enables it to target any component model world.

boomskats20 days ago

Correct, this looks like wasip1 support only for now. Discussion thread here[0].

Might be for the best, with wasip3 just around the corner.

[0]: https://github.com/golang/go/issues/66984#issuecomment-20762...

jallasprit20 days ago

Feel free to elaborate, because I have never heard about it either.

boomskats20 days ago

From the wasi.dev[0] front page:

> WASI can be implemented by both core Wasm modules and applications built according to the Component Model, a specification for Wasm applications that are interoperable and composable. You can learn more about components in the Bytecode Alliance's WebAssembly Component Model documentation[1].

[0]: https://wasi.dev/ [1]: https://component-model.bytecodealliance.org/

jcmfernandes20 days ago

I thought that WASI and the component model were separate specs. Am I wrong?

+1
4ad20 days ago
nilslice20 days ago

of course not - Go has taste and appreciates quality and simplicity.

euroderf20 days ago

The WASM IDL for components (WIT) seems sleek enough.

nilslice20 days ago

its fine, I prefer ours more oriented around a familiar format like OpenAPI

https://github.com/dylibso/xtp-bindgen

jimjimwii19 days ago

I may be misremembering but wasn't it already possible to export go functions to js before go 1.24? I distinctly remember being able to call exported go functions in js without issues in prior. It would be really helpful if someone could explain to me how the new wasi niceties improve on what we had before (beyond supporting more types accross the ffi)?

Second question: it was possible for me to extract strings and other complex types from the wasm module's instance memory by casting pointers to integers, returning those integers to js, then using those values as an offset. If the binary representation of my types is guaranteed to be stable in go, is this method of passing pointers to js still viable for wasm modules produced when passing goos=wasip1?

kukiTsui17 days ago

MoonBit looks like a promising language for WASM. They have WASM-GC and produce quite tiny code size. https://www.moonbitlang.com/

amelius20 days ago

How does garbage collection work with Go and WASM?

marianoguerra20 days ago

not sure what's the current status of Go so there are two alternatives:

if they are targeting Wasm pre Wasm-GC then they have to ship their GC in the Wasm module and it can't interoperate with "the outside world"

if they use Wasm-GC then they use the host's GC and values can be shared with the host and other guests.

4ad20 days ago

No, Go can never use WasmGC because Go has interior pointers. Not even fat pointers help because Go has unboxed types.

Go will continue to use its own GC for the foreseeable future.

amelius20 days ago

Interesting. Can it collect garbage _concurrently_ in WASM?

kflgkans19 days ago

Last I checked WASM applications are single threaded, not sure if that's changed. GC is concurrent, but not parallelized.

4ad20 days ago

Just the same as it works on amd64 or arm64 or any other architecture.

amelius20 days ago

You mean inside another thread, collecting garbage concurrently? I'd be very surprised if it worked that way.

thrance20 days ago

I am kind of worried that the eagerness for more WASM features may irremediably harm the young ecosystem. Most of what Go added to WASM could be done natively if the component model proposal [1] was merged already.

The standard evolves slowly and as adoption rises we risk having to support more and more non-standard features forever, like WASI and now this.

[1] https://github.com/WebAssembly/component-model

4ad20 days ago

The component model is... part of WASI, so that criticism doesn't make any sense.

When Go adds support for WASI Preview 2 (or 3), it will have no choice but to support component model. The way WASI Preview 2 works is by making use of the component model.

nilslice20 days ago

if Go supports it. I do not believe it is likely. There will still be no native Go runtime to execute components. No one is using WASI p2.

4ad20 days ago

Predicting things is hard, especially about the future, that being said, the people doing the Wasm work for Go want to move to the component model.

+1
nilslice20 days ago
pookeh16 days ago

do you need a disclaimer to your comments that you work on a product (XTP Bindgen) that is basically competing with the WASI component model? I don't care for either but thought the transparency was necessary.

Jyaif20 days ago

How do you debug WASM modules that are running in a host program?

jitl20 days ago

In v8 environments like browser or node, source maps work, so if you inject console.trace you can get printf debugging w/ legible stack traces on each log line, at least that’s my experience with c code compiled with Emscripten.

According to this 2020 blog post from Google, the Chrome debugger appears to have pretty full featured support for interactive debugging, although I’ve never needed to use it. https://developer.chrome.com/blog/wasm-debugging-2020

smetannik19 days ago

Unfortunately Go doesn’t support exporting DWARF for WASM atm, so printf is the only option.

https://github.com/golang/go/issues/33503

arccy20 days ago

the one true debugging method: printf debug

4ad20 days ago

Poorly. The tooling is very bad. But to be fair, this is not specific to Go.

Blackarea20 days ago

If there only was a low level language that came with strong typing and excellent wasm support...

kodablah20 days ago

And an excellent standard library with first class coroutine support and garbage collection?

Blackarea18 days ago

let's agree to have different tastes, my dear gentleman

hu320 days ago

Zig is great too. But different audiences imo.

Blackarea18 days ago

every language has a little bit different audiences. Haven't tried zig myself but I respect it (more than go... :D) and wanna check out some time - any recommendation to get an introduction (sth. like rust language book)?

rs18619 days ago

Companies like Figma have been doing that for years.

Blackarea18 days ago

doing what?

uhindi316 days ago

[dead]

parasense20 days ago

[flagged]

tyho20 days ago

Uppercase for exported is actually one of my favorite go features.

https://cs.opensource.google/go/go/+/refs/tags/go1.24.0:src/...

https://www.compart.com/en/unicode/category/Lu

there are plenty of non-latin scripts that have upper case letters.