Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Hands-on WebAssembly (evilmartians.com)
237 points by progapandist on Aug 26, 2020 | hide | past | favorite | 80 comments


Most of my early excitement for WASM circled around “Hey, I can finally use a decent language on the web” but since then I have grown to love TypeScript, so a lot of that early excitement has been tempered.

I have done a number of experiments with Go’s WASM target, one of the earliest and silliest porting Go’s “Otto” JavaScript runtime to WASM, so you can much less efficiently run ES3 JavaScript in a browser.

I have also been working on porting a number of tools I currently have that run server side with web frontends to pure WASM. I have had mixed success here however and have found interacting with web elements and JavaScript from Go an awkward, non statically validated, frustrating experience of trial-and-error that somewhat undermines the joy of a statically typed language. On top of everything, Go’s .wasm files are frustratingly large, “Hello World” starting at 1mb and growing quickly from there.

All that said though, it is pretty magical seeing something written as a CLI tool running in a browser with minimal changes.


Go has to embed it's runtime and GC into builds, so build size and performance will take a hit here. Same goes for most popular languages.

For me Rust offers the best WASM experience and tooling at the moment.

Rust has no GC and (almost) no runtime.

It also has official wasm build target support out of the box, including generating Typescript definitions for Rust types.

The ecosystem is somewhat well-developed:

* wasm-bindgen for general interop in both directions

* js-sys / web-sys for (low-level) type-safe and surprisingly efficient browser API bindings

* wasm-bindgen-futures for easy Promise <-> Future interop

* wasm-pack for easy building

* ...

Getting the code size low is still a challenge though.

A Rust implementation of the Krausest frontend framework benchmark takes a top 4 spot [1]. Despite this benchmark not favouring wasm at all since it requires little computation and mostly does FFI calls.

(ps: C++ is in a decent state as well)

[1] https://rawgit.com/krausest/js-framework-benchmark/master/we...


> C++ is in a decent state as well

I’d still choose Go or Rust over C++, though, because they’re memory-safe. This recent discussion shows that’s still important in the context of WebAssembly: https://news.ycombinator.com/item?id=24216764


Sure, for new projects I choose Rust over C++ whenever feasible.

But a huge amount of the worlds software is written in C++, and will remain so for decades.

Getting some of that code to run well in WASM can be a big win for certain domains.


Modern C++ has a lot of memory safe operations if you use them (smart pointers, atomics, actually using RAII properly and universally), although I'm not sure how much overhead they might add to a wasm blob. Rust seems almost perfect for it though, but no reason to discourage C++ people; it can be a relatively modern language if you let it.


As someone that loves still having C++ on the toolbox, that is unfortunately only true if you have 100% control over the source code and have all team on modern C++ mindset.


> Go has to embed it's runtime and GC into builds, so build size and performance will take a hit here

I expect that WASM doesn't have the concurrency model required by a modern concurrent garbage collector like Go's, but perhaps I'm wrong.


It's likely much slower, but it should be able to run just fine single threaded as well.


It is still much smaller than most SPA used to display static text.


> it is pretty magical seeing something written as a CLI tool running in a browser with minimal changes.

Years ago, I wanted to write some code that could run both as part of a command-line app and an in-browser tool. I asked what language I should use for this shared code and was told, basically, that there was no suitable language.

Now, thanks to WASM, there are lots!


This is indeed nice but in fact you could run C/C++/Rust before too, with asm.js. Not to mention the languages that target JS and can run on Node or the browser.


How come? I have done that eons ago with Java.


Perhaps if you like TypeScript and still want compile to WebAssembly you might be interested in AssemblyScript which stricter subset of TypeScript which compile to wasm


I poked a similar project a while back. If I can just as easily have understandable, easily debug-able JavaScript as output target, I believe that’s preferable. Particularly where extreme performance is not a concern.

Tools like GopherJS, Elm, Dart and such output JavaScript no human could reasonably interpret. WASM seems logical for them. TypeScript outputs very human readable JS (for the most part) and WASM seems to have little virtue.

As someone who learned JavaScript in the late 90s / early 2000s reading websites code, outputting readable JS feels like giving back.


> Most of my early excitement for WASM circled around “Hey, I can finally use a decent language on the web”

This has been true for years, various languages can compile down to JavaScript. Kotlin and Dart, for instance.

Also, doesn't WASM lack a garbage collector? I imagine porting a language to WASM is still quite a task, and presumably a GC written in WASM is going to be far inferior to one provided by the browser, as with JavaScript. (Corrections welcome if I'm wrong about that.) That's not to dismiss the potential for performance gains in situations well suited to WASM.

(I see the_duke already mentioned the GC situation.)


>> non statically validated, frustrating experience of trial-and-error that somewhat undermines the joy of a statically typed language.

That's because officially Go has not support for web APIs(as wasm has no access to web apis) so you have to callback through JavaScript. However once you build the right wrappers around JS APIs and the tools to deal with HTML the experience is pretty nice.


As another poster said-- C++, C or Rust may give you better luck when compiling to WASM. You could also try TinyGo. Vanilla Go's embedding of its entire runtime and GC is ... a lot.


does it matter much after it's cached locally though? I mean say for single page web apps. I could see that not being a big issue, but if you have to reload the page a lot it might, but loading 1 Mb into memory is nothing to most modern CPUs if it's pulled off the hard drive.


I started working on a game in Go (with Ebiten-- which is an awesome engine for native!) and hit perf issues in simple scenes that just worked flawlessly with similarly trivial code in C++ with SDL. It wasn't about the size as much as the performance overhead of having a GC and just generally maybe more lax runtime overhead philosophy than Rust / C/C++. I think deterministic memory management and memory layout control is the main perf benefit you get with WASM over JS for the type of stuff I was working on-- both things there's more natural methods for in Rust and C++. I also prefer being able to monomorphize generic code and have it inline etc. (esp. with entt) vs. the vtable stuff you tend to see with Go interfaces (this is totally up to coding style / practices and both are doable in both...).

You can also see this with Gio UI library in Go where they try to minimize GC overhead yet one keeps hitting GC pauses in WASM. The author talks about some of the issues with Go on WASM here: https://changelog.com/gotime/128

It did help that after diving back into the new stuff in 'modern C++' with auto, lambdas and move semantics I was able to write quite ergonomic code without any actual manual memory management stuff going on.


Fun fact: The new MS Flight Simulator 2020 uses WebAssembly to run the software for the aircrafts in game. You can check the release notes, where they do detail that webassembly is used for the SDK as well.

The intention is likely that if the onboard computer emulation crashes (which it sometimes does) you can simulate the actual onboard computer restarting without it taking down the game.


> The intention is likely that if the onboard computer emulation crashes (which it sometimes does) you can simulate the actual onboard computer restarting without it taking down the game.

That doesn't really make any sense to me as a motivation. The vast majority of ways they could have chosen to simulate the onboard computer would have that quality. I would guess they chose WASM because they expect a lot of third-party expansion and it provides a VM that can be targeted by almost any language.


Well, also yes, the side effect being that third party OBC's won't crash your game if they go down.

And I would point out I've gotten the OBC in the default plane models to crash a few times, they either get stuck in some undefined state or go black and recover after a few seconds. It's rather interesting to observe.


It might be more likely that WASM is used for its sandboxing qualities while providing "near-native" performance for all types of extensions (not just the aircraft computers), because new aircraft modules and extensions are often provided by third-party companies or the modding community. Traditionally, scripting languages have been used to integrate "untrusted code" (e.g. DCS World, another popular flight sim, uses Lua for its aircraft modules and also exposes a lot of other functionality through Lua scripts so that they are moddable).


Basically, yeah, it isolates the interactive parts of the plane (animations, likely, as well as OBC and friends).

The WASM Runtime used is on github, https://innative.dev/


Interesting. How does this differ from WASMtime, the focus on C compatibility?


WASMtime is JIT, Innative is AOT.

WASMtime means you can run webassembly data on the fly from memory.

Innative compiles everything into a proper linkable C object, so you can link it as if it was natively compiled. It embeds LLVM so it should get decent performance out of everything.


Fun fact: The Cloudflare Workers FaaS platform uses WebAssembly to run the software for your non-JS function in the cloud. You can check their blog, where they have a guide for Rust[1].

The intention is likely that they use V8 isolates instead of VMs which support WebAssembly.

[1] https://blog.cloudflare.com/introducing-wrangler-cli/

SCNR :D


What holds me back from using WebAssembly is that it doesn't seem to provide any performance benefit for small functions. Well, not yet, I hope, because I really like the idea of WASM.

For example, last week me and a few other users on Observable have been experimenting with various JS ports of one particular permuted congruential random number generator. Using assemblyscript I managed to write a decent-enough WASM version, and it's slower than implementing 64 bit arithmetic with four 32 bit numbers, which involves a lot more multiplications and additions:

https://observablehq.com/d/ce811886e1071d69

There are two possible explanations: either 64 bit integer arithmetic is still really slow in WASM, or the call overhead is really, really significant.

This fits my earlier experience with trying to implement fast log approximation functions for data-viz purposes (since the rounding errors would not be visible at sub-pixel levels). They weren't much faster than the proper Math.log function (whereas in pure C or C++ that difference would be much larger).

I suspect that if this call overhead were reduced, it would be much easier to gradually add more WASM to a Web App and reap the benefits of it.


64-bit integer arithmetic is fast in WASM (these emulators are full of bit-twiddling on 64-bit integers: https://floooh.github.io/tiny8bit/).

Calling overhead between WASM and JS is significant though, but it has already been optimized massively [1], I doubt that this can be improved much further). Calling a very small function across language borders is amost always a bad idea, not just in the specific JS=>WASM context.

[1] https://hacks.mozilla.org/2018/10/calls-between-javascript-a...

PS: also contrary to popular belief, JS and WASM have similar performance for simple number crunching tasks because in that situation JS is pretty fast, Javascript only becomes slow when objects and properties come into play.


Thanks for the insight and the links! Scrolling down the article I spotted this:

> There’s only one case where an optimized call from JavaScript » WebAssembly is not faster than JavaScript » JavaScript. That is when JavaScript has in-lined a function.

Combined with your remark that 64 bit arithmetic is fast, I'd say this pretty much confirms the suspicion that a lack of inlining is what makes this particular WASM function execute slower


Function calls are expensive inside hot loops, that's why we rely on compilers to do inlining and unrolling optimizations for us. I don't think that's possible in that example, you should write the loop in C and return the data you expect instead of writing the loop in JS and calling tiny functions that will never be inlined.

For what it's worth, (scalar) numerical operations in JS are actually quite close to C. It's not like python.


Ah, I'm such an idiot for not thinking of inlining!

The problem with writing the loop on the WASM side is that passing the data back and forth between JS and WASM involves typed arrays and array buffers. That has significant overhead of its own. Still, good suggestion, I should try that with a pixel buffer!

> For what it's worth, (scalar) numerical operations in JS are actually quite close to C. It's not like python.

Indeed, and I am generally very impressed and happy with the performance of JavaScript these days! In my experience it only takes a little bit of effort to hint at the compiler if we're working with doubles or 32 bit integers, and then it goes really fast. In this particular case however we needed 64 bit integer arithmetic, so that's a bit of an edge-case.


Calling a short function is generally a poor idea, to be honest. You want significant execution time on either side of the bridge, not constant boundary crossing.


The thing is that I had expectations of why the boundary crossing was so slow. I expected a no-heap WASM function that returns a simple value to be pretty fast, but as the other comment points out the "boundary" in this case might not be the WASM/JS boundary so much as the inability to inline WASM.


Not really. Unless it's called quite often. Premature optimization is not a good thing in general. Find your bottlenecks, code for correctness and ease of understanding first and use optimization if it's basically "free" and don't worry much about it otherwise.


Btw you could speedup your AssemblyScript code: https://bit.ly/2FW2iyN

``` const eps: f64 = 2.3283064365386963e-10; const m: u64 = 6364136223846793005; const inc: u64 = 1442695040888963407;

let state: u64 = 1;

export function random(): f64 { let oldstate = state * 6364136223846793005 + 1442695040888963407; state = oldstate; const xorshifted = u32(((oldstate >>> 18) ^ oldstate) >> 27); const out_int = rotr(xorshifted, u32(oldstate >> 59)); return eps * out_int; } ```

I recommend return f64 instead f32. This has slightly faster interop


Thanks for the tips!

(also, as you might have noticed, HN doesn't support MD proper. To get code blocks you have to indent it with four spaces)

    const eps: f64 = 2.3283064365386963e-10;
    const m: u64 = 6364136223846793005;
    const inc: u64 = 1442695040888963407;
    
    let state: u64 = 1;
    
    export function random(): f64 {
      let oldstate = state * 6364136223846793005 + 1442695040888963407;
      state = oldstate;
      const xorshifted = u32(((oldstate >>> 18) ^ oldstate) >> 27);
      const out_int = rotr(xorshifted, u32(oldstate >> 59));
      return eps * out_int;
    }


(Technical note: two spaces of indentation is sufficient. Note also how the common leading spaces are not removed.)


Ah, thank you!


> To get code blocks you have to indent it with four spaces

I'll keep that in mind in next time. Thanks for the tip!


Are you compiling C or Rust to WASM? In my experience their LLVM backend is the best developed.

WASM shines for numeric computation like encoding, decoding, encryption.

In the benchmarks I've seen, image encoding is 10-20X faster in WASM. Crypto is about the same speed as WebCrypto which isn't available in all browsers and runs native code.

There's an unmistakable performance advantage for compute heavy workloads.


I know in which niches WASM already shines, my point is that I have a different one and wish I could effectively use WASM there as well.


You could, but you would have to move most of the app into WASM.

I think it's inevitable for WASM to replace JS eventually. We're just in the early days.

WASM has 2-5X performance advantage for "most" workloads. It also parses much faster and is more compact than minified JS. Eventually the performance advantages will be too large to ignore


> I think it's inevitable for WASM to replace JS eventually.

Do you think front-end developers will start writing C/C++/Rust instead of JavaScript? Or do we need other languages that can compile to WASM before it can replace JS?


I think it will be like back end, a mix of Java/C#/C++.

JS isn't a great language, it's only popular because you can't use anything else on the web. The massive popularity of Typescript, even though it's only syntax sugar on top of JS, shows that the language leaves a lot to be desired.

It doesn't support threading, doesn't compile, has garbage standard library. And it's slower and more memory hungry than languages with these features like C# and Java.

There's talk of adding a "universal" GC to WASM. When this happens you'll see "traditional" enterprise languages invade enmasse


I'm not the parent poster, but I think it's quite possible we see JavaScript, TypeScript, and maybe even CoffeeScript and ActionScript frontends that target WASM backends. Right now it's possible to support subsets of the languages. As WASM is fleshed out and tools like Nectar/NectarJS and wasm-bindgen improve the workflow might change more than the source language.

Mostly unrelated to JavaScript, there's also an effort called JWebAssembly which strives to translate JVM bytecode to WASM's binary (.wasm) or textual (.wat) format. So WASM apps could be written fine in Scala, Clojure, Kotlin, JRuby, or any other JVM language.

I think where WASM definitely replaces JavaScript is distribution over the net to browsers for heavy web applications and in Node apps. It's just way more efficient. JS might still have a place for a little bit or bauble on some sites that are mostly static HTML, but things that use huge stacks of JS libraries will have those precompiled to WASM for distribution.


I'm just excited that I'll be able to write app extension APIs defined in C and host them in a wasm runtime that doesn't have to run in a different process to keep those extensions on their best behavior and not crash the app when they segfault.

Not 100% sure if that's possible with WASM runtimes now or if it's faster (IPC can be quite fast) but that's my hope.


I hope some of the extensions for WASM land like https://github.com/bytecodealliance/wasmtime/issues/677. I take a look for build an interpreter to run fully on it, but still is required to have a host that understand fully the types so is not much time saving.


WebAssembly in the browser didn't have its breakthrough so far and probably will never have. The problems WA tries to solve are no big problems, if problems at all:

- Typescript is an excellent language.

- Performance of Javascript is for 99% of all the websites more than good enough.

- New frameworks like Svelte are much more mobile friendly in terms of computational overhead or time to first render.

Also, work on WA has pretty much stalled.


Look at squoosh.app. They cross-compiled image compression libs to the browser, which means that they don't have to reimplement anything for the web, and now you can use it securely from any browser. AutoDesk was able to cross-compile their decades-old millions+ line C++ codebase to wasm, now they have a web product. Fastly and Cloudflare _only_ support wasm/javascript for their edge cloud compute solutions; but since they support wasm, you can write your functions in C++/Rust and run it with nearly the same performance characteristics. Lots of reasons to use webassembly, it's just getting started!


UI in the browser isn't the right frame to think about webassembly.

The right frame is wherever you can benefit from running a piece of potentially untrusted code within a sandbox. This is important for platform vendors who have to run cuatomer code that they can't trust. It is also important to run what would otherwise be unsafe code in the browser (ex: I use it for audio encoding/decoding and processing). It can serve as a "plugin" mechanism for host applications that need high performance for the plugins along with safety (ex: image proc, NN models). Environments where you need extreme granular control over what's permitted for the module (hence WASI and CloudABI).


Do not constrain yourself to today, but rather to what is to come. For instance, Iot apps running in Edge and cloud datacenters.


You can compile languages like C/C++ and Rust to it. Sometimes this gives a performance boost, but most of the time it gives you just more uniform performance behavior.

Then you can compile stuff like Java or C# to it, but end up with huge runtime bundles. But you get much of the C# behavior from TypeScript without the huge runtime, because it compiles directly to JavaScript.


It also allows you to run existing code in the browser, without having to do a rewrite.


This seems like the biggest advantage to me. Not everyone wants to rewrite and debug their several thousands of line rust/c++ program into typescript.


From where I am, there are lots of frameworks adopting WebAssembly and I am looking forward to get back my Flash like development experience.


Quite frankly, I need to see a reason why one would use WASM. Figma is a fully justified implementation.

If you don’t like JavaScript, like get over yourself already. Enough is enough.

It’s a a simple language, you will still have to deal with browser apis and UI development patterns no matter what language you pick, and no, type checking isn’t going to magically solve your shitty UI codebase.

WebAssembly is going to be the new Tensorflow on people’s LinkedIn unfortunately, a small word to signal ‘hey, I’m more than just a web developer’.

Unless you are compiling a video encoder or a video game, like please, just give a rest. Solve the fact that your simple app is more than 3-4 files and over abstracted first.


I've ported a number of apps to WASM that weren't video encoders or games. The Golang toolchain support has made the process relatively painless and it's getting better all the time.

I'm not going to rewrite my hobby projects in JavaScript.


The reason is the revenge of plugins.

I can have all my plugins back, and this time there is no turning back or someone claiming how insecure it is, despite lack of bounds checking inside linear memory segments.


It does look like using webasm for plugins does have some risks - precisely from the lack of memory accessing checking you mention.

Mind you - I'm hopeful these can be overcome with careful design of the surrounding application - I'm currently looking at using webasm as a plugin extension mechanism for an application.


And there is no need to program in JS even if you target it, as a compile target it has a lot of advantages over wasm for high level languages.


To me WASM is interesting because it provides a way to distribute _non-JavaScript_ applications that _already exists_ over an easy to access platform that is the web browser. It provides a much simpler target than transpiling to JS.

For example, I'd really like to see interactive Smalltalk environments like Pharo be available as WASM websites instead of a separate VM. They already implement their own rendering stack within a Window, so they could switch to WebGL/Canvas and not have to even interact with the DOM directly. (That may not be the best long-term solution, I don't know enough to decide, but it is definitely a good short term solution than not doing it at all).


No idea why you're getting downvoted, but it's the truth.

The current state of WebAssembly is an MVP as a compilation target for a C/C++. There's still a long road ahead [1] and various browsers are in various stages along that road.

And yup. It has nothing to do with UI. That one you'll have to solve that yourself, regardless. And it doesn't help that WASM is its own sandbox with a tiny little window into the rest of the browser.


People are shipping WebAssembly applications, though.


Yes, they do. People will ship applications in, well, anything :)

WASM is definitely suited for some applications (see Figma), but you have to have a use case for it, and prepared to work really hard to make it work for you (once again, see Figma). Just compiling something down to it and pretending that's it, well, isn't it. And the vast majority of examples and experiments with WASM are just that: "let's compile something to WASM, yay, awesome".


Can you cite some examples? I see a couple in this thread, but none of them target the browser environment.


Probably most cases you wouldn't even notice. One case is if you make a drawing on keep.google.com


Figma, squoosh.app (experimental proof-of-concept, but demonstrates a legitimate use-case), Autodesk. Fastly and Cloudflare has edge cloud-compute solutions that only support wasm/javascript.


Another interesting one is iWork on the web, which is Objective-C compiled to WebAssembly.


Is it? Wasn't it built with SproutCore [1]?

[1] https://en.wikipedia.org/wiki/SproutCore?wprov=sfti1


I'm sure it's some frankenstein abomination internally, but it is certainly using WebAssembly as of recently for parts of the application.


This is like suggesting PHP to C# or Java developers because "everyone uses it". Today the web (just like the server) supports any language - get over yourself, people have moved beyond JavaScript.


They haven’t. This is all deferred anger coming from non-ui developers. It kills them inside that ui development is a field within software development that has it’s own nuances. They blamed the closest thing they could and that was, literally, the simplest shittiest language on planet earth (it doesn’t deserve all the hate).

You will still have take time and think through your app flows, your async user interactivity, your transitions, state, templates, etc, no matter what language you pick.

Type checking, compiling, the greatest language, will not solve the hard problems for you.

Your all pissed UI development isn’t a ‘if I know backend, then I must surely know frontend, and if I don’t, then surely somethings wrong with frontend’.

Nope. Trust me, I know a thing or two about deferred anger, I’m a serial offender when it comes to that so I can spot it easy.


Apparently someone has mistaken UI development with HTML.

I am more than happy with Forms, Qt/Qml, WPF, Android, VCL, Win32, WinUI, WebGL, GL, DirectX, LibGNM, GX,Cocoa,....

Now HTML, with <li> customized via CSS to simulate a menubar, nuke it!


So the modern web is not first class ui development, and JavaScript is not a real language, and the browser is not a real platform.

Classism has no bounds.

It’s entirely possible you’ve written bad UIs with all of those technologies too you know. Afterall, no one has eeever seen a bad desktop application before.

All those compiled type safe languages with native rendering got you some amazing UIs like Microsoft Office suite for decades, right?. All those beautiful java apps for example, right? Everyone’s so full of shit. You guys couldn’t solve it with the exact technologies you are pushing for the web for literally decades.


Do you feel like you're worth less because you do frontend webdev? Because you're giving off that vibe hard. (immediately concluding GP is just shitting on webdev, saying "you guys", etc. - btw, do you not think that's classist as well?)


The commenter pretty clearly made a distinction between those technologies and HTML, I don’t know, inference is often taught early in life in reading comprehension classes.

I’m also happy you picked up on the vibes, wasn’t sure if I was being overt enough.


If you feel like that then it's on you though, and basically demotes your posts to merely some insecure person's rambling.


I also include Web as part of my UI development services and have been doing it since 1996, most likely longer than you.

Classisim are Web developers that don't know any better and can only think of Web as the only way to do UIs.

Thankfully most UI/UX experts know their business to think otherwise.


I am a TypeScript developer for the past 6 years. I am not at all angry and definitely not expecting issues I have with SPA development to be magically solved, I am simply excited about being able to use languages with type systems similar to TS but without the JS under it - not that I care much about it, but it would be nice to go a step further with the type system-language integration, which TypeScript is unwilling to do.

WebAssembly is a done deal, all browsers support it, now we're waiting for reference types and GC, once that lands (ref types are already available in some browsers), the web will not be tied to JS in any way. I think it's fair to say that the world has moved beyond JS.

Btw I think people doing UI apps in C# (Blazor is probably second largest Wasm community after Rust) are fully aware of the hurdles of UI development.




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

Search: