Introduction
One of the technologies we should keep eyes on in the web world is WebAssembly or wasm. As I am new to it, I won’t describe much of its fundamentals. Please visit the official web site to learn from scratch.
This short article describes my first trial on wasm. What I want to try is just to run example code, but I’d start with writing the example code in wasm(wat).
Hand crafted wasm code
Wasm is a binary format, but there’s a textual representation of it called wat. It’s written in S-exp. Here’s my first example.
(module
(func $fib (param $i i32) (result i32)
(if (result i32)
(i32.le_s (get_local $i) (i32.const 1))
(then (get_local $i))
(else
(i32.add
(call $fib (i32.sub (get_local $i) (i32.const 1)))
(call $fib (i32.sub (get_local $i) (i32.const 2)))))))
(export "fib" (func $fib)))
Can you guess what this code is? The almost-equivalent code in JavaScript is the following.
export const fib = (i) => {
if (i <= 1) {
return i;
} else {
return fib(i - 1) + fib(i - 2);
}
};
Or:
export const fib = i => (i <= 1 ? i : fib(i - 1) + fib(i - 2));
Compiling wat to wasm
There’s a tool called wat2wasm in wabt, which is something you need to build with gcc. Fortunately, I found wat2wasm in npm, which is probably compiled in wasm. Let’s name the example wat code slow_fib.wat
and do the following.
$ npx wat2wasm slow_fib.wat
That’s it. You get slow_fib.wasm
.
$ wc slow_fib.*
1 6 61 slow_fib.wasm
10 39 329 slow_fib.wat
11 45 390 total
How small it is.
Running wasm in Node
Let me show how to run the code in Node REPL.
$ node
> const fs = require('fs')
undefined
> const buf = fs.readFileSync('./slow_fib.wasm')
undefined
> let res
undefined
> (async () => { res = await WebAssembly.instantiate(buf); })()
Promise
> const { fib } = res.instance.exports
undefined
> fib(10)
55
> [0, 1, 2, 3, 4, 5, 6, 7, 8].map(fib)
[ 0, 1, 1, 2, 3, 5, 8, 13, 21 ]
Comparing the speed
Let’s see how long it takes. Simply measure time:
> console.time('wasm fib'); fib(40); console.timeEnd('wasm fib');
wasm fib: 716.270ms
undefined
Now, how about it in JS?
> const fib2 = i => i <= 1 ? i : fib2(i - 1) + fib2(i - 2);
undefined
> console.time('js fib'); fib2(40); console.timeEnd('js fib');
js fib: 1047.255ms
undefined
Not that big difference? Maybe, because it is a recursive function. I’m not so sure.
Summary
This article showed an example to write wasm code in wat, compile it to wasm binary, and run it in Node REPL. Intentionally, the wat code is written like LISP for fun, which might not be a usual compiler output. I’m still learning the wasm semantics, and try something else in the future.