Hacker News new | past | comments | ask | show | jobs | submit login
WebAssembly is more than the web (steveklabnik.com)
178 points by steveklabnik on July 13, 2018 | hide | past | favorite | 81 comments



I'm also excited about this aspect of WebAssembly, but interoperability will hard without a well-defined ABI. There are only four types in wasm: int32, int64, float32, and float64. Anything other than that needs to be encoded somehow, either as multiple values or in memory.

For example, say you want to pass a 16-byte struct as an argument: how do you do it? Do you store the struct in memory and pass an int32 pointer to its start address? Or an int64 pointer? Maybe you encode it as two int64s? Once you've made a choice, good luck calling functions generated by a compiler that made a different one!

Another problem (in the web context) is blocking--Emscripten runs into this one. You can't suspend WebAssembly execution in the middle of calling out to an imported function. If a WebAssembly program wants to read keypresses interactively, it can't use a blocking function like read(). You have to pass the keypresses in at the outer level--or reimplement the call stack yourself like Go did (https://docs.google.com/document/d/131vjr4DH6JFnb-blm_uRdaC0...).

In general, WebAssembly code today is very tied to the JavaScript code that manages its interaction with the outside world. You can't just write a WebAssembly program that uses OpenGL. You also need to write the JavaScript that turns those calls into WebGL calls. I don't think this is a fundamental problem--we just haven't seen standards develop yet. As platforms start to define their API directly in terms of WebAssembly, without a JavaScript layer, we may see more of a "standard ABI" develop. Until then, it's hard to know what kind of APIs to target or support as an application or compiler writer.


> For example, say you want to pass a 16-byte struct as an argument: how do you do it? Do you store the struct in memory and pass an int32 pointer to its start address? Or an int64 pointer? Maybe you encode it as two int64s? Once you've made a choice, good luck calling functions generated by a compiler that made a different one!

Sounds like every architecture ever? There’s an architecture specification and then there’s an ABI. It’s common to only support some small number of sizes, x86 is a bit the odd one out these days.


Sure, but there's no standard ABI for WebAssembly yet. I'm basically just saying we need an ABI before we can target it as a platform apart from the web.


It's not really a standalone platform, though. It's a sandbox utility to contain high-speed or highly customized computation, that can be plugged into any other platform. By design, it doesn't seem intended to be limited to a single higher-level (well, higher than assembly language) ABI.


LLVM IR is interesting in that it's the opposite: it supports from i1 to i(2^23-1)


I share that view. Currently each high level language compiles to its own thing creating huge islands of code. C#, Java, Go, Rust, C etc will all compile their frameworks into the wasm files with very simple facade interfaces. The overhead will be enormous. Also the core is abstraction like memory and thread management is far from here.

It will take a decade to figure that out. Till then, wasm is good for single apps but not an ecosystem


> Currently each high level language compiles to its own thing creating huge islands of code. C#, Java, Go, Rust, C etc will all compile their frameworks into the wasm files with very simple facade interfaces.

This is more or less what SmallTalk has been doing for ages. The keyword for that is "image", in particular "single image".

And yes, it poses interoperability problems: http://www.ianbicking.org/where-smalltalk-went-wrong.html


> There are only four types in wasm: int32, int64, float32, and float64.

You can't directly address and manipulate a single byte in WASM?


You can address bytes (using instructions like i32.load8_u), but doing arithmetic with them can be awkward.


GC with structs and fields, host types, threads, etc are in the pipeline to address your concerns. While their implementation is coming along really slowly, I suppose that's better than hasty.


Yup; each embedding environment will need to spec an ABI, just like any other platform that exists. This is a possible future, not one that is 100% for sure happening.

The blocking thing has some solutions in the browser, like web workers, but that'll be fixed in time too. Emscripten can already do the OpenGL -> WebGL thing, incidentally.


At the risk of sounding like an old grumpy guy[0], isn't this kind of what Java[0] set out to achieve some 20 years ago?

I am in no position to compare the two by any metric other than age. But I am old enough to appreciate how such ideas tend return every couple of decades.

Is this an IT thing, or is this phenomenon known in other areas, too?

[0] I am not even that old, and I do my best to not be grumpy.

[1] Yes, yes, Smalltalk, too. But Java - AFAIK - was the first language that aimed for platform-independent bytecode + network delivery of code (Applets, Webstart).


Java's intent was "write Java code once, run anywhere". The JVM exposes a strict Java-centric view of what it can run, and it can be difficult to run other language environments on top of the JVM reasonably.

Webasm is basically "write any code once, run anywhere". It models a very low-level CPU-like environment that the code fully controls at the byte level, so even runtime language VMs can run in such a model without harsh slowdowns. While this literally offers fewer features than the JVM, it doesn't restrict which features the final runtime can implement.

This of course is all based on the assumption that you have a CPU-level environment that already bootstraps what you need (like C-based garbage collectors running underneath your dynamic language); or that you're running low-level, assembly-style code for speed reasons.


Unfortunately, as a side effect of it supporting only one memory model, it means that the host code only has a giant memory bag to work with, meaning it needs to reconstruct objects from a giant heap. Currently, anything involving more complex host interaction (say, handles to kernel objects like files) is punted on to either the host-bindings proposal (which has a number of issues and basically was agreed was not a way forward) or the GC proposal (which has even more issues).

The current plan for the GC proposal is to allow skipping having memory heaps at all and talk about things in terms of an object graph and types. See comments here: https://github.com/WebAssembly/gc/issues/32

The important part of a runtime to me is the object model, and wasm basically punted on one for now.


Yes, that's what a CPU-level model with byte-level access means, and is how all code running on your computer has its basis. Whatever high level constructs you want are built on top of your asm-level runtime. This is as-designed, as is the only reasonable way to allow different language paradigms to run on top of it. It's also heavily implied by the name.

It does have a notion of function imports & exports, so in the same way it allows you to do what you want, without specifying what you're going to do. For untrusted code, like in a browser, the environment can choose only to extend limited access. For trusted code, the environment can give access to more OS-level functionality. By webasm not specifying what functions to bind, but giving the ability to bind functions, it remains flexible and appropriate for wildly varying deployments.


A generic object model will be a poor fit for many languages. I hope they keep it simple and low level and don't build in an object model. I'd rather have low level features that let you implement your own GC than a built-in object model. If you have zero cost exceptions and multiple function entry points you can implement your own GC, and coroutines and various other constructs without any more overhead than if you were compiling directly to machine code.

Basically, zero cost exceptions let you unwind and capture the stack while not slowing down the common case, and multiple entry points let you build the stack back up without compiling a large amount of duplicate code. This gives you arbitrary stack manipulation without explicit stack manipulation support in the VM. It enables features that usually require special VM features, such as GC, coroutines, lightweight threads, (delimited) continuations, effect handlers, C# style async.


Thanks for this comment! Very informative, and not something I would have found otherwise.


> Java's intent was "write Java code once, run anywhere". The JVM exposes a strict Java-centric view of what it can run, and it can be difficult to run other language environments on top of the JVM reasonably.

> Webasm is basically "write any code once, run anywhere". It models a very low-level CPU-like environment that the code fully controls at the byte level [...]

The only real difference between the JVM and WASM is that the JVM came from the academic world of "stack-based VMs are better" while WASM comes from the world of "register-based VMs are better".

Both have the same limitations that all the other VMs have. And languages are equally difficult to port to either of them.

What WASM has (and the JVM always lacked) is a compatible zero-friction VM installed on more or less every computer of this planet.

This is the _"everywhere"_ that Java and the JVM wanted but never had.


Wasm is stack based too. The way data flow is encoded is not so important. A compiler can easily generate code for either a stack machine or a register machine. The VM will decode that into SSA again, so it only matters insofar as the representation has to be compact and fast to decode.

The main difference is that the JVM has a garbage collected heap and high level instructions for OOP, whereas wasm is lower level than C and only slightly higher level than assembly. Instead of an object model with GC you have raw pointers.


Basically mainframes execution environments.


Another major difference to Java is that WASM doesn't come with any library whatsoever (as already implied by some sibling comments). The only interfaces to the outside world are function callbacks and shared memory. Callbacks have to be set up manually when loading the code into the VM.

While this makes integrating WASM code into applications a little awkward—you're basically down to peeking and poking addresses in memory, see [1] for some code I wrote that does exactly that—this has some key advantages.

First, the WASM designers cannot make any opinionated decisions regarding what a library should look like. Providing a library is entirely up to the particular toolchain you're using (e.g., Emscripten, which provides a partial libc). And it's up to you what toolchain you use (if any). Second, this facilitates perfect sandboxing. All you're doing is placing your input on a sliver of memory, running your program on the virtual WASM CPU, and reading back the result from memory (and/or responding to callbacks).

[1] https://github.com/astoeckel/linprog2d/blob/b557f69d00dcadfe...


You are correct, but this time, all industry leaders cooperate instead of competing: https://webassembly.org/ (see browser support). Also implementation in browsers is different (more "close to the metal" let's say) and the whole web platform has evolved a lot, and that changes a lot of things.


>(more "close to the metal" let's say)

I don't think that's really fair to say at all.


I would say that JVM bytecode is significantly higher level than WebAssembly, no?


Not really, lot of parallels actually. But it does have higher level constructs than WASM currently has, but they are quite specific to Java the language. So I wouldn't say higher level, just more and larger scope currently.


Yes, and the whole browser<->plugin and plugin<->applet communication layers are missing, WASM runtime is embedded directly in V8 so the WASM code literally is ran closer to the metal.


WASM runs directly on V8, which runs natively.

Bytecode runs directly on the JVM which runs natively.

I'm not seeing how eithers closer to the metal.


JVM yes, but when Java web apps were popular, there was a communication layer between the JVM application and the JVM support plugin, and then between the plugin and the browser.


I think the large difference here is that the VM for WASM is much thinner.

Java was not then and still isn't today the fastest of languages. I think WASM's model of being a lower level VM is going to produce fantastic results. Heck I "ported" Lua to WASM[1] and it was pretty snappy compared to what I was expecting.

[1] https://github.com/vvanders/wasm_lua


The major difference I'd say is that we're not trying to cram a desktop solution into the browser. Java is great but Java apps are not designed like websites, they are not designed to be downloaded one page at a time and they have totally different security models.

Other things like the browser being able to prewarm the vm and other first class citizen type support will hopefully lead to more success.


Java was a language. They created a platform-independent bytecode associated with that language, but it was closely associated with the language itself. Webassembly is much more of a portable version of ordinary binaries.


That's a completely fair question. I believe it's mainly just that Java bytecode is optimized to run on the JVM whereas WASM is optimized to run on CPUs. https://github.com/WebAssembly/design/issues/960


Java bytecode is tightly coupled with the JVM.. it doesn't make sense to consider one without the other. There's bytecodes such as invokevirtual or invokeinterface which don't make sense unless you have Java objects and classes.

The benefit of WASM is that it's more general purpose because it's lower level. It's agnostic to your memory model, so you can have a not-Java object model if you want.


No, Java wasn't the first.

Bytecode formats for application executables delivery was and still is a common practice in the mainframe world.

Xerox PARC workstation, UCSD Pascal systems, IBM and Unisys mainframes as examples.


Haha! WebAssembly is a really simple spec. I’d say optimized for speed and low level simplicity. You could put the entire byte code spec on a single piece of A4 and you wouldn’t have to squint.

It literally is a fancy turning machine bytecode that anyone can write an interpreter/transformer to actual cpu bytecode if needed.

As more languages support WASM, I bet we’ll see more of the front ends written in other languages.

WASM is to the web what JVMBC (java bytecode) is to apps. The open java spec flourished a big ecosystem like Adobe coldfusion/railo, jython for python, Nashhorn/rhino for JS, Jruby for Ruby, Jphp for php, cscjvm for C# and a host of others.

In my university, my compilers course assignment was to write a subset of C compiler that would output java bytecode. It really made me love compilers and programming languages.

I predict a bright future for WASM


There are other bytecode formats even simpler, UCSD Pascal P-Code for example.

The only big difference is that, like many other "modern" ideas, mainstream computers are catching up with mainframes.


"But Java - AFAIK - was the first language that aimed for platform-independent bytecode + network delivery of code (Applets, Webstart)."

Oberon had that too to some extent -- at a time when the Internet was still in its infancy. Oberon wasn't a commercially viable platform though.


Not only was the JVM java centric, the VMs were not high quality across all platforms, and OSs did not come with them.

Also the canvas, webgl, webrtc, audio and video IO APIs that can hook into it are much better than anything Java had to offer.


> Is this an IT thing, or is this phenomenon known in other areas, too?

This phenomenon is certainly known in fashion and, looking at the boom of populist and nationalist politicians around the world, most probably in politics too. I would even risk saying it's imprinted in human nature.


I would say a big difference here is that WASM is designed from the ground up for sandboxing, rather than it being added in after the fact like applets.


While Java has its failings, sandboxing was not an afterthought. Java was designed from day one to be secure-able - with pointer safety, array index safety, the SecurityManager object, classloader security rules, signed applets, etc. (Whether Java actually achieved the security it claimed is a different discussion.)


That's the thing though. Java's "safety" began and ended as a bullet point in a powerpoint presentation. Not for not trying, but because the API surface is so big and the VM so abstract that they made it impossible for themselves to actually deliver sandbox-level security. To compare, js is tiny, has had thousands of man-years poured into it's security, and its customers prioritize security above everything else, and there are still exploits found in every engine every year. Java's security surface area is enormous in comparison, it has a fraction of the man hours dedicated to sandboxing it, and many of it's customers don't even care if the sandboxing is airtight. You can read through the full webassembly spec in a couple hours.


This is technically web, but not in the way you might think. We (Cloudflare) are working on providing WASM support in our Workers [1] product that lets you run code in our 155 data centers around the world.

WASM is great because we can run it in V8 isolates which are much lighter weight than containers or full VMs.

[1] https://cloudflareworkers.com


What are the security implications here?

i.e. the Chrome team, post-Spectre, are assuming that any value in a process' memory is readable by any code executing within that process.


V8's developer guide[1] recommends running un-trusted code in separate processes. So each worker job would need to fork.

[1] https://github.com/v8/v8/wiki/Untrusted-code-mitigations#san...


That’s awesome! Much closer to what I think of as "serverless".


Thanks for sharing, this looks very exciting! Bring on more docs and examples!!


I think it would be neat if WebAssembly were embedded in other languages, like it is in JavaScript now. For example, suppose Python had a WebAssembly engine. I bet that could replace a lot of C extensions, and it would be as portable/universal as pure Python code, so no need for C compilers, OS-specific binary packages, etc.


The Wasm tool chain is built around llvm. So, you can take some complex C code and target wasm instead of X86, ARM, or whatever. That works right now and you can already do things silly like compile emulators and vms to run in a web browser and get them to boot e.g. windows 95 or linux.

So, you could try to recompile the entire python ecosystem (interpreter, libraries, extensions, etc.) to wasm. This might be tricky right now because not all stuff readily compiles to wasm yet probably or uses gcc instead of llvm. Als, python has its own vm and compiler that would need to be reengineered on top of wasm.

Of course people have been working on moving python to llvm for some time so this might actually become feasible.


I totally agree with what you said. Currently I'm loading compiled C code with the Python foreign function interface (often not (solely) for performance reasons, but because it just so happens that I already have a C library that solves a particular problem). It would be great if I wouldn't have to worry about providing a shared library for all possible target platforms.

In fact, when I searched for a Python WebAssembly interpreter a few weeks ago I stumbled across this project [1]. So rest assured, people are working on this already.

[1] https://github.com/kanaka/warpy


You'll never be as fast as compiled, unless you're thinking this engine would JIT at which point your "no need for C compilers" became "carry around a WASM compiler". The more rational approach might be to transpile into the target lang, but there are two primary reasons for C extensions, performance and/or FFI, and neither is accomplished via the transpile route. If it's the third, less oft-cited reason for C extensions, avoiding rewrite and/or reuse existing C code, then yes it has value.


In such a world we'd still need compilers to compile code _to_ WebAssembly -- it isn't really a great source language to work in.


I think that will happen actually. It would open some some interesting possibilities.


It would certainly make "application level code" more portable (which is still a big win) but some sort of OS specific library still needs to talk to the filesystem, network, etc.


While this would supplant "C for speed" libraries, it wouldn't supplant "C for OS access" libraries. At some point, system code needs to call the OS.


Not all OS were or are written in C.


Sure, replace "C" with whatever OS language is appropriate. The situation is still the same: wasm doesn't talk directly to the host interfaces, regardless of it being low-level. You'd still need another FFI layer somewhere to talk to those.


WebAssembly isn't just a cross platform technology, it's also a way to target platforms that are currently walled gardens. Being able to run software on e.g., the iPad without paying Apple's 30% fee and being able to ship features / bug fixes without added delays is a big deal.

"Write-once, run everywhere without paying someone for the privilege"

I think the term's overused, but this could actually be a "game changer".


> WebAssembly isn't just a cross platform technology, it's also a way to target platforms that are currently walled gardens. Being able to run software on e.g., the iPad without paying Apple's 30% fee and being able to ship features / bug fixes without added delays is a big deal.

Apple still control what API you can use in their mobile browser, and it's not like they are at the forefront when it comes to implementing Web API today, especially when they also forbid any alternative browser on IOS. Ultimately WebAssembly is limited by the API it has access to in the context of the browser (and the resources that are granted by the browser). All the promises of the "web as an app platform" have not been fulfilled yet.

IMHO WebAssembly will play a bigger role on the server, with nodejs for instance as it will allow portable native code distribution.


Sure, that's certainly a factor. But there's a large class of applications that can get by with the API that is available.

I'm not suggesting it's a way to build mobile applications without paying Apple. I'm suggesting it will be possible to deliver a product to (nearly) all platforms using 1) a single codebase and 2) bypassing walled gardens.

Personally I won't build products for an App Store anymore. They're too restrictive, expensive and time consuming to be worth it. But being able to, say, deliver a desktop product (either via the web or packed in something like Electron) and also deliver nearly the same experience to an iPad Pro would be huge.


If you can assure the same security quality, then by all means.

Meanwhile I will keep my sandboxes.


If were to Apple or Google block wasm on iOS/Android then it's momentum would slow down significantly.


This is the first time someone explained the value proposition of WebAssembly and I understood it. Now I'm actually intrigued.


Thanks!


As one of the co-founders of WASM, I can say, yes, the layering was a deliberate design decision for exactly the reasons laid out in the article.

Glad this is penetrating the wider consciousness.

Thanks for writing this, Steve!


<3


Obligatory mention of this tongue in the cheek and visionary look presentation "The Birth & Death of JavaScript": https://www.destroyallsoftware.com/talks/the-birth-and-death...

This is basically what is happening with wasm and it's happening much faster Gary Bernhardt was anticipating in that presentation.

IMHO wasm finally displaces javascript as the only practical language to run stuff in a browser. Frontend stuff happens in javascript primarily because browsers cannot run anything else now that plugins have been killed off. Wasm changes that.

At the same time a lot of desktop apps are in fact javascript apps running on top of electron/chrome. Anything like that can also run wasm.

Finally people have been porting vms to javascript for pretty much as long as wasm and its predecessors have been around. So booting a vm that runs windows 95 or linux in a browser is not very practical but was shown to work years ago. This is what probably inspired the presentation above.

I've actually been pondering doing some frontend stuff in kotlin in or rust. I'm a backend guy and I just never had any patience for javascript. I find working in it to be an exercise in frustration. But I actually did some UI work in Java back in the day (swing and applets). Also there's a huge potential for stuff like webgl, webvr, etc. to be driven using languages more suitable to building performance critical software if they can target wasm. I think wasm is going to be a key enabler for that.

Most of the stuff relevant for this has been rolling out in the last few years. E.g. webgl is now supported in most browsers. Webvr is getting there as well. Wasm support has been rolled out as well. Unity has been targeting html5 + wasm for a while now. And one of the first things that Mozilla demoed years ago with wasm was Unreal running in a browser.

I would not be surprised to see some of this stuff showing up in OSes like fuchsia (if and when that ships) or chrome os.


I was lucky enough to see that talk live, and it's stuck with me. It's certainly where a big part of my enthusiasm for WebAssembly comes from.


If you have to compile a high level language L to wasm you could follow many paths, for example L to C, C to wasm, or L to Lisp, Lisp to wasm. I should suggest to create a graph in which vertices are languages and edges are labeled with the efficiency of translation. This way one could use graph theory to select the best path. This is only the ground idea. At a second step one could study what features of languages are main ingredients of that efficiency and design a middleware high level language M for the translation to wasm: L -> M -> wasm. Or weight the translation using a vector of features (concurrency, speed, bugs reports, etc) and select the path that gives the best result. L -> M1 -> M2 -> ... wasm,where Mi are selected by the specific features used for a concrete program in L. All computations of efficiency should discount or take into account the use of specific libraries. For instance, numpy with python makes the speed difference between c and python smaller.


One thing I am concerned about is startup performance. On my (slow) Chromebook, some of my multi-megabyte WASM files take 10 seconds to compile. Seems we need either faster CPU cores, faster code generation, or some kind of staged JIT compile -- though I guess the latter could be accomplished today with sufficiently clever tooling.


Chrome Canary contains Liftoff, which is about 8x faster on my machine for initial compilation of wasm. I'm assuming it'll be in Chrome 69.


Indeed. X64 and ia32 support vor Liftoff will be in 69.


Wasm is set up to be able to do streaming compilation, I wonder if that helps at all? It's in a least Chrome and Firefox...


Think about if the same codebase were in JS, it would definitely be slower. But like others are saying, streaming is one of the main features of wasm.


>So, WebAssembly itself, the core, is completely independent of the web. The web is a particular environment that Wasm is embedded in, but not actually required to use wasm. Other embedding APIs can be created, with their own interfaces, to do various things.

Does that mean one could e.g. create Node.js modules in Wasm, like we also have the C++ modules? That could give a nice boost to certain kinds of code like text processing and math (e.g. a big integer or matrix algebra library).

(Perhaps this is already a thing).


It's already a thing. In node you by default have a WebAssembly object giving you access to the necessary features.

Here's an example (untested, just found it via Google) https://gist.github.com/kanaka/3c9caf38bc4da2ecec38f41ba24b7...


I would like WebAssembly runtimes for embedded devices, both Linux and bare-metal for microcontrollers. The WS program would be called with current inputs (as data), and produce a datastructure describing output to set. This logic can then easily and safely be tested before deploy and updated over the air.


The EOS blockchain uses webassembly and it's pretty cool. I've been working on a side project and its interesting to be writing a "smart contract" in C++ that compiles down to wasm.

https://developers.eos.io/


> Eventually I’ll write a post about the wrong, but for now, the right: in some sense, WebAssembly is a different way of accomplishing what the JVM set out to do

I agree, it's exciting, but this is half (or less than) an article. These hyped-up technologies make me grumpy neck beard.


It is a bit light; I haven't blogged in a while because I'm trying to get back into it, and writing long posts is hard. Some people prefer shorter posts; it just depends.


I, for one, want to rush out and grab a trademark on Node.wasm.




Consider applying for YC's Summer 2025 batch! Applications are open till May 13

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

Search: