| by Arround The Web | No comments

Andy Wingo: missing the point of webassembly

I find most descriptions of WebAssembly to be uninspiring: if you
start with a phrase like “assembly-like language” or a “virtual machine”,
we have already lost the plot. That’s not to say that these
descriptions are incorrect, but it’s like explaining what a dog is by
starting with its circulatory system. You’re not wrong, but you should probably lead with the bark.

I have a different preferred starting point which is less descriptive
but more operational: WebAssembly is a new fundamental abstraction
boundary
. WebAssembly is a new way of dividing computing systems into
pieces and of composing systems from parts.

This all may sound high-falutin´, but it’s for real: this is the
actually interesting thing about Wasm.

fundamental & abstract

It’s probably easiest to explain what I mean by example. Consider the
Linux ABI: Linux doesn’t care what code it’s running; Linux just handles
system calls and schedules process time. Programs that run against the
x86-64 Linux ABI don’t care whether they are in a container or a virtual
machine or “bare metal” or whether the processor is AMD or Intel or even a Mac M3 with Docker and Rosetta 2. The Linux ABI interface is fundamental in the sense that
either side can implement any logic, subject to the restrictions of the
interface, and abstract in the sense that the universe of possible
behaviors has been simplified to a limited language, in this case that
of system calls.

Or take HTTP: when you visit wingolog.org,
you don’t have to know (but surely would be delighted to learn) that
it’s Scheme code that handles the request. I don’t have to care if the
other side of the line is curl or Firefox or Wolvic. HTTP is such a
successful fundamental abstraction boundary that at this point it is the
default for network endpoints; whether you are a database or a golang
microservice, if you don’t know that you need a custom protocol, you use
HTTP.

Or, to rotate our metaphorical compound microscope to high-power
magnification, consider the SYS-V amd64 C ABI: almost every programming
language supports some form of extern C {} to access external
libraries, and the best language implementations can produce artifacts
that implement the C ABI as well. The standard C ABI splits programs
into parts, and allows works from separate teams to be composed into a
whole. Indeed, one litmus test of a fundamental abstraction boundary
is, could I reasonably define an interface and have an implementation of
it be in Scheme or OCaml or what-not: if the answer is yes, we are in
business.

It is in this sense that WebAssembly is a new fundamental abstraction
boundary.

WebAssembly shares many of the concrete characteristics of other
abstractions. Like the Linux syscall interface, WebAssembly defines an
interface language in which programs rely on host capabilities to access
system features. Like the C ABI, calling into WebAssembly code has a
predictable low cost. Like HTTP, you can arrange for WebAssembly code
to have no shared state with its host, by construction.

But WebAssembly is a new point in this space. Unlike the Linux ABI,
there is no fixed set of syscalls: WebAssembly imports are named, typed,
and without pre-defined meaning, more like the C ABI. Unlike the C ABI,
WebAssembly modules have only the shared state that they are given;
neither side has a license to access all of the memory in the “process”.
And unlike HTTP, WebAssembly modules are “in the room” with their hosts:
close enough that hosts can allow themselves the luxury of synchronous
function calls, and to allow WebAssembly modules to synchronously call
back into their hosts.

applied teleology

At this point, you are probably nodding along, but also asking yourself,
what is it for? If you arrive at this question from the “WebAssembly
is a virtual machine” perspective, I don’t think you’re well-equipped to
answer. But starting as we did by the interface, I think we are better
positioned to appreciate how WebAssembly fits into the computing
landscape: the narrative is generative, in that you can explore
potential niches by identifying existing abstraction boundaries.

Again, let’s take a few examples. Say you ship some “smart cities” IoT
device, consisting of a microcontroller that runs some non-Linux
operating system. The system doesn’t have an MMU, so you don’t have
hardware memory protections, but you would like to be able to enforce
some invariants on the software that this device runs; and you would
also like to be able to update that software over the air. WebAssembly
is getting used in these environments; I wish I had a list of
deployments at hand, but perhaps we can at least take this article last
year from a WebAssembly IoT
vendor

as proof of commercial interest.

Or, say you run a function-as-a-service cloud, meaning that you run
customer code in response to individual API requests. You need to limit
the allowable set of behaviors from the guest code, so you choose some
abstraction boundary. You could use virtual machines, but that would be
quite expensive in terms of memory. You could use containers, but you
would like more control over the guest code. You could have these
functions written in JavaScript, but that means that your abstraction is
no longer fundamental; you limit your applicability. WebAssembly fills
an interesting niche here, and there are a number of products in this
space, for example Fastly
Compute
or Fermyon
Spin
.

Or to go smaller, consider extensible software, like the GIMP image
editor
or VS Code: in
the past you would use loadable plug-in modules via the C ABI, which can
be quite gnarly, or you lean into a particular scripting language, which
can be slow, inexpressive, and limit the set of developers that can
write extensions. It’s not a silver bullet, but WebAssembly can have a
role here. For example, the
Harfbuzz text shaping library
supports fonts with an embedded (em-behdad?) WebAssembly
extension

to control how strings of characters are mapped to positioned glyphs.

aside: what boundaries do

They say that good fences make good neighbors, and though I am not quite
sure it is true—since my neighbor put up a fence a few months ago, our
kids don’t play together any more—boundaries certainly facilitate
separation of functionality. Conway’s
law

is sometimes applied as a descriptive observation—ha-ha, isn’t that
funny, they just shipped their org chart—but this again misses the
point, in that boundaries facilitate separation, but also composition:
if I know that I can fearlessly allow a font to run code because I have
an appropriate abstraction boundary between host application and
extension, I have gained in power. I no longer need to be responsible
for every part of the product, and my software can scale up to solve
harder problems by composing work from multiple teams.

There is little point in using WebAssembly if you control both sides of
a boundary, just as (unless you have chickens) there is little point in
putting up a fence that runs through the middle of your garden. But
where you want to compose work from separate teams, the boundaries
imposed by WebAssembly can be a useful tool.

narrative generation

WebAssembly is enjoying a tail-wind of hype, so I think it’s fair to say
that wherever you find a fundamental abstraction boundary, someone is
going to try to implement it with WebAssembly.

Again, some examples: back in 2022 I speculated that someone would
“compile” Docker containers to WebAssembly
modules
,
and now that is a thing.

I think at some point someone will attempt to replace
eBPF with Wasm in the Linux kernel; eBPF is just not
as good a language as Wasm, and the toolchains that produce it are
worse. eBPF has clunky calling-conventions about what registers are
saved and spilled at call sites, a decision that can be made more
efficiently for the program and architecture at hand when
register-allocating WebAssembly locals. (Sometimes people lean on the
provably-terminating aspect of eBPF as its virtue, but that could apply
just as well to Wasm if you prohibit the loop
opcode

(and the tail-call instructions) at verification-time.) And why don’t
people write whole device drivers in eBPF? Or rather, targetting eBPF
from C or what-have-you. It’s because eBPF is just not good enough.
WebAssembly is, though! Anyway I think Linux people are too
chauvinistic to pick this idea up but I bet Microsoft could do it.

I was thinking today, you know, it actually makes sense to run a
WebAssembly operating system, one which runs WebAssembly binaries. If
the operating system includes the Wasm run-time, it can interpose itself
at syscall boundaries, sometimes allowing it to avoid context switches.
You could start with something like the Linux ABI, perhaps via
WALI, but for a subset of guest
processes that conform to particular conventions, you could build
purpose-built composition that can allocate multiple WebAssembly modules
to a single process, eliding inter-process context switches and data copies for
streaming computations. Or, focussing on more restricted use-cases, you
could make a microkernel; googling around I found this article from a
couple days ago where someone is giving this a
go
.

wwwhat about the wwweb

But let’s go back to the web, where you are reading this. In one sense, WebAssembly is a massive
web success, being deployed to literally billions of user agents. In
another, it is marginal: people do not write web front-ends in
WebAssembly. Partly this is because the kind of abstraction supported
by linear-memory WebAssembly 1.0 isn’t a good match for the
garbage-collected DOM API exposed by web browsers. As a corrolary,
languages that are most happy targetting this linear-memory model (C,
Rust, and so on) aren’t good for writing DOM applications either.
WebAssembly is used in auxiliary modules where you want to run legacy
C++ code on user devices, or to speed up a hot leaf function, but isn’t
a huge success.

This will change with the recent addition of managed data types to
WebAssembly, but not necessarily in the way that you might think. Like,
now that it will be cheaper and more natural to pass data back and forth
with JavaScript, are we likely to see Wasm/GC progressively occupying
more space in web applications? For me, I doubt that progressive is
the word. In the same way that you wouldn’t run a fence through the
middle of your front lawn, you wouldn’t want to divide your front-end
team into JavaScript and WebAssembly sub-teams. Instead I think that we
will see more phase transitions, in which whole web applications switch
from JavaScript to Wasm/GC, compiled from Dart or Elm or what have you. The natural fundamental abstraction boundary
in a web browser is between the user agent and the site’s code, not
within the site’s code itself.

conclusion

So, friends, if you are talking to a compiler engineer, by all means:
keep describing WebAssembly as an virtual machine. It will keep them
interested. But for everyone else, the value of WebAssembly is what it
does, which is to be a different way of breaking a system into pieces.
Armed with this observation, we can look at current WebAssembly uses to
understand the nature of those boundaries, and to look at new boundaries
to see if WebAssembly can have a niche there. Happy hacking, and may
your components always compose!

Share Button

Source: Planet GNU

Leave a Reply