Hacker News new | past | comments | ask | show | jobs | submit login
FaaS in Go with WASM, WASI and Rust (thegreenplace.net)
182 points by ibobev on May 6, 2023 | hide | past | favorite | 49 comments



WaZero is pretty freaking amazing. Been using it at RunReveal for log processing and alerts. Highly recommend picking it up to anyone wanting to learn about Web Assembly. Their docs are amazing and the team is really responsive, knowledgable and helpful!


Have you tried wasmtime? Any noticeable difference?


Wasmtime is i think the only engine that does proper jit, everything else does interpreter and aot (like wazero)


Wamr has two JITs. Wizard recently added a JIT. And of course all of the Web engines and node have JITs.


None of those is relevant to me here imo. It's not that I don't know of them.


Yeah, Wasmer (which I don't pay much attention to) uses Cranelift from Wasmtime, so Cranelift is pretty much the only game in town for JIT.

Wazero is awesome, I wish them luck and hope to use it, but I would use wasmtime for most everything until it gets a lot further along.


So much tech with the word wasm in it now, it's beginning to get confusing!


Yeah I perused the wazero docs and AOT doesn't sound too bad for a lot of use cases. A lot of different choices in the WASM space!


Seconded, I used it in one of my side projects and it’s pretty amazing to have full WASI with no CGO


hey wazero dev here, AMA!


Hey!

First, wazero is awesome, thanks for building it!

I saw initial steps of a Cranelift Wazero backend in your organization. Any plans or ETAs around this?


thanks for the praise! Our community is super-awesome too!

Very experimental! No ETAs yet but we're considering a few strategies for the optimizing compiler


FaaS is cool, but what I would love is if wasm can be used as a universal FFI boundary. Excellent tooling permitting, you could basically bring in dependencies from other language ecosystems when necessary. They could also be fully sandboxed.


(wazero maintainer) Right now, there's no auto-magic repackaging, but it is extremely common to do manually in our ecosystem. Most will use go:embed to embed the wasm binary into the compiled application so it still appears all static linked, self contained at the end.

A couple examples of this: * https://github.com/jerbob92/go-pdfium-wasm * https://github.com/ncruces/go-sqlite3

More here: https://wazero.io/community/users/

Note that often times a lot of work is porting because not all tools build well to wasm. This would apply for any runtime as we're just running the standard with some special extensions for emscripten, wasi etc.


This is where it is going, checkout the component model https://cosmonic.com/blog/engineering/evolution-of-wasm-stan...


We’re trying to get close to this, via our Universal Plugin System, Extism: https://github.com/extism/extism

Call common wasm code across 16 different programming languages using our SDKs.


The problem with this is that memory management across subsystem boundaries is very difficult.


I think that’s what OP meant by tooling permitting.


Truffle is that but better.


Ooh ... this guy makes the forbidden argument. "FAAS" is essentially cgi-bin with a few functional upgrades and supposedly better language support.

I applaud it! He's right. It is.

Except of course in reality FAAS and WASM and all implementations have less language support than cgi-bin ...


This is a slightly off-topic question but I'll go anyway: if I would want to learn a cross platform low-level language to target with custom toy language compilers, which would be the best alternative betweeen

- JVM;

- LLVM;

- WebAssembly; or

- Something else entirely?


I did some WebAssembly targeting with a toy SQL to WASM compiler recently and imo the ecosystem is still not that mature.

There's no real multi-module linking right now, which means that for I.e. a runtime library you'll end up 1. Compiling the runtime library to wasm 2. Editing that wasm code in place to add your generated code (that uses runtime functions). And that's a bit hacky.

Fingers crossed for the component model to solve this, but LLVM is definitely much more mature (and can compile down to WASM, and vice versa). In the end, for toy projects, both are great fun, and WASM is definitely more "cutting edge".

Btw. I do recommend using Rust for playing with WASM. Wazero is awesome (and it's what I used) but all the experimental features of WASM that are very useful (like 64 bit memory support) are not available here yet. Wasmtime and wasmer are probably the most popular engines (other than V8), both are in Rust, and both have a lot of experimental features implemented and activatable. Their bindings in other languages aren't as complete in that regard.


It's a bit like saying you want to organise around the world tour to perfom as musician after hitting top charts and you're asking people what instrument to use.

Use all of them, in no time you'll stick with one that fits you.


Are you saying all of them are appropriate tools for the exact same set of problems, and their only differences are up to subjective taste?


I think we're saying that you may not be able to form a proper orchestra or even a good, successful band that can stay together for many years with only the one instrument that you like most. If you are building a monolithic app, you do have to choose one language to build it in. But you could choose wrong. Or the choices could merely isolate you from other talented musicians that you'd otherwise be happy to play with. If you learn to play their instrument, even just superficially; it will help you expand your understanding of the entire problem space if you did learn to play together.

(The problem space... of music theory. Or programming. Or "just writing our app" but in a way that generates reusable modules that are constrained to be small, well-defined in terms of types and functionality, and easy to test.) And learning to play the instruments all together in some kind of harmony, one that you can even type check, is just... chefs kiss

Some people think Ruby is the wrong choice, and others are building successful businesses on it as a foundation. Did they make this choice because of subjective tastes, or for practical reasons?

(And you can do this with Wasm, implement compilers in your Ruby framework in a way that's fairly unobtrusive, to take advantage of apps that were compiled in another language that isn't C... and likely to net you some type of benefits if not performance benefits – but the constraints are interesting. Did I mention that Wasm doesn't seem to have a string type, unless you count WASI?)

I mentioned Ruby because I'm giving a lightning talk on this topic this week Tuesday at GitOpsCon and a bigger talk at OSS Summit (one on Wednesday and another on Thursday...)

It's called "Exotic Runtime Targets"

Ruby and Wasm on Kubernetes and GitOps Delivery Pipelines

There's also "Microservices and WASM, Are We There Yet?"


(wazero contributor) In case you are interested, you can use Ruby (or any other interpreter compiled to wasm) with wazero. Our CLI shows what most of the runtimes can do, too.

$ echo salaboy | wazero run ruby-3.2.0.wasm -ne 'print "Hello "; print'

Hello salaboy

Of course getting and gems etc gets weird in wasm..

Anyway, thanks to VMware labs for publishing interpreter wasm builds, people can play around. https://github.com/vmware-labs/webassembly-language-runtimes...

Random, but enjoy.


Thank you! I learned about wazero in this thread, I checked it out briefly but I will definitely look into it further by Thursday :)


This should be the accepted answer


The JVM has amazing, mature tooling, and a bunch of existing compile-to languages. If the goal is to do a productive exploration with some toys, that’s a good place to start.


Does anyone know if you could target the JVM without having to use UTF-16 for string encoding?

Probably not justified, but that’s a hassle I’d want to avoid if I had any plans to support Unicode.


Really? What JVM tooling amazes you, specifically?


Everything that isn’t Java is top notch. JIT, GCs, debugging. It’s sad that Java the language sucked so bad for this long.


That is a matter of opinion, plenty of people like Java, the language.


Java today is a likable language. It was not expressive enough a few years ago which lead to major boilerplate copypasta and making do with abhorrent ’design patterns’ instead of well designed language features.

Plenty of people liked it then I agree, can’t really understand why, because the simplicity argument just moved the complexity into libraries.


Java was the Go of its time, specially when compared with the chaos of commercial C and C++ compilers.

Java predates ISO C++, and the UNIX world was a mess of K&R C, and half backed ISO C89.

Design patterns were invented for Smalltalk and C++, as much as people like to blame Java for them.


.NET CLR has a lot of different first party languages. Don't know if DLR is completely abandoned these days for dynamic languages, seems like ironpython is still getting maintained by OSS so it should work at least.


DLR still powers dynamic keyword, and related class libraries.


dynamic is considered a legacy/effectively deprecated feature AFAIK.


If you want native runtime speed, compiling to C is also a popular option (e.g. Nim).


Wasm is the simplest target format and more generic than Java Bytecode, I’d start there


Exactly. And the Java guys criticism is bullshit: WASM supports garbage collection, just like a cpu does, you just have to bring your own. In fact, Java's most advanced garbage collector runs on WASM just fine.


A good GC can benefit greatly from lower level constructs, so while it is absolutely possible to run a GC within WASM, running a good GC is significantly harder if not impossible.


Here's an off-topic answer.

Depends on what you want your toy language to do and what sort of runtime support you'd like to lean on.

JVM is pretty good for a lot of script-y languages, does impose overhead of having a JVM around. Provides GC, Threads, Reflection, consistent semantics. Tons of tools, libraries, support.

WebAssembly is constrained (for running-in-a-browser safety reasons) but then you get to run your code in a browser, or as a service, etc, and Other People are working hard on the problem of getting your WA to go fast. That used to be a big reason for using JVM, but it turns out that Security Is Darn Hard.

I have used C in the (distant) past as an IL, and that works up to a point, implementing garbage collection can be a pain if that's a thing that you want. C compilers have had a lot of work on them over the years, and you also have access to some low-level stuff, so if you were E.G. trying to come up with a little language that had super-good performance, C might be a good choice. (See also, [Wuffs](https://github.com/google/wuffs), by Nigel Tao et al at Google).

A suggestion, if you do target C -- don't work too hard to find isomorphisms between C's data structures and YourToyLang's data structures. Back around 1990, I did my C-generating compiler for Modula-3, and a friend at Xerox PARC used C as a target for Cedar Mesa, and Hans used it in a lower-level way (so I was mapping between M-3 records and C structs, for example, Hans was not) and the lower-level way worked better -- i.e., I chose poorly. It worked, but lower-level worked better.

If you are targeting a higher-level language, Rust and Go both seem like interesting options to me. Both have the disadvantage that they are still changing slightly but you get interesting "services" from the underlying VM -- for Rust, the borrow checker, plus libraries, for Go, reflection, goroutines, and the GC, plus libraries.

Rust should get you slightly higher performance, but I'd worry that you couldn't hide the existence of the borrow checker from your toy language, especially if you wanted to interact with Rust libraries from YTL. If you wanted to learn something vaguely publishable/wider-interesting, that question right there ("can I compile a TL to Rust, touch the Rust libraries, and not expose the borrow checker? No+what-I-tried/Yes+this-worked") is not bad.

I have a minor conflict of interest suggesting Go; I work on Go, usually on the compiler, and machine-generated code makes great test data. But regarded as a VM, I am a little puzzled why it hasn't seen wider use, because the GC is great (for lower-allocation rates than Java however; JVM GC has higher throughout efficiency, but Go has tagless objects, interior pointer support, and tiny pause times. Go-the-language makes it pretty easy to allocate less.) Things Go-as-a-VM currently lacks:

- tail call elimination (JVM same)

- don't ever construct a pointer to Object+sizeof(Object) (i.e., to the first byte past the end) (JVM same)

- defined semantics for racy programs that don't use atomics (structures can tear; there is a race detector, use it). (JVM same-ish; racy programs suck)

- integer overflow checking (JVM same)

- consistent conversion from +/-FPInf to integers,

- if you're not careful about expressing floating point a+b*c, you'll get the platform multiply-add rounding

- signaling/quiet NaN representation follows the platform

Some of these are on my list of Would-Be-Nice to fix, but that doesn't mean they will happen, because there are O(zero) people using Go as a VM, as far as I know, so their problems have zero weight. Tail-call-elimination in particular would be hard, and I see no substantial benefit in solving the pointer-past-end problem (it's a minor issue in our own code generation, and we deal with it).


This example sounds like exactly how Fastly’s Compute@Edge works.

Fastly provides an API via a rust crate that you use to build your edge (CDN) logic with. They also provide a local development cli that can run these WASM bundles with.

https://crates.io/crates/fastly


This seems cool for a lot of normal faas

I keep wanting a flow like this to work for faas python data science sandboxed serving, esp @ scale -- python interpreters talking to cpython kernels and one day GPU passthrough -- but the experience leaves me wondering about wasm on the backend in general

Basically, coldstart overhead + overall runtime overhead + inability to use GPU mean it'll be awhile yet for something you want to run. The former may be partly about pyodide (but not entirely?), but the latter seems to be about wasm

Comparatively, firecracker VMs seem to solve a lot here. Though still a lot you have to role out on your own to actually scale untrusted code here vs just say fargate

I feel like I must be missing something.. am curious.


I've been hoping to have WASM/WASI as an official AWS Lambda runtime for a long time. Ideally, it would be an evergreen platform that runs code forever once deployed. I'm pretty bored with updating Node.js versions every year or two to hundreds of Lambdas.


For anyone else like me who doesn't know without looking it up, FaaS is Function as a Service.


The author really believes that everyone around knows what FaaS and WASI are. It's a bit frustrating.


(wazero contributor) This is a fairly common problem in WebAssembly, jargon overload mixed with conflation. We're starting our own docs, and doing the rare thing of adding a terminology section to it ;) https://wazero.io/docs/




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: